《次时代Java编程(一):续 vertx-sync实践》

2023-05-16

转载自:https://blog.maxleap.cn/archives/1013

vertx-sync是什么

上一篇我们已经讲了 Fiber 相关的知识,想必大家对Java实现类似Golang的coroutine已经有印象了,既然Java世界里有第三方提供了这么好的库, 那我们就看看怎么跟 vert.x 结合起来使用。

vert.x官方为了解决异步代码编写的困难,使之更加同步化对开发人员更友好,便基于quasar包装了一个的同步库,vertx-sync,该库的作者同样也是vert.x的原作者,所以完成度还是很高的。
vertx-sync 对外只是暴露了几个简单的静态API,来完成对vert.x体系内一系列的操作包装,其实主要也就是三静态API而已。

  • Sync.fiberHandler 如果你希望你的handler里有一些逻辑需要在Fiber里运行,则你的handler必须用这个方法包一下。
  • Sync.awaitEvent 从Handler里返回一个事件结果(同步的),且 不会阻塞EventLoop
  • Sync.awaitResult 从Handler里返回一个异步事件结果(同步的),且 不会阻塞EventLoop

直接看介绍可能有点不直观,下面跑几个例子。

使用vertx-sync

之前介绍过quasar,如果你希望在项目里使用coroutine的话,需要在JVM里设置一个参数,用于应用启动前修改字节码(注入一些中断方法),从而可以达到协程的目的。 具体方法也很简单。

1
- javaagent : / path / to / the / quasar - core - 0.7.5 - jdk8 . jar

如果是基于Maven跑单元测试,那只需要引用quasar instrument的插件就可以里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<plugin>
     <groupId> com.vlkan </groupId>
     <artifactId> quasar-maven-plugin </artifactId>
     <version> 0.7.3 </version>
     <configuration>
         <check> true </check>
         <debug> true </debug>
         <verbose> true </verbose>
     </configuration>
     <executions>
         <execution>
             <goals>
                 <goal> instrument </goal>
             </goals>
         </execution>
     </executions>
     <dependencies>
         <dependency>
             <groupId> co.paralleluniverse </groupId>
             <artifactId> quasar-core </artifactId>
             <version> 0.7.5 </version>
         </dependency>
     </dependencies>
</plugin>

 

上面是一些非常必要的准备工作,否则你无法使用quasar以及vertx-sync。

vertx定时器例子

之前通过vert.x调用定时器,需要传一个回调handler,然后所有的代码逻辑都包在里面。

1
2
3
vertx . setTimer ( 1000L , h -> {
     System . out . println ( "time's up" ) ;
} ) ;

现在我们来重新塑造一下三观。

1
2
awaitEvent ( h -> vertx . setTimer ( 1000L , h ) ) ;
System . out . println ( "time's up" ) ;

这里定时器会阻塞在awaitEvent这一行,直到一秒后才会执行下面的一行。有点类似执行 Thread.sleep(1000L),但是并不会阻塞 EventLoop 因为quasar会在EventLoop基础之上再开启一个fiber。

下面我看个稍微复杂点的例子。

HTTP Client请求例子

我们先用传统的回调方式使用vert.x的HttpClient API。

1
2
3
4
5
6
HttpClientRequest httpClientRequest = vertx . createHttpClient ( ) . get ( "leapcloud.cn" ) ;
httpClientRequest . handler ( response -> {
     response . handler ( responseBody -> {
         System . out . println ( responseBody . toString ( ) ) ;
     } ) ;
} ) . end ( ) ;

这里有两层回调嵌套,一层是得到Http的Response,另一层是从Response里得到详细的body。因为有lambda表达式才使得Java现在看起来并不是那么恶心。但是如果我们需要根据body的内容进一步做判断去继续请求其他页面,则嵌套会变的非常的深。下面尝试改造成sync方式看看。

1
2
3
4
HttpClientRequest httpClientRequest = vertx . createHttpClient ( ) . get ( "leapcloud.cn" ) ;
HttpClientResponse response = awaitEvent ( Sync :: fiberHandler ) ;
Buffer body = awaitEvent ( response :: handler ) ;
System . out . println ( body . toString ( ) ) ;

额,是不是感觉看着很舒服,无嵌套,直接顺序下来,非常的直观,加上Java8特有的方法引用,会让代码更精简。

通过vertx-sync使用Vert.x JDBC

