背景
WebSocket协议是一种基于TCP的新协议。不同于http仅能实现单向通信,它实现了浏览器与服务器的全双工通信,因此可以被应用在bs架构的聊天系统、远程日志查看等系统中。websocket在建立连线时仅需要进行一次“握手”就能建立“长连接”,也就是浏览器发出websocket连线请求,然后服务器发出回应即可。目前chrome、firefox和IE10+等主流浏览器都支持Websocket.
问题
由于项目需要,最近在做一个如下的自主上线系统。为了便于尽早发现上线过程中的问题,该系统中有一个在web页面上实时显示服务日志的功能,我在通过websocket实现这个功能的过程中遇到了2个问题:websocket建立连接时需要传递参数(例如服务器ip,项目名称,日志文件位置等)进去;需要注入service 层的类,以便在onOpen()方法中进行数据查询和业务处理。百度谷歌一顿搜索后,发现这两个问题还挺有共性的,很多人都在问,但是靠谱的答案却比较少见。通过查看源码和各种折腾,最后还是解决了这些问题,做个总结。
解决方案
先上解决方案:
@ServerEndpoint("/websocket/{moduleName}/{serverIp}") public class LogSocket { // 与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; private Process process; private InputStream inputStream; private static Logger logger = Logger.getLogger(LogSocket.class); private final int DEFAULT_SSH_PORT = 22; //websocket中不能使用注解进行依赖注入 //Resource //private OnlineService onlineService; private OnlineService onlineService = (OnlineService) ContextLoader.getCurrentWebApplicationContext().getBean("onlineService"); /** * 连接建立成功调用的方法 * * @param session * 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据 */ @OnOpen public void onOpen(@PathParam("moduleName") String moduleName, @PathParam("serverIp") String serverIp, Session session) { this.session = session; webSocketSet.add(this); // 加入set中 logger.info("日志开始"); logger.info("websocket:" + moduleName); System.out.println("websocket:" + moduleName); String command = "tail -100f " + onlineService.getLogpathByModuleName( moduleName.trim(), serverIp.trim());
参数传递
其实这个问题不难,主要是websocket的应用比较少,自己平时也少有看源码的习惯。先来看看websocket的OnOpen注解的源码,突破点已用粗体标出了。
package javax.websocket; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This method level annotation can be used to decorate a Java method that wishes to be called when a new * web socket session is open. * * <p>The method may only take the following parameters:- * <ul> * <li>optional {@link Session} parameter</li> * <li>optional {@link EndpointConfig} parameter</li> * <li>Zero to n String parameters annotated with the {@link javax.websocket.server.PathParam} annotation.</li> * </ul> * * <p>The parameters may appear in any order. * * @author dannycoward */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface OnOpen { }
依赖注入
起初是通过注解的方式注入的依赖,运行时报空指针,没有注入进去。一番折腾之后,发现虽然使用注解无法注入,但是使用显示的方式加载bean(ContextLoader.getCurrentWebApplicationContext().getBean)还是可以的。显示加载时,同时需要在spring的配置文件中显示地配置ben: <bean id="onlineService" class="com.wangyin.ebl.service.impl.OnlineServiceImpl"></bean> 。问题总算是解决了,但是还是没搞明白原因,等过段时间有空了再慢慢思考吧。