【问题分析】解决java中epoll依赖缺失问题
- 一、前言
- 二、问题描述
- 三、问题分析
- 四、解决方法
- 五、总结
一、前言
在学习使用lettuce框架实现UNIX域套接字unix domain socket连接redis时,遇到了一个问题,提示java.lang.IllegalStateException: A unix domain socket connection requires epoll or kqueue and neither is available
针对这个问题的解决,我们做一个总结
二、问题描述
1、关键业务代码
RedisURI uri = RedisURI.create("redis-socket:///tmp/redis.sock");
uri.setDatabase(0);
uri.setTimeout(Duration.of(100, ChronoUnit.SECONDS));
uri.setPassword("******");
RedisClient redisClient = RedisClient.create(uri);
StatefulRedisConnection<String, String> connection = redisClient.connect();
log.info("---------------11111111-------------------");
connection.setAutoFlushCommands(true);
RedisCommands<String, String> jedis = connection.sync();
2、报错异常详情
22:17:05.175 [http-nio-8098-exec-1] ERROR org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:175) - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: A unix domain socket connection requires epoll or kqueue and neither is available] with root cause
java.lang.IllegalStateException: A unix domain socket connection requires epoll or kqueue and neither is available
at io.lettuce.core.internal.LettuceAssert.assertState(LettuceAssert.java:236) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
at io.lettuce.core.resource.Transports$NativeTransports.assertDomainSocketAvailable(Transports.java:124) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
at io.lettuce.core.ConnectionBuilder.configureBootstrap(ConnectionBuilder.java:250) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
at io.lettuce.core.AbstractRedisClient.connectionBuilder(AbstractRedisClient.java:293) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
at io.lettuce.core.RedisClient.connectStatefulAsync(RedisClient.java:322) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
at io.lettuce.core.RedisClient.connectStandaloneAsync(RedisClient.java:287) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
at io.lettuce.core.RedisClient.connect(RedisClient.java:216) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
at io.lettuce.core.RedisClient.connect(RedisClient.java:201) ~[lettuce-core-6.2.1.RELEASE.jar!/:6.2.1.RELEASE]
at com.sk.init.MyClient05.start(MyClient05.java:31) ~[classes!/:0.0.1-SNAPSHOT]
at com.sk.action.TestAction.test(TestAction.java:68) ~[classes!/:0.0.1-SNAPSHOT]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_352]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_352]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_352]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_352]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.24.jar!/:5.3.24]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.24.jar!/:5.3.24]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:670) ~[tomcat-embed-core-9.0.70.jar!/:?]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.24.jar!/:5.3.24]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:779) ~[tomcat-embed-core-9.0.70.jar!/:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.70.jar!/:?]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.70.jar!/:?]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.70.jar!/:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.70.jar!/:?]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.70.jar!/:?]
三、问题分析
首先分析源码异常信息A unix domain socket connection requires epoll or kqueue and neither is available
是哪里报出来的
1、异常信息
由于kqueue
是FreeBSD
系统上的多路复用技术,epoll
是linux上的多路复用技术,所以异常是来自于EpollProvider.isAvailable()
2、EpollProvider.isAvailable()
由源码可知,由可能availability = Epoll.isAvailable()
;值为false
导致的,Epoll.java
源码如下:
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.epoll;
import io.netty.channel.ChannelOption;
import io.netty.channel.unix.FileDescriptor;
import io.netty.util.internal.SystemPropertyUtil;
/**
* Tells if <a href="https://netty.io/wiki/native-transports.html">{@code netty-transport-native-epoll}</a> is
* supported.
*/
public final class Epoll {
private static final Throwable UNAVAILABILITY_CAUSE;
static {
Throwable cause = null;
if (SystemPropertyUtil.getBoolean("io.netty.transport.noNative", false)) {
cause = new UnsupportedOperationException(
"Native transport was explicit disabled with -Dio.netty.transport.noNative=true");
} else {
FileDescriptor epollFd = null;
FileDescriptor eventFd = null;
try {
epollFd = Native.newEpollCreate();
eventFd = Native.newEventFd();
} catch (Throwable t) {
cause = t;
} finally {
if (epollFd != null) {
try {
epollFd.close();
} catch (Exception ignore) {
// ignore
}
}
if (eventFd != null) {
try {
eventFd.close();
} catch (Exception ignore) {
// ignore
}
}
}
}
UNAVAILABILITY_CAUSE = cause;
}
/**
* Returns {@code true} if and only if the <a href="https://netty.io/wiki/native-transports.html">{@code
* netty-transport-native-epoll}</a> is available.
*/
public static boolean isAvailable() {
return UNAVAILABILITY_CAUSE == null;
}
/**
* Ensure that <a href="https://netty.io/wiki/native-transports.html">{@code netty-transport-native-epoll}</a> is
* available.
*
* @throws UnsatisfiedLinkError if unavailable
*/
public static void ensureAvailability() {
if (UNAVAILABILITY_CAUSE != null) {
throw (Error) new UnsatisfiedLinkError(
"failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
}
}
/**
* Returns the cause of unavailability of <a href="https://netty.io/wiki/native-transports.html">
* {@code netty-transport-native-epoll}</a>.
*
* @return the cause if unavailable. {@code null} if available.
*/
public static Throwable unavailabilityCause() {
return UNAVAILABILITY_CAUSE;
}
/**
* Returns {@code true} if the epoll native transport is both {@linkplain #isAvailable() available} and supports
* {@linkplain ChannelOption#TCP_FASTOPEN_CONNECT client-side TCP FastOpen}.
*
* @return {@code true} if it's possible to use client-side TCP FastOpen via epoll, otherwise {@code false}.
*/
public static boolean isTcpFastOpenClientSideAvailable() {
return isAvailable() && Native.IS_SUPPORTING_TCP_FASTOPEN_CLIENT;
}
/**
* Returns {@code true} if the epoll native transport is both {@linkplain #isAvailable() available} and supports
* {@linkplain ChannelOption#TCP_FASTOPEN server-side TCP FastOpen}.
*
* @return {@code true} if it's possible to use server-side TCP FastOpen via epoll, otherwise {@code false}.
*/
public static boolean isTcpFastOpenServerSideAvailable() {
return isAvailable() && Native.IS_SUPPORTING_TCP_FASTOPEN_SERVER;
}
private Epoll() {
}
}
3、验证Epoll.isAvailable()
方法返回值
@Slf4j
@RestController
@RequiredArgsConstructor
public class TestAction {
private static final Throwable UNAVAILABILITY_CAUSE;
static {
Throwable cause = null;
if (SystemPropertyUtil.getBoolean("io.netty.transport.noNative", false)) {
cause = new UnsupportedOperationException(
"Native transport was explicit disabled with -Dio.netty.transport.noNative=true");
} else {
FileDescriptor epollFd = null;
FileDescriptor eventFd = null;
try {
epollFd = Native.newEpollCreate();
eventFd = Native.newEventFd();
} catch (Throwable t) {
cause = t;
} finally {
if (epollFd != null) {
try {
epollFd.close();
} catch (Exception ignore) {
// ignore
}
}
if (eventFd != null) {
try {
eventFd.close();
} catch (Exception ignore) {
// ignore
}
}
}
}
UNAVAILABILITY_CAUSE = cause;
}
private final MyClient05 myClient05;
//private final RedisConfig redisConfig;
@GetMapping("/test")
public void test(){
log.error("-----:{}",UNAVAILABILITY_CAUSE);
log.info("================Epoll:{}",Epoll.isAvailable());
myClient05.start();
}
}
执行结果:
19:26:54.428 [http-nio-8098-exec-1] ERROR com.sk.action.TestAction.test(TestAction.java:65) - -----:{}
java.lang.UnsatisfiedLinkError: could not load a native library: netty_transport_native_epoll_x86_64
at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:239) ~[netty-common-4.1.86.Final.jar!/:4.1.86.Final]
at io.netty.channel.epoll.Native.loadNativeLibrary(Native.java:323) ~[netty-transport-classes-epoll-4.1.86.Final.jar!/:4.1.86.Final]
at io.netty.channel.epoll.Native.<clinit>(Native.java:85) ~[netty-transport-classes-epoll-4.1.86.Final.jar!/:4.1.86.Final]
at com.sk.action.TestAction.<clinit>(TestAction.java:31) ~[classes!/:0.0.1-SNAPSHOT]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_352]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:1.8.0_352]
.
.
.
19:26:54.447 [http-nio-8098-exec-1] INFO com.sk.action.TestAction.test(TestAction.java:67) - ================Epoll:false
19:26:54.758 [http-nio-8098-exec-1] ERROR org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:175) - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: A unix domain socket connection requires epoll or kqueue and neither is available] with root cause
java.lang.IllegalStateException: A unix domain socket connection requires epoll or kqueue and neither is available
at io.lettuce.core.internal.LettuceAssert.assertState(LettuceAssert.java:236) ~[lettuce-core-6.1.5.RELEASE.jar!/:6.1.5.RELEASE]
at io.lettuce.core.resource.Transports$NativeTransports.assertDomainSocketAvailable(Transports.java:124) ~[lettuce-core-6.1.5.RELEASE.jar!/:6.1.5.RELEASE]
at io.lettuce.core.ConnectionBuilder.configureBootstrap(ConnectionBuilder.java:250) ~[lettuce-core-6.1.5.RELEASE.jar!/:6.1.5.RELEASE]
at io.lettuce.core.AbstractRedisClient.connectionBuilder(AbstractRedisClient.java:274) ~[lettuce-core-6.1.5.RELEASE.jar!/:6.1.5.RELEASE]
通过上述异常信息可知netty_transport_native_epoll_x86_64文件加载异常
四、解决方法
关于netty_transport_native_epoll_x86_64
文件引起的异常的原因有多种,所以只能针对不同的原因进行分析和验证;
首先想到的有两种,一种是缺少这个依赖,第二种就是这个依赖有冲突
针对第一种,引入epoll
相关的java依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<classifier>linux-x86_64</classifier>
</dependency>
没想到第一种方式就解决了问题
代码执行结果:
19:37:11.550 [http-nio-8098-exec-1] ERROR com.sk.action.TestAction.test(TestAction.java:65) - -----:{}
19:37:11.553 [http-nio-8098-exec-1] INFO com.sk.action.TestAction.test(TestAction.java:67) - ================Epoll:true
执行结果没有异常产生
五、总结
有一些问题相对比较偏门,网上的资料也比较少,只能通过分析源码,才能更精准的定位问题,所以经常性的分析源码对我们解决问题也比较有利的。