SpringMVC WebSocket Apache代理 相关技术点总结

来源:互联网 发布:微信分销源码下载 编辑:程序博客网 时间:2024/09/21 06:35

SpringMVC + WebSocket这个技术的文章一大把,最后笔者发现对WebSocket一知半解的情况下使用这个技术,任然会存在问题,有时还会觉得莫名其妙,而且在直接使用别人代码的情况下更是如此。本文希望通过本篇文章介绍一下个人经验总结。
如果英语尚可,直接参考SpringMVC官方对WebSocket的支持文档,点击这里

WebSocket

这个技术不神秘,而且简单易用,只需要使用@ServerEndpoint注解一个类即可,再使用@OnMessage, @OnOpen, @OnClose注解一下方法,实现对WebSocket的管理和消息处理。需要Tomcat 7.0以上。
客户端,使用WebSocket实现访问。
参考资料:JSR-356标准, TOMCAT 7.0 以上是支持该标准的。
ServerEndPoint文档点击这里
下面就一步步的通过Eclipse+Tomcat实现WebSocket通讯,建议新手像我一样先试试。

自己动手创建第一个WebSocket

  • Eclipse建立Web 3.0的项目(Eclipse4.5.1)
    Eclipse的项目配置
  • 编写WebSocket服务端代码
@ServerEndpoint("/hello")public class HelloServer {    @OnMessage    public String processGreeting(String message, Session session) {        System.out.println("Greeting received:" + message);        return "Server echo:" + message + " at " + Calendar.getInstance().getTime().toString();    }}

上述代码参考了前面JSR-356的标准文档,ServerEndPoint文档,稍做修改,使用了带返回值的OnMessage注解的方法,这样就可以实现和前端通讯。
- 编写HTML测试代码
test/jsr356.html

<!DOCTYPE html><html><head><title>Testing websockets using JSR-356</title><script type="text/javascript">    var webSocket =  new WebSocket('ws://localhost:9090/TestWebSocket/hello');    webSocket.onerror = function(e){onError(e)};    webSocket.onopen = function(e) {onOpen(e)};    webSocket.onmessage = function(e) {onMessage(e)};    function onMessage(event) {        document.getElementById('messages').innerHTML += '<br />' + event.data;    }    function onOpen(event) {        document.getElementById('messages').innerHTML  = 'Connection established';    }    function onError(e) {alert(e.data);}    function startWebSocket() { webSocket.send('hello'); return false; }</script></head><body>    <div>        <input type="submit" value="Start" onclick="startWebSocket()" />    </div>    <div id="messages"></div></body></html>

以上代码需要修改的地方只有:WebSocket对象建立时指定的WEB服务器名称,端口,虚拟目录名称。笔者为本机9090, 项目名称为TestWebSocket, hello为websocket服务端口URL。
http://localhost:9090/TestWebSocket/test/jsr356.html

小结

以上代码就是简要使用TOMCAT直接支持的方法和浏览器一同实现了WebSocket通讯,客户端提交hello消息,会从服务端就收消息。好像和一般的请求没啥区别啊!是啊!没有区别!Web请求,实现一个数据请求,就可以返回数据啊!WebSocket的关键在于,他的通讯是在水下面发生的,通过浏览器的工具没有办法直接查看WebSocket的数据报文,这是第一点,另外一点就是可以实现相互通讯,即通过服务器就可以直接和在线的客户端通讯,解决了以前客户端只能通过轮询实现数据更新。在JS4-356标准种,@OnOpen可以获得新打开的会话,从而实现和浏览器的关联,并可以实现服务器向客户端直接通讯。
下面是WebSocket在浏览器工具中看到的通讯过程:
101状态码
注意:WebSocket对象 IE 9以下不支持……“WebSocket”未定义
MAVEN项目需要包含如下依赖,以便让TOMCAT使用新版本的javeee api.

<dependency>    <groupId>javax</groupId>    <artifactId>javaee-api</artifactId>    <version>7.0</version>    <scope>provided</scope></dependency>

SpringMVC + WebSocket

参考文献:WebSocket Support
通过SpringMVC实现WebSocket好处多多,具体请看英文文档,主要是解决了协议兼容的问题。
使用SpringMVC需要的库有好几个,本文示例,就直接通过普通J2EE项目搭建示例,建议项目中采用maven或其他管理依赖。
本示例需要用到jar包:
commons-logging-1.2.jar; spring-aop-4.2.5.RELEASE.jar; spring-beans-4.2.5.RELEASE.jar; spring-context-4.2.5.RELEASE.jar; spring-context-support-4.2.5.RELEASE.jar; spring-core-4.2.5.RELEASE.jar; spring-expression-4.2.5.RELEASE.jar; spring-web-4.2.5.RELEASE.jar; spring-webmvc-4.2.5.RELEASE.jar; spring-websocket-4.2.5.RELEASE.jar.

