前言
我自己建了個博客網站,歡迎大家來訪問,閱讀體驗更佳點擊進入
正在入門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.yml
PROVIDER-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一個實例。