微服务客户端负载均衡组件Ribbon

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
简介: 微服务客户端负载均衡组件Ribbon

负载均衡分类

服务端负载均衡

常见的服务端负载均衡有Nginx,客户端将请求发送给Nginx,Nginx根据负载均衡算法选择一个服务器调用,具体调用哪个服务器由Nginx说了算,客户端是不知道服务器的真实IP的。image.png

客户端负载均衡

Spring Cloud Ribbon是基于NetFilix Ribbon实现的一套客户端负载均衡,Ribbon客户端组件提供了一系列的完善的配置,例如超时,重试等。客户端从注册中心获取到服务器的列表,由客户端自己根据负载均衡算法选择将流量分发给哪个服务器,客户端是知道服务器的真实IP的。image.png

重写RestTemplate的doExecute()方法实现负载均衡

通过阅读RestTemplate源码得知,不管是POST,GET请求,最终都会调用doExecute()方法,因此我们可以通过继承RestTemplate类并重写doExecute()方法来实现负载均衡算法。

package com.tuling.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.client.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Random;
/**
 * 根据RestTemplate特性自己改造
 */
@Slf4j
public class TulingRestTemplate extends RestTemplate {
    private DiscoveryClient discoveryClient;
    public TulingRestTemplate (DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }
    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
                              @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
        Assert.notNull(url, "URI is required");
        Assert.notNull(method, "HttpMethod is required");
        ClientHttpResponse response = null;
        try {
            log.info("请求的url路径为:{}",url);
            //把服务名 替换成我们的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);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }
    /**
     * 方法实现说明:把微服务名称去注册中心拉取对应IP进行替换
     * http://product-center/selectProductInfoById/1
     * @param url:请求的url
     * @return: 替换后的url
     * @exception:
     * @date:2020/2/6 13:11
     */
    private URI replaceUrl(URI url){
        //1:从URI中解析调用的调用的serviceName=product-center
        String serviceName = url.getHost();
        log.info("调用微服务的名称:{}",serviceName);
        //2:解析我们的请求路径 reqPath= /selectProductInfoById/1
        String reqPath = url.getPath();
        log.info("请求path:{}",reqPath);
        //通过微服务的名称去nacos服务端获取 对应的实例列表
        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
        if(serviceInstanceList.isEmpty()) {
            throw new RuntimeException("没有可用的微服务实例列表:"+serviceName);
        }
        String serviceIp = chooseTargetIp(serviceInstanceList);
        String source = serviceIp+reqPath;
        try {
            return new URI(source);
        } catch (URISyntaxException e) {
            log.error("根据source:{}构建URI异常",source);
        }
        return url;
    }
    /**
     * 方法实现说明:从服务列表中 随机选举一个ip
     * @param serviceInstanceList 服务列表
     * @return: 调用的ip
     * @exception:
     * @date:2020/2/6 13:15
     */
    private String chooseTargetIp(List<ServiceInstance> serviceInstanceList) {
        //采取随机的获取一个
        Random random = new Random();
        Integer randomIndex = random.nextInt(serviceInstanceList.size());
        String serviceIp = serviceInstanceList.get(randomIndex).getUri().toString();
        log.info("随机选举的服务IP:{}",serviceIp);
        return serviceIp;
    }
}

order服务的Controller:

package com.tuling.controller;
import com.tuling.entity.OrderInfo;
import com.tuling.entity.ProductInfo;
import com.tuling.mapper.OrderInfoMapper;
import com.tuling.vo.OrderVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
 * 自定义负载均衡的服务消费者
 */
@RestController
public class OrderInfoController {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @RequestMapping("/selectOrderInfoById/{orderNo}")
    public Object selectOrderInfoById(@PathVariable("orderNo") String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.selectOrderInfoById(orderNo);
        if(null == orderInfo) {
            return "根据orderNo:"+orderNo+"查询没有该订单";
        }
        ResponseEntity<ProductInfo> responseEntity = restTemplate.getForEntity("http://product-center/selectProductInfoById/"+orderInfo.getProductNo(), ProductInfo.class);
        ProductInfo productInfo = responseEntity.getBody();
        if(productInfo == null) {
            return "没有对应的商品";
        }
        OrderVo orderVo = new OrderVo();
        orderVo.setOrderNo(orderInfo.getOrderNo());
        orderVo.setUserName(orderInfo.getUserName());
        orderVo.setProductName(productInfo.getProductName());
        orderVo.setProductNum(orderInfo.getProductCount());
        return orderVo;
    }
}

在后端并行启动两个product实例:我们用 8081启动一个服务后,然后修改端口为8082,如下图所示勾选Allow parallel run,就可以同一个工程启动两个实例。

spring:
  datasource:
      druid:
        username: root
        password: 123456
        jdbcUrl: jdbc:mysql://192.168.1.14:3306/tuling-ms-alibaba?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
        driverClassName: com.mysql.jdbc.Driver
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        filters: stat,wall #\u914D\u7F6E\u8FC7\u6EE4\u5668
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  application:
    name: product-center
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.5:8848
server:
  port: 8081
  #port: 8082

image.pngimage.pngimage.pngimage.png查看日志可以看到我们重写的负载均衡方法先从注册中心获取到product服务的列表,并且随机选择一个IP替换url。

2021-03-02 22:26:14.098  INFO 8449 --- [nio-8080-exec-9] com.tuling.config.TulingRestTemplate     : 请求的url路径为:http://product-center/selectProductInfoById/1
2021-03-02 22:26:14.098  INFO 8449 --- [nio-8080-exec-9] com.tuling.config.TulingRestTemplate     : 调用微服务的名称:product-center
2021-03-02 22:26:14.098  INFO 8449 --- [nio-8080-exec-9] com.tuling.config.TulingRestTemplate     : 请求path:/selectProductInfoById/1
2021-03-02 22:26:14.098  INFO 8449 --- [nio-8080-exec-9] com.tuling.config.TulingRestTemplate     : 随机选举的服务IP:http://192.168.1.136:8081
2021-03-02 22:26:14.098  INFO 8449 --- [nio-8080-exec-9] com.tuling.config.TulingRestTemplate     : 替换后的路径:http://192.168.1.136:8081/selectProductInfoById/1
2021-03-02 22:26:18.477  INFO 8449 --- [io-8080-exec-10] com.tuling.config.TulingRestTemplate     : 请求的url路径为:http://product-center/selectProductInfoById/1
2021-03-02 22:26:18.477  INFO 8449 --- [io-8080-exec-10] com.tuling.config.TulingRestTemplate     : 调用微服务的名称:product-center
2021-03-02 22:26:18.477  INFO 8449 --- [io-8080-exec-10] com.tuling.config.TulingRestTemplate     : 请求path:/selectProductInfoById/1
2021-03-02 22:26:18.478  INFO 8449 --- [io-8080-exec-10] com.tuling.config.TulingRestTemplate     : 随机选举的服务IP:http://192.168.1.136:8082
2021-03-02 22:26:18.478  INFO 8449 --- [io-8080-exec-10] com.tuling.config.TulingRestTemplate     : 替换后的路径:http://192.168.1.136:8082/selectProductInfoById/1

Ribbon组件使用

第一步:加入依赖(nacos-client和ribbon)

<!--加入nocas-client-->
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<!--加入ribbon-->
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

第二步:在RestTemplate上加上@LoadBalanced注册

package com.tuling.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate( ) {
        return new RestTemplate();
    }
}

