在分布式環境下,微服務之間不可避免的發生互相調用的情況,但是沒有一個系統是能保證自身絕對正确的,在服務的調用過程中,很可能面臨服務失敗的問題,是以需要一個公共元件能夠在服務通過網絡請求通路其他微服務時,能對服務失效情況下有很強的容錯能力,對微服務提供保護和監控。
Hystrix是netflix的一個開源項目,他能夠在依賴服務失效的情況下,通過隔離系統依賴的方式,防止服務的級聯失敗(服務的雪崩)
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSPrR0T1UleOFTSU5ENj1mYoR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwkDN2ETNxEjM1ADMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
對于服務的熔斷機制,其實需要考慮兩種情況
- 服務提供方存活,但調用接口報錯
- 服務提供方本身就出問題了
服務提供方報錯
其實這種情況類似于異常捕獲機制,當出現異常,傳回一個通用的接口封包
【springcloud-provider-product】 複制一份成為【springcloud-provider-product-hystrix】
【springcloud-provider-product-hystrix】修改pom檔案,增加 Hystrix依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
【springcloud-provider-product-hystrix】 修改ProductController
package cn.enjoy.controller;
import cn.enjoy.service.IProductService;
import cn.enjoy.vo.Product;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("/prodcut")
public class ProductController {
@Resource
private IProductService iProductService;
@Resource
private DiscoveryClient client ; // 進行Eureka的發現服務
@RequestMapping(value="/get/{id}")
@HystrixCommand(fallbackMethod = "getFallback")
public Object get(@PathVariable("id") long id) {
Product product = this.iProductService.get(id);
if(product == null) {
throw new RuntimeException("該産品已下架!") ;
}
return product;
}
public Object getFallback(@PathVariable("id") long id){
Product product = new Product();
product.setProductName("HystrixName");
product.setProductDesc("HystrixDesc");
product.setProductId(0L);
return product;
}
@RequestMapping(value="/add")
public Object add(@RequestBody Product product) {
return this.iProductService.add(product) ;
}
@RequestMapping(value="/list")
public Object list() {
return this.iProductService.list() ;
}
@RequestMapping("/discover")
public Object discover() { // 直接傳回發現服務資訊
return this.client ;
}
}
一旦 get()方法上抛出了錯誤的資訊,那麼就認為該服務有問題
會預設使用“@HystrixCommand”注解之中配置好的fallbackMethod 調用類中的指定方法,傳回相應資料
【springcloud-provider-product-hystrix】修改啟動類,增加對熔斷的支援
package cn.enjoy;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@MapperScan("cn.enjoy.mapper")
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ProductHystrixApp {
public static void main(String[] args) {
SpringApplication.run(ProductHystrixApp.class,args);
}
}
測試:localhost:8080/prodcut/get/100 通路
服務失連
在某些情況下,服務提供方并沒有失效,但可能由于網絡原因,服務的消費方并不能調用到服務接口,在這種情況下,直接在服務的提供方提供熔斷機制依然還是不夠的,這方面的處理需要在服務的消費方進行服務的回退(服務的降級)處理
服務的熔斷:熔斷指的是當服務的提供方不可使用的時候,程式不會出現異常,而會出現本地的操作調用,服務的熔斷是在服務消費方實作的,在斷網情況下服務提供方的任何處理都是沒有意義的。
【springcloud-service】新增一個IProductClientService的失敗調用(降級處理)
package cn.enjoy.service.fallback;
import cn.enjoy.service.IProductClientService;
import cn.enjoy.vo.Product;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class IProductClientServiceFallbackFactory implements FallbackFactory<IProductClientService> {
@Override
public IProductClientService create(Throwable throwable) {
return new IProductClientService() {
@Override
public Product getProduct(long id) {
Product product = new Product();
product.setProductId(999999L);
product.setProductName("feign-hystrixName");
product.setProductDesc("feign-hystrixDesc");
return product;
}
@Override
public List<Product> listProduct() {
return null;
}
@Override
public boolean addPorduct(Product product) {
return false;
}
};
}
}
【springcloud-service】 修改IProductClientService,增加fallback配置
package cn.enjoy.service;
import cn.enjoy.feign.FeignClientConfig;
import cn.enjoy.service.fallback.IProductClientServiceFallbackFactory;
import cn.enjoy.vo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@FeignClient(name = "MICROCLOUD-PROVIDER-PRODUCT",configuration = FeignClientConfig.class,
fallbackFactory = IProductClientServiceFallbackFactory.class)
public interface IProductClientService {
@RequestMapping("/prodcut/get/{id}")
public Product getProduct(@PathVariable("id")long id);
@RequestMapping("/prodcut/list")
public List<Product> listProduct() ;
@RequestMapping("/prodcut/add")
public boolean addPorduct(Product product) ;
}
【springcloud-consumer-feign】 複制一份成為【springcloud-consumer-hystrix】子產品
【springcloud-consumer-hystrix】 修改application.yml配置檔案,啟用hystrix配置
feign:
hystrix:
enabled: true
compression:
request:
enabled: true
mime-types: # 可以被壓縮的類型
- text/xml
- application/xml
- application/json
min-request-size: 2048 # 超過2048的位元組進行壓縮
啟動,服務提供者
通路:http://localhost/consumer/product/get?id=1,能正常通路
關閉,服務提供者
通路:http://localhost/consumer/product/get?id=1,也能正常通路
HystrixDashboard
在hystrix裡面提供一個Dashboard(儀表盤)的功能,他是一種監控的功能,可以利用它來進行整體服務的監控
建立一個子產品【springcloud-consumer-hystrix-dashboard】
【springcloud-consumer-hystrix-dashboard】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">
<parent>
<artifactId>springcloud</artifactId>
<groupId>enjoy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-hystrix-dashboard</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
</project>
【springcloud-provider-product-hystrix】 pom檔案確定裡面有健康檢查子產品
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
【springcloud-consumer-hystrix-dashboard】 修改application.yml配置檔案
server:
port: 9001
【springcloud-consumer-hystrix-dashboard】 建立一個啟動類
package cn.enjoy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApp {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApp.class,args);
}
}
啟動運作:http://localhost:9001/hystrix
【springcloud-provider-product-hystrix】 修改applcation.yml檔案
management:
endpoints:
web:
exposure:
include: '*'
【springcloud-provider-product-hystrix】啟動
通路:localhost:8080/actuator/hystrix.stream
http://localhost:9001/hystrix 填寫資訊如下
http://admin:[email protected]:8080/actuator/hystrix.stream
這個時候對localhost:8080的通路都可以被監控到
Turbine
HystrixDashboard 前面已經知道了,它的主要功能是可以對某一項微服務進行監控,但真實情況下,不可能隻對某一個服務進行監控,更多的是對很多服務進行一個整體的監控,這個時候就需要使用到turbine來完成了。
為了示範監控多個服務子產品,這個時候建立一個子產品【springcloud-provider-user-hystrix】,為簡單起見,這個子產品并不連接配接資料庫,也不做安全控制。
【springcloud-provider-user-hystrix】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">
<parent>
<artifactId>springcloud</artifactId>
<groupId>enjoy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-user-hystrix</artifactId>
<dependencies>
<dependency>
<groupId>enjoy</groupId>
<artifactId>springcloud-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
</project>
【springcloud-api】新增一個VO類:Users
package cn.enjoy.vo;
import java.io.Serializable;
public class Users implements Serializable {
private String name;
private int age;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
【springcloud-provider-user-hystrix】 建立一個UserController
package cn.enjoy.controller;
import cn.enjoy.vo.Users;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users")
public class UserController {
@RequestMapping("/get/{name}")
@HystrixCommand
public Object get(@PathVariable("name")String name) {
Users users = new Users();
users.setName(name);
users.setAge(18);
users.setSex("F");
return users;
}
}
【springcloud-provider-user-hystrix】新增啟動類
package cn.enjoy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableCircuitBreaker
@EnableEurekaClient
public class UsersApp {
public static void main(String[] args) {
SpringApplication.run(UsersApp.class,args);
}
}
【springcloud-provider-user-hystrix】修改application.yml配置檔案
server:
port: 8090
spring:
application:
name: springcloud-provider-users
logging:
level:
cn.enjoy.mapper: debug
eureka:
client: # 用戶端進行Eureka注冊的配置
service-url:
defaultZone: http://admin:enjoy@eureka1:7001/eureka,http://admin:enjoy@eureka2:7002/eureka,http://admin:enjoy@eureka3:7003/eureka
instance:
instance-id: springcloud-provider-users
prefer-ip-address: true
lease-renewal-interval-in-seconds: 2 # 設定心跳的時間間隔(預設是30秒)
lease-expiration-duration-in-seconds: 5 # 如果現在超過了5秒的間隔(預設是90秒)
info:
app.name: springcloud-provider-users
company.name: enjoy
build.artifactId: $project.artifactId$
build.modelVersion: $project.modelVersion$
management:
endpoints:
web:
exposure:
include: '*'
啟動後:
通路位址:http://localhost:8090/users/get/enjoy
hystrix監控位址:http://localhost:8090/actuator/hystrix.stream
前面準備工作完成後,如果想要實作 turbine 的配置,準備一個turbine子產品
新增【springcloud-consumer-turbine】子產品,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">
<parent>
<artifactId>springcloud</artifactId>
<groupId>enjoy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-turbine</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
</dependencies>
</project>
【springcloud-consumer-turbine】修改application.yml配置檔案
server:
port: 9101
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://admin:enjoy@eureka1:7001/eureka,http://admin:enjoy@eureka2:7002/eureka,http://admin:enjoy@eureka3:7003/eureka
turbine:
app-config: MICROCLOUD-PROVIDER-PRODUCT,MICROCLOUD-PROVIDER-USERS
cluster-name-expression: new String("default")
可以發現對于turbine,其實是從eureka配置在app-config中服務,然後進行監控
【springcloud-consumer-turbine】 建立一個啟動類
package cn.enjoy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
@SpringBootApplication
@EnableTurbine
public class TurbineApp {
public static void main(String[] args) {
SpringApplication.run(TurbineApp.class,args);
}
}
turbine監控位址:
啟動Dashboard: http://localhost:9001/hystrix
在Dashboard裡面填上 turbine監控位址
發現目前turbine隻監控了UserController的資訊,看下turbine背景發現報錯
其實原因也很簡單,User服務并不需要使用者驗證,是以能正常通路,但對于Product服務,配置了使用者名密碼的,turbine肯定無法通路
【springcloud-security】如果現在需要turbine進行加密服務的通路,那麼隻能折衷處理,讓通路/actuator/hystrix.stream與/turbine.stream這兩個位址的時候不需要使用者密碼驗證
【springcloud-security】 修改WebSecurityConfiguration
package cn.enjoy.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("root").password(new BCryptPasswordEncoder().encode("enjoy")).roles("USER").
and().withUser("admin").password(new BCryptPasswordEncoder().encode("enjoy")).roles("USER", "ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().anyRequest()
.fullyAuthenticated();
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/actuator/hystrix.stream","/turbine.stream") ;
}
}
turbine監控位址:http://localhost:9101/turbine.stream
啟動Dashboard: http://localhost:9001/hystrix
在Dashboard裡面填上 turbine監控位址
重新整理:
http://localhost:8080/prodcut/get/1
http://localhost:8090/users/get/1
這就可以正常監控兩個服務了