每日一博 | 使用 Quarkus/GraalVM 将 JGroups 编译成可执行文件
本文翻译自:http://belaban.blogspot.com/2019/07/compiling-jgroups-to-native-code-with.html
我很高兴的宣布 Quarkus 官方发布 JGroups 的扩展!
What?
Quarkus 是一个将 Java 代码编译为本机代码(使用GraalVM)并删除运行时不需要的代码的框架。
Quarkus 在构建阶段分析代码,并删除在运行时未使用的代码,以便拥有一个可以快速启动的小型可执行文件。不过这意味着无法在运行时使用反射,因为在构建时删除了所有未使用的类。 但是,可以在构建时使用反射。
影响 JGroups 的其他限制是线程和套接字的创建。 两者都无法在构建时完成,但必须在运行时完成。
那么为Quarkus提供JGroups扩展的重点是什么呢?
虽然JGroups应用程序可以直接编译为本机代码(使用GraalVM的本机映像),但它很麻烦,并且必须重新构建应用程序以适应本机编译的限制。
相反,JGroups 扩展提供了一个可以注入应用程序的JChannel。 已根据配置文件创建通道,并通过扩展连接(=加入群集)。 扩展负责在正确的时间(构建或运行时)执行反射,套接字创建和线程启动,用户无需担心这一点。
How?
接下来让我们看一个具体的例子。
POM 引入扩展 groupId=org.jgroups.quarkus.extension 和 artifactId=quarkus-jgroups. 这样就可以提供一个可注入的 JChannel。
主类是 ChatResource,代码如下:
@ApplicationScoped @Path("/chat") public class ChatResource extends ReceiverAdapter implements Publisher<String> { protected final Set<Subscriber<? super String>> subscribers=new HashSet<>(); @Inject JChannel channel; protected void init(@Observes StartupEvent evt) throws Exception { channel.setReceiver(this); System.out.printf("-- view: %s\n", channel.getView()); } protected void destroy(@Observes ShutdownEvent evt) { Util.close(channel); subscribers.forEach(Subscriber::onComplete); subscribers.clear(); } @GET @Produces(MediaType.TEXT_PLAIN) @Path("/send/{msg}") public String sendMessage(@PathParam("msg") String msg) throws Exception { channel.send(null, Objects.requireNonNull(msg).getBytes()); return String.format("message \"%s\" was sent on channel \n", msg); } @GET @Produces(MediaType.SERVER_SENT_EVENTS) @Path("/subscribe") public Publisher<String> greeting() { return this; } public void receive(Message msg) { onNext(msg); } public void receive(MessageBatch batch) { for(Message msg: batch) onNext(msg); } public void viewAccepted(View view) { System.out.printf("-- new view: %s\n", view); } public void subscribe(Subscriber<? super String> s) { if(s != null) subscribers.add(s); } protected void onNext(Message msg) { String s=new String(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); System.out.printf("-- from %s: %s\n", msg.src(), s); subscribers.forEach(sub -> sub.onNext(s)); } }
它有一个由Arc注入的JChannel通道(Quarkus中使用的依赖机制)。 该通道在注入时已经完全创建并连接。
receive(Message) 和 receive(MessageBatch) 方法接收由其自身或集群中的其他成员发送的消息。 它反过来通过Publisher接口发布它们。 因此,所有订户都将收到群集中发送的所有消息。
当收到格式为http://localhost:8080/chat/send/mymessage 的 URL 时,将调用 sendMessage() 方法。 它接受字符串参数(“mymessage”)并使用注入的通道将其发送给集群的所有成员。
URL http://localhost:8080/chat/subscribe (或者在浏览器中的 http://localhost:8080/streaming.html) 可用来订阅消息。
演示
接下来我们运行两个实例的集群,打开两个命令行窗口,并输入如下的命令:
Shell1: [belasmac] /Users/bela/quarkus-jgroups-chat$ mvn compile quarkus:dev ... [INFO] --- quarkus-maven-plugin:0.18.0:dev (default-cli) @ quarkus-jgroups-chat --- 2019-07-03 14:12:05,025 DEBUG [org.jgr.qua.ext.JChannelTemplate] (main) creating channel based on config config=chat-tcp.xml, bind_addr=, initial_hosts=, cluster=quarkus-jgroups-chat ------------------------------------------------------------------- GMS: address=belasmac-19612, cluster=quarkus-jgroups-chat, physical address=127.0.0.1:7800 ------------------------------------------------------------------- -- view: [belasmac-19612|0] (1) [belasmac-19612] Shell2: [belasmac] /Users/bela/quarkus-jgroups-chat$ mvn compile quarkus:dev -Dquarkus.http.port=8081 ... [INFO] --- quarkus-maven-plugin:0.18.0:dev (default-cli) @ quarkus-jgroups-chat --- 2019-07-03 14:15:02,463 DEBUG [org.jgr.qua.ext.JChannelTemplate] (main) creating channel based on config config=chat-tcp.xml, bind_addr=, initial_hosts=, cluster=quarkus-jgroups-chat ------------------------------------------------------------------- GMS: address=belasmac-25898, cluster=quarkus-jgroups-chat, physical address=127.0.0.1:7801 ------------------------------------------------------------------- -- view: [belasmac-19612|1] (2) [belasmac-19612, belasmac-25898]
这里我们需要一个系统属性设置 quarkus.http.port=8081 ,否则会产生端口冲突,因为默认的 8080 端口已经被第一个应用占用。
输出信息显示集群共有两个成员。
我们可以通过调用 curl http://localhost:8080/chat/send/hello%20world 和 curl http://localhost:8081/chat/send/message2 来发送消息。
两个命令行窗口都显示接收到同样的消息:
-- view: [belasmac-19612|1] (2) [belasmac-19612, belasmac-25898] -- from belasmac-19612: hello world -- from belasmac-25898: message2
当然我们也可以使用浏览器来发送 HTTP GET 请求。
当在浏览器中订阅消息时 (http://localhost:8081/streaming.html),会有如下效果:
注意这些频道都是绑定到本机 loopback (127.0.0.1) 地址上。可以通过 application.properties 配置中的 bind_addr 和 initial_hosts 来进行修改。
quarkus.channel.config=chat-tcp.xml quarkus.channel.cluster=quarkus-jgroups-chat # quarkus.channel.bind_addr=192.168.1.105 # quarkus.channel.initial_hosts=192.168.1.105[7800]
另外我们也可以通过系统属性来进行设置,例如:
[belasmac] /Users/bela/quarkus-jgroups-chat$ mvn compile quarkus:dev -Dbind_addr=192.168.1.105 -Dinitial_hosts=192.168.1.105[7800],192.168.1.105[7801] ... [INFO] --- quarkus-maven-plugin:0.18.0:dev (default-cli) @ quarkus-jgroups-chat --- 2019-07-03 14:38:28,258 DEBUG [org.jgr.qua.ext.JChannelTemplate] (main) creating channel based on config config=chat-tcp.xml, bind_addr=, initial_hosts=, cluster=quarkus-jgroups-chat ------------------------------------------------------------------- GMS: address=belasmac-10738, cluster=quarkus-jgroups-chat, physical address=192.168.1.105:7800 ------------------------------------------------------------------- -- view: [belasmac-10738|0] (1) [belasmac-10738]
编译本机可执行程序
要将应用编译成可执行程序,可以使用 mvn package -Pnative 命令:
[belasmac] /Users/bela/quarkus-jgroups-chat$ mvn package -Pnative [INFO] Building jar: /Users/bela/quarkus-jgroups-chat/target/quarkus-jgroups-chat-1.0.0-SNAPSHOT.jar [INFO] [INFO] --- quarkus-maven-plugin:0.18.0:build (default) @ quarkus-jgroups-chat --- [INFO] [io.quarkus.deployment.QuarkusAugmentor] Beginning quarkus augmentation [INFO] [org.jboss.threads] JBoss Threads version 3.0.0.Beta4 [INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 1343ms [INFO] [io.quarkus.creator.phase.runnerjar.RunnerJarPhase] Building jar: /Users/bela/quarkus-jgroups-chat/target/quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner.jar [INFO] [INFO] --- quarkus-maven-plugin:0.18.0:native-image (default) @ quarkus-jgroups-chat --- [INFO] [io.quarkus.creator.phase.nativeimage.NativeImagePhase] Running Quarkus native-image plugin on OpenJDK 64-Bit Server VM [INFO] [io.quarkus.creator.phase.nativeimage.NativeImagePhase] /Users/bela/graalvm/Contents/Home/bin/native-image -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -jar quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner.jar -J-Djava.util.concurrent.ForkJoinPool.common.parallelism=1 -H:FallbackThreshold=0 -H:+ReportUnsupportedElementsAtRuntime -H:+ReportExceptionStackTraces -H:+PrintAnalysisCallTree -H:-AddAllCharsets -H:EnableURLProtocols=http -H:-SpawnIsolates -H:+JNI --no-server -H:-UseServiceLoaderFeature -H:+StackTrace [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] classlist: 6,857.25 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] (cap): 4,290.72 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] setup: 6,430.30 ms 14:43:05,540 INFO [org.jbo.threads] JBoss Threads version 3.0.0.Beta4 14:43:06,468 INFO [org.xnio] XNIO version 3.7.2.Final 14:43:06,528 INFO [org.xni.nio] XNIO NIO Implementation Version 3.7.2.Final [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] (typeflow): 17,331.26 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] (objects): 24,511.12 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] (features): 1,194.16 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] analysis: 44,204.65 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] (clinit): 579.00 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] universe: 1,715.40 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] (parse): 3,315.80 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] (inline): 4,563.11 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] (compile): 24,906.58 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] compile: 34,907.28 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] image: 4,557.78 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] write: 2,531.16 ms [quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner:93574] [total]: 109,858.54 ms [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 01:58 min [INFO] Finished at: 2019-07-03T14:44:40+02:00
这使用的是 GraalVM 的本地应用映像来生成一个本地可执行程序。生成完成后将在 ./target 目录产生一个可执行文件:
其大小约为 27MB ,在 MacOS 的可执行程序如下:
[belasmac] /Users/bela/quarkus-jgroups-chat/target$ ls -lh quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner -rwxr-xr-x 1 bela staff 27M Jul 3 14:44 quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner [belasmac] /Users/bela/quarkus-jgroups-chat/target$ file quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner: Mach-O 64-bit executable x86_64
接下来可以运行这个程序:
[belasmac] /Users/bela/quarkus-jgroups-chat/target$ ./quarkus-jgroups-chat-1.0.0-SNAPSHOT-runner ------------------------------------------------------------------- GMS: address=belasmac-55106, cluster=quarkus-jgroups-chat, physical address=127.0.0.1:7800 ------------------------------------------------------------------- -- view: [belasmac-55106|0] (1) [belasmac-55106]
当您自己运行时,您会注意到第二个及后续成员的快速启动时间。 为什么不是第一个成员? 第一个成员必须等待GMS.join_timeout millis(在chat-tcp.xml中定义)以查看它是否发现任何其他成员,因此它总是会遇到此超时。
要改动 bind_addr 和 initial_hosts 的话,application.properties 必须在编译成本机代码之前进行修改。
注意事项
quarkus-jgroups扩展依赖于JGroups-4.1.2-SNAPSHOT,除非已将快照存储库添加到POM(或settings.xml),否则它可能无法找到。 或者通过如下命令在您的本地maven仓库中生成并安装此工件:
git clone https://github.com/belaban/JGroups.git; cd JGroups; mvn install
当前的版本只支持 TCP 通讯,UDP 需要在 GraalVM 支持 MulticastSockets 后才可以使用。
出于某些不明原因,必须在 POM 中启用 enableJni ,否则编译成本机代码时会失败。
<enableJni>true</enableJni>
希望我能快速理解这个原因并解决问题。
总结
这是快速将 JGroups 移植到本机代码的方法。 有关反馈和问题,请使用JGroups邮件列表。
接下来的计划:
- 通过扩展提供更多 JGroups 类的支持,例如 RpcDispatcher (用以执行远程方法调用)
- 提供本地可执行程序的 Docker 映像
- 支持 UDP
- 降低可执行文件的体积
Enjoy!
相关链接:
[1] https://github.com/jgroups-extras/quarkus-jgroups
[2] https://github.com/jgroups-extras/quarkus-jgroups-chat
[3] https://quarkus.io
[4] https://www.graalvm.org
[5] https://github.com/belaban/JGroups/blob/master/doc/design/PortingToGraalVM.txt
[6] https://github.com/belaban/JGroups/blob/master/tests/perf/org/jgroups/tests/perf/ProgrammaticUPerf2.java
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
腾讯开源软件镜像站上线
腾讯开源软件镜像站(Tencent Open Source Mirror Site)已于近日上线,其官方名称为「腾讯云软件源」,由腾讯云提供支持。 地址 >>>https://mirrors.cloud.tencent.com/index.html 官方表示搭建此开源镜像站的目的在于宣传自由软件的价值,提高自由软件社区文化氛围,推广自由软件在国内的应用。 腾讯开源软件镜像站提供了主流的 Linux 发行版下载,如 Ubuntu、Arch Linux、CentOS 和 Debian 等,以及常用的开源项目和 SDK 下载,如 Android SDK、Ceph、Flutter、Qt 和 Zabbix 等。 速度方面,有网友提供了一张下载 Ubuntu ISO 文件的截图以供参考: 通过使用腾讯云镜像源,可在一定程度上解决安装软件依赖时官方源访问速度慢的问题。 另外,腾讯软件源站支持公网访问和内网访问,没有公网出口的云服务器可通过内网使用软件源站,方便用户自由搭建服务架构。 公网域名 http://mirrors.cloud.tencent.com/ 内网域名 ht...
- 下一篇
微软内核工程师称 ReactOS 剽窃 Windows Research Kernel
微软内核工程师 Axel Rietschin 在问答网站Quora 上回答“你怎么看待 ReactOS” 时称,ReactOS 盗用了微软授权给大学使用的 Windows Research Kernel。 Axel认为 ReactOS 不可能是仅仅根据公开文档的净室实现。他表示自己已经看过 ReactOS 的代码树,并指出其中许多内部数据结构和内部函数名称和微软的 Research Kernel 完全相同。 他还猜想,ReactOS 的代码可能来源于过去发生的几个 Windows NT 源码泄漏事件之一。 这篇回答发布于 2017 年 11 月,但直到最近才被人们关注起来。一天前,帖子的链接被放上了 Hacker News,Axel 再次讨论了这个问题。 “宏编号名称、参数等永远不会出现在编译代码中。几乎肯定不可能的是,净室的重新实现最终会使用宏来完成同样的事情,更不用说具有相同或相似名称的宏了。” Axel 还提到,他与一位经验丰富的内核工程师讨论了这件事,后者的团队前一段时间调查了 ReactOS 并得出了完全相同的结论:不可能。 昨天,一个名为 Filip Navara 的人在 ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS6,CentOS7官方镜像安装Oracle11G
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- CentOS6,7,8上安装Nginx,支持https2.0的开启