基于springcloud異步線程池、高并發(fā)請求feign的解決方案
ScenTaskTestApplication.java
package com.test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @author scen * @version 2018年9月27日 上午11:51:04 */ @EnableFeignClients @SpringBootApplication public class ScenTaskTestApplication { public static void main(String[] args) { SpringApplication.run(ScenTaskTestApplication.class, args); } }
application.properties
spring.application.name=scen-task-test server.port=9009 feign.hystrix.enabled=true #熔斷器失敗的個數(shù)==進入熔斷器的請求達到1000時服務(wù)降級(之后的請求直接進入熔斷器) hystrix.command.default.circuitBreaker.requestVolumeThreshold=1000 #回退最大線程數(shù) hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=50 #核心線程池數(shù)量 hystrix.threadpool.default.coreSize=130 #請求處理的超時時間 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=100000 ribbon.ReadTimeout=120000 #請求連接的超時時間 ribbon.ConnectTimeout=130000 eureka.instance.instance-id=${spring.application.name}:${spring.application.instance_id:${server.port}} eureka.instance.preferIpAddress=true eureka.client.service-url.defaultZone=http://127.0.0.1:9000/eureka logging.level.com.test.user.service=debug logging.level.org.springframework.boot=debug logging.level.custom=info
AsyncConfig.java
package com.test; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; /** * springboot異步線程池配置 * @author Scen * @date 2018/11/7 18:28 */ @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { //定義線程池 ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); //核心線程數(shù) taskExecutor.setCorePoolSize(20); //線程池最大線程數(shù) taskExecutor.setMaxPoolSize(100); //線程隊列最大線程數(shù) taskExecutor.setQueueCapacity(10); //初始化 taskExecutor.initialize(); return taskExecutor; } }
DoTaskClass.java
package com.test; import com.test.pojo.User; import com.test.pojo.UserEducation; import com.test.user.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import java.util.List; /** * 任務(wù)類 定義異步工作任務(wù) * @author Scen * @date 2018/11/7 18:40 */ @Component public class DoTaskClass { /** * 一個feign的客戶端 */ private final UserService userService; @Autowired public DoTaskClass(UserService userService) { this.userService = userService; } /** * 核心任務(wù) * * @param uid */ @Async public void dotask(String uid) { /** * 模擬復(fù)雜工作業(yè)務(wù)(109個線程同時通過feign請求微服務(wù)提供者) */ { List<UserEducation> userEducationByUid = userService.findUserEducationByUid(uid); List<String> blackList = userService.getBlackList(); String userSkilled = userService.getUserSkilled(uid); String userFollow = userService.getUserFollow(uid); User userById = userService.getUserById(uid); List<String> followList = userService.getFollowList(uid); int userActivityScore = userService.getUserActivityScore(uid); } // 打印線程名稱分辨是否為多線程操作 System.out.println(Thread.currentThread().getName() + "===任務(wù)" + uid + "執(zhí)行完成==="); } }
TestController.java
package com.test; import com.test.pojo.User; import com.test.user.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * 測試案例 * @author Scen * @date 2018/11/7 18:10 */ @RestController public class TestController { /** * 此處僅用此feign客戶端請求微服務(wù)獲取核心工作所需參數(shù) */ private final UserService userService; /** * 核心工作異步算法 */ private final DoTaskClass doTaskClass; @Autowired public TestController(DoTaskClass doTaskClass, UserService userService) { this.doTaskClass = doTaskClass; this.userService = userService; } /** * 手動觸發(fā)工作 * @throws InterruptedException */ @RequestMapping("/test") public void task() throws InterruptedException { /* 取到1000個要執(zhí)行任務(wù)的必備參數(shù) */ List<User> userList = userService.findAllLite(1, 1000); for (int i = 0; i < userList.size(); i++) { try { // 異步線程開始工作 doTaskClass.dotask(userList.get(i).getId()); } catch (Exception e) { /* 若并發(fā)線程數(shù)達到MaxPoolSize+QueueCapacity=110(參考AsyncConfig配置)會進入catch代碼塊 i--休眠3秒后重試(嘗試進入線程隊列:當(dāng)且僅當(dāng)109個線程有一個或多個線程完成異步任務(wù)時重試成功) */ i--; Thread.sleep(3000*3); } System.out.println(i); } } }
相關(guān)線程池、超時時間等數(shù)量和大小按實際業(yè)務(wù)配置
補充:SpringCloud關(guān)于@FeignClient和Hystrix集成對http線程池監(jiān)控問題
@FeignClient可以作為Http代理訪問其他微服務(wù)節(jié)點,可以用apache的httpclient替換@FeignClient原生的URLConnection請求方式,以達到讓http請求走Http線程池的目的。
而@FeignClient和hystrix集成之后,在hystrix dashboard上可以監(jiān)控到 @FeignClient 中接口調(diào)用情況和 @FeignClient 中httpclient中線程池使用狀況。
下面是demo的示例:
1、@FeignClient的接口代碼如下:
@FeignClient(value="service-A", fallback=ServiceClientHystrix.class) public interface ServiceClient { @RequestMapping(method = RequestMethod.GET, value = "/add/{id}") String add(@PathVariable("id") Integer id); }
2、ServiceClientHystrix.java
@Component public class ServiceClientHystrix implements ServiceClient{ @Override public String add(Integer id) { return "add value from ServiceClientHystrix"; } }
3、關(guān)于@FeignClient和hystrix
集成后,Http線程池配置如下:
hystrix.threadpool.服務(wù)實例ID.參數(shù)
例如設(shè)置httpclient的線程池最大線程數(shù)量
hystrix.threadpool.service-A.coreSize=20//默認是hystrix.threadpool.default.coreSize = 10 hystrix.threadpool.service-A.maximumSize=20//默認是hystrix.threadpool.default.maximumSize = 10
啟動服務(wù)后用測試用例連續(xù)調(diào)用接口測試,用hystrix dashboard
監(jiān)控得到下圖監(jiān)控效果:
去掉hystrix.threadpool.服務(wù)實例ID.參數(shù)配置后,再次用測試用例調(diào)用接口得到監(jiān)控如下圖:
PoolSize的大小取決于hystrix.threadpool.服務(wù)實例ID.coreSize大小設(shè)置
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Java通過XPath獲取XML文件中符合特定條件的節(jié)點
今天小編就為大家分享一篇關(guān)于Java通過XPath獲取XML文件中符合特定條件的節(jié)點,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行的方法詳解
這篇文章主要介紹了java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-09-09Java SpringMVC的@RequestMapping注解使用及說明
這篇文章主要介紹了Java SpringMVC的@RequestMapping注解使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01Java Kafka分區(qū)發(fā)送及消費實戰(zhàn)
本文主要介紹了Kafka分區(qū)發(fā)送及消費實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07