服务间的通信
前几篇讲述了SpringCloud的注册中心,讲解了怎么配置注册中心,已经将我们的服务注册,那么现在有一个问题:加入我们现在有两个服务User,Order,我User中需要调用Order中的接口,这个服务间的通讯该怎么实现。
前面讲过SpringCloud中各个服务之间的通讯使用的是HTTP协议传递数据。
那么我们怎么在java代码中发起HTTP请求呢?
RestTemplate:Spring提供给我们的HttpClient对象,这个对象可以想我们使用的浏览器一样给我们的后端发送请求。
我们来写个demo就以上面的User,Oder为例
1、首先我们现在一个父项目中,创建两个两个子项目,并配置成SpringBoot项目
2、配置Consul注册中心,配置如下
依赖:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | <dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 
 <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-consul-discovery</artifactId>
 </dependency>
 
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
 </dependencies>
 
 | 
yaml:以user为例
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | server:port: 8888
 spring:
 application:
 name: USERS
 cloud:
 consul:
 host: 127.0.0.1
 port: 8500
 
 | 
3、因为只是模仿业务这里就简单的创建controller
OrderController
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | 
 
 
 @RestController
 public class OrderController {
 @GetMapping("order")
 public String invokeOrder(){
 Log.info("order demo>>>>>>>");
 return "order 服务";
 }
 }
 
 | 
UsersControlleer
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | 
 
 
 @RestController
 public class UserController {
 @GetMapping("user")
 public String invokeUser(){
 Log.info("user demo>>>>>");
 RestTemplate restTemplate = new RestTemplate();
 String forObject = restTemplate.getForObject("http://   127.0.0.1:9999/order", String.class);
 Log.info("调用订单服务"+forObject);
 return "结果为:"+forObject;
 }
 }
 
 | 

可以看到成功访问。
存在的问题
1、调用服务的路径主机和服务器端口直接写死在url中无法实现服务集群时请求负载均衡
2、调用服务的请求路径写死在代码中,日后路径发生变化时不利于后续维护
如何解决
自定义负载均衡解决策略
简单来说就是将路径集放到一个集合中,然后随机从集合中得到一个,但还是无法实现服务健康检查,并且策略过于单一
所以推荐使用springcloud提供的组件ribbon解决负载均衡调用。
Ribbon
Spring-cloud-netflix-ribbon
是一个基于HTTP和TCP的客户端负载均衡工具,它基于NetFlix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。
Ribbon负载均衡原理

Ribbon的调用

首先我们发现,在上次我们引入的consul包中,他已经包含了ribbon的依赖,所以我们无需再次引用。
DiscoveryClient
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | 
 
 
 @RestController
 public class UserController {
 @Autowired
 private DiscoveryClient discoveryClient;
 @GetMapping("user")
 public String invokeUser(){
 RestTemplate restTemplate = new RestTemplate();
 
 
 List<ServiceInstance> orders = discoveryClient.getInstances("ORDERS");
 String forObject = restTemplate.getForObject(orders.get(0).getUri() + "/order", String.class);
 return "结果为:"+forObject;
 }
 }
 
 | 
我们可以发现DiscoveryClient 方式,仅仅是根据服务的名称的得到了服务的列表,然后列表中获取一个地址,然后用RestTemplate发起请求访问,并没有做什么负载均衡处理。
LoadBalancerClient
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | @RestControllerpublic class UserController {
 @Autowired
 private DiscoveryClient discoveryClient;
 @Autowired
 private LoadBalancerClient loadBalancerClient;
 @GetMapping("user")
 public String invokeUser(){
 RestTemplate restTemplate = new RestTemplate();
 
 
 
 ServiceInstance orders = loadBalancerClient.choose("ORDERS");
 String forObject = restTemplate.getForObject(orders.getUri() + "/order", String.class);
 return "结果为:"+forObject;
 }
 }
 
 | 
这个可以看出,有默认的负载均衡策略,这个策略就是轮询(比如A,B 一次A,一次B)。但是使用时需要每次现根据服务id获取一个负载均衡机器之后再通过RestTemplate调用服务。
@loadBalance+RestTemplate
这是比较常用的方式,首先上面代码中都出现了new关键字,我们都知道我们通常将这种对象注册到bean中,我们配置bean并且添加@LoadBalanced
| 12
 3
 4
 5
 6
 7
 8
 
 | @Configurationpublic class RestTemplateConfig {
 @Bean
 @LoadBalanced
 public RestTemplate restTemplate(){
 return  new RestTemplate();
 }
 }
 
 | 
 然后
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | @RestControllerpublic class UserController {
 @Autowired
 private RestTemplate restTemplate;
 @GetMapping("user")
 public String invokeUser(){
 String forObject = restTemplate.getForObject("http://ORDERS/order", String.class);
 return "结果为:"+forObject;
 }
 }
 
 | 
是不是发现这个url不同?
@loadBalance+RestTemplate的方式,就是会自动解析url,ORDERS是我们注册服务的名字,这样依旧具有负载均衡,依旧是轮询。
但是路径还是死的,还是不利于维护。
Ribbon负载均衡策略
- RoundRobinRule:轮询策略 按照顺序循环选择
- RandomRule:随机策略 随机选择
- AvailabilityFilteringRule:可用过滤策略
会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
- WeigthedResponseTimeRule:响应时间加权策略
根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够然后切换策略
先按照RoundRobinRule的策略获取服务,如果获取失败则在制定的时间内进行重试,获取可用服务。
会过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
