Spring Cloud Alibaba - 06 RestTemplate 实现自定义负载均衡算法

2022-06-13 09:06:49

负载均衡分类

  • 服务端负载均衡 ,比如我们常见的ng
  • 客户端负载均衡 ,比如微服务体系中的ribbon

spring cloud ribbon是 基于NetFilix ribbon 实现的一套客户端的负载均衡工具,Ribbon客户端组件提供一系列的完善的配置,如超时,重试等。

通过Load Balancer(LB)获取到服务提供的所有机器实例,Ribbon会自动基于某种规则(轮询,随机)去调用这些服务。

Ribbon也支持自定义负载均衡算法



分析

我们前面的工程都是通过DiscoveryClient组件来去Nacos服务端拉取指定名称的微服务列表,然后通过RestTemplate执行远程调用

在这里插入图片描述

如果服务存在多个的话,加上我们使用的地址都是使用注册中心的地址http://artisan-product-center/selectProductInfoById/ , RestTemplate 就处理不了这种问题了。所以我们才有第二步。

那如何让RestTemplate 自身也具备这种功能呢?

思路: 分析RestTemplate的源码,不管是post,get请求最终是会调用doExecute()方法,所以写一个CustomRestTemplate类继承RestTemplate,重写doExucute()方法即可



工程

artisan-cloud-custom-lb-order

artisan-cloud-custom-lb-product

packagecom.artisan.config;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.cloud.client.ServiceInstance;importorg.springframework.cloud.client.discovery.DiscoveryClient;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.HttpMethod;importorg.springframework.http.client.ClientHttpRequest;importorg.springframework.http.client.ClientHttpResponse;importorg.springframework.util.Assert;importorg.springframework.web.client.*;importjava.io.IOException;importjava.net.URI;importjava.net.URISyntaxException;importjava.util.List;importjava.util.Random;/**
 * @author 小工匠
 * @version 1.0
 * @description: 根据RestTemplate特性自己改造
 * @date 2022/2/2 13:32
 * @mark: show me the code , change the world
 */@Slf4jpublicclassCustomRestTemplateextendsRestTemplate{@AutowiredprivateDiscoveryClient discoveryClient;publicCustomRestTemplate(DiscoveryClient discoveryClient){this.discoveryClient= discoveryClient;}@Overrideprotected<T>TdoExecute(URI url,HttpMethod method,RequestCallback requestCallback,ResponseExtractor<T> responseExtractor)throwsRestClientException{Assert.notNull(url,"URI is required");Assert.notNull(method,"HttpMethod is required");ClientHttpResponse response=null;try{/**
             * 在这里拦截一下子 偷梁换柱
             *///把服务名 替换成我们的IP
            url=replaceUrl(url);
            log.info("替换后的请求路径:{}", url);ClientHttpRequest request=createRequest(url, method);if(requestCallback!=null){
                requestCallback.doWithRequest(request);}
            response= request.execute();handleResponse(url, method, response);return(responseExtractor!=null? responseExtractor.extractData(response):null);}catch(IOException ex){String resource= url.toString();String query= url.getRawQuery();
            resource=(query!=null? resource.substring(0, resource.indexOf('?')): resource);thrownewResourceAccessException("I/O error on "+ method.name()+" request for \""+ resource+"\": "+ ex.getMessage(), ex);}finally{if(response!=null){
                response.close();}}}/**
     * 方法实现说明:把微服务名称  去注册中心拉取对应IP进行调用
     * http://artisan-product-center/selectProductInfoById/1
     *
     * @param url:请求的url
     * @return:
     * @exception:
     */privateURIreplaceUrl(URI url){
        log.info("原始请求路径为:{}", url);//1:从URI中解析调用的调用的serviceName=artisan-product-centerString serviceName= url.getHost();
        log.info("调用微服务的名称:{}", serviceName);//2:解析我们的请求路径 reqPath= /selectProductInfoById/1String reqPath= url.getPath();
        log.info("请求path:{}", reqPath);//通过微服务的名称去nacos服务端获取 对应的实例列表List<ServiceInstance> serviceInstanceList= discoveryClient.getInstances(serviceName);if(serviceInstanceList.isEmpty()){thrownewRuntimeException("没有可用的微服务实例列表:"+ serviceName);}String serviceIp=chooseTargetIp(serviceInstanceList);String source= serviceIp+ reqPath;try{returnnewURI(source);}catch(URISyntaxException e){
            log.error("根据source:{}构建URI异常", source);}return url;}/**
     * 方法实现说明:从服务列表中 随机选举一个ip
     *
     * @param serviceInstanceList 服务列表
     * @return: 调用的ip
     * @exception:
     */privateStringchooseTargetIp(List<ServiceInstance> serviceInstanceList){//采取随机的获取一个Random random=newRandom();Integer randomIndex= random.nextInt(serviceInstanceList.size());String serviceIp= serviceInstanceList.get(randomIndex).getUri().toString();
        log.info("随机选举的服务IP:{}", serviceIp);return serviceIp;}}

调用

【配置方式一】

在这里插入图片描述

【调用】
在这里插入图片描述

测试

访问 : http://localhost:8080/v2/selectOrderInfoById/1
在这里插入图片描述


源码

https://github.com/yangshangwei/SpringCloudAlibabMaster


  • 作者:小小工匠
  • 原文链接:https://artisan.blog.csdn.net/article/details/122768084
    更新时间:2022-06-13 09:06:49