前面我们了解了负载均衡的作用以及负载均衡的一些负载策略,这篇我们来看一下,在微服务(SpringCloud)架构中,如何来实现服务间的高可用通信。
我们先来了解下SpringCloud自带的RestTemplate对象,RestTemplate 对象是Spring Cloud 封装的 RESTful 通信对象,它封装了基于 HTTP 协议的操作,通过简单的API便可发起 HTTP 请求并自动处理响应。RestTemplate 天然与 Ribbon 兼容,两者配合可以极大简化服务间通信过程。Ribbon + RestTemplate 提供了两种开发模式:代码模式,注解模式。
下面我们来看一下两种开发模式的具体代码实现。
一、代码模式
代码模式是指通过纯 Java 代码实现微服务间通信,虽然工作中代码模式很少使用,但它却是理解 Ribbon+RestTemplate 最直观的途径,所以我们先来看他。该模式使用主要分为两个阶段:
1、创建服务提供者
服务提供者也就是请求的实际处理者,也是标准的springboot工程,利用controller对外暴露RESTful API供服务消费者调用。
(1)创建provider-service微服务

其中pom.xml要确保引用了web和nacos-discovery两个依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
(2)在application.yml中调整微服务与Nacos的通信配置
spring:
application:
name: provider-service #应用/微服务名字
cloud:
nacos:
discovery:
server-addr: 106.14.221.171:8848 #nacos服务器地址
username: nacos #用户名密码
password: nacos
server:
port: 80
(3)创建ProviderController,通过controller控制器对外暴露接口
package com.example.providerservice.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProviderController {
@GetMapping("/provider/msg")
public String sendMessage(){
return "the message from provider service!";
}
}
到这里,我们的服务提供者已经开发完毕(这里只是做个案例,实际业务肯定没这么简单,大家理解就行)。服务提供者就是标准的 Spring Cloud 微服务,向 Nacos 进行注册的同时对外暴露 msg 接口并返回一段静态文本,并没有任何与 Ribbon 相关的内容。确实,Ribbon 与 RestTemplate 应出现在服务消费者,而非提供者一端。
(4)微服务多节点模拟
为了演示效果,我们要准备五台虚拟机来模拟:
Ip | 端口 | 用途 |
---|---|---|
192.168.3.1 | 8848 | Nacos注册中心实例部署(非集群) |
192.168.3.2 | 80 | 服务提供者实例1 |
192.168.3.3 | 80 | 服务提供者实例2 |
192.168.3.4 | 80 | 服务提供者实例3 |
192.168.3.5 | 80 | 服务消费者实例1 |
这里,我们通过一台Nacos实例来做注册中心,服务提供者分别部署三台,来模拟负载均衡效果。
-
将上面的服务提供者工程,打包成jar包。 -
将jar包上传到192.168.3.2、192.168.3.3、192.168.3.4三台服务器上,并使用java命令启动微服务。
java -jar provider-service.jar
-
节点启动后,我们可以在Nacos注册中心中看一下微服务启动注册情况。

-
单独访问任意节点的接口,都可以看到返回的相同文本。
http://192.168.3.2/provider/msg
http://192.168.3.3/provider/msg
http://192.168.3.4/provider/msg

