SpringCloud學習筆記之用戶端負載均衡Ribbon
前言
Ribbon 初識
Spring Cloud Ribbon 是一個基于 HTTP 和 TCP 的用戶端負載均衡工具,它基于 Netflix Ribbon 實作。
通過 Spring Cloud Ribbon 的封裝,我們在微服務架構中使用用戶端負載均衡調用非常簡單,僅需兩步:
- 服務提供者隻需啟動多個服務執行個體不注冊到一個注冊中心或者是多個相關聯的服務注冊中心;
- 服務消費者直接調用被 @LoadBalanced 注解修飾過的 RestTemplate 來實作面向服務的接口調用;
項目實戰
pom 依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>top.simba1949</groupId>
<artifactId>spring-cloud-rabbion-consumer</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--spring boot starter : Core starter, including auto-configuration support, logging and YAML-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--spring boot starter test : Starter for testing Spring Boot applications with libraries including JUnit, Hamcrest and Mockito-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--spring boot starter actuator:
Starter for using Spring Boot’s Actuator which provides production ready features to help you monitor and manage your application
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--spring boot starter web : Starter for building web, including RestFul, applications using Spring MVC. Uses Tomcat as the default embedded container-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- ribbon 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!--編譯插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- 配置使用的 jdk 版本 -->
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
<!--springboot-maven打包插件 和 熱部署配置-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork> <!-- 如果沒有該配置,devtools不會生效 -->
<executable>true</executable><!--将項目注冊到linux服務上,可以通過指令開啟、關閉以及伴随開機啟動等功能-->
</configuration>
</plugin>
<!--資源拷貝插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
<!--IDEA是不會編譯src的java目錄的xml檔案,如果需要讀取,則需要手動指定哪些配置檔案需要讀取-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
</build>
</project>
application.properties
server.port=7000
spring.application.name=spring-cloud-rabbion-consumer
eureka.instance.hostname=127.0.0.1
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:8081/eureka
eureka.client.eureka-server-connect-timeout-seconds=20
eureka.client.eureka-server-read-timeout-seconds=20
Rabbtion 配置類
package top.simba1949.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author SIMBA1949
* @date 2019/7/12 15:57
*/
@Configuration
public class RibbonConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
JavaBean 類
package top.simba1949.common;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* @author SIMBA1949
* @date 2019/7/12 14:25
*/
@Data
public class UserDTO implements Serializable {
private static final long serialVersionUID = 5572396433234547850L;
private Integer id;
private String username;
private String password;
private Date birthday;
private Boolean adultFlag;
private List<String> stringList;
}
控制層
package top.simba1949.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import top.simba1949.common.UserDTO;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author SIMBA1949
* @date 2019/7/12 15:59
*/
@RestController
@RequestMapping("user")
public class UserController {
private static final String SERVICE_NAME = "http://SPRING-CLOUD-RABBION-PROVIDER";
@Autowired
private RestTemplate restTemplate;
@GetMapping("test")
public String test(){
return "SUCCESS";
}
@GetMapping("list")
public List<UserDTO> userList(){
ResponseEntity<List> resp = restTemplate.getForEntity(SERVICE_NAME + "/user/list", List.class);
List body = resp.getBody();
List list = restTemplate.getForObject(SERVICE_NAME + "/user/list", List.class);
return body;
}
@GetMapping("get")
public UserDTO get(UserDTO userDTO){
Map map = new HashMap<>(16);
map.put("id", userDTO.getId());
map.put("username", userDTO.getUsername());
// 通過可變參數傳遞參數
ResponseEntity<UserDTO> resp = restTemplate.getForEntity(SERVICE_NAME + "/user/get?id={1}&username={2}", UserDTO.class, userDTO.getId(), userDTO.getUsername());
// 通過 Map 傳遞參數
ResponseEntity forEntity = restTemplate.getForEntity(SERVICE_NAME + "/user/get?id={id}&username={username}", UserDTO.class, map);
UserDTO userDTO1 = restTemplate.getForObject(SERVICE_NAME + "/user/get?id={1}&username={2}", UserDTO.class, userDTO.getId(), userDTO.getUsername());
UserDTO userDTO2 = restTemplate.getForObject(SERVICE_NAME + "/user/get?id={id}&username={username}", UserDTO.class, map);
return null;
}
@PostMapping
public String insert(@RequestBody UserDTO userDTO){
ResponseEntity<UserDTO> resp = restTemplate.postForEntity(SERVICE_NAME + "/user/", userDTO, UserDTO.class);
UserDTO dto = restTemplate.postForObject(SERVICE_NAME + "/user/", userDTO, UserDTO.class);
URI uri = restTemplate.postForLocation(SERVICE_NAME + "/user/", userDTO, UserDTO.class);
return "SUCCESS";
}
@PutMapping
public String update(@RequestBody UserDTO userDTO){
restTemplate.put(SERVICE_NAME + "/user/", userDTO, UserDTO.class);
return "SUCCESS";
}
@DeleteMapping
public String delete(Integer id){
restTemplate.delete(SERVICE_NAME + "/user?id={1}", id);
return "SUCCESS";
}
}
啟動類
package top.simba1949;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author SIMBA1949
* @date 2019/7/12 15:53
*/
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
RestTemplate 詳解
ResponseEntity
getForEntity() 方法傳回的是 ResponseEntity ,該對象是 Spring 對 HTTP 請求響應的封裝,其中主要存儲了 HTTP 的幾個重要元素,比如 HTTP 請求狀态碼的枚舉對象 HttpStatus,在它的父類 HttpEntity 中還存儲 HTTP 請求頭資訊對象 HttpHeaders 以及泛型類型的請求體對象。
GET 請求
getForEntity
- url:為請求位址,http:// 服務名 / 具體請求路徑
- responseType:為請求響應體 body 的包裝類型
- uriVariables:url 中綁定的參數
通過可變參數,使用數值占位符
// 格式
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
// 示例
ResponseEntity<UserDTO> resp = restTemplate.getForEntity("http://SPRING-CLOUD-RABBION-PROVIDER/user/get?id={1}&username={2}", UserDTO.class, userDTO.getId(), userDTO.getUsername());
通過 Map, 使用 key 值占位符
// 格式
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
// 示例
Map map = new HashMap<>(16);
map.put("id", userDTO.getId());
map.put("username", userDTO.getUsername());
ResponseEntity forEntity = restTemplate.getForEntity("http://SPRING-CLOUD-RABBION-PROVIDER/user/get?id={id}&username={username}", UserDTO.class, map);
無參數
// 格式
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
// 示例
ResponseEntity<List> resp = restTemplate.getForEntity("http://SPRING-CLOUD-RABBION-PROVIDER/user/list", List.class);
getObject
// 格式
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
// 示例
ResponseEntity<UserDTO> resp = restTemplate.getForEntity("http://SPRING-CLOUD-RABBION-PROVIDER/user/get?id={1}&username={2}", UserDTO.class, userDTO.getId(), userDTO.getUsername());
// 格式
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
// 示例
Map map = new HashMap<>(16);
map.put("id", userDTO.getId());
map.put("username", userDTO.getUsername());
ResponseEntity forEntity = restTemplate.getForEntity("http://SPRING-CLOUD-RABBION-PROVIDER/user/get?id={id}&username={username}", UserDTO.class, map);
// 格式
public <T> T getForObject(URI url, Class<T> responseType)
// 示例
List list = restTemplate.getForObject("http://SPRING-CLOUD-RABBION-PROVIDER/user/list/user/list", List.class);
POST 請求
- request:為請求體的資料
postForEntity
// 1. 數值占位符(示例參考 get 請求)
public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,
Class<T> responseType, Object... uriVariables)
// 2. key 值占位符(示例參考 get 請求)
public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,
Class<T> responseType, Map<String, ?> uriVariables)
// 3. 無 url 綁定參數(示例參考 get 請求)
public <T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType)
postForObject
// 1. 數值占位符(示例參考 get 請求)
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
Object... uriVariables)
// 2. key 值占位符(示例參考 get 請求)
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
Map<String, ?> uriVariables)
// 3. 無 url 綁定參數(示例參考 get 請求)
public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType)
postForLocation
該方法實作以 POST 請求送出資源,并傳回新資源的 URI
// 1. 數值占位符(示例參考 get 請求)
public URI postForLocation(String url, @Nullable Object request, Object... uriVariables)
// 2. key 值占位符(示例參考 get 請求)
public URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVariables)
// 3. 無 url 綁定參數(示例參考 get 請求)
public URI postForLocation(URI url, @Nullable Object request)
PUT 請求
// 1. 數值占位符(示例參考 get 請求)
public void put(String url, @Nullable Object request, Object... uriVariables)
// 2. key 值占位符(示例參考 get 請求)
public void put(String url, @Nullable Object request, Map<String, ?> uriVariables)
// 3. 無 url 綁定參數(示例參考 get 請求)
public void put(URI url, @Nullable Object request)
DELETE 請求
// 1. 數值占位符(示例參考 get 請求)
public void delete(String url, Object... uriVariables)
// 2. key 值占位符(示例參考 get 請求)
public void delete(String url, Map<String, ?> uriVariables)
// 3. 無 url 綁定參數(示例參考 get 請求)
public void delete(URI url)