前言
我自己建了个博客网站,欢迎大家来访问,阅读体验更佳点击进入 正在入门SpringCloud中,在学习的过程中也正好做个项目练手。这个项目是想做成一个模板,这样之后遇到同规模项目的时候可以拿来就用,版本也好控制。涉及到的中间件会有Eureka、Ribbon、Feign、HyStrix、Zuul、ConfigServer。这一节项目将改写Ribbon。
版本
SpringBoot:2.2.1.RELEASE
Spring Cloud:Finchley.RS1
spring-cloud-starter-netflix-eureka-client:2.2.1.RELEASE
- Eureka:2.2.1.RELEASE
- Ribbon:2.2.1.RELEASE
目录结构
如果为同一个的提供者在Eureka中注册了多个服务,那么客户端该如何选择服务呢?
这时,就需要在客户端实现服务的负载均衡。
Ribbon是Netflixⅸx发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。为Ribbon配置服务提供者地址列表后, Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。 Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。当然,我们也可为 Ribbon实现自定义的负载均衡算法。
一、简单的负载均衡程序
从consume-user复制粘贴出一个新的consume-user-ribbon,也可以不重新创建,直接修改consume-user,需要修改的是Controller和启动程序。
修改入口函数
@SpringBootApplication
@EnableEurekaClient
@RibbonClient("PROVIDER-USER") // 启用Ribbon并对PROVIDER-USER负载均衡
public class ConsumeUserRibbonApplication {
public static void main( String[] args ) {
SpringApplication.run(ConsumeUserRibbonApplication.class, args);
}
}
修改消费者Controller
修改获取url的方式
private EurekaClient eurekaClient;
@LoadBalanced
private RestTemplate restTemplate = new RestTemplate();
@GetMapping("getUserByEureka/{id}")
public User getUser(@PathVariable Long id){
User user = restTemplate.getForObject("http://PROVIDER-USER/user/"+id, User.class);
return user;
}
开启两个生产者,注意端口不同
但我通过消费者7901获取用户时,出现错误
I/O error on GET request for "http://PROVIDER-USER/user/1": PROVIDER-USER; nested exception is java.net.UnknownHostException: PROVIDER-USER
这是因为获取RestTemplat对象时要加上@LoadBalanced注解 ,否则restTemplate.getForObject方法会报java.net.UnknownHostException。
要解决这个问题还有一点要改,就是Controller中的restTemplete要交由Spring容器来管理。我原先是直接new RestTtemplete(),在负载均衡的场景下不可用
二、修改负载均衡策略
修改消费者的application.ymlPROVIDER-USER
为生产者中配置的应用名,指定其策略为随机,这样就会随机使用两个生产者
PROVIDER-USER:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
修改消费者Controller方便查看输出
@RestController
public class UserController {
/* 使用eureka动态获取url */
@Autowired
private EurekaClient eurekaClient;
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("getUserByEureka/{id}")
public User getUser(@PathVariable Long id){
ServiceInstance instance = loadBalancerClient.choose("PROVIDER-USER");
System.out.println(instance.getHost()+":"+instance.getPort());
User user = restTemplate.getForObject("http://PROVIDER-USER/user/"+id, User.class);
return user;
}
/* 硬编码url
private RestTemplate restTemplate = new RestTemplate(); // spring提供的用于访问接rest口的模板对象
@GetMapping("user/{id}")
public User getUser(@PathVariable Long id){
User user = restTemplate.getForObject("http://localhost:7900/user/"+id, User.class);
return user;
}
*/
/*
服务提供者中的控制器
@GetMapping("user/{id}")
public User getUser(@PathVariable Long id){
return new User(id);
}
*/
}
三、总结
通过restTemplete获得服务生产者实例时,restTemplete需由Spring容器来生成并加上LoadBlance注解,不能直接new一个实例。