Spring Cloud(四)RestTemplate服务调用与Ribbon负载均衡
上面已经把产品和用户两个微服务注册到服务治理中心了。对于业务,则往往需要各个微服务之间相互地协助才能完成。例如,可能把产品交易信息放到产品服务中,而在交易时,有时需要根据用户的等级来决定默写商品的折扣,如白银会员9折,黄金会员8折等等。也就是说分布式系统在执行交易逻辑时,还需要使得产品微服务得到用户信息才可以决定产品的折扣,而用户的信息则是放置在用户微服务中的。为了方便用户微服务中获取用户信息,用户微服务会以REST风格提供一个请求URL。这样对于产品微服务就可以通过REST请求获取用户服务。
除了处理获取其他服务的数据外,这里还需要注意服务节点之间的负载均衡,毕竟一个微服务可以由多个节点提供服务。不过这些都不困难,因为Spring Cloud提供了Ribbon
和Feign
组件来帮助我们来完成这些任务。通过它们,各个微服务之间就能够相互调用,并且它会默认实现了负载均衡。
Ribbon客户端负载均衡
对于Ribbon
,其实并没有什么神秘的,它实际就是一个RestTemplate
对象。只是上面还讨论了多个节点的问题,例如调度用户微服务时,因为用户微服务存在多个节点,具体会使用哪个节点提供服务呢?关于这点Spring Cloud已经屏蔽了一些底层的细节,它只需要一个简单的@LoadBalance
注解就看可以提供负载均衡的算法。这十分符合spring boot的原则,提供默认的实现方式,减少开发者的工作量。在默认情况下,它会提供轮询的负载均衡算法。其他的负载均衡算法比较复杂,这里不讲(我也不会。。。)
下面用实例来说明Ribbon的使用。现在用户微服务上写一个Pojo
User
package com.lay.user.pojo;
import java.io.Serializable;
/**
* @Description:
* @Author: lay
* @Date: Created in 16:31 2018/11/26
* @Modified By:IntelliJ IDEA
*/
public class UserPo implements Serializable {
private static final long serialVersionUID = 6984412951642658165L;
private Long id;
private String userName;
//1-白银会员,2-黄金会员,3-钻石会员
private int level;
private String note;
public static long getSerialVersionUID() {
return serialVersionUID;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
然后我们可以给出基于REST风格的实现用户返回的代码
@RestController
public class UserController {
//日志
private static final Logger log= LoggerFactory.getLogger(UserController.class);
//服务发现客户端
@Autowired
private DiscoveryClient discoveryClient;
//获取用户信息
@GetMapping("/user/{id}")
public UserPo getUserPo(@PathVariable("id") Long id){
ServiceInstance service = discoveryClient.getInstances("USER").get(0);
log.info("【"+service.getServiceId()+"】"+service.getHost()+": "+service.getPort());
UserPo userPo=new UserPo();
userPo.setId(id);
userPo.setLevel((int) (id&3+1));
userPo.setUserName("user_name_"+id);
userPo.setNote("note_"+id);
return userPo;
}
这里DiscoveryClient
对象是spring boot自动创建的。
然后我们在产品微服务添加对Ribbon的依赖
Ribbon依赖
<!-- ribbon负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
然后对RestTemplate
进行初始化
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
//初始化RestTemplate
@LoadBalanced //多节点负载均衡
@Bean(name="restTemplate")
public RestTemplate initRestTemplate(){
return new RestTemplate();
}
}
测试
@RestController
@RequestMapping("product")
public class ProductController {
// 注入RestTemplate
@Autowired
private RestTemplate restTemplate;
@GetMapping("/ribbon")
public List<UserPo> testRibbon() {
List<UserPo> list = new ArrayList<>();
UserPo user = null;
for (int i = 0; i < 10; i++) {
//这里使用USER这个服务ID,代表用户维服务系统
//该ID通过属性spring.application.name来指定
//user = restTemplate.getForObject("http://USER/user/"+(i+1), UserPo.class);
Long id = Long.valueOf((i + 1));
user = restTemplate.getForObject("http://USER/user/"+(i+1),UserPo.class);
list.add(user);
}
return list;
}
}
代码中注入了RestTemplate
对象,这是自动实现客户端均衡负载的对象。然后再方法中使用"USER"
这个字符串代替了服务器以及其段哦偶,这是一个服务ID(Service ID),在Eureka服务器中可以看到它的各个节点,他是用户微服务通过属性spring.application.name
来指定的。这里故意地调用了10次,这样就能够观察各个用户微服务节点的日志来观察负载均衡的情况。