Ribbon是什麼
ribbon是一種可以實現客戶端負載均衡的組件,它和服務端負載均衡有什麼區別呢?
客戶端負載均衡:
原理:部署在客戶端,客戶端獲取所有的服務器列表,然後可以根據各自不同客戶端的負載均衡算法,找到要訪問的服務器
優勢:
1.更自由的爲不同客戶端實現不同的負載均衡算法
2. 穩定性好,就算掛了一臺客戶端或服務器,也不會對其他可用的客戶端的負載均衡有任何影響
服務端負載均衡
原理:當有請求時,攔截請求,然後使用比如nginx的反向代理的方式去獲取到要訪問的服務器
優勢:
1. 統一維護,成本低,因爲都放在類似nginx這個統一的地方進行處理了
不足:如果nginx宕機了,那麼所有服務都用不了了
1.Ribbon使用前
服務提供者與服務消費者之間的調用方式一般就是通過RestTemplate,或者HttpClient等請求方式直接請求接口獲取數據
1.1 服務提供者
1.1.1 application.yml:
server:
port: 8085
spring:
application:
name: ribbon-service-provider
1.1.2 controller
@RestController
public class ServiceProviderController {
@Value("${server.port}")
private Integer port;
@PostMapping("/greeting")
public String greeting(@RequestBody User user) {
return "Greeting , " + user + " on port : " + port;
}
}
然後使用postman請求一下,發現返回正常,那麼服務提供方是沒有問題了
1.2 服務消費者
1.2.1 啓動類
package cn.tanfp.cloudstudy.ribbondemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class RibbonDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonDemoApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
1.2.2 application.yml
server:
port: 8086
spring:
application:
name: ribbon-demo
serivce-provider:
host: localhost
port: 8085
1.2.3 controller
package cn.tanfp.cloudstudy.ribbondemo.web.controller;
import cn.tanfp.cloudstudy.ribbondemo.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ClientController {
@Autowired
private RestTemplate restTemplate;
@Value("${serivce-provider.host}")
private String serviceProviderHost;
@Value("${serivce-provider.port}")
private Integer serviceProviderPort;
@GetMapping("/a")
public String index() {
User user = new User();
user.setId(1L);
user.setName("tanfp");
return restTemplate.postForObject("http://" +
serviceProviderHost + ":" + serviceProviderPort +
"/greeting",
user, String.class);
}
}
1.2.4 測試
調用http://localhost:8086/a發現正常返回結果,說明使用restTemplate直接調用服務提供者的url是可以的。這種方式很直接,但是有幾個問題:
1. 用不了客戶端負載均衡(服務端負載均衡是可以的,但是使用服務端的負載均衡配置量會很大,而且每有新的客戶端服務器都需要配置一下負載均衡的配置,不能動態生成)
2. 寫起來麻煩
2. 使用ribbon(不集成eureka的情況)
2.1 引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2.2 啓動類
@SpringBootApplication
public class RibbonDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonDemoApplication.class, args);
}
//聲明 RestTemplate
// 啓用ribbon
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.3 application.yml
server:
port: 8086
spring:
application:
name: ribbon-demo
serivce-provider:
host: localhost
port: 8085
name: ribbon-service-provider
ribbon-service-provider:
ribbon:
listOfServers: http://${serivce-provider.host}:${serivce-provider.port}
定義ribbon的服務列表格式:服務提供方的應用名.ribbon.listOfServers,多個用逗號隔開
2.4 controller
package cn.tanfp.cloudstudy.ribbondemo.web.controller;
import cn.tanfp.cloudstudy.ribbondemo.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* Client {@link RestController}
*
* @author <a href="mailto:[email protected]">Mercy</a>
* @since TODO
*/
@RestController
public class ClientController {
@Autowired
private RestTemplate restTemplate;
@Value("${serivce-provider.name}")
private String serviceProviderName;
@GetMapping("/a")
public String index() {
User user = new User();
user.setId(1L);
user.setName("tanfp");
return restTemplate.postForObject("http://" +
serviceProviderName +
"/greeting",
user, String.class);
}
}
這裏是使用服務名代替了上面的域名+端口的形式
2.5 測試是否可用
調用http://localhost:8086/a發現正常返回結果,說明使用restTemplate直接調用服務提供者的服務名是可以的
2.6 總結:使用ribbon這種方式相對於上面的傳統直接調用的方式優勢就在於,只需要服務名就能調用了,就算有多個服務提供者,也不需要關注他們的域名和端口,只需要在application.yml進行簡單配置即可,且實現了客戶端負載均衡,但是這樣還是有些問題,比如我又加了一臺服務提供者,那麼我需要在每個消費者的application.yml中維護一下listOfServers屬性,這是非常麻煩的,所以我們想到了將eureka+ribbon結合的方式來做,這樣我們就不需要管理那些服務提供者的具體實例了,只需要知道他們的應用名就行了
3. Ribbon+Eureka
高可用的服務註冊中心我就不寫了,網上很容易找,我的博客中也有
3.1 服務提供方
3.1.1 添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.1.2 啓動類
@SpringBootApplication
@EnableDiscoveryClient
public class RibbonServiceProviderDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonServiceProviderDemoApplication.class, args);
}
}
3.1.3 配置
server:
port: 8085
spring:
application:
name: ribbon-service-provider
eureka:
client:
serviceUrl:
#配置多個eureka註冊中心
defaultZone: http://localhost:9094/eureka/,http://localhost:9093/eureka/
#關閉管理端actuator的安全,即/env /health端口完全開放
#management.security.enabled 在spring-boot2.0時過時,使用如下配置代替
management:
endpoints:
web:
exposure:
include: "*"
3.1.4 controller,這裏打印port是爲了測試負載均衡
@RestController
public class ServiceProviderController {
@Value("${server.port}")
private Integer port;
@PostMapping("/greeting")
public String greeting(@RequestBody User user) {
return "Greeting , " + user + " on port : " + port;
}
}
3.2 服務消費者
3.2.1 pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
3.2.2 啓動類
@SpringBootApplication
@EnableDiscoveryClient
public class RibbonDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonDemoApplication.class, args);
}
//聲明 RestTemplate
// 啓用ribbon
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3.2.3 配置
server:
port: 8086
spring:
application:
name: ribbon-demo
serivce-provider:
name: ribbon-service-provider
eureka:
client:
serviceUrl:
#配置多個eureka註冊中心
defaultZone: http://localhost:9094/eureka/,http://localhost:9093/eureka/
3.2.4 controller
@RestController
public class ClientController {
@Autowired
private RestTemplate restTemplate;
@Value("${serivce-provider.name}")
private String serviceProviderName;
@GetMapping("/a")
public String index() {
User user = new User();
user.setId(1L);
user.setName("tanfp");
return restTemplate.postForObject("http://" +
serviceProviderName +
"/greeting",
user, String.class);
}
}
3.2.5 測試是否可用
調用http://localhost:8086/a,結果返回正常就行了
3.2.6 測試負載均衡
爲了方便一點,我就不復制服務提供者項目了,直接在idea 中修改下啓動參數,然後開不同端口就好了。
選擇項目後,點擊編輯config,然後:
這裏一定要把allow parallel run 勾選上,意思是是否允許同一個項目可並行運行,第二步就是添加program 參數,
--server.port=8087,修改下端口,這樣等火啓動時不會報端口衝突錯誤。
保存後,你會發現之前已經運行的項目現在變成了可啓動的圖標,然後點擊即可
啓動後需要稍微一夥,讓該服務註冊到註冊中心上,然後請求http://localhost:8086/a,會發現,打印的端口會有變化,且時按輪詢的規則訪問的服務提供者