接着上一篇:SpringCloud學習之Hystrix(一)java
上一篇寫到可使用請求緩存來減輕高併發時的請求線程消耗、下降請求相應時間。請求合併又是什麼東西呢?在微服務架構中,咱們將項目拆分紅多個模塊,每一個模塊間經過遠程調用進行通訊。遠程調用最多見的問題是通訊消耗與鏈接數佔用。在高併發狀況下。隨着通訊次數的增長,通訊時間會增長;由於依賴服務的線程池資源有限,將出現排隊等待與響應延遲的狀況。請求合併正是Hystrix爲解決這兩個問題而開發的,以減小通訊消耗和線程數的佔用。<!--more-->git
Hystrix提供HystrixCollapser來實現請求轉發,在HystrixCommand以前放置一個合併處理器,將處於一個很短的時間窗(默認爲10毫秒)內對同一個依賴服務的多個請求進行整合並以批量方式發起請求,
![]()
HystrixCollapser是一個抽象類,進入源碼能夠看到,它指定了三個不一樣的類。
對於這三個類型的使用:spring
//用來定義獲取請求參數的方法 public abstract RequestArgumentType getRequestArgument(); //合併請求產生批量命令的具體實現 protected abstract HystrixCommand<BatchReturnType> createCommand(Collection<com.netflix.hystrix.HystrixCollapser.CollapsedRequest<ResponseType, RequestArgumentType>> requests); //批量命令結果返回後的處理,這裏須要實現將批量命令結果拆分並傳遞給合併前各個原子請求命令的邏輯 protected abstract void mapResponseToRequests(BatchReturnType batchResponse, Collection<com.netflix.hystrix.HystrixCollapser.CollapsedRequest<ResponseType, RequestArgumentType>> requests);
修改服務提供者
在以前的代碼基礎上,在USER-SERVICE中添加一個接口,這裏使用到的兩個接口:express
@GetMapping("/user/{id}") public User findById(@PathVariable("id") Long id){ return userRepository.findOne(id); } @GetMapping("/users/ids") public List<User> findUserByIds(String ids){ System.out.println(">>>>>>>>>>"+ids); String[] split = ids.split(","); List<User> result = new ArrayList<>(); for (String s : split){ Long id = Long.valueOf(s); User user = userRepository.findOne(id); result.add(user); } return result; }
服務消費者緩存
public User getUserById(@CacheKey("id") Long id) { return restTemplate.getForObject("http://USER-SERVICE/user/{1}", User.class, id); } public List<User> findUserByIds(List<Long> ids){ System.out.println("findUserByIds---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName()); String str = StringUtils.join(ids,","); User[] users = restTemplate.getForObject("http://USER-SERVICE/users/ids?ids={1}", User[].class,str); return Arrays.asList(users); }
public class UserBatchCommand extends HystrixCommand<List<User>> { private final Logger logger = LoggerFactory.getLogger(UserCommand.class); private List<Long> ids; private ArticleService articleService; public UserBatchCommand(ArticleService articleService,List<Long> ids){ super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("userGroup"))); this.ids = ids; this.articleService = articleService; } @Override protected List<User> run() throws Exception { return articleService.findUserByIds(ids); } }
public class UserCollapdeCommand extends HystrixCollapser<List<User>,User,Long> { private ArticleService articleService; private Long id; public UserCollapdeCommand(ArticleService articleService,Long id){ //設置時間延遲屬性,延遲時間窗爲100毫秒 super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("userCollapdeCommand")).andCollapserPropertiesDefaults( HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100) )); this.articleService = articleService; this.id = id; } /** * 返回單個請求參數id * @return */ @Override public Long getRequestArgument() { return id; } /** * 這裏經過獲取單個請求的參數來組織批量請求命令UserBatchCommand的實例 * @param collapsedRequests 保存了延遲時間窗中收集到的全部獲取單個User的請求。 * @return */ @Override protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Long>> collapsedRequests) { ArrayList<Long> userIds = new ArrayList<>(collapsedRequests.size()); userIds.addAll(collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList())); return new UserBatchCommand(articleService,userIds); } /** * 該方法在批量請求命令UserBatchCommand執行完成以後執行 * 經過遍歷batchResponse來爲collapsedRequests設置請求結果。 * @param batchResponse 保存了createCommand中組織的批量請求返回結果 * @param collapsedRequests 每一個被合併的請求, */ @Override protected void mapResponseToRequests(List<User> batchResponse, Collection<CollapsedRequest<User, Long>> collapsedRequests) { int count = 0; for (CollapsedRequest<User,Long> collapsedRequest : collapsedRequests){ User user = batchResponse.get(count++); collapsedRequest.setResponse(user); } } }
@GetMapping("/testBathCommand") public List<User> testBathCommand() throws ExecutionException, InterruptedException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); UserCollapdeCommand u1 = new UserCollapdeCommand(articleService, 1L); UserCollapdeCommand u2 = new UserCollapdeCommand(articleService, 2L); UserCollapdeCommand u3 = new UserCollapdeCommand(articleService, 3L); UserCollapdeCommand u4 = new UserCollapdeCommand(articleService, 4L); Future<User> q1 = u1.queue(); Future<User> q2 = u2.queue(); Future<User> q3 = u3.queue(); Future<User> q4 = u4.queue(); User e1 = q1.get(); User e2 = q2.get(); User e3 = q3.get(); User e4 = q4.get(); List<User> res = new ArrayList<>(); res.add(e1); res.add(e2); res.add(e3); res.add(e4); System.out.println(res); return res; }
上面使用繼承類的方式可能會有些繁瑣,在Hystrix中一樣提供了註解來優雅的實現請求合併。架構
@HystrixCollapser(batchMethod = "findAll",collapserProperties = { @HystrixProperty(name = "DelayInMilliseconds",value = "100") }) public User findOne(Long id){ return null; } @HystrixCommand public List<User> findAll(List<Long> ids){ System.out.println("findUserByIds---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName()); String str = StringUtils.join(ids,","); User[] users = restTemplate.getForObject("http://USER-SERVICE/users/ids?ids={1}", User[].class,str); return Arrays.asList(users); }
這裏經過@HystrixCollapser註解建立合併請求器,經過batchMethod屬性指定實現批量請求的findAll方法,經過HystrixProperty屬性爲合併請求器設置相關屬性。 @HystrixProperty(name = "DelayInMilliseconds",value = "100")
設置時間窗爲100毫秒。這裏直接調用findOne方法便可,使用註解確實是簡單。
測試接口併發
@GetMapping("/testBathCommandAnn") public List<User> testBathCommandAnn() throws ExecutionException, InterruptedException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); Future<User> q1 = articleService.findOne(1L); Future<User> q2 = articleService.findOne(2L); Future<User> q3 = articleService.findOne(3L); Future<User> q4 = articleService.findOne(4L); User e1 = q1.get(); User e2 = q2.get(); User e3 = q3.get(); User e4 = q4.get(); List<User> res = new ArrayList<>(); res.add(e1); res.add(e2); res.add(e3); res.add(e4); System.out.println(res); return res; }
Hystrix提供了很是靈活的配置方式,全部屬性存在下面四個優先級的配置(優先級由低到高):app
Hystrix中主要的三個屬性:異步
關於屬性參數的更多詳解能夠查看《SpringCloud微服務實戰》ide
儀表盤是Hystrix Dashboard提供的用來實時監控Hystrix的指標信息的組件。經過該組件反饋的實時信息,能夠幫助咱們快速的發現系統存在的問題。項目結構圖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
@EnableHystrixDashboard
開啓HystrixDashboard功能。spring: application: name: hystrix-dashboard server: port: 9999
spring-boot-starter-actuator
監控模塊的以開啓監控相關的端點。還有hystrix依賴是必定要的。而且確保服務以及使用@EnableCircuitBreaker
開啓了斷路器功能。<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
可使用Turbine實現集羣監控,該端點爲/trubine.stream。和上面同樣,新建一個SpringBoot項目,這裏命名爲hystrix-turbine。添加如下依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-turbine</artifactId> </dependency> </dependencies>
在主類使用@EnableTurbine
註解開啓Trubine
@SpringBootApplication @EnableDiscoveryClient @EnableTurbine public class TurbineApplication { public static void main(String[] args) { SpringApplication.run(TurbineApplication.class, args); } }
修改配置文件
spring.application.name=hystrix-turbine server.port=9998 management.port=9000 eureka.client.service-url.defaultZone=http://localhost:8888/eureka/ turbine.app-config=article-service turbine.cluster-name-expression="default" turbine.combine-host-port=true
- turbine.app-config=ribbon-consumer指定了要監控的應用名字爲ribbon-consumer
- turbine.cluster-name-expression=」default」,表示集羣的名字爲default
- turbine.combine-host-port=true表示同一主機上的服務經過host和port的組合來進行區分,默認狀況下是使用host來區分,這樣會使本地調試有問題
最後啓動項目,並啓動兩個article-service,而後添加對 http://localhost:9998/turbine.stream的監控。
做爲SpringCloud學習筆記,有不少地方很差。望指出!!!