实现WebSocket + SpringMVC的基本处理类:
test.websocket.MyHandler.java

package test.websocket;import java.io.IOException;import java.util.Calendar;import org.springframework.web.socket.TextMessage;import org.springframework.web.socket.WebSocketSession;import org.springframework.web.socket.handler.TextWebSocketHandler;public class MyHandler extends TextWebSocketHandler{    public MyHandler(){        super();        System.out.println("MyHandler constractor...");    }    @Override    public void handleTextMessage(WebSocketSession session, TextMessage message) {        System.out.println("Client received:" + message.getPayload());        try {            session.sendMessage(new TextMessage("Server echo:" + message.getPayload() + " at " + Calendar.getInstance().getTime().toString()));        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

通过注解实现WebSocket的配置类(直接用注解实现初始化):
test.websocket.WebSocketConfig

package test.websocket;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.config.annotation.EnableWebSocket;import org.springframework.web.socket.config.annotation.WebSocketConfigurer;import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer {    @Override    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {        registry.addHandler(myHandler(), "/myHandler");    }    @Bean    public WebSocketHandler myHandler() {        return new MyHandler();    }}

有两种方式通过Spring初始化WebSocket.
- 通过注解的方式(@EnableWebSocket)
- 通过XML配置文件的方式(websocket:handlers)
无论是通过上述哪种方式,都需要配置Spring.
- 在web.xml 配置文件中添加SpringMVC监听器,和分发器配置
web.xml

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">  <display-name>TestWebSocket</display-name>  <welcome-file-list>    <welcome-file>index.html</welcome-file>    <welcome-file>index.htm</welcome-file>    <welcome-file>index.jsp</welcome-file>    <welcome-file>default.html</welcome-file>    <welcome-file>default.htm</welcome-file>    <welcome-file>default.jsp</welcome-file>  </welcome-file-list>  <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>/WEB-INF/applicationContext.xml</param-value>    </context-param>    <servlet>        <servlet-name>dispatcher</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value></param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>dispatcher</servlet-name>        <url-pattern>/*</url-pattern>    </servlet-mapping>    <servlet-mapping>        <servlet-name>default</servlet-name>        <url-pattern>/test/*</url-pattern>    </servlet-mapping>    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener></web-app>
  • 配置Spring配置文件applicationContext.xml
    这个文件有两种配置,分别对于注解的方式和XML配置的方式,两种方式的任务主要是配置Bean的初始化,还有websocket的配置初始化。
    笔者的applicationContext.xml如下:
<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:context="http://www.springframework.org/schema/context"    xmlns:websocket="http://www.springframework.org/schema/websocket"    xsi:schemaLocation="        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context.xsd        http://www.springframework.org/schema/websocket        http://www.springframework.org/schema/websocket/spring-websocket.xsd">    <context:component-scan base-package="test.websocket"/>    <!--     <websocket:handlers>        <websocket:mapping path="/myHandler" handler="myHandler"/>    </websocket:handlers>    <bean id="myHandler" class="MyHandler"/> --></beans>

component-scan 实现的注解的扫描,从而实现注解功能的实现,该功能是Spring AOP功能,需要AOP库。参考文档

如果没有采用注解实现,也可以手动在applicationContext.xml种配置websocket处理器,参见上面的配置文件。

SpringMVC + WebSocket + SockJs

客户端使用SockJs可以实现更好的浏览器兼容性, 示例参考官方网站,很简单,sockjs-client github链接

SockJs是如何实现服务端/客户端兼容的?通过尝试服务器的支持能力,从而确认是否支持特定的协议,这些协议包括:websocket ,xhr-streaming,xdr-streaming,eventsource…这些在特定的环境下会依次采用,也可以由客户端指定。比如websocket需要ws协议,如果经过没有相关配置的apache/ngnix,协议就会退化到轮询的websocket实现。通过协议退化,就保重了功能的实现。
比如Aapache需要配置如下才可以访问后端的Tomcat的websocket:

RewriteEngine On    RewriteCond %{REQUEST_URI}  ^/JFGJ/sockjs/webSocketServer/.*/websocket$            [NC]    RewriteRule /(.*)           ws://localhost:9090/$1 [P,L]    ProxyPass /JFGJ/sockjs/webSocketServer http://localhost:9090/JFGJ/sockjs/webSocketServer    ProxyPassReverse /JFGJ/sockjs/webSocketServer http://localhost:9090/JFGJ/sockjs/webSocketServer

笔者项目的sockjs的端点为:/JFGJ/sockjs/webSocketServer,/JFGJ/sockjs/webSocketServer/.*/websocket表达式标识对websocket的请求需要映射为ws协议(这个协议保护特殊的http头)。

如果遇到问题欢迎在留言区交流。

原创粉丝点击