常见的服务端负载均衡有Nginx,客户端将请求发送给Nginx,Nginx根据负载均衡算法选择一个服务器调用,具体调用哪个服务器由Nginx说了算,客户端是不知道服务器的真实IP的。
Spring Cloud Ribbon是基于NetFilix Ribbon实现的一套客户端负载均衡,Ribbon客户端组件提供了一系列的完善的配置,例如超时,重试等。客户端从注册中心获取到服务器的列表,由客户端自己根据负载均衡算法选择将流量分发给哪个服务器,客户端是知道服务器的真实IP的。
通过阅读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
在Nacos上查看order和product服务注册情况:
product服务有两个实例注册,端口号分别为8081和8082:
通过浏览器访问order服务多次,order服务会通过随机的方式去调用两个product服务。
查看日志可以看到我们重写的负载均衡方法先从注册中心获取到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
第一步:加入依赖(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服务:
浏览器访问order服务,order服务会通过Ribbon负载均衡流量到product服务,在两个product服务上查看日志可以看到流量是轮询的。
场景: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服务,跨版本不允许调用:
分别按照上述要求启动1个order服务和4个product服务:
自定义所有默认Ribbon:@RibbonClients它可以为所有的Ribbon客户端提供默认配置:
package com.tuling.config;
import com.ribbonconfig.GlobalRibbonConfig;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
/**
* ribbon的全局配置
*/
@RibbonClients(defaultConfiguration = GlobalRibbonConfig.class)
public class CustomRibbonConfig {
}
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服务,将无法调用: