电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

基于springboot 長輪詢的實現操作

瀏覽:117日期:2023-03-29 15:16:05

springboot 長輪詢實現

基于 @EnableAsync , @Sync

@SpringBootApplication@EnableAsyncpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}

@RequestMapping('/async')@RestControllerpublic class AsyncRequestDemo { @Autowired private AsyncRequestService asyncRequestService; @GetMapping('/value') public String getValue() { String msg = null; Future<String> result = null; try{ result = asyncRequestService.getValue(); msg = result.get(10, TimeUnit.SECONDS); }catch (Exception e){ e.printStackTrace(); }finally { if (result != null){ result.cancel(true); } } return msg; } @PostMapping('/value') public void postValue(String msg) { asyncRequestService.postValue(msg); }}

@Servicepublic class AsyncRequestService { private String msg = null; @Async public Future<String> getValue() throws InterruptedException { while (true){ synchronized (this){ if (msg != null){ String resultMsg = msg; msg = null; return new AsyncResult(resultMsg); } } Thread.sleep(100); } } public synchronized void postValue(String msg) { this.msg = msg; }}備注

@EnableAsync 開啟異步

@Sync 標記異步方法

Future 用于接收異步返回值

result.get(10, TimeUnit.SECONDS); 阻塞,超時獲取結果

Future.cancel() 中斷線程

補充:通過spring提供的DeferredResult實現長輪詢服務端推送消息

DeferredResult字面意思就是推遲結果,是在servlet3.0以后引入了異步請求之后,spring封裝了一下提供了相應的支持,也是一個很老的特性了。DeferredResult可以允許容器線程快速釋放以便可以接受更多的請求提升吞吐量,讓真正的業務邏輯在其他的工作線程中去完成。

最近再看apollo配置中心的實現原理,apollo的發布配置推送變更消息就是用DeferredResult實現的,apollo客戶端會像服務端發送長輪訓http請求,超時時間60秒,當超時后返回客戶端一個304 httpstatus,表明配置沒有變更,客戶端繼續這個步驟重復發起請求,當有發布配置的時候,服務端會調用DeferredResult.setResult返回200狀態碼,然后輪訓請求會立即返回(不會超時),客戶端收到響應結果后,會發起請求獲取變更后的配置信息。

下面我們自己寫一個簡單的demo來演示這個過程

springboot啟動類:

@SpringBootApplicationpublic class DemoApplication implements WebMvcConfigurer { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Bean public ThreadPoolTaskExecutor mvcTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setQueueCapacity(100); executor.setMaxPoolSize(25); return executor; } //配置異步支持,設置了一個用來異步執行業務邏輯的工作線程池,設置了默認的超時時間是60秒 @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { configurer.setTaskExecutor(mvcTaskExecutor()); configurer.setDefaultTimeout(60000L); }}

import com.google.common.collect.HashMultimap;import com.google.common.collect.Multimap;import com.google.common.collect.Multimaps;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.context.request.async.DeferredResult; import java.util.Collection; @RestControllerpublic class ApolloController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); //guava中的Multimap,多值map,對map的增強,一個key可以保持多個value private Multimap<String, DeferredResult<String>> watchRequests = Multimaps.synchronizedSetMultimap(HashMultimap.create()); //模擬長輪詢 @RequestMapping(value = '/watch/{namespace}', method = RequestMethod.GET, produces = 'text/html') public DeferredResult<String> watch(@PathVariable('namespace') String namespace) { logger.info('Request received'); DeferredResult<String> deferredResult = new DeferredResult<>(); //當deferredResult完成時(不論是超時還是異常還是正常完成),移除watchRequests中相應的watch key deferredResult.onCompletion(new Runnable() { @Override public void run() { System.out.println('remove key:' + namespace); watchRequests.remove(namespace, deferredResult); } }); watchRequests.put(namespace, deferredResult); logger.info('Servlet thread released'); return deferredResult; } //模擬發布namespace配置 @RequestMapping(value = '/publish/{namespace}', method = RequestMethod.GET, produces = 'text/html') public Object publishConfig(@PathVariable('namespace') String namespace) { if (watchRequests.containsKey(namespace)) { Collection<DeferredResult<String>> deferredResults = watchRequests.get(namespace); Long time = System.currentTimeMillis(); //通知所有watch這個namespace變更的長輪訓配置變更結果 for (DeferredResult<String> deferredResult : deferredResults) { deferredResult.setResult(namespace + ' changed:' + time); } } return 'success'; }}

當請求超時的時候會產生AsyncRequestTimeoutException,我們定義一個全局異常捕獲類:

