文档章节

使用 Quarkus/GraalVM 将 JGroups 编译成可执行文件

红薯
 红薯
发布于 07/04 13:28
字数 2027
阅读 1378
收藏 23

本文翻译自: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 

© 著作权归作者所有

红薯

红薯

粉丝 21516
博文 138
码字总数 54396
作品 8
深圳
产品经理
私信 提问
JGroups 4.1.0 发布,解析 PCAP 文件、同时使用 IPv4 与 IPv6

JGroups 4.1.0 发布了。JGroups 是一个可靠的群组通讯 Java 工具包,它基于 IP 组播,但在可靠性与组成员管理上进行了扩展。 此版本主要更新内容包括: GraalVM/Quarkus 支持 现在可以使用 ...

h4cd
05/23
0
0
Quarkus框架入门之一:Quarkus框架介绍及简单示例

开篇 最近几年,Spring全家桶横扫其它Java框架,已然成为事实标准,单体应用使用Spring Framework+Spring Boot,服务治理Spring Cloud,生态完善,各种组件层出不穷。曾经还玩过JFinal和Nut...

centychen
05/15
0
5
初探Oracle全栈虚拟机---GraalVM

官方说明: GraalVM是一个生态系统和共享运行时,不仅提供基于JVM的语言(如Java,Scala,Groovy和Kotlin)的性能优势,还提供其他编程语言(如JavaScript,Ruby,Python和R)的性能优势。此...

幻丶城
2018/07/27
0
0
厉害了,Oracle 发布了一个全栈虚拟机 GraalVM,支持 Python!

前阵子,Oracle 发布了一个黑科技 "GraalVM",号称是一个全新的通用全栈虚拟机,并具有高性能、跨语言交互等逆天特性,真有这么神奇? GraalVM 简介 GraalVM 是一个跨语言的通用虚拟机,不仅...

Java技术栈
2018/07/26
0
0
GraalVM 19.1.0 发布,高性能跨语言虚拟机

GraalVM 19.1.0 发布了。GraalVM 是高性能跨语言虚拟机,用于运行 JavaScript、Python 3、Ruby、R、基于 JVM 的语言,如 Java、Scala、Kotlin 和基于 LLVM 的语言,如 C 和 C++。 GraalVM 消...

h4cd
07/09
0
8

没有更多内容

加载失败,请刷新页面

加载更多

拥有有趣灵魂的程序员们,程序员访谈(一)

点击上方关注我们,让小care关爱你! 程序员群体一直都是低调多金的代表,而近段时间以来,程序员在网络上除了高薪之外,总是会和屌丝、苦逼、格子衫、没情趣...联系在一起。黑程序员的段子也...

ITCare
今天
29
0
Linux输入法fcitx的安装问题

Fcitx 总共要安装的包如下 fcitxfcitx-binfcitx-config-commonfcitx-config-gtk | fcitx-config-gtk2fcitx-datafcitx-frontend-allfcitx-frontend-gtk2fcitx-frontend-gtk3......

CHONGCHEN
今天
18
0
网络基础

前言: 最近整理一些以前的学习笔记(有部分缺失,会有些乱,日后再补)。 过去都是存储在本地,此次传到网络留待备用。 计算机网络的功能: 1.数据通信; 2.资源共享; 3.增加数据可靠性; 4....

迷失De挣扎
今天
15
0
spring boot升级到spring cloud

1、先升级spring boot 版本到2.1.3 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.3.RELEAS......

moon888
今天
32
0
从蓝鲸视角谈DevOps

DevOps源于Development和Operations的组合 常见的定义 DevOps是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变...

嘉为科技
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部