SpringBoot實戰電商項目mall(20k+star)地址:github.com/macrozheng/…java
Spring Cloud Hystrix 是Spring Cloud Netflix 子項目的核心組件之一,具備服務容錯及線程隔離等一系列服務保護功能,本文將對其用法進行詳細介紹。git
在微服務架構中,服務與服務之間經過遠程調用的方式進行通訊,一旦某個被調用的服務發生了故障,其依賴服務也會發生故障,此時就會發生故障的蔓延,最終致使系統癱瘓。Hystrix實現了斷路器模式,當某個服務發生故障時,經過斷路器的監控,給調用方返回一個錯誤響應,而不是長時間的等待,這樣就不會使得調用方因爲長時間得不到響應而佔用線程,從而防止故障的蔓延。Hystrix具有服務降級、服務熔斷、線程隔離、請求緩存、請求合併及服務監控等強大功能。github
這裏咱們建立一個hystrix-service模塊來演示hystrix的經常使用功能。web
<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</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
複製代碼
主要是配置了端口、註冊中心地址及user-service的調用路徑。spring
server:
port: 8401
spring:
application:
name: hystrix-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8001/eureka/
service-url:
user-service: http://user-service
複製代碼
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class HystrixServiceApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixServiceApplication.class, args);
}
複製代碼
@GetMapping("/testFallback/{id}")
public CommonResult testFallback(@PathVariable Long id) {
return userService.getUser(id);
}
複製代碼
@HystrixCommand(fallbackMethod = "getDefaultUser")
public CommonResult getUser(Long id) {
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
public CommonResult getDefaultUser(@PathVariable Long id) {
User defaultUser = new User(-1L, "defaultUser", "123456");
return new CommonResult<>(defaultUser);
}
複製代碼
@GetMapping("/testCommand/{id}")
public CommonResult testCommand(@PathVariable Long id) {
return userService.getUserCommand(id);
}
複製代碼
@HystrixCommand(fallbackMethod = "getDefaultUser",
commandKey = "getUserCommand",
groupKey = "getUserGroup",
threadPoolKey = "getUserThreadPool")
public CommonResult getUserCommand(@PathVariable Long id) {
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
複製代碼
@GetMapping("/testException/{id}")
public CommonResult testException(@PathVariable Long id) {
return userService.getUserException(id);
}
複製代碼
@HystrixCommand(fallbackMethod = "getDefaultUser2", ignoreExceptions = {NullPointerException.class})
public CommonResult getUserException(Long id) {
if (id == 1) {
throw new IndexOutOfBoundsException();
} else if (id == 2) {
throw new NullPointerException();
}
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
public CommonResult getDefaultUser2(@PathVariable Long id, Throwable e) {
LOGGER.error("getDefaultUser2 id:{},throwable class:{}", id, e.getClass());
User defaultUser = new User(-2L, "defaultUser2", "123456");
return new CommonResult<>(defaultUser);
}
複製代碼
當系統併發量愈來愈大時,咱們須要使用緩存來優化系統,達到減輕併發請求線程數,提供響應速度的效果。緩存
@GetMapping("/testCache/{id}")
public CommonResult testCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.getUserCache(id);
userService.getUserCache(id);
return new CommonResult("操做成功", 200);
}
複製代碼
@CacheResult(cacheKeyMethod = "getCacheKey")
@HystrixCommand(fallbackMethod = "getDefaultUser", commandKey = "getUserCache")
public CommonResult getUserCache(Long id) {
LOGGER.info("getUserCache id:{}", id);
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
/** * 爲緩存生成key的方法 */
public String getCacheKey(Long id) {
return String.valueOf(id);
}
複製代碼
@GetMapping("/testRemoveCache/{id}")
public CommonResult testRemoveCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.removeCache(id);
userService.getUserCache(id);
return new CommonResult("操做成功", 200);
}
複製代碼
@CacheRemove(commandKey = "getUserCache", cacheKeyMethod = "getCacheKey")
@HystrixCommand
public CommonResult removeCache(Long id) {
LOGGER.info("removeCache id:{}", id);
return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id);
}
複製代碼
java.lang.IllegalStateException: Request caching is not available. Maybe you need to initialize the HystrixRequestContext?
at com.netflix.hystrix.HystrixRequestCache.get(HystrixRequestCache.java:104) ~[hystrix-core-1.5.18.jar:1.5.18]
at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:478) ~[hystrix-core-1.5.18.jar:1.5.18]
at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:454) ~[hystrix-core-1.5.18.jar:1.5.18]
複製代碼
/** * Created by macro on 2019/9/4. */
@Component
@WebFilter(urlPatterns = "/*",asyncSupported = true)
public class HystrixRequestContextFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
context.close();
}
}
}
複製代碼
微服務系統中的服務間通訊,須要經過遠程調用來實現,隨着調用次數愈來愈多,佔用線程資源也會愈來愈多。Hystrix中提供了@HystrixCollapser用於合併請求,從而達到減小通訊消耗及線程數量的效果。架構
@GetMapping("/testCollapser")
public CommonResult testCollapser() throws ExecutionException, InterruptedException {
Future<User> future1 = userService.getUserFuture(1L);
Future<User> future2 = userService.getUserFuture(2L);
future1.get();
future2.get();
ThreadUtil.safeSleep(200);
Future<User> future3 = userService.getUserFuture(3L);
future3.get();
return new CommonResult("操做成功", 200);
}
複製代碼
@HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = {
@HystrixProperty(name = "timerDelayInMilliseconds", value = "100")
})
public Future<User> getUserFuture(Long id) {
return new AsyncResult<User>(){
@Override
public User invoke() {
CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
Map data = (Map) commonResult.getData();
User user = BeanUtil.mapToBean(data,User.class,true);
LOGGER.info("getUserById username:{}", user.getUsername());
return user;
}
};
}
@HystrixCommand
public List<User> getUserByIds(List<Long> ids) {
LOGGER.info("getUserByIds:{}", ids);
CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/getUserByIds?ids={1}", CommonResult.class, CollUtil.join(ids,","));
return (List<User>) commonResult.getData();
}
複製代碼
hystrix:
command: #用於控制HystrixCommand的行爲
default:
execution:
isolation:
strategy: THREAD #控制HystrixCommand的隔離策略,THREAD->線程池隔離策略(默認),SEMAPHORE->信號量隔離策略
thread:
timeoutInMilliseconds: 1000 #配置HystrixCommand執行的超時時間,執行超過該時間會進行服務降級處理
interruptOnTimeout: true #配置HystrixCommand執行超時的時候是否要中斷
interruptOnCancel: true #配置HystrixCommand執行被取消的時候是否要中斷
timeout:
enabled: true #配置HystrixCommand的執行是否啓用超時時間
semaphore:
maxConcurrentRequests: 10 #當使用信號量隔離策略時,用來控制併發量的大小,超過該併發量的請求會被拒絕
fallback:
enabled: true #用於控制是否啓用服務降級
circuitBreaker: #用於控制HystrixCircuitBreaker的行爲
enabled: true #用於控制斷路器是否跟蹤健康情況以及熔斷請求
requestVolumeThreshold: 20 #超過該請求數的請求會被拒絕
forceOpen: false #強制打開斷路器,拒絕全部請求
forceClosed: false #強制關閉斷路器,接收全部請求
requestCache:
enabled: true #用於控制是否開啓請求緩存
collapser: #用於控制HystrixCollapser的執行行爲
default:
maxRequestsInBatch: 100 #控制一次合併請求合併的最大請求數
timerDelayinMilliseconds: 10 #控制多少毫秒內的請求會被合併成一個
requestCache:
enabled: true #控制合併請求是否開啓緩存
threadpool: #用於控制HystrixCommand執行所在線程池的行爲
default:
coreSize: 10 #線程池的核心線程數
maximumSize: 10 #線程池的最大線程數,超過該線程數的請求會被拒絕
maxQueueSize: -1 #用於設置線程池的最大隊列大小,-1採用SynchronousQueue,其餘正數採用LinkedBlockingQueue
queueSizeRejectionThreshold: 5 #用於設置線程池隊列的拒絕閥值,因爲LinkedBlockingQueue不能動態改版大小,使用時須要用該參數來控制線程數
複製代碼
實例配置只須要將全局配置中的default換成與之對應的key便可。併發
hystrix:
command:
HystrixComandKey: #將default換成HystrixComrnandKey
execution:
isolation:
strategy: THREAD
collapser:
HystrixCollapserKey: #將default換成HystrixCollapserKey
maxRequestsInBatch: 100
threadpool:
HystrixThreadPoolKey: #將default換成HystrixThreadPoolKey
coreSize: 10
複製代碼
springcloud-learning
├── eureka-server -- eureka註冊中心
├── user-service -- 提供User對象CRUD接口的服務
└── hystrix-service -- hystrix服務調用測試服務
複製代碼
mall項目全套學習教程連載中,關注公衆號第一時間獲取。async