回调地狱和反应模式

我可以更好地了解a的用途的一种方式
基于反应流的方法是它简化了无阻塞IO调用的方式。

这篇文章将快速讲解进行同步远程调用所涉及的那种代码,然后说明如何在非阻塞IO中分层,尽管在资源(尤其是线程)的使用方面非常高效,但会带来称为回调地狱的复杂性,并且基于反应流的方法如何简化编程模型。

目标服务

由于我将编写一个客户呼叫,因此代表城市详细信息的目标服务有两个端点。 当使用uri类型的“ / cityids”调用时返回一个城市ID列表,并且示例结果如下所示:

 [ 1 , 2 , 3 , 4 , 5 , 6 , 7  ] 

端点返回给定城市ID的城市的详细信息,例如,当使用ID 1 –“ / cities / 1”进行调用时:

 { "country" : "USA" , "id" : 1 , "name" : "Portland" , "pop" : 1600000  } 

客户的责任是获取城市ID的列表,然后为每个城市ID获取城市的详细信息并将其放到城市列表中。

同步通话

我正在使用Spring Framework的RestTemplate进行远程调用。 Kotlin函数获取城市ID列表如下所示:

 private fun getCityIds(): List<String> { val cityIdsEntity: ResponseEntity<List<String>> = restTemplate .exchange( " http://localhost: $localServerPort/cityids" , HttpMethod.GET, null , object : ParameterizedTypeReference<List<String>>() {}) return cityIdsEntity.body!!  } 

并获取城市的详细信息:

 private fun getCityForId(id: String): City { return restTemplate.getForObject( " http://localhost: $localServerPort/cities/$id" , City:: class .java)!!  } 

给定这两个功能,可以很容易地将它们组合起来,以便返回城市列表:

 val cityIds: List<String> = getCityIds()  val cities: List<City> = cityIds .stream() .map<City> { cityId -> getCityForId(cityId) } .collect(Collectors.toList())  cities.forEach { city -> LOGGER.info(city.toString()) } 

该代码非常易于理解,但是涉及8个阻塞调用–

1.获取7个城市ID的列表,然后获取每个城市的详细信息 2.获取七个城市中每个城市的详细信息

这些调用中的每一个都会位于不同的线程上。

结合使用非阻塞IO和回调

我将使用一个名为AsyncHttpClient的库来进行非阻塞IO调用。

进行远程调用时,AyncHttpClient返回一个ListenableFuture类型。

 val responseListenableFuture: ListenableFuture<Response> = asyncHttpClient .prepareGet( " http://localhost: $localServerPort/cityids" ) .execute() 

