
eureka 啟動多個,實作高可用
2,3,4 都向注冊中心進行注冊,每一個子產品都應該使用多個,進行高可用
item和eureka實作高可用
實際情況下,需要在不同主機上進行部署,多台主機,端口号不用修改,但是在一台主機上測試,需要修改端口号
provider高可用配置
8001 端口
java -jar item.jar
java -jar item.jar --server.port=8002
通路兩個端口測試
http://localhost:8001/35
http://localhost:8002/35
成功測試界面:
Eureka高可用配置
兩個配置
主配置和profile同時存在會運作profile,profile是個性配置,主配置是通用配置
euraka1配置
eureka:
instance:
hostname: eureka1
client:
register-with-eureka: true
fetch-registry: true
euraka2配置
eureka:
instance:
hostname: eureka2
client:
register-with-eureka: true
fetch-registry: true
配置連接配接位址、
service-url.defaultZone: http://eureka2:2002/eureka
通路測試:
http://eureka1:2001/
http://eureka2:2002/
eureka用戶端注冊時,向兩個伺服器注冊
修改以下微服務:
sp02-itemservice
sp03-userservice
sp04-orderservice
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
當一個 eureka 服務當機時,仍可以連接配接另一個 eureka 服務
測試連接配接:
06 功能示範項目—遠端調用
06 調用02,03,04
02有叢集,需要負載均衡調用 Ribbon容錯
Ribbon 負載均衡和重試
負載均衡:
遠端調用一個服務,在它的叢集伺服器之間來回調用,分散通路壓力
重試:
一種容錯方式,當調用遠端伺服器失敗,可以自動進行重試調用
遠端調用工具:RestTemplate
Spring 提供的遠端調用工具,執行一個HTTP請求,并獲得響應,一般獲得的是JSON資料
底層有工具API,可将json轉化為java對象
對Rest API調用過程做了高度封裝,一步就可以完成發送請求,處理響應,json轉換
getForObject(url,轉化的類型,送出的參數)
postForObject(url,協定體資料,轉化的類型)
不是springCloud工具是Spring提供的遠端調用工具
Ribbon是對RestTemplate進行了封裝,增強了RestTemplate的功能增強
Ribbon 案例測試
增加配置:
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>sp01-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
RestTemplate
-http協定
-一次請求一次響應,之後斷開連接配接,是一種短連結
-直接連接配接Web(tomcat)伺服器
-調用的是Controller
Dubbo
-java序列化
-長連接配接
-連接配接Dubbo自己的端口(20880 預設)
-調用Service
-Dubbo效率高
1. 建立執行個體對象
1 在啟動類中配置
2 添加一個配置類
2.建立Controller
package cn.tedu.sp06.controller;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@Slf4j
public class RibbonController {
@Autowired
private RestTemplate rt;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId){
//調用遠端服務,調用的是路徑
return rt.getForObject("http://localhost:8001/{1}}", JsonResult.class,orderId);
}
}
通路測試:
調用遠端的user
@GetMapping("/user-service/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId){
return rt.getForObject("http://localhost:8101/{1}",
JsonResult.class,userId);
}
@GetMapping("/user-service/{userId}/score")
public JsonResult<?> addScore(@PathVariable Integer userId,Integer score){
return rt.getForObject("http://localhost:8101/{1}/score?score={2}",
JsonResult.class,userId,score);
}
調用遠端的訂單
@GetMapping("/order-service/")
public JsonResult<?> addOrder(String orderId){
return rt.getForObject("http://localhost:8201/", JsonResult.class);
}
@GetMapping("/order-service/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId){
return rt.getForObject("http://localhost:8201/{1}", JsonResult.class,orderId);
}
啟動服務
測試eureka:
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
ribbon 重試
pom.xml 添加 spring-retry 依賴
複制代碼到 pom.xml
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
yml
spring:
application:
name: ribbon
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
MaxAutoRetriesNextServer: 2
MaxAutoRetries: 1
OkToRetryOnAllOperations: true
ConnectionTimeout
ReadTimeout
OkToRetryOnAllOperations=true
預設隻對GET請求重試, 當設定為true時, 對POST等所有類型請求都重試
MaxAutoRetriesNextServer
更換執行個體的次數
MaxAutoRetries
目前執行個體重試次數,嘗試失敗會更換下一個執行個體
主程式設定 RestTemplate 的請求工廠的逾時屬性
package cn.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
f.setConnectTimeout(1000);
f.setReadTimeout(1000);
return new RestTemplate(f);
//RestTemplate 中預設的 Factory 執行個體中,兩個逾時屬性預設是 -1,
//未啟用逾時,也不會觸發重試
//return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Sp06RibbonApplication.class, args);
}
}
item-service 的 ItemController 添加延遲代碼,以便測試 ribbon 的重試機制
package cn.tedu.sp02.item.controller;
import java.util.List;
import java.util.Random;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
public class ItemController {
@Autowired
private ItemService itemService;
@Value("${server.port}")
private int port;
@GetMapping("/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) throws Exception {
log.info("server.port="+port+", orderId="+orderId);
///--設定随機延遲
if(Math.random()<0.6) {
long t = new Random().nextInt(5000);
log.info("item-service-"+port+" - 暫停 "+t);
Thread.sleep(t);
}
///~~
List<Item> items = itemService.getItems(orderId);
return JsonResult.ok(items).msg("port="+port);
}
@PostMapping("/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
itemService.decreaseNumbers(items);
return JsonResult.ok();
}
}
通路,測試 ribbon 重試機制
通過 ribbon 通路 item-service,當逾時,ribbon 會重試請求叢集中其他伺服器
http://localhost:3001/item-service/35
ribbon的重試機制,在 feign 和 zuul 中進一步進行了封裝,後續可以使用feign或zuul的重試機制
Hystrix 斷路器
微服務當機時,ribbon 無法轉發請求
複制 sp06-ribbon 項目,命名為sp07-hystrix
選擇 sp06-ribbon 項目,ctrl-c,ctrl-v,複制為sp07-hystrix
關閉 sp06-ribbon 項目,後續測試使用 sp07-hystrix 項目
修改 pom.xml
添加 hystrix 起步依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
修改 application.yml
spring:
application:
name: hystrix
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 2
OkToRetryOnAllOperations: true
主程式添加 @EnableCircuitBreaker 啟用 hystrix 斷路器
啟動斷路器,斷路器提供兩個核心功能:
降級,逾時、出錯、不可到達時,對服務降級,傳回錯誤資訊或者是緩存資料
熔斷,當服務壓力過大,錯誤比例過多時,熔斷所有請求,所有請求直接降級
可以使用 @SpringCloudApplication 注解代替三個注解
package cn.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
//@EnableCircuitBreaker
//@EnableDiscoveryClient
//@SpringBootApplication
@SpringCloudApplication
public class Sp06RibbonApplication {
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
f.setConnectTimeout(1000);
f.setReadTimeout(1000);
return new RestTemplate(f);
//RestTemplate 中預設的 Factory 執行個體中,兩個逾時屬性預設是 -1,
//未啟用逾時,也不會觸發重試
//return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Sp06RibbonApplication.class, args);
}
}
RibbonController 中添加降級方法
為每個方法添加降級方法,例如 getItems() 添加降級方法 getItemsFB()
添加 @HystrixCommand 注解,指定降級方法名
package cn.tedu.sp06.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@RestController
public class RibbonController {
@Autowired
private RestTemplate rt;
@GetMapping("/item-service/{orderId}")
@HystrixCommand(fallbackMethod = "getItemsFB") //指定降級方法的方法名
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
return rt.getForObject("http://item-service/{1}", JsonResult.class, orderId);
}
@PostMapping("/item-service/decreaseNumber")
@HystrixCommand(fallbackMethod = "decreaseNumberFB")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
}
/
@GetMapping("/user-service/{userId}")
@HystrixCommand(fallbackMethod = "getUserFB")
public JsonResult<User> getUser(@PathVariable Integer userId) {
return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
}
@GetMapping("/user-service/{userId}/score")
@HystrixCommand(fallbackMethod = "addScoreFB")
public JsonResult addScore(@PathVariable Integer userId, Integer score) {
return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
}
/
@GetMapping("/order-service/{orderId}")
@HystrixCommand(fallbackMethod = "getOrderFB")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);
}
@GetMapping("/order-service")
@HystrixCommand(fallbackMethod = "addOrderFB")
public JsonResult addOrder() {
return rt.getForObject("http://order-service/", JsonResult.class);
}
/
//降級方法的參數和傳回值,需要和原始方法一緻,方法名任意
public JsonResult<List<Item>> getItemsFB(String orderId) {
return JsonResult.err("擷取訂單商品清單失敗");
}
public JsonResult decreaseNumberFB(List<Item> items) {
return JsonResult.err("更新商品庫存失敗");
}
public JsonResult<User> getUserFB(Integer userId) {
return JsonResult.err("擷取使用者資訊失敗");
}
public JsonResult addScoreFB(Integer userId, Integer score) {
return JsonResult.err("增加使用者積分失敗");
}
public JsonResult<Order> getOrderFB(String orderId) {
return JsonResult.err("擷取訂單失敗");
}
public JsonResult addOrderFB() {
return JsonResult.err("添加訂單失敗");
}
}
hystrix 逾時設定
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
hystrix等待逾時後, 會執行降級代碼, 快速向用戶端傳回降級結果, 預設逾時時間是1000毫秒
為了測試 hystrix 降級,我們把 hystrix 等待逾時設定得非常小(500毫秒)
此設定一般應大于 ribbon 的重試逾時時長,例如 10 秒
spring:
application:
name: hystrix
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
MaxAutoRetriesNextServer: 2
MaxAutoRetries: 1
OkToRetryOnAllOperations: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
啟動項目進行測試
通過 hystrix 服務,通路可能逾時失敗的 item-service
http://localhost:3001/item-service/35
通過 hystrix 服務,通路未啟動的 user-service
http://localhost:3001/user-service/7
可以看到,如果 item-service 請求逾時,hystrix 會立即執行降級方法
通路 user-service,由于該服務未啟動,hystrix也會立即執行降級方法
hystrix dashboard 斷路器儀表盤
hystrix 對請求的降級和熔斷,可以産生監控資訊,hystrix dashboard可以實時的進行監控
sp07-hystrix 項目添加 actuator,并暴露 hystrix 監控端點
actuator 是 spring boot 提供的服務監控工具,提供了各種監控資訊的監控端點
management.endpoints.web.exposure.include 配置選項,
可以指定端點名,來暴露監控端點
如果要暴露所有端點,可以用 “*”
pom.xml 添加 actuator 依賴
右鍵點選項目或pom.xml, 編輯起步依賴, 添加 actuator 依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
調整 application.yml 配置,并暴露 hystrix.stream 監控端點
spring:
application:
name: hystrix
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
MaxAutoRetriesNextServer: 1
MaxAutoRetries: 1
OkToRetryOnAllOperations: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
management:
endpoints:
web:
exposure:
include: hystrix.stream
通路 actuator 路徑,檢視監控端點
http://localhost:3001/actuator
Hystrix dashboard 儀表盤
建立 sp08-hystrix-dashboard 項目
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>sp08-hystrix-dashboard</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sp08-hystrix-dashboard</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<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-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
spring:
application:
name: hystrix-dashboard
server:
port: 4001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
hystrix:
dashboard:
proxy-stream-allow-list: localhost
主程式添加 @EnableHystrixDashboard 和 @EnableDiscoveryClient
package cn.tedu.sp08;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@EnableDiscoveryClient
@EnableHystrixDashboard
@SpringBootApplication
public class Sp08HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(Sp08HystrixDashboardApplication.class, args);
}
}
啟動,并通路測試
通路 hystrix dashboard
http://localhost:4001/hystrix
填入 hystrix 的監控端點,開啟監控
http://localhost:3001/actuator/hystrix.stream
通過 hystrix 通路服務多次,觀察監控資訊
http://localhost:3001/item-service/35
http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/