第三步:写配置文件(现在暂时用默认的ribbon负载均衡方法,配置文件只写nacos的就行)

spring:
  datasource:
      druid:
        username: root
        password: 123456
        jdbcUrl: jdbc:mysql://192.168.1.14:3306/tuling-ms-alibaba?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
        driverClassName: com.mysql.jdbc.Driver
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        filters: stat,wall #\u914D\u7F6E\u8FC7\u6EE4\u5668
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.5:8848
  application:
    name: order-center
server:
  port: 8080

第四步:写Controller

package com.tuling.controller;
import com.tuling.entity.OrderInfo;
import com.tuling.entity.ProductInfo;
import com.tuling.mapper.OrderInfoMapper;
import com.tuling.vo.OrderVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
 * 基于ribbon负载均衡
 */
@RestController
public class OrderInfoController {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @RequestMapping("/selectOrderInfoById/{orderNo}")
    public Object selectOrderInfoById(@PathVariable("orderNo") String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.selectOrderInfoById(orderNo);
        if(null == orderInfo) {
            return "根据orderNo:"+orderNo+"查询没有该订单";
        }
        ResponseEntity<ProductInfo> responseEntity = null;
        try {
            responseEntity = restTemplate.getForEntity("http://product-center/selectProductInfoById/"+orderInfo.getProductNo(), ProductInfo.class);
        }catch (Exception e) {
            System.out.println(e.getStackTrace());
        }
        ProductInfo productInfo = responseEntity.getBody();
        if(productInfo == null) {
            return "没有对应的商品";
        }
        OrderVo orderVo = new OrderVo();
        orderVo.setOrderNo(orderInfo.getOrderNo());
        orderVo.setUserName(orderInfo.getUserName());
        orderVo.setProductName(productInfo.getProductName());
        orderVo.setProductNum(orderInfo.getProductCount());
        return orderVo;
    }
}