写过vert.x同学肯定知道其vertx-jdbc-client为了使其兼容异步开发模型,将JDBC的底层线程池用异步方式包装了一下,也就是说JDBC层还是通过线程池去访问数据库的,但是是通过vert.x的context做了层封装,使其可以将结果放到对应的 EventLoop 里,这样比较符合vert.x的开发风格。但是带来的弊端就是嵌套太深。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
final JDBCClient client = JDBCClient . createShared ( vertx , new JsonObject ( )
. put ( "url" , "jdbc:hsqldb:mem:test?shutdown=true" )
. put ( "driver_class" , "org.hsqldb.jdbcDriver" )
. put ( "max_pool_size" , 30 ) ) ;
 
client . getConnection ( conn -> {
     if ( conn . failed ( ) ) {
         System . err . println ( conn . cause ( ) . getMessage ( ) ) ;
         return ;
     }
 
     final SQLConnection connection = conn . result ( ) ;
     connection . execute ( "create table test(id int primary key, name varchar(255))" , res -> {
         if ( res . failed ( ) ) {
             throw new RuntimeException ( res . cause ( ) ) ;
         }
         // insert some test data
         connection . execute ( "insert into test values(1, 'Hello')" , insert -> {
             // query some data
             connection . query ( "select * from test" , rs -> {
                 for ( JsonArray line : rs . result ( ) . getResults ( ) ) {
                     System . out . println ( line . encode ( ) ) ;
                 }
 
                 // and close the connection
                 connection . close ( done -> {
                     if ( done . failed ( ) ) {
                         throw new RuntimeException ( done . cause ( ) ) ;
                     }
                 } ) ;
             } ) ;
         } ) ;
     } ) ;
} ) ;

上面代码可以是不是有点恶心呢?尝试改造一下吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
final JDBCClient client = JDBCClient . createShared ( vertx , new JsonObject ( )
. put ( "url" , "jdbc:hsqldb:mem:test?shutdown=true" )
. put ( "driver_class" , "org.hsqldb.jdbcDriver" )
. put ( "max_pool_size" , 30 ) ) ;
 
try ( SQLConnection conn = awaitResult ( jdbc :: getConnection ) ) {
     awaitResult ( h -> conn . execute ( "create table test(id int primary key, name varchar(255))" , h ) ) ;
     awaitResult ( h -> conn . execute ( "insert into test values(1, 'Hello')" , h ) ) ;
     ResultSet query = awaitResult ( h -> conn . query ( "select * from test" , h ) ) ;
     for ( JsonArray line : query . result . getResults ( ) ) {
         System . out . println ( line . encode ( ) ) ;
     }
     AsyncResult done = awaitResult ( h -> conn . close ( h ) ) ;
     if ( done . failed ( ) ) {
         throw new RuntimeException ( done . cause ( ) )
     }
} catch ( Exception e ) {
     e . printStackTrace ( ) ;
}

除了一个try catch,其他都没有嵌套,整体逻辑的可读性非常高,完全是线性的。

如何将逻辑放倒Fiber里

你也许会发现我们似乎一直都没有用到 fiberHandler 这个静态方法,上面虽然写了定义,可能大家还是不能够理解,这里结合场景也许能更好理解。
我们尝试实现一个操作很耗时的逻辑然后包到fiber里,避免 EventLoop 被阻塞。这里你也许会很好奇,既然 Fiber 这么廉价开启10万8万的无所谓啊,恩,这里再提一下quasar的重点部分: fiber可以很廉价的被创造出来,但是他本质上还是跑在一个线程上面,如果其中一个fiber执行了非常耗时的操作,则后面的fiber会一直等待,从而造成整个线程阻塞。 也就是说一个fiber不能执行非常耗时的操作,比如计算100万以内的素数之和,对于这种操作,我们可以通过直接将逻辑放到vert.x的worker线程里单独去跑,然后通过fiber包装一下就可以了。

1
2
3
4
5
6
7
AsyncResult <Long> result = awaitResult ( fiberHandler ( h -> vertx . executeBlocking ( ( Handler < Future <Long> > ) event -> {
     //求百万以内素数之和,这里的逻辑会在vert.x的worker线程里跑。随便耗时多久,都不会阻塞EventLoop
     long sum = sumOfPrime ( 1 , 000 , 000 ) ;
     event . complete ( sum ) ;
} , h ) ) ) ;
//打印结果
System . out . println ( result . result ( ) ) ;

