在第3章讲解Eureka时,我们提到了Eureka会保存各个服务的元数据,元数据中包含了各个服务的地址等信息。那么服务之间到底是怎样通过这些信息进行交互的呢?
Spring Cloud服务间的调用默认支持两种方式:Ribbon和Feign,具体来说就是使用RestTemplate和FeignClient来调用。不管使用什么方式,本质上都是通过REST接口调用服务的HTTP接口,参数和结果默认都是通过jackson序列化和反序列化的。
前面我们已经创建了customer微服务,这里我们再新建一个order微服务。使用两个服务来进行服务间调用的学习。
order微服务的端口号设置为8002。order微服务的pom.xml引入了如下包:
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
org.springframework.boot
spring-boot-starter-test
test
其中spring-cloud-starter-netflix-ribbon 引入了Ribbon支持。
为了使用Ribbon访问customer服务的接口,我们在order的启动类中加入了如下代码,加载RestTemplate。
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
注意:这里使用的注解@Loadbalanced是用来开启Ribbon负载均衡的。如果没有添加此注解,那么得到的restTemplate就不具备Ribbon的负载均衡功能,也不会识别微服务的实例名,只能当作传统的http访问工具类来使用。
编写测试controller。
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/users/{userId}")
public String getUserInfo(@PathVariable Long userId) {
System.out.printf("这里要使用Ribbon来远程调用customer服务获取用户信息");
String userInfoStr = restTemplate.getForObject("http://customer/users/" +
userId, String.class);
return userInfoStr;
}
}
通过这样,就可以访问customer服务中的接口,customer中的接口代码如下:
@GetMapping("/users/{userId}")
public String getUserInfoByUserId(@PathVariable Long userId){
return "userInfo with userId = " + userId;
}
在浏览器中访问http://localhost:8002/users/1001,可以得到如5.1的结果。
通过上面的例子,我们通过RestTemplate在order和customer之间进行了服务间的http请求,内部请求过程中使用了http://customer/users/这个地址,并没有涉及到域名端口之类的东西。这里的customer就是我们之前提到的实例名。也就是这个服务在整个系统中的唯一名称。
Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP的客户端的行为。为Ribbon配置服务提供者地址后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多负载均衡算法,例如轮询、随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。
在Spring Cloud中,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。在本章刚开始,我们新建的order服务使用RestTemplate调用customer接口的实例就是使用了带有Ribbon功能的RestTemplate。
RestTemplate提供了若干方便我们使用的工具方法,通过这些方法可以很方便地实现网络访问/服务间调用。
接下来我们介绍几个常见的方法。
getForEntity():发送一个HTTP
GET请求,返回的ResponseEntity包含了响应体所映射成的对象。
postForEntity():POST
数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得到的。
Feign 是一个声明web服务客户端,它使编写web服务客户端更容易,使用Feign
创建一个接口并对它进行注解,它具有可插拔的注解支持包括Feign注解与JAX-RS注解,Feign还支持可插拔的编码器与解码器,Spring
Cloud 增加了对 Spring MVC的注解,Spring Web 默认使用了HttpMessageConverters,
Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign。
为order服务添加feign支持。
org.springframework.cloud
spring-cloud-starter-netflix-feign
为启动类添加注解@EnableFeignClients,开启Feign扫描。
新建service接口类,代码如下:
@FeignClient("customer")
public interface UserService {
@GetMapping("/users/{userId}")
String getUserInfoByUserId(@PathVariable Long userId);
}
在UserController中添加接口方法。
@GetMapping("/feign/users/{userId}")
public String getUserInfoWithFeign(@PathVariable Long userId){
System.out.printf("这里要使用Feign来远程调用customer服务获取用户信息");
String userInfoStr = userService.getUserInfoByUserId(userId);
return userInfoStr;
}
启动order服务,浏览器访问http://localhost:8002/feign/users/100112。得到如图5.2的结果。
本章主要了解了如何使用Ribbon和Feign通过Eureka的实例来进行服务间的接口调用。到本文为止,我们已经可以构建一些简单的微服务,并可以让它们注册到同一个注册中心,连接到同一个配置中心,并且可以在不需要url的情况下进行服务间交互。