和前面一样并行起2个product服务:image.pngimage.png

Ribbon细粒度自定义配置

场景:order服务需要采用随机算法调用product服务,使用轮询算法调用pay服务,其他服务使用随机算法调用。

在order服务的配置文件中指定即可:

spring:
  datasource:
      druid:
        username: root
        password: 123456
        jdbcUrl: jdbc:mysql://192.168.1.14:3306/tuling-ms-alibaba?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
        driverClassName: com.mysql.jdbc.Driver
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        filters: stat,wall #\u914D\u7F6E\u8FC7\u6EE4\u5668
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.5:8848
        #自定义负载均衡时使用
        cluster-name: NJ-CLUSTER
        metadata:
          current-version: V1
  application:
    name: order-center
#开启ribbon饥饿加载,解决微服务调用第一次很慢的情况下
ribbon:
  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  eager-load:
    enabled: true
    #可以指定多个微服务用逗号分隔
    clients: product-center,pay-center
#
##自定义Ribbon的细粒度配置
product-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
pay-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
server:
  port: 8080

自定义负载均衡策略

order服务有V1一个版本,在NJ-Cluster(1个实例);product服务有V1,V2两个版本分别在BJ-Cluster和NJ-Cluster(4个实例)。

要求order服务按照以下优先级调用product服务,跨版本不允许调用:

  • 同集群同版本。
  • 同版本不同集群。image.pngimage.png
package com.ribbonconfig;
import com.netflix.loadbalancer.IRule;
import com.tuling.myrule.TheSameClusterPriorityWithVersionRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GlobalRibbonConfig {
    @Bean
    public IRule theSameClusterPriorityRule() {
        return new TheSameClusterPriorityWithVersionRule();  //自定义负载均衡策略
    }
}

编写自定义负载均衡策略TheSameClusterPriorityWithVersionRule,继承AbstractLoadBalancerRule并重写choose()方法:

package com.tuling.myrule;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
 * 自定义负载均衡,同集群同版本优先调用,然后是跨集群同版本
 */
