Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

服务间的通信

前几篇讲述了SpringCloud的注册中心,讲解了怎么配置注册中心,已经将我们的服务注册,那么现在有一个问题:加入我们现在有两个服务User,Order,我User中需要调用Order中的接口,这个服务间的通讯该怎么实现。

前面讲过SpringCloud中各个服务之间的通讯使用的是HTTP协议传递数据。

那么我们怎么在java代码中发起HTTP请求呢?

RestTemplate:Spring提供给我们的HttpClient对象,这个对象可以想我们使用的浏览器一样给我们的后端发送请求。

我们来写个demo就以上面的User,Oder为例

1、首先我们现在一个父项目中,创建两个两个子项目,并配置成SpringBoot项目

2、配置Consul注册中心,配置如下

依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<!--引入springbootweb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入consul依赖-->
<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为例

1
2
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

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author bestrookie
* @date 2021/8/4 3:35 下午
*/
@RestController
public class OrderController {
@GetMapping("order")
public String invokeOrder(){
Log.info("order demo>>>>>>>");
return "order 服务";
}
}

UsersControlleer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author bestrookie
* @date 2021/8/4 3:40 下午
*/
@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;
}
}

image-20210804172835907

可以看到成功访问。

存在的问题

1、调用服务的路径主机和服务器端口直接写死在url中无法实现服务集群时请求负载均衡

2、调用服务的请求路径写死在代码中,日后路径发生变化时不利于后续维护

如何解决

自定义负载均衡解决策略

简单来说就是将路径集放到一个集合中,然后随机从集合中得到一个,但还是无法实现服务健康检查,并且策略过于单一

所以推荐使用springcloud提供的组件ribbon解决负载均衡调用。

Ribbon

Spring-cloud-netflix-ribbon

是一个基于HTTP和TCP的客户端负载均衡工具,它基于NetFlix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。

Ribbon负载均衡原理

image-20210805141010116

Ribbon的调用

image-20210805141827324

首先我们发现,在上次我们引入的consul包中,他已经包含了ribbon的依赖,所以我们无需再次引用。

DiscoveryClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author bestrookie
* @date 2021/8/4 3:40 下午
*/
@RestController
public class UserController {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("user")
public String invokeUser(){
RestTemplate restTemplate = new RestTemplate();
// String forObject = restTemplate.getForObject("http://127.0.0.1:9999/order", String.class);
//使用ribbon组件+RestTemplate实现负载均衡调用 1、DiscoveryClient 2、LoadBalanceClient
List<ServiceInstance> orders = discoveryClient.getInstances("ORDERS");
String forObject = restTemplate.getForObject(orders.get(0).getUri() + "/order", String.class);
return "结果为:"+forObject;
}
}

我们可以发现DiscoveryClient 方式,仅仅是根据服务的名称的得到了服务的列表,然后列表中获取一个地址,然后用RestTemplate发起请求访问,并没有做什么负载均衡处理。

LoadBalancerClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
public class UserController {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("user")
public String invokeUser(){
RestTemplate restTemplate = new RestTemplate();
// String forObject = restTemplate.getForObject("http://127.0.0.1:9999/order", String.class);
//使用ribbon组件+RestTemplate实现负载均衡调用 1、DiscoveryClient 2、LoadBalanceClient
// List<ServiceInstance> orders = discoveryClient.getInstances("ORDERS");
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

1
2
3
4
5
6
7
8
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}

然后

1
2
3
4
5
6
7
8
9
10
@RestController
public 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策略,等统计信息足够然后切换策略

  • RetryRule:重试策略

先按照RoundRobinRule的策略获取服务,如果获取失败则在制定的时间内进行重试,获取可用服务。

  • BestAviableRule

会过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

image-20210805175515980

评论