import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.ResponseStatus;import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; @ControllerAdviceclass GlobalControllerExceptionHandler { protected static final Logger logger = LoggerFactory.getLogger(GlobalControllerExceptionHandler.class); @ResponseStatus(HttpStatus.NOT_MODIFIED)//返回304狀態碼 @ResponseBody @ExceptionHandler(AsyncRequestTimeoutException.class) //捕獲特定異常 public void handleAsyncRequestTimeoutException(AsyncRequestTimeoutException e, HttpServletRequest request) { System.out.println('handleAsyncRequestTimeoutException'); }}

然后我們通過postman工具發送請求http://localhost:8080/watch/mynamespace,請求會掛起,60秒后,DeferredResult超時,客戶端正常收到了304狀態碼,表明在這個期間配置沒有變更過。

然后我們在模擬配置變更的情況,再次發起請求http://localhost:8080/watch/mynamespace,等待個10秒鐘(不要超過60秒),然后調用http://localhost:8080/publish/mynamespace,發布配置變更。這時postman會立刻收到response響應結果:

mynamespace changed:1538880050147

表明在輪訓期間有配置變更過。

這里我們用了一個MultiMap來存放所有輪訓的請求,Key對應的是namespace,value對應的是所有watch這個namespace變更的異步請求DeferredResult,需要注意的是:在DeferredResult完成的時候記得移除MultiMap中相應的key,避免內存溢出請求。

采用這種長輪詢的好處是,相比一直循環請求服務器,實例一多的話會對服務器產生很大的壓力,http長輪詢的方式會在服務器變更的時候主動推送給客戶端,其他時間客戶端是掛起請求的,這樣同時滿足了性能和實時性。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持好吧啦網。如有錯誤或未考慮完全的地方,望不吝賜教。

標簽: Spring
相關文章:
主站蜘蛛池模板: 重庆钣金加工厂家首页-专业定做监控电视墙_操作台 | 自动化改造_智虎机器人_灌装机_贴标机-上海圣起包装机械 | 电磁辐射仪-电磁辐射检测仪-pm2.5检测仪-多功能射线检测仪-上海何亦仪器仪表有限公司 | 巨野月嫂-家政公司-巨野县红墙安康母婴护理中心 | 英国公司注册-新加坡公司注册-香港公司开户-离岸公司账户-杭州商标注册-杭州优创企业 | 生物风-销售载体,基因,质粒,ATCC细胞,ATCC菌株等,欢迎购买-百风生物 | 东莞螺丝|东莞螺丝厂|东莞不锈钢螺丝|东莞组合螺丝|东莞精密螺丝厂家-东莞利浩五金专业紧固件厂家 | 不锈钢监控杆_监控立杆厂家-廊坊耀星光电科技有限公司 | led全彩屏-室内|学校|展厅|p3|户外|会议室|圆柱|p2.5LED显示屏-LED显示屏价格-LED互动地砖屏_蕙宇屏科技 | YAGEO国巨电容|贴片电阻|电容价格|三星代理商-深圳市巨优电子有限公司 | 建筑工程资质合作-工程资质加盟分公司-建筑资质加盟 | 铣床|万能铣床|立式铣床|数控铣床|山东滕州万友机床有限公司 | 郑州墨香品牌设计公司|品牌全案VI设计公司 | 香港新时代国际美容美发化妆美甲培训学校-26年培训经验,值得信赖! | 时代北利离心机,实验室离心机,医用离心机,低速离心机DT5-2,美国SKC采样泵-上海京工实业有限公司 工业电炉,台车式电炉_厂家-淄博申华工业电炉有限公司 | 东莞ERP软件_广州云ERP_中山ERP_台湾工厂erp系统-广东顺景软件科技有限公司 | 江苏南京多语种翻译-专业翻译公司报价-正规商务翻译机构-南京华彦翻译服务有限公司 | 茅茅虫AI论文写作助手-免费AIGC论文查重_写毕业论文降重 | 光伏家 - 太阳能光伏发电_分布式光伏发电_太阳能光伏网 | _网名词典_网名大全_qq网名_情侣网名_个性网名 | C形臂_动态平板DR_动态平板胃肠机生产厂家制造商-普爱医疗 | 上海道勤塑化有限公司| 卷筒电缆-拖链电缆-特种柔性扁平电缆定制厂家「上海缆胜」 | 东莞动力锂电池保护板_BMS智能软件保护板_锂电池主动均衡保护板-东莞市倡芯电子科技有限公司 | elisa试剂盒价格-酶联免疫试剂盒-猪elisa试剂盒-上海恒远生物科技有限公司 | 农业四情_农业气象站_田间小型气象站_智慧农业气象站-山东风途物联网 | 北京发电车出租-发电机租赁公司-柴油发电机厂家 - 北京明旺盛安机电设备有限公司 | app开发|app开发公司|小程序开发|物联网开发||北京网站制作|--前潮网络 | 钢木实验台-全钢实验台-化验室通风柜-实验室装修厂家-杭州博扬实验设备 | 泰国专线_泰国物流专线_广州到泰国物流公司-泰廊曼国际 | 不锈钢列管式冷凝器,换热器厂家-无锡飞尔诺环境工程有限公司 | 宿松新闻网 宿松网|宿松在线|宿松门户|安徽宿松(直管县)|宿松新闻综合网站|宿松官方新闻发布 | 辐射仪|辐射检测仪|辐射巡测仪|个人剂量报警仪|表面污染检测仪|辐射报警仪|辐射防护网 | 日本SMC气缸接头-速度控制阀-日本三菱伺服电机-苏州禾力自动化科技有限公司 | 耐腐蚀泵,耐腐蚀真空泵,玻璃钢真空泵-淄博华舜耐腐蚀真空泵有限公司 | SRRC认证_电磁兼容_EMC测试整改_FCC认证_SDOC认证-深圳市环测威检测技术有限公司 | 南京泽朗生物科技有限公司-液体饮料代加工_果汁饮料代加工_固体饮料代加工 | 衬氟止回阀_衬氟闸阀_衬氟三通球阀_衬四氟阀门_衬氟阀门厂-浙江利尔多阀门有限公司 | 湖南档案密集架,智能,物证,移动,价格-湖南档案密集架厂家 | 耐热钢-耐磨钢-山东聚金合金钢铸造有限公司 | 武汉刮刮奖_刮刮卡印刷厂_为企业提供门票印刷_武汉合格证印刷_现金劵代金券印刷制作 - 武汉泽雅印刷有限公司 |