@Slf4j
public class TheSameClusterPriorityWithVersionRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties discoveryProperties;
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
    /**
     * 重写choose方法,自定义负载均衡策略
     * @param key
     * @return
     */
    @Override
    public Server choose(Object key) {
        try {
            //获取当前的集群名称
            String currentClusterName = discoveryProperties.getClusterName();
            //获取和当前集群 相同集群下,相同版本的 所有实例
            List<Instance> theSameClusterNameAndTheSameVersionInstList = getTheSameClusterAndTheSameVersionInstances(discoveryProperties);
            //声明被调用的实例
            Instance toBeChooseInstance;
            //判断同集群同版本号的微服务实例是否为空
            if(theSameClusterNameAndTheSameVersionInstList.isEmpty()) {
                //如果没有同集群同版本的服务,跨集群调用相同的版本
                toBeChooseInstance = crossClusterAndTheSameVersionInovke(discoveryProperties);
            }else {
                toBeChooseInstance = TulingWeightedBalancer.chooseInstanceByRandomWeight(theSameClusterNameAndTheSameVersionInstList);
                log.info("同集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",
                        currentClusterName,toBeChooseInstance.getClusterName(),discoveryProperties.getMetadata().get("current-version"),
                        toBeChooseInstance.getMetadata().get("current-version"),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
            }
            return new NacosServer(toBeChooseInstance);
        } catch (NacosException e) {
            log.error("同集群优先权重负载均衡算法选择异常:{}",e);
            return null;
        }
    }
    /**
     * 方法实现说明:获取相同集群下,相同版本的 所有实例
     * @param discoveryProperties nacos的配置
     * @return: List<Instance>
     * @exception: NacosException
     */
    private List<Instance> getTheSameClusterAndTheSameVersionInstances(NacosDiscoveryProperties discoveryProperties) throws NacosException {
        //当前的集群的名称
        String currentClusterName = discoveryProperties.getClusterName();
        String currentVersion = discoveryProperties.getMetadata().get("current-version");
        //获取所有实例的信息(包括不同集群的)
        List<Instance> allInstance =  getAllInstances(discoveryProperties);
        List<Instance> theSameClusterNameAndTheSameVersionInstList = new ArrayList<>();
        //过滤相同集群的
        for(Instance instance : allInstance) {
            if(StringUtils.endsWithIgnoreCase(instance.getClusterName(),currentClusterName)&&
               StringUtils.endsWithIgnoreCase(instance.getMetadata().get("current-version"),currentVersion)) {
                theSameClusterNameAndTheSameVersionInstList.add(instance);
            }
        }
        return theSameClusterNameAndTheSameVersionInstList;
    }
    /**
     * 方法实现说明:获取被调用服务的所有实例
     * @param discoveryProperties nacos的配置
     * @return: List<Instance>
     * @exception: NacosException
     */
    private List<Instance> getAllInstances(NacosDiscoveryProperties discoveryProperties) throws NacosException {
        //第1步:获取一个负载均衡对象
        BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) getLoadBalancer();
        //第2步:获取当前调用的微服务的名称
        String invokedSerivceName = baseLoadBalancer.getName();
        //第3步:获取nacos clinet的服务注册发现组件的api
        NamingService namingService = discoveryProperties.namingServiceInstance();
        //第4步:获取所有的服务实例
        List<Instance> allInstance =  namingService.getAllInstances(invokedSerivceName);
        return allInstance;
    }
    /**
     * 方法实现说明:跨集群环境下 相同版本的
     * @param discoveryProperties
     * @return: List<Instance>
     * @exception: NacosException
     */
    private List<Instance> getCrossClusterAndTheSameVersionInstList(NacosDiscoveryProperties discoveryProperties) throws NacosException {
        //版本号
        String currentVersion = discoveryProperties.getMetadata().get("current-version");
        //被调用的所有实例
        List<Instance> allInstance = getAllInstances(discoveryProperties);
        List<Instance>  crossClusterAndTheSameVersionInstList = new ArrayList<>();
        //过滤相同版本
        for(Instance instance : allInstance) {
            if(StringUtils.endsWithIgnoreCase(instance.getMetadata().get("current-version"),currentVersion)) {
                crossClusterAndTheSameVersionInstList.add(instance);
            }
        }
        return crossClusterAndTheSameVersionInstList;
    }
    private Instance crossClusterAndTheSameVersionInovke(NacosDiscoveryProperties discoveryProperties) throws NacosException {
        //获取所有集群下相同版本的实例信息
        List<Instance>  crossClusterAndTheSameVersionInstList = getCrossClusterAndTheSameVersionInstList(discoveryProperties);
        //当前微服务的版本号
        String currentVersion = discoveryProperties.getMetadata().get("current-version");
        //当前微服务的集群名称
        String currentClusterName = discoveryProperties.getClusterName();
        //声明被调用的实例
        Instance toBeChooseInstance = null ;
        //没有对应相同版本的实例
        if(crossClusterAndTheSameVersionInstList.isEmpty()) {
            log.info("跨集群调用找不到对应合适的版本当前版本为:currentVersion:{}",currentVersion);
            throw new RuntimeException("找不到相同版本的微服务实例");
        }else {
            toBeChooseInstance = TulingWeightedBalancer.chooseInstanceByRandomWeight(crossClusterAndTheSameVersionInstList);
            log.info("跨集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",
                    currentClusterName,toBeChooseInstance.getClusterName(),discoveryProperties.getMetadata().get("current-version"),
                    toBeChooseInstance.getMetadata().get("current-version"),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
        }
        return toBeChooseInstance;
    }
}