到这里,服务提供者的开发告一段落。
2、创建服务消费者
服务消费者说白了就是服务的使用方,我们需要在服务消费者内置 Ribbon+RestTemplate 实现服务间高可用通信。
(1)创建服务消费者工程
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
starter-netflix-ribbon 就是通过 Spring Boot Starter 向当前微服务工程集成 Ribbon,无须做其他额外配置。与此同时,用于 RESTful 通信的 RestTemplate 对象已被集成到 starter-web 模块,无须额外依赖。
(2)配置application.yml
这里除微服务id与服务提供者不同外,其他都一样
spring:
application:
name: customer-service #应用/微服务名字
cloud:
nacos:
discovery:
server-addr: 106.14.221.171:8848 #nacos服务器地址
username: nacos #用户名密码
password: nacos
server:
port: 80
(3)声明RestTemplate
利用 Spring Java Config 方式声明 RestTemplate。在 ConsumerServiceApplication 类中新增以下声明代码
@SpringBootApplication
public class ConsumerServiceApplication {
//Java Config声明RestTemplate对象
//在应用启动时自动执行restTemplate()方法创建RestTemplate对象,其BeanId为restTemplate。
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerServiceApplication.class, args);
}
}
(4)创建消费controller,并通过Ribbon+RestTemplate实现负载均衡通信
package com.example.consumerservice.controller;
...
@RestController
public class ConsumerController {
private Logger logger = LoggerFactory.getLogger(ConsumerController.class);
//注入 Ribbon 负载均衡器对象
//在引入 starter-netflix-ribbo n后在 SpringBoot 启动时会自动实例化 LoadBalancerClient 对象。
//在 Controlle 使用 @Resource 注解进行注入即可。
@Resource
private LoadBalancerClient loadBalancerClient;
@Resource
//将应用启动时创建的 RestTemplate 对象注入 ConsumerController
private RestTemplate restTemplate;
@GetMapping("/consumer/msg")
public String getProviderMessage() {
//loadBalancerClient.choose()方法会从 Nacos 获取 provider-service 所有可用实例,
//并按负载均衡策略从中选择一个可用实例,封装为 ServiceInstance(服务实例)对象
//结合现有环境既是从192.168.3.2:80、192.168.3.3:80、192.168.3.4:80三个实例中选择一个包装为ServiceInstance
ServiceInstance serviceInstance = loadBalancerClient.choose("provider-service");
//获取服务实例的 IP 地址
String host = serviceInstance.getHost();
//获取服务实例的端口
int port = serviceInstance.getPort();
//在日志中打印服务实例信息
logger.info("本次调用由provider-service的" + host + ":" + port + " 实例节点负责处理" );
//通过 RestTemplate 对象的 getForObject() 方法向指定 URL 发送请求,并接收响应。
//getForObject()方法有两个参数:
//1. 具体发送的 URL,结合当前环境发送地址为:http://192.168.3.2:80/provider/msg
//2. String.class说明 URL 返回的是纯字符串,如果第二参数是实体类, RestTemplate 会自动进行反序列化,为实体属性赋值
String result = restTemplate.getForObject("http://" + host + ":" + port + "/provider/msg", String.class);
//输出响应内容
logger.info("provider-service 响应数据:" + result);
//向浏览器返回响应
return "consumer-service 响应数据:" + result;
}
}
(5)消费者服务打包部署
将上述消费者工程打包成jar包后部署至192.168.3.5服务器上,并执行命令运行起来。
运行后可以到Nacos注册中心查看注册情况:

(6)运行效果
部署和注册好之后,我们就可以调用消费者接口来看一下服务情况,日志如下:
本次调用由 provider-service 的 192.168.3.2:80 实例节点负责处理
consumer-service 获得数据:the message from provider service!
本次调用由 provider-service 的 192.168.3.3:80 实例节点负责处理
consumer-service 获得数据:the message from provider service!
本次调用由 provider-service 的 192.168.3.4:80 实例节点负责处理
consumer-service 获得数据:the message from provider service!
本次调用由 provider-service 的 192.168.3.2:80 实例节点负责处理
consumer-service 获得数据:the message from provider service!
从上面输出日志可以看出,服务提供者有三个节点,默认是按照轮巡的方式按顺序逐个调用的。如果发生某个服务提供者宕机,Nacos心跳机制就会检测并将其剔除,这样我们继续调用消费者来获取服务者提供的服务时,消费者就不会再向宕机的节点发起请求了。有兴趣的大家可以试试。
以上便是代码模式的处理过程,它清晰的说明了Ribbon的执行过程,先从 Nacos 获取可用服务提供者实例信息,再通过 RestTemplate.getForObject() 向该实例发起 RESTful 请求完成处理。
也许你也察觉到了代码模式使用复杂,需要自己获取可用实例 IP、端口信息,再拼接 URL 实现服务间通信,那有没有更简单的办法呢?答案是肯定的,利用 @LoadBalanced 注解可自动化实现这一过程。
关于注解模式实现的步骤,我们下一篇再接着聊。
欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发。
原文始发于微信公众号(服务端技术精选):【微服务-Ribbon】Ribbon+RestTemplate的服务通信实现方式之代码模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/258923.html