0x00 背景
当我在 M1(arm处理器)上进行开发的时候,需要用到 netty,尤其是使用它的 Unix Domain Socket 功能,于是在 pom.xml 中有这样的定义
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<classifier>osx-x86_64</classifier>
</dependency>
复制代码
这会在项目中引入 netty-transport-native-kqueue-4.1.63.Final-osx-x86_64.jar
,项目启动回失败,异常日志如下
Caused by: java.lang.IllegalArgumentException: Unsupported channel type: ServerDomainSocketChannel
at reactor.netty.resources.DefaultLoopNIO.getChannel(DefaultLoopNIO.java:50) ~[reactor-netty-core-1.0.6.jar:1.0.6]
at reactor.netty.resources.LoopResources.onChannel(LoopResources.java:214) ~[reactor-netty-core-1.0.6.jar:1.0.6]
复制代码
0x01 分析
异常来自如下代码
public <CHANNEL extends Channel> CHANNEL getChannel(Class<CHANNEL> channelClass) {
if (channelClass.equals(SocketChannel.class)) {
return (CHANNEL) new NioSocketChannel();
}
if (channelClass.equals(ServerSocketChannel.class)) {
return (CHANNEL) new NioServerSocketChannel();
}
if (channelClass.equals(DatagramChannel.class)) {
return (CHANNEL) new NioDatagramChannel();
}
throw new IllegalArgumentException("Unsupported channel type: " + channelClass.getSimpleName());
}
复制代码
这段代码在 DefaultLoopNIO 里,继承自 DefaultLoop,DefaultLoop 的实现类还有 DefaultLoopKQueue,按理说, netty 会判断所在的系统来使用 DefaultLoop 的实现类,比如 macos 系统 使用 DefaultLoopKQueue,linux 系统使用 DefaultLoopEpoll。
我用的是 macos 系统,所以代码应该走入到 DefaultLoopKQueue,而不是 DefaultLoopNIO,带着这个问题追踪溯源
原来是 netty 在启动的时候,进行了如下判断
//代码位于 PlatformDependent 类中
private static final String NORMALIZED_ARCH = normalizeArch(SystemPropertyUtil.get("os.arch", ""));
// 在 arm64 机器上,NORMALIZED_ARCH = aarch_64
……
public static String normalizedArch() {
return NORMALIZED_ARCH;
}
复制代码
经由 Native#loadNativeLibrary 函数中调用 PlatformDependent.normalizedArch() 判断所处操作系统类型
然后从Native#loadNativeLibrary 到 NativeLibraryLoader#load() 函数,最终拼接出在 macos(arm64) 平台上应该需要使用的的 jnilib 的名字,libnetty_transport_native_kqueue_aarch_64.jnilib
,然后在工程定义的搜索文件路径中搜索该文件。
当然找不到了,因为我引入的是 netty-transport-native-kqueue-osx_x86_64
这个 jar,里面携带的 jnilib 文件名字是libnetty_transport_native_kqueue_x86_64.jnilib
所以我以为在 pom.xml 中声明一下正确的依赖就可以了
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<classifier>osx-aarch_64</classifier>
</dependency>
复制代码
0x02 解决
可问题是,maven center 的 release 和snapshot 库都没有 netty-transport-native-kqueue-osx_aarch_64
这个 jar,无奈,心里一横,只能尝试自己去编译这个 jar。
受到 netty-transport-native-epoll-4.1.43.Final-linux-aarch_64.jar 移植指南(CentOS 7.6) 这个启发,于是决定在 macos(arm64) 机器上编译出 netty-transport-native-kqueue-osx_aarch_64
,实施步骤和这个文档描述的一样,只是编译第二步的命令需要从 ./mvnw clean install -pl transport-native-epoll -DskipTests=true
替换成 ./mvnw clean install -pl transport-native-kqueue -DskipTests=true
。
编译成功之后,果然编译出了 netty-transport-native-kqueue-4.1.63.Final-osx-aarch_64.jar
,下一步是怎么引入到自己的工程里了。编译结果如下
参考 springboot导入本地jar包,我选择将 jar 包放入项目目录中。
首先在src下建目录lib,并将编译好的 netty-transport-native-kqueue-4.1.63.Final-osx-aarch_64.jar
和 netty-transport-native-kqueue-4.1.63.Final.jar
包放入 lib 文件夹中,然后在 IDEA 的 Libraries 中添加这个 lib 文件夹
最后修改 pom.xml
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<classifier>osx-aarch_64</classifier>
<version>4.1.63.Final</version>
</dependency>
复制代码
启动工程,可以很顺利地启动了。
0x03 尾声
这只是临时方案,希望 netty 官方尽快发布 netty-transport-native-kqueue-4.1.63.Final.jar
到 maven center。