《次时代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实践》 的相关文章

  • TaskSchedulerImpl:初始作业尚未接受任何资源;

    这就是我正在尝试做的事情 我创建了DataStax企业集群的两个节点 在其上创建了一个java程序来获取一张表 Cassandra数据库表 的计数 该程序是在 eclipse 中构建的 实际上是来自 windows 盒子 从 Windows
  • 非常大的 JTable、RowFilter 和额外负载

    我想请求澄清 RowFilter 的使用及其对性能的影响 我通过 include Entry 方法实现了一个过滤器 对于每一行 只需检查模型中其对应值是否设置了布尔标志 如果是 则返回 true 否则返回 false 现在 我拥有的 JTa
  • Android Canvas.DrawBitmap 没有模糊/抗锯齿?

    我正在尝试使用精灵 或非常像素化的角色 背景等 制作一个Android游戏 我把它们画在画布上 就像这样 matrix preScale xrat yrat canvas drawBitmap img matrix null 其中 img
  • 使用java进行JSON模式验证[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在为返回 JSON 对象的 java webapp 编写一些验收测试 我想验证返回的 JSON 是否针对架构进行验证 任何人都可以建议
  • 谁能解释一下POJO或POCO的含义和用法[重复]

    这个问题在这里已经有答案了 可能的重复 纯旧 Java 对象 POJO 一词的确切含义是什么 https stackoverflow com questions 3326319 what does the term plain old ja
  • API 级别 15 的印地语字体(又名 Android 4.0.2)

    我有一个基于印地语内容的 Android 应用程序 并使用了 Android API 16 SDK 中的 devangiri 字体 并重命名为印地语 ttf 文本在 API 级别 16 和 17 上渲染良好 但在 Android API 级
  • 良好的客户端套接字池

    我需要管理从我的 Java 应用程序到外部服务器的长时间运行的 TCP 套接字连接 我正在寻找一个好的套接字池 这样我就可以重复使用套接字 有什么建议吗 你可以看看在上面建立一个套接字池公共池 http commons apache org
  • 如何将 JTextField 添加到 JFrame 的菜单栏?

    我一直在尝试重载 JMenu 并放入一些自定义代码来支持 JTextField 但进展并不顺利 我在这里的主要目的是在菜单项的右侧添加一个搜索字段 因此 我在左侧有 文件 编辑 帮助 之类的东西 然后在右侧是搜索栏 几乎就像某些浏览器中的谷
  • 如何更改鼠标进入/鼠标退出时按钮的图标图像?

    我想更改鼠标输入和鼠标退出时按钮的图标图像 private void jButton1MouseEntered java awt event MouseEvent evt this jButton1 setBackground Color
  • 根据条件更改 JSlider 的最小值和最大值

    我正在 Netbeans 中创建 Swing GUI 此 GUI 的目的是打开一个 缓冲 图像 在 JLabel 中作为图标 并对其应用仿射变换 现在我正在做 4 个转换 如下所示 现在 每个变换都需要两个滑块来更改 X 和 Y 值 但旋转
  • Spring方法获取给定类型的所有bean

    我试图从一个相同类型的豆子中获取所有豆子FileSystemXmlApplicationContext 我正在使用factory getBeansOfType SomeType class 但我注意到它只返回顶级 bean 是否有任何其他方
  • 在 Android 应用程序中读取 CSV 文件

    我正在开发一个概念验证应用程序 以便我可以在我正在制作的更大的应用程序中实现该功能 我对 Java 和 Android 开发有点陌生 但希望这个问题不会太简单或太复杂 基本上 我试图从 CSV 文件中读取字符串列表 并使其可用于在应用程序的
  • 如何在 spring-boot 中禁用 spring-data-mongodb 自动配置

    有没有人尝试过在 spring boot 中禁用 mongodb 的自动配置 我正在尝试使用 spring data mongodb 来启动 spring boot 使用基于java的配置 使用 spring boot 1 2 1 RELE
  • Java 接口合成方法生成,同时缩小返回类型

    我有 2 个接口和 2 个返回类型 interface interfaceA Publisher
  • 如何在android中格式化长整型以始终显示两位数

    我有一个倒计时器 显示从 60 到 0 的秒数 1 分钟倒计时器 当它达到 1 位数字 例如 9 8 7 时 它显示 9 而不是 09 我尝试使用String format B 02d B x 我将 x 从 long 转换为字符串 它不起作
  • 使用当前日期时间的 RecyclerView 的动态节标题

    我将使用 RecyclerView 作为节标题 我想在 SQLite 数据库中插入每个日期和时间数据时创建节标题 我按照以下链接获取了此解决方案 但没有成功 请参考下图 对于上面的图像数据 使用以下代码或部分是静态的 List
  • 无法在 JDBCPreparedStatement 中使用 LIKE 查询吗?

    查询代码及查询方式 ps conn prepareStatement select instance id from eam measurement where resource id in select RESOURCE ID from
  • onActivityresult 数据为空

    这是我的相机应用程序 我想在其中捕获图像并裁剪它 但它拍照保存在我的 myimage 目录中 但不执行裁剪功能 请我需要帮助 我是这个领域的新人 这是我的相机开源代码 Intent intent new Intent MediaStore
  • 使用 Java Swing 平均成绩 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我有一个家庭作业 我一直在编码 我以
  • 我可以从 LDAP 更改自己的 Active Directory 密码(无需管理帐户)

    我没有 也不会 拥有管理员帐户 我想从 java 更改 Active Directory 中自己 用户 的密码 我怎样才能做到这一点 使用来自网络的代码 private void changePass throws Exception St

随机推荐

  • 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已经有