这里你会注意到 awaitReslt 里用了 fiberHandler ,因为executeBlocking里的 handler 逻辑本身并没有跑在fiber体系下,所以会导致无效,而fiberHandler的作用就是将一段vert.x的handler包到 fiber 里。使之后续的await可以将其结果返回,这里使用awaitResult返回结果。

我们再深入一点看看 fiberHandler 方法里到底干了什么。

1
2
3
4
5
@Suspendable
public static <T> Handler <T> fiberHandler ( Handler <T> handler ) {
   FiberScheduler scheduler = getContextScheduler ( ) ;
   return p -> new Fiber <Void> ( scheduler , ( ) -> handler . handle ( p ) ) . start ( ) ;
}

这里获取Fiber的调度器,然后直接new了一个 Fiber ,避免了我们自己对逻辑做Fiber包装。是不是很简单呢。

总结

相比较传统的回调Handler,vertx-sync的包装十分优雅,基本可以恢复到同步方法调用级别,很好的减轻了异步回调带来的心智负担。
但是这个毕竟不是JVM级别的实现,所以或多或少还是有点门槛的,比如部署的时候,需要通过设置JVM参数来修改部分字节码,同时还要注意一些 需要挂起的方法上面加注释或者强行让其抛出可中断异常。个人建议在一些不重要的工具级项目里使用,非常重要的项目不推荐使用,当然了如果你觉得你的业务只需要依赖vert.x那么强烈你推荐你使用,只要记得打开 BlockingChecker 就好,可以即时的发现潜在的阻塞逻辑。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《次时代Java编程(一):续 vertx-sync实践》 的相关文章

  • 如何将本机库链接到 IntelliJ 中的 jar?

    我正在尝试在 IntelliJ 中设置 OpenCV 但是我一直在弄清楚如何告诉 IntelliJ 在哪里可以找到本机库位置 在 Eclipse 中 添加 jar 后 您可以在 Build Config 屏幕中设置 Native 库的位置
  • 日期语句之间的 JPQL SELECT [关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 我想将此 SQL 语句转换为等效的 JPQL SELECT FROM events WHERE events date BETWE
  • 在内存中使用 byte[] 创建 zip 文件。 Zip 文件总是损坏

    我创建的 zip 文件有问题 我正在使用 Java 7 我尝试从字节数组创建一个 zip 文件 其中包含两个或多个 Excel 文件 应用程序始终完成 没有任何异常 所以 我以为一切都好 当我尝试打开 zip 文件后 Windows 7 出
  • 如何使用assertEquals 和 Epsilon 在 JUnit 中断言两个双精度数?

    不推荐使用双打的assertEquals 我发现应该使用带有Epsilon的形式 这是因为双打不可能100 严格 但无论如何我需要比较两个双打 预期结果和实际结果 但我不知道该怎么做 目前我的测试如下 Test public void te
  • Pig Udf 显示结果

    我是 Pig 的新手 我用 Java 编写了一个 udf 并且包含了一个 System out println 其中的声明 我必须知道在 Pig 中运行时该语句在哪里打印 假设你的UDF 扩展了 EvalFunc 您可以使用从返回的 Log
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • Java 集合的并集或交集

    建立并集或交集的最简单方法是什么Set在 Java 中 我见过这个简单问题的一些奇怪的解决方案 例如手动迭代这两个集合 最简单的单行解决方案是这样的 set1 addAll set2 Union set1 retainAll set2 In
  • java.lang.IllegalStateException:提交响应后无法调用 sendRedirect()

    这两天我一直在尝试找出问题所在 我在这里读到我应该在代码中添加一个返回 我做到了 但我仍然得到 java lang IllegalStateException Cannot call sendRedirect after the respo
  • 帮助将图像从 Servlet 获取到 JSP 页面 [重复]

    这个问题在这里已经有答案了 我目前必须生成一个显示字符串文本的图像 我需要在 Servlet 上制作此图像 然后以某种方式将图像传递到 JSP 页面 以便它可以显示它 我试图避免保存图像 而是以某种方式将图像流式传输到 JSP 自从我开始寻
  • Hibernate 的 PersistentSet 不使用 hashCode/equals 的自定义实现

    所以我有一本实体书 public class Book private String id private String name private String description private Image coverImage pr
  • 像 Java 这样的静态类型语言中动态方法解析背后的原因是什么

    我对 Java 中引用变量的动态 静态类型和动态方法解析的概念有点困惑 考虑 public class Types Override public boolean equals Object obj System out println i
  • 如何对不同的参数类型使用相同的java方法?

    我的问题 我有 2 个已定义的记录 创建对象请求 更新对象请求 必须通过实用方法进行验证 由于这两个对象具有相同的字段 因此可以对这两种类型应用相同的验证方法 现在我只是使用两种方法进行重载 但它很冗长 public record Crea
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • 尝试将 Web 服务部署到 TomEE 时出现“找不到...的 appInfo”

    我有一个非常简单的项目 用于培训目的 它是一个 RESTful Web 服务 我使用 js css 和 html 创建了一个客户端 我正在尝试将该服务部署到 TomEE 这是我尝试部署时遇到的错误 我在这里做错了什么 刚刚遇到这个问题 我曾
  • logcat 中 mSecurityInputMethodService 为 null

    我写了一点android应显示智能手机当前位置 最后已知位置 的应用程序 尽管我复制了示例代码 并尝试了其他几种解决方案 但似乎每次都有相同的错误 我的应用程序由一个按钮组成 按下按钮应该log经度和纬度 但仅对数 mSecurityInp
  • java for windows 中的文件图标叠加

    我正在尝试像 Tortoise SVN 或 Dropbox 一样在文件和文件夹上实现图标叠加 我在网上查了很多资料 但没有找到Java的解决方案 Can anyone help me with this 很抱歉确认您的担忧 但这无法在 Ja
  • Eclipse 启动时崩溃;退出代码=13

    I am trying to work with Eclipse Helios on my x64 machine Im pretty sure now that this problem could occur with any ecli
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 如何将双精度/浮点四舍五入为二进制精度?

    我正在编写对浮点数执行计算的代码的测试 不出所料 结果很少是准确的 我想在计算结果和预期结果之间设置一个容差 我已经证实 在实践中 使用双精度 在对最后两位有效小数进行四舍五入后 结果始终是正确的 但是usually四舍五入最后一位小数后
  • Spring Rest 和 Jsonp

    我正在尝试让我的 Spring Rest 控制器返回jsonp但我没有快乐 如果我想返回 json 但我有返回的要求 完全相同的代码可以正常工作jsonp我添加了一个转换器 我在网上找到了用于执行 jsonp 转换的源代码 我正在使用 Sp

随机推荐

  • ARM的快速上下文切换(FCSE)

    一 FCSE的原理 通常情况下 xff0c 如果两个进程占用的虚拟地址空间由重叠 xff0c 系统在这两个进程之间进行切换时 xff0c 必须进行虚拟地址到物理地址的重映射 而虚拟地址到物理地址的重映射涉及到重建MMU中的页表 xff0c
  • 使用PyQt5/PySide2编写一个极简的音乐播放器

    文章目录 一 创建UI界面二 获取网络歌曲三 创建和链接信号槽 疫情肆虐 xff0c 憋在家实在无聊 xff0c 索性写点东西 xff0c 于是就有了这个极极极极极简的音乐播放器 这个极极极简的音乐播放器类似于 阅后即焚 的软件 xff0c
  • Android zxing二维码扫描 扫描框适应各种分辨率

    public synchronized Rect getFramingRect if framingRect 61 61 null if camera 61 61 null return null Point screenResolutio
  • 实战:MySQL Sending data导致查询很慢的问题详细分析

    这两天帮忙定位一个mysql查询很慢的问题 xff0c 定位过程综合各种方法 理论 工具 xff0c 很有代表性 xff0c 分享给大家作为新年礼物 xff1a xff09 问题现象 使用sphinx支持倒排索引 xff0c 但sphinx
  • TypeScript入门到入土(3)webpack打包ts代码

    上一节我们讲了ts的编译选项 xff0c 这一节我们介绍利用webpack打包我们的ts代码 前文链接 xff1a TypeScript入门到入土 xff08 2 xff09 小杨爱编程的博客 CSDN博客 ts 的安装以及ts中类型 xf
  • ROS中关于topic和service的运用场合

    本文的翻译来自 ROS answers 官方的问答区 2014 7 30 点击打开链接 topics seem like the best way for storing 34 states 34 of the robot stateful
  • 又见一帘幽梦

    又见一帘幽梦 最是魂断伤心处 xff0c 浮生若残梦 冷遇秋 xff0c 夕阳下 xff0c 碧野又清空 xff0c 泪眼朦胧 一帘幽梦红尘路 xff0c 爱深处 xff0c 情留驻 秋菊相思 xff0c 旭日重生 xff0c 无奈生死殊途
  • Openfeign ErrorDecoder caused java.io.IOException: stream is closed

    实现错误解码器 保留 feign 服务异常信息 xff0c 左侧是之前设计的 偶尔下游系统出现异常时 xff0c 拿到返回结果时 xff0c 居然会是stream is closed xff0c 就找了下原因 Don 39 t use an
  • 鞋厂园区--稳食生涯--2017~2018

    2017年初 进入广州的鞋类企业搞数字化新零售中台业务开发 这个地方 应该是我进入电商新零售业务的启蒙和提升之地 不但学习了线上线下的电商与门店业务 也学习了完整的一套新零售中台业务技术架构和整体的微服务演变进程 同时也应该算是收获最多朋友
  • 棉类工业园--奋斗的故事--2018~2020

    nbsp 2018年8月 进入深圳的工业园继续搞数字化新零售中台业务开发 18年初步规划设计 从0到1打造整个电商与新零售中台体系 1 用户中心 用户表 角色表 系统表 菜单表 用户角色表 角色菜单表 角色系统表 数据范围表 用户数据范围
  • YOLOv5改进:引入DenseNet思想打造密集连接模块,彻底提升目标检测性能

    目录 一 密集连接模块的介绍1 密集连接的概念2 密集连接与残差连接的对比3 DenseNet的结构 二 YOLOv5中引入密集连接模块的原因1 密集连接模块对于目标检测的优势2 密集连接模块对目标检测性能的影响 三 YOLOv5中密集连接
  • 不是我写的原文

    青春的尾声悄然敲响 xff0c 我站在尽头送别剩下为数不多的光阴 婚礼上同学聚会上 xff0c 越来越多的人开始为人妻为人母 xff0c 我们在经受着一场不可逆的转变 转变以前是快乐的 xff0c 转变后也会是另一种快乐 xff0c 但是转
  • 我这是一条什么路

    首先 xff0c 我从未在这里发表博客 xff0c 有些胆战 其次 xff0c 我技术不精 xff0c Java web php Android 再次 xff0c 没有共享过源码 xff0c 倒是从不少开源代码获益 最后 xff0c 这也许
  • 在珠海的一年

    时间倒退到2013年 作为应届生 xff0c 初入职场 xff0c 来到珠海 xff0c 开始了我IT生涯的第一步 以Java初级程序员的身份 xff0c 加入一家港企 xff0c 位于珠海的香洲区唐家湾镇 xff0c 还真的是一个镇啊 一
  • Hadoop的构造模块

    Hadoop集群中运行的守护进程共有5类 xff1a NameNodeDataNodeSecondary NameNodeJobTrackerTaskTracker Hadoop集群中的机器 节点 分为2类 xff1a 主节点和从节点 xf
  • 通过yum来进行mysql的安装

    1 卸载掉原有mysql rpm qa grep mysql 这个命令就会查看该操作系统上是否已经安装了mysql数据库 rpm e mysql 普通删除模式 rpm e nodeps mysql 强力删除模式 xff0c 如果使用上面命令
  • 因为咳嗽

    看了下我曾写过的博客 xff0c 居然发现年初的时候 xff0c 原来也咳嗽的撕心裂肺一次 xff0c 而这次又发作了 xff0c 我预估这是一种很严重的肺炎 xff0c 但还没去医院检测过 xff0c 心情甚是忧伤 回头望了下自己这几年写
  • 微服务横行的今天, 你的文档跟上节奏了么?

    转载自 xff1a https blog maxleap cn archives 1241 说起微服务 想必现在的技术圈内人士个个都能谈笑风云 娓娓道来 的确 技术变革日新月异 各种工具框架雨后春笋般涌现 现在我们可以轻巧便捷地根据自己的业
  • 微服务实战:从架构到发布(一)

    转载自 xff1a https blog maxleap cn archives 195 引言 xff1a 微服务 是当前软件架构领域非常热门的词汇 xff0c 能找到很多关于微服务的定义 准则 xff0c 以及如何从微服务中获益的文章 x
  • 《次时代Java编程(一):续 vertx-sync实践》

    转载自 xff1a https blog maxleap cn archives 1013 vertx sync是什么 上一篇我们已经讲了 Fiber 相关的知识 xff0c 想必大家对Java实现类似Golang的coroutine已经有