您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關如何在spring cloud hystrix中使用collapsing實現請求合并,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
在HystrixCommand
之前可以使用請求合并器(HystrixCollapser
就是一個抽象的父類)來把多個請求合并成一個然后對后端依賴系統發起調用。
下圖顯示了兩種情況下線程的數量和網絡的連接數的情況:第一種是不使用合并器,第二種是使用請求合并器(假設所有的鏈接都是在一個短的時間窗口內并行的,比如10ms內)。
為什么要使用請求合并?
使用請求合并來減少執行并發HystrixCommand
執行所需的線程數和網絡連接數,請求合并是自動執行的,不會強制開發人員手動協調批處理請求。
全局上下文-global context(跨越所有Tomcat線程)
這種合并類型是在全局應用級別上完成的,因此任何Tomcat線程上的任何用戶的請求都可以一起合并。
例如,如果您配置一個HystrixCommand
支持任何用戶請求依賴關系來檢索電影評級,那么當同一個JVM中的任何用戶線程發出這樣的請求時,Hystrix會將其請求與任何其他請求一起添加到同一個已折疊網絡通話。
用戶請求上下文-request context(單個Tomcat線程)
如果你配置一個HystrixCommand
僅僅為一個單個用戶處理批量請求,Hystrix可以在一個Tomcat線程(請求)中合并請求。
例如,一個用戶想要加載300個視頻對象的書簽,不是去執行300次網絡請求,Hystrix能夠將他們合并成為一個。
Hystrix默認是的就是request-scope,要使用request-scoped的功能(request caching,request collapsing, request log)你必須管理HystrixRequestContext
的生命周期(或者實現一個可替代的HystrixConcurrencyStrategy
)
這就意味你在執行一個請求之前需要執行以下的代碼:
復制代碼 代碼如下:
HystrixRequestContext context=HystrixRequestContext.initializeContext();
并且在請求的結束位置執行:
context.shutdown();
在標準的JavaWeb應用中,你也可以使用一個Servlet過濾器來初始化這個生命周期
public class HystrixRequestContextServletFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { chain.doFilter(request, response); } finally { context.shutdown(); } } }
然后將它配置在web.xml中
<filter> <display-name>HystrixRequestContextServletFilter</display-name> <filter-name>HystrixRequestContextServletFilter</filter-name> <filter-class>com.netflix.hystrix.contrib.requestservlet.HystrixRequestContextServletFilter</filter-class> </filter> <filter-mapping> <filter-name>HystrixRequestContextServletFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
如果你是springboot開發的話代碼如下:
@WebFilter(filterName = "hystrixRequestContextServletFilter",urlPatterns = "/*") public class HystrixRequestContextServletFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try{ filterChain.doFilter(servletRequest,servletResponse); }finally { context.shutdown(); } } @Override public void destroy() { } }
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableHystrix //這個是必須的,否則filter無效 @ServletComponentScan public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } }
請求合并的成本是多少?
啟用請求合并的成本是在執行實際命令之前的延遲。最大的成本是批處理窗口的大小,默認是10ms。
如果你有一個命令需要花費5ms去執行并且有一個10ms的批處理窗口,執行的時間最壞的情況是15ms,一般情況下,請求不會在批處理窗口剛打開的時候發生,所以時間窗口的中間值是時間窗口的一半,在這種情況下是5ms。
這個成本是否值得取決于正在執行的命令,高延遲命令不會受到少量附加平均延遲的影響。而且,給定命令的并發量也是關鍵:如果很少有超過1個或2個請求被組合在一起,那么這個成本就是不值得的。事實上,在一個單線程的順序迭代請求合并將會是一個主要的性能瓶頸,每一次迭代都會等待10ms的窗口等待時間。
但是,如果一個特定的命令同時被大量使用,并且可以同時批量打幾十個甚至幾百個呼叫,那么成本通常遠遠超過所達到的吞吐量的增加,因為Hystrix減少了它所需的線程數量,依賴。(這段話不太好理解,其實就是說如果并發比較高,這個成本是值得的,因為hystrix可以節省很多線程和連接資源)。
請求合并的流程(如下圖)
理論知識已經講完了,下面來看看例子,下面的例子集成了eureka+feign+hystrix,完整的例子請查看:https://github.com/jingangwang/micro-service
實體類
public class User { private Integer id; private String username; private Integer age; public User() { } public User(Integer id, String username, Integer age) { this.id = id; this.username = username; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { final StringBuffer sb = new StringBuffer("User{"); sb.append("id=").append(id); sb.append(", username='").append(username).append('\''); sb.append(", age=").append(age); sb.append('}'); return sb.toString(); } }
服務提供者代碼
@RestController @RequestMapping("user") public class UserController { @RequestMapping("getUser") public User getUser(Integer id) { return new User(id, "test", 29); } @RequestMapping("getAllUser") public List<User> getAllUser(String ids){ String[] split = ids.split(","); return Arrays.asList(split) .stream() .map(id -> new User(Integer.valueOf(id),"test"+id,30)) .collect(Collectors.toList()); } }
消費者代碼
UserFeignClient
@FeignClient(name = "eureka-provider",configuration = FeignConfiguration.class) public interface UserFeignClient { /** * 根據id查找用戶 * @param id 用戶id * @return User */ @RequestMapping(value = "user/getUser.json",method = RequestMethod.GET) User findUserById(@RequestParam("id") Integer id); /** * 超找用戶列表 * @param ids id列表 * @return 用戶的集合 */ @RequestMapping(value = "user/getAllUser.json",method = RequestMethod.GET) List<User> findAllUser(@RequestParam("ids") String ids); }
UserService(設置為全局上下文)
@Service public class UserService { @Autowired private UserFeignClient userFeignClient; /** * maxRequestsInBatch 該屬性設置批量處理的最大請求數量,默認值為Integer.MAX_VALUE * timerDelayInMilliseconds 該屬性設置多長時間之內算一次批處理,默認為10ms * @param id * @return */ @HystrixCollapser(collapserKey = "findCollapserKey",scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,batchMethod = "findAllUser",collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds",value = "5000" ), @HystrixProperty(name = "maxRequestsInBatch",value = "5" ) }) public Future<User> find(Integer id){ return null; } @HystrixCommand(commandKey = "findAllUser") public List<User> findAllUser(List<Integer> ids){ return userFeignClient.findAllUser(StringUtils.join(ids,",")); } }
FeignCollapserController
@RequestMapping("user") @RestController public class FeignCollapserController { @Autowired private UserService userService; @RequestMapping("findUser") public User getUser(Integer id) throws ExecutionException, InterruptedException { return userService.find(id).get(); }
上面的代碼我們這是的是全局上下文(所有tomcat的線程的請求都可以合并),合并的時間窗口為5s(每一次請求都得等5s才發起請求),最大合并數為5。我們在postman中,5s之內發起兩次請求,用戶id不一樣。
localhost:8082/user/findUser.json?id=123189891
localhost:8082/user/findUser.json?id=222222
結果如下圖所示,兩次請求合并為一次請求批量請求。
我們再來測試一下請求上下文(Request-Scope)的情況,加入上面所提到的HystrixRequestContextServletFilter
,并修改UserService
HystrixRequestContextServletFilter
/** * @author wjg * @date 2017/12/22 15:15 */ @WebFilter(filterName = "hystrixRequestContextServletFilter",urlPatterns = "/*") public class HystrixRequestContextServletFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try{ filterChain.doFilter(servletRequest,servletResponse); }finally { context.shutdown(); } } @Override public void destroy() { } }
UserService(設置為請求上下文)
@Service public class UserService { @Autowired private UserFeignClient userFeignClient; /** * maxRequestsInBatch 該屬性設置批量處理的最大請求數量,默認值為Integer.MAX_VALUE * timerDelayInMilliseconds 該屬性設置多長時間之內算一次批處理,默認為10ms * @param id * @return */ @HystrixCollapser(collapserKey = "findCollapserKey",scope = com.netflix.hystrix.HystrixCollapser.Scope.REQUEST,batchMethod = "findAllUser",collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds",value = "5000" ), @HystrixProperty(name = "maxRequestsInBatch",value = "5" ) }) public Future<User> find(Integer id){ return null; } @HystrixCommand(commandKey = "findAllUser") public List<User> findAllUser(List<Integer> ids){ return userFeignClient.findAllUser(StringUtils.join(ids,",")); } }
FeignCollapser2Controller
@RequestMapping("user") @RestController public class FeignCollapser2Controller { @Autowired private UserService userService; @RequestMapping("findUser2") public List<User> getUser() throws ExecutionException, InterruptedException { Future<User> user1 = userService.find(1989); Future<User> user2= userService.find(1990); List<User> users = new ArrayList<>(); users.add(user1.get()); users.add(user2.get()); return users; } }
我們在postman中輸入:localhost:8082/user/findUser2.json
上述就是小編為大家分享的如何在spring cloud hystrix中使用collapsing實現請求合并了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。