可以将回调附加到可监听的将来,以便在可用时对响应进行操作。

 responseListenableFuture.addListener(Runnable { val response: Response = responseListenableFuture.get() val responseBody: String = response.responseBody val cityIds: List<Long> = objectMapper.readValue<List<Long>>(responseBody, object : TypeReference<List<Long>>() {}) ....  } 

给定cityids列表,我想获取城市的详细信息,因此从响应中,我需要进行更多的远程调用,并为每个调用附加一个回调,以按照以下方式获取城市的详细信息:

 val responseListenableFuture: ListenableFuture<Response> = asyncHttpClient .prepareGet( " http://localhost: $localServerPort/cityids" ) .execute()  responseListenableFuture.addListener(Runnable { val response: Response = responseListenableFuture.get() val responseBody: String = response.responseBody val cityIds: List<Long> = objectMapper.readValue<List<Long>>(responseBody, object : TypeReference<List<Long>>() {}) cityIds.stream().map { cityId -> val cityListenableFuture = asyncHttpClient .prepareGet( " http://localhost: $localServerPort/cities/$cityId" ) .execute() cityListenableFuture.addListener(Runnable { val cityDescResp = cityListenableFuture.get() val cityDesc = cityDescResp.responseBody val city = objectMapper.readValue(cityDesc, City:: class .java) LOGGER.info( "Got city: $city" ) }, executor) }.collect(Collectors.toList())  }, executor) 

这是一段粗糙的代码,在一个回调中有一组回调,很难对此进行推理和理解,因此被称为回调地狱。

在Java CompletableFuture中使用非阻塞IO

通过返回Java的CompletableFuture作为返回类型而不是ListenableFuture,可以对代码进行一些改进。 CompletableFuture提供允许修改返回的返回类型的运算符。

例如,考虑获取城市ID列表的函数:

 private fun getCityIds(): CompletableFuture<List<Long>> { return asyncHttpClient .prepareGet( " http://localhost: $localServerPort/cityids" ) .execute() .toCompletableFuture() .thenApply { response -> val s = response.responseBody val l: List<Long> = objectMapper.readValue(s, object : TypeReference<List<Long>>() {}) l }  } 

在这里,我使用“ thenApply”运算符将“ CompletableFuture <Response>”转换为“ CompletableFuture <List <Long >>”

并类似地获得城市的详细信息:

 private fun getCityDetail(cityId: Long): CompletableFuture<City> { return asyncHttpClient.prepareGet( " http://localhost: $localServerPort/cities/$cityId" ) .execute() .toCompletableFuture() .thenApply { response -> val s = response.responseBody LOGGER.info( "Got {}" , s) val city = objectMapper.readValue(s, City:: class .java) city }  } 

这是基于回调方法的改进,但是,CompletableFuture缺少足够的运算符,例如,在这种特定情况下,需要将所有城市详细信息放在一起:

 val cityIdsFuture: CompletableFuture<List<Long>> = getCityIds()  val citiesCompletableFuture: CompletableFuture<List<City>> = cityIdsFuture .thenCompose { l -> val citiesCompletable: List<CompletableFuture<City>> = l.stream() .map { cityId -> getCityDetail(cityId) }.collect(toList()) val citiesCompletableFutureOfList: CompletableFuture<List<City>> = CompletableFuture.allOf(*citiesCompletable.toTypedArray()) .thenApply { _: Void? -> citiesCompletable .stream() .map { it.join() } .collect(toList()) } citiesCompletableFutureOfList } 

我使用了一个名为CompletableFuture.allOf的运算符,该运算符返回“ Void”类型,并且必须强制返回所需的“” CompletableFuture <List <City >>类型。

使用Project Reactor

Project Reactor是Reactive Streams规范的实现。 它有两种特殊类型,可返回0/1项目流和0 / n项目流–前者是Mono,后者是Flux。

Project Reactor提供了一组非常丰富的运算符,这些运算符允许以多种方式转换数据流。 首先考虑该函数以返回城市ID列表:

 private fun getCityIds(): Flux<Long> { return webClient.get() .uri( "/cityids" ) .exchange() .flatMapMany { response -> LOGGER.info( "Received cities.." ) response.bodyToFlux<Long>() }  } 

我正在使用Spring出色的WebClient库进行远程调用,并获得Project反应器“ Mono <ClientResponse>”类型的响应,可以使用“ flatMapMany”运算符将其修改为“ Flux <Long>”类型。

在给定城市ID的情况下,沿着相同的路线获取城市的详细信息:

 private fun getCityDetail(cityId: Long?): Mono<City> { return webClient.get() .uri( "/cities/{id}" , cityId!!) .exchange() .flatMap { response -> val city: Mono<City> = response.bodyToMono() LOGGER.info( "Received city.." ) city }  } 

在这里,使用“ flatMap”运算符将项目反应堆“ Mono <ClientResponse>”类型转换为“ Mono <City>”类型。

以及从中获取城市ID和城市的代码:

 val cityIdsFlux: Flux<Long> = getCityIds()  val citiesFlux: Flux<City> = cityIdsFlux .flatMap { this .getCityDetail(it) }  return citiesFlux 

这非常具有表现力-对比了基于回调的方法的混乱和基于响应流的方法的简单性。

结论

在我看来,这是使用基于响应流的方法的最大原因之一,尤其是在涉及跨越异步边界的场景(例如在这种情况下进行远程调用)的情况下,尤其是Project Reactor。 它清除了各种回调和回调地狱,并提供了使用一组丰富的运算符修改/转换类型的自然方法。

我在这里使用的所有示例的工作版本的存储库位于https://github.com/bijukunjummen/reactive-cities-demo/tree/master/src/test/kotlin/samples/geo/kotlin

翻译自: https://www.javacodegeeks.com/2019/06/callback-hell-reactive-patterns.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/341818.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

光纤收发器的实际应用范围介绍

众所周知&#xff0c;光纤收发器的有很多不同的分类&#xff0c;种类很多&#xff0c;在实际使用中大多注意的是按光纤接头不同而区分的类别&#xff1a;SC接头光纤收发器和FC/ST接头光纤收发器。接下来小编就来为大家介绍下光纤收发器的具体应用范围有哪些&#xff0c;想要了解…

光纤收发器的优势介绍

提到光纤收发器&#xff0c;人们常常不免会将光纤收发器与带光口的交换机来进行比较&#xff0c;下面咱们就主要来谈一下光纤收发器相对于光口交换机的优势。想要了解的朋友就一起来看看吧&#xff01; 首先&#xff0c;光纤收发器和普通交换机在价格上远远比光口交换机便宜&a…

[渝粤教育] 南通大学 分子生物学 参考 资料

教育 -分子生物学-章节资料考试资料-南通大学【】 第二章 思考题 第二章 测试 1、【单选题】核酸的基本组成单位是 A、A.磷酸和核糖 B、B.核苷和碱基 C、C.单核苷酸 D、D.脱氧核苷和碱基 参考资料【 】 2、【单选题】DNA与RNA完全水解后&#xff0c;其产物的特点是 A、A.戊糖不…

[渝粤教育] 南通大学 大学计算机信息技术基础 参考 资料

教育 -大学计算机信息技术基础-章节资料考试资料-南通大学【】 数制转换 1、【填空题】与十六进制数(BC)16等值的八进制数是___________ 。 A、 参考资料【 】 2、【填空题】十进制数205.5的八进制表示是___________________。 A、 参考资料【 】 整数在计算机中的表示 1、【填…

Selenium脚本编写技巧和窍门

如果您刚刚开始学习硒&#xff0c;则以下技巧和窍门将成为您的救星。 这些技巧和窍门具有您可能会忘记的所有基本知识&#xff0c;将帮助您记住所有这些。 您只需浏览一下它们&#xff0c;几秒钟后您就会了解所有内容。 让我们一一看一下所有的技巧和窍门。 创建Webdriver实例…

光纤收发器在使用过程中有哪些需要注意的事项?

光纤收发器的种类非常丰富&#xff0c;而在实际使用中大多是按照光纤接头不同进行区分&#xff0c;SC接头光纤收发器和FC/ST接头光纤收发器。今天&#xff0c;飞畅科技的小编就带大家来详细了解下光纤收发器在使用过程中需要注意的事项有哪些&#xff1f;一起来看看吧&#xff…

[渝粤教育] 南通大学 电路分析 参考 资料

教育 -电路分析-章节资料考试资料-南通大学【】 随堂测验&#xff1a;关联方向和非关联方向判断 1、【填空题】图中所示的电压源电压、电流i参考方向为 方向&#xff0c;电流源电压u、电流i参考方向为 方向。 A、 参考资料【 】 随堂测验&#xff1a;电源功率计算及判断 1、【填…

光纤收发器发展趋势

现如今&#xff0c;随着互联网的高速发展&#xff0c;以太网的局域发展已经无法适应其发展了&#xff0c;因此出现了光纤收发器这种设备&#xff0c;来进行长距离的光信号输送&#xff0c;这样可以扩大光网的使用范围&#xff0c;使更多的人能够受到网络的优惠和速度。接下来飞…

[渝粤教育] 南通职业大学 混合动力汽车构造与检修 参考 资料

教育 -混合动力汽车构造与检修-章节资料考试资料-南通职业大学【】 单元测验1 1、【单选题】混合动力汽车的英文简称是&#xff08;&#xff09; A、EV B、FCV C、HEV D、HHV 参考资料【 】 2、【单选题】本课程主要讲的混合动力汽车是 A、太阳能燃油 B、燃油氢燃料 C、燃油动力…

[渝粤教育] 南阳理工学院 大学计算机基础 参考 资料

教育 -大学计算机基础-章节资料考试资料-南阳理工学院【】 第一章单元测验 1、【单选题】大学计算机中所要学习的计算思维是指( )。 A、计算机相关的知识 B、算法与设计技巧 C、知识与技巧的结合 D、蕴含在计算科学知识背后的具有贯通性和联想性的内容 参考资料【 】 2、【单选…

光纤收发器和协议转换器之间有哪些区别?

在通信网络领域&#xff0c;我们经常会用到光纤收发器和协议转换器&#xff0c;但对此不是很了解的朋友&#xff0c;往往可能会将二者搞混淆。那么&#xff0c;关于光纤收发器与协议转换器之间有什么区别呢&#xff1f;接下来就跟随杭州飞畅的小编一起来看看吧&#xff01; 光…

[渝粤教育] 周口师范学院 大学计算机基础 参考 资料

教育 -大学计算机基础-章节资料考试资料-周口师范学院【】 随堂测验 1、【单选题】计算机数据的一个基本特点是&#xff08; &#xff09;。 A、只能表示计算所用的数值 B、以各种形式存储在内存中 C、都是以二进制形式存储在存储器上 D、所有的数据按存储器类型组织 参考资料【…

长期支持对OpenJDK意味着什么?

Bruno Borges最近在推特上发布了一个有关OpenJDK的长期支持&#xff08;LTS&#xff09;的问题&#xff0c;这表明对它的真正含义还有一些困惑。 在此博客文章中&#xff0c;我将解释不同部分如何组合在一起。 那么……谁说或者说“ #OpenJDK 11”是LTS&#xff1f; 我觉得关…

光纤收发器结构介绍和故障解决

光纤收发器是一款高集成度的设备&#xff0c;最多可集成14台光纤收发器并进行统一供电&#xff0c;结构简单&#xff0c;便于管理和维护。光纤收发器包括百兆/千兆&#xff0c;单纤/双纤、单模/多模等多种规格。光纤收发器支持每台光纤收发器的热插拔操作&#xff0c;使用非常灵…

[渝粤教育] 四川信息职业技术学院 高频电子技术 参考 资料

教育 -高频电子技术-章节资料考试资料-四川信息职业技术学院【】 无线通信系统 1、【单选题】下列表述正确的是&#xff08; &#xff09; A、低频信号可直接从天线上有效地辐射 B、低频信号必须装载到高频信号上才能有效地辐射 C、低频信号和高频信号都不能从天线上有效地辐射…

[渝粤教育] 四川农业大学 理论力学 参考 资料

教育 -理论力学-章节资料考试资料-四川农业大学【】 第2讲 单元测试 1、【单选题】以下说法中错误的是( ) A、理论力学是研究物体机械运动一般规律的科学。 B、理论力学与物理中力学部分的主要区别在于理论力学的研究对象和研究方法更加面向工程实际。 C、刚体是理论力学中的重…

[渝粤教育] 四川大学 工程水文学 参考 资料

教育 -工程水文学-章节资料考试资料-四川大学【】 绪论问答题 第一章测试 1、【单选题】水文现象的发生 A、完全是偶然性的 B、完全是必然性的 C、完全是随机性的 D、既有必然性也有随机性 参考资料【 】 2、【单选题】水文分析与计算&#xff0c;是预计水文变量在[ ]的概率分布…

光纤收发器怎么连接?光纤收发器连接方式解析

光纤收发器将以太网中的连接介质转换为光纤&#xff0c;由于光纤的低损耗、高抗电磁干扰性&#xff0c;从而使网络传输距离从200米扩展到2公里甚至几十公里&#xff0c;乃至于上百公里的同时&#xff0c;也使数据通讯质量有了较大提高。光纤收发器使服务器、中继器、集线器、终…

[渝粤教育] 四川大学 药用植物学 参考 资料

教育 -药用植物学-章节资料考试资料-四川大学【】 正常根的形态 1、【判断题】根由于生长在地下&#xff0c;细胞中不含叶绿体&#xff0c;有节和节间之分&#xff0c;一般不生芽、叶和花。 A、正确 B、错误 参考资料【 】 2、【判断题】药用植物百合、大蒜等多数单子叶植物的根…

java 开发人员工具_Java开发人员应该知道的7种新工具

java 开发人员工具通过快速浏览一些最新的&#xff0c;创新的工具&#xff0c;准备好锁定和加载。 万一您错过了它&#xff0c;RebelLabs最近发布了Java工具和技术前景的全球调查结果 。 除了著名的工具和成熟的工具之外&#xff0c;市场还充斥着鲜为人知的新鲜工具和框架。 在…