问题描述
使用netty-socket构建websocket,初始化NettySocketioService时报错,但是使用eclipse中的gradle启动没有问题.
不清楚是是什么原因,报错提示是找不到netty的一个方法,但是我本地引了包,解压的war包里面也找到了netty的这个jar和其中的方法。
本项目没有在其他地方引用netty的jar,这几个netty的jar也是引用了netty-socketio后,由它依赖下载的,不存在冲突
问题出现的环境背景及自己尝试过哪些方法
java1.8,tomcat8.5.12,netty-socketio:1.7.17
尝试过将init方法用@PostConstruct启动,无效。
相关代码
applicationContext-socket.xml
<bean id="socketIO" class="com.excenon.community.socket.config.NettySocketioService" init-method="init"></bean>
NettySocketioService.java
`public class NettySocketioService {
private static final Logger logger = LoggerFactory.getLogger(NettySocketioService.class);
private Cache<String, Object> socketCache = GCacheGenerator.generator(10, 5, 1, TimeUnit.DAYS);
private SocketIOServer server;
@Autowired
private RedisHelper redisHelper;
public void init() {
// ZlibCodecFactory.isSupportingWindowSizeAndMemLevel();
logger.info("Socket_IO开始初始化");
Configuration configuration = new Configuration();
configuration.setHostname("10.3.30.182");
configuration.setPort(9009);
server = new SocketIOServer(configuration);
server.addConnectListener(new ConnectListener() {
@Override
public void onConnect(SocketIOClient client) {
String uuid = client.getSessionId().toString();
if (StringUtils.isBlank(uuid)) {
logger.info("异常,socket请求sessionId为空");
client.sendEvent("error", "异常,socket请求sessionId为空");
client.disconnect();
}
// 获取Redis中的token信息
String token = client.getHandshakeData().getSingleUrlParam("mac");
byte[] userB = redisHelper.hget((SystemConstant.SPRING_SESSION_PREFIX + token).getBytes(),
"sessionAttr:sysUser".getBytes());
if (userB == null) {
logger.info("未找到用户登录信息, uuid : {}, userToken : {}", uuid, token);
client.sendEvent("error", "未找到用户登录信息");
client.disconnect();
}
// 反序列化user信息
ByteArrayInputStream bais = new ByteArrayInputStream(userB);
ObjectInputStream ois = null;
SysUser user = null;
try {
ois = new ObjectInputStream(bais);
user = (SysUser)ois.readObject();
} catch (Exception e) {
logger.error("用户信息反序列化失败, uuid : {}, userToken : {}", uuid, token);
client.sendEvent("error", "用户信息反序列化失败");
client.disconnect();
}
addCache(uuid, user);
logger.info("客户端: {}已连接,mac={}, user:{}, user:{}, userid:{}", client.getSessionId(), token, user,
userB.toString(), user.getId());
}
});
server.addDisconnectListener(new DisconnectListener() {
@SuppressWarnings("unchecked")
@Override
public void onDisconnect(SocketIOClient client) {
// 链接disconnect,销毁该链接uuid和userid的关联
String uuid = client.getSessionId().toString();
if (StringUtils.isNotBlank(uuid)) {
String userId = (String)socketCache.getIfPresent(uuid);
if (StringUtils.isNotBlank(userId)) {
socketCache.invalidate(uuid);
List<String> uuids = (List<String>)socketCache.getIfPresent(userId);
if (CollectionUtils.isNotEmpty(uuids) && uuids.contains(uuid)) {
uuids.remove(uuid);
if (CollectionUtils.isEmpty(uuids)) {
socketCache.invalidate(userId);
} else {
socketCache.put(userId, uuids);
}
}
}
}
}
});
server.start();
logger.info("Socket_IO已启动");
}`
}
启动报错
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'socketIO' defined in URL [file:/D:/apache-tomcat-8.0.52/webapps/cmty_socket/WEB-INF/classes/applicationContext-socket.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: io.netty.handler.codec.compression.ZlibCodecFactory.isSupportingWindowSizeAndMemLevel()Z
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1566)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4900)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5363)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:755)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:731)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:973)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1850)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoSuchMethodError: io.netty.handler.codec.compression.ZlibCodecFactory.isSupportingWindowSizeAndMemLevel()Z
at com.excenon.community.socket.config.NettySocketioService.init(NettySocketioService.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1694)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1562)
... 25 more
最后的解决方案记录一下:
1、为netty-socket单独用一个线程启动(加锁保证唯一),在v2ex上看到一种说法,就是netty好像会在某些情况下阻塞spring的加载
2、检查gradle依赖,发现有一个netty-all的包,虽然不是netty-socket依赖的,是由rocketMQ依赖,但是它的版本过低也会导致启动失败,最后在配置中指定了netty-all引用最新的版本