RestTemplate添加@LoadBalanced注解:

package com.tuling.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class WebConfig {
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate( ) {
        return new RestTemplate();
    }
}

order服务Controller方法和之前一样。

连续在浏览器请求order服务,在所有product服务都好的情况下,会优先调用同集群同版本的:

2021-03-04 23:42:20.492  INFO 9478 --- [nio-8080-exec-2] .m.TheSameClusterPriorityWithVersionRule : 同集群同版本调用--->当前微服务所在集群:NJ-CLUSTER,被调用微服务所在集群:NJ-CLUSTER,当前微服务的版本:V1,被调用微服务版本:V1,Host:192.168.1.136,Port:8081
2021-03-04 23:42:32.408  INFO 9478 --- [nio-8080-exec-4] .m.TheSameClusterPriorityWithVersionRule : 同集群同版本调用--->当前微服务所在集群:NJ-CLUSTER,被调用微服务所在集群:NJ-CLUSTER,当前微服务的版本:V1,被调用微服务版本:V1,Host:192.168.1.136,Port:8081
2021-03-04 23:42:33.029  INFO 9478 --- [nio-8080-exec-5] .m.TheSameClusterPriorityWithVersionRule : 同集群同版本调用--->当前微服务所在集群:NJ-CLUSTER,被调用微服务所在集群:NJ-CLUSTER,当前微服务的版本:V1,被调用微服务版本:V1,Host:192.168.1.136,Port:8081

将NJ-Cluster,V1版本的product服务下线,此时会去调BJ-Cluster的V1版本的服务:

2021-03-04 23:47:10.242  INFO 9478 --- [nio-8080-exec-8] .m.TheSameClusterPriorityWithVersionRule : 跨集群同版本调用--->当前微服务所在集群:NJ-CLUSTER,被调用微服务所在集群:BJ-CLUSTER,当前微服务的版本:V1,被调用微服务版本:V1,Host:192.168.1.136,Port:8083
2021-03-04 23:47:11.627  INFO 9478 --- [nio-8080-exec-9] .m.TheSameClusterPriorityWithVersionRule : 跨集群同版本调用--->当前微服务所在集群:NJ-CLUSTER,被调用微服务所在集群:BJ-CLUSTER,当前微服务的版本:V1,被调用微服务版本:V1,Host:192.168.1.136,Port:8083
2021-03-04 23:47:12.380  INFO 9478 --- [io-8080-exec-10] .m.TheSameClusterPriorityWithVersionRule : 跨集群同版本调用--->当前微服务所在集群:NJ-CLUSTER,被调用微服务所在集群:BJ-CLUSTER,当前微服务的版本:V1,被调用微服务版本:V1,Host:192.168.1.136,Port:8083

如果将NJ-Cluster,V1版本的product服务也下线,此时由于剩下的只有V2版本的product服务,将无法调用:image.png

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
2月前
|
负载均衡 算法 架构师
Ribbon负载均衡
上一节就已经实现的负载均衡笔者并未深入探讨,本节通过分析负载均衡算法、Ribbon实现负载均衡的底层原理和实现过程,让大家对负载均衡有了一个大体认识,同时针对Ribbon自定义负载均衡策略,饥饿加载让大家对于Ribbon的了解又多一些。Ribbon实现的负载均衡只是方案之一,我们可以尽量多了解但不要局限于此。
|
22天前
|
负载均衡 Java Nacos
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
微服务介绍、SpringCloud、服务拆分和远程调用、Eureka注册中心、Ribbon负载均衡、Nacos注册中心
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
|
6天前
|
负载均衡 Java 开发者
Ribbon框架实现客户端负载均衡的方法与技巧
Ribbon框架为微服务架构中的客户端负载均衡提供了强大的支持。通过简单的配置和集成,开发者可以轻松地在应用中实现服务的发现、选择和负载均衡。适当地使用Ribbon,配合其他Spring Cloud组件,可以有效提升微服务架构的可用性和性能。
7 0
|
2月前
|
存储 设计模式 缓存
OpenFeign集成Ribbon负载均衡-过滤和选择服务核心实现
该文章主要介绍了如何在OpenFeign中集成Ribbon以实现负载均衡,并详细分析了Ribbon中服务选择和服务过滤的核心实现过程。文章还涉及了Ribbon中负载均衡器(ILoadBalancer)和负载均衡策略(IRule)的初始化方式。
OpenFeign集成Ribbon负载均衡-过滤和选择服务核心实现
|
2月前
|
缓存 负载均衡 Java
OpenFeign最核心组件LoadBalancerFeignClient详解(集成Ribbon负载均衡能力)
文章标题为“OpenFeign的Ribbon负载均衡详解”,是继OpenFeign十大可扩展组件讨论之后,深入探讨了Ribbon如何为OpenFeign提供负载均衡能力的详解。
OpenFeign最核心组件LoadBalancerFeignClient详解(集成Ribbon负载均衡能力)
|
3月前
|
负载均衡 算法 网络协议
Ribbon 负载均衡源码解读
Ribbon 负载均衡源码解读
52 15
Ribbon 负载均衡源码解读
|
3月前
|
负载均衡 Java API
Feign 进行rpc 调用时使用ribbon负载均衡源码解析
Feign 进行rpc 调用时使用ribbon负载均衡源码解析
64 11
|
3月前
|
消息中间件 存储 负载均衡
消息队列 MQ使用问题之如何在grpc客户端中设置负载均衡器
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
2月前
|
负载均衡 算法 Java
SpringCloud之Ribbon使用
通过 Ribbon,可以非常便捷的在微服务架构中实现请求负载均衡,提升系统的高可用性和伸缩性。在实际使用中,需要根据实际场景选择合适的负载均衡策略,并对其进行适当配置,以达到更佳的负载均衡效果。
35 13
|
4月前
|
负载均衡 算法 Java
Spring Cloud Netflix 之 Ribbon
Spring Cloud Netflix Ribbon是客户端负载均衡器,用于在微服务架构中分发请求。它与RestTemplate结合,自动在服务发现(如Eureka)注册的服务之间进行调用。配置包括在pom.xml中添加依赖,设置application.yml以连接Eureka服务器,并在配置类中创建@LoadBalanced的RestTemplate。通过这种方式,当调用如`/user/userInfoList`的接口时,Ribbon会自动处理到多个可用服务实例的负载均衡。
下一篇
无影云桌面