Servlet3异步特性+Ajax+Websocket


Java Servlet 3.0有了异步特性,这使得服务器端可以推后处理某些请求。
Ajax可以使得客户端post部分请求,并接受异步回调。
websocket可以使得客户端与服务器进行全双工的通讯,客户端接受ServerPush信息很容易。

1. use servlet3.0 async feature 

1.1 服务器端代码(异步Servlet)
servletpath:/AsyncPath - in server side
@WebServlet(urlPatterns={"/AsyncPath"},asyncSupported=true)
public class AsyncServlet extends HttpServlet 

1.2. access the async url on web browser chrome - in client side
(在浏览器输�%Fspan>			result.add(ServerEndpointConfig.Builder.create(EchoEndpoint.class,
					"/websocket/echoProgrammatic").build());
		}
		
		if (scanned.contains(EchoEndpoint.class)) {
			result.add(ServerEndpointConfig.Builder.create(EchoEndpointAsync.class,
					"/websocket/echoProgrammaticAsync").build());
		}

		return result;
	}

	@Override
	public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
		// Deploy all WebSocket endpoints defined by annotations in the examples
		// web application. Filter out all others to avoid issues when running
		// tests on Gump
		Set<Class<?>> results = new HashSet<Class<?>>();
		for (Class<?> clazz : scanned) {
			if (clazz.getPackage().getName().startsWith("websocket.")) {
				results.add(clazz);
			}
		}
		return results;
	}
}

2.2 EchoEndpointAsync

package websocket.echo;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Rem5�http://localhost:8080/SMPUI/AsyncPath?name=tony&age=42&delay=4,然后敲回车)
Async servlet starts:Mon Feb 29 14:20:01 CST 2016 </br>
OK </br>
Async servlet ends  :Mon Feb 29 14:20:01 CST 2016
</br> </br>
Event 1 started...Mon Feb 29 14:20:01 CST 2016</br>
Event 2 started...Mon Feb 29 14:20:06 CST 2016</br>
Event 3 started...Mon Feb 29 14:20:06 CST 2016</br>

浏览器的忙碌图标还是至少5秒以上。


2. use servlet traditional feature (sync) in server side 

2.1 服务器端代码(同步Servlet)
servletpath:/SyncPath -in server side
@WebServlet("/SyncPath")

2.2 access the async url on web browser chrome - in client side
(在浏览器输入http://localhost:8080/SMPUI/AsyncPath?name=tony&age=42&delay=4,然后敲回车)
Sync servlet starts:Mon Feb 29 16:38:23 CST 2016 </br>
Event 1 started...Mon Feb 29 16:38:23 CST 2016</br>
Event 2 started...Mon Feb 29 16:38:27 CST 2016</br>
Event 3 started...Mon Feb 29 16:38:27 CST 2016</br>
OK </br>
Sync servlet ends  :Mon Feb 29 16:38:27 CST 2016
</br> </br>

浏览器的忙碌图标还是至少5秒以上。

由1,2可总结:
这种异步带来的好处是在服务器端的,对客户端纯浏览器(无Ajax)操作并无改善。
异步的好处是:服务器异步工作。可以先缓存大量请求,然后业务逻辑的处理可以延迟进行。
缺点是:客户端仍是阻塞式等待。

在同步环境下,service(HttpServletRequest request, HttpServletResponse response) 执行完成(出了这个方法)后,其request, response 不能再使用。
在异步环境下,service(HttpServletRequest request, HttpServletResponse response) 执行完成后,在另外的线程中仍可以使用asyncontext得到response来向客户端写。这就是本质区别。

3. Use Ajax in client (Web browser) 
(在浏览器输入http://localhost:8080/SMPUI/AsyncPath回车后,点击Submit按钮)
 var xhttp = new XMLHttpRequest();
 var url="http://localhost:8080/SMPUI/AsyncPath";
 xhttp.open("GET", url, true); //true = ajax
 xhttp.send();
 
 点击Submit按钮后看到的结果:
 Async servlet starts:Tue Mar 01 17:00:27 CST 2016 
 OK 
 Async servlet ends :Tue Mar 01 17:00:27 CST 2016 

 Event 1 started...Tue Mar 01 17:00:27 CST 2016
 Event 2 started...Tue Mar 01 17:00:37 CST 2016
 Event 3 started...Tue Mar 01 17:00:37 CST 2016

 浏览器图标不再忙碌
 
 由3总结:
 浏览器异步发送方和接收结果,导致浏览器忙碌图标不再是不停转动10秒后再显示,而是很平静的过一会(10秒)把服务器延迟产生的数据也显示出来。
 服务器端仍然是异步工作。
 好处是:客户端不再阻塞式等待结果,而是利用回调函数处理服务器发来的数据。
 
 缺点是:还是靠拉的方式。 不是服务器主动推的方式。[客户端主动发一次请求服务器才有响应,客户端不发请求服务器没有响应,客户端发几次请求服务器有几次响应]
 即使客户端做成轮询的方式[每隔几秒发一次请求],运行若干(10-20)分钟后浏览器页面会崩溃,显然不是可接受的方式。
 
4. Use WebSocket
实现全双工通信。每次客户端发的请求都有回应,同时客户端还可以接受服务器推的信息。
1.客户端:echoAsync.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <title>Tomcat WebSocket Async Examples: Echo</title>
    <style type="text/css"><![CDATA[
        #connect-container {
            float: left;
            width: 400px
        }

        #connect-container div {
            padding: 5px;
        }

        #console-container {
            float: left;
            margin-left: 15px;
            width: 400px;
        }

        #console {
            border: 1px solid #CCCCCC;
            border-right-color: #999999;
            border-bottom-color: #999999;
            height: 170px;
            overflow-y: scroll;
            padding: 5px;
            width: 100%;
        }

        #console p {
            padding: 0;
            margin: 0;
        }
    ]]></style>
    <script type="application/javascript"><![CDATA[
        var ws = null;

        function setConnected(connected) {
            document.getElementById('connect').disabled = connected;
            document.getElementById('disconnect').disabled = !connected;
            document.getElementById('echo').disabled = !connected;
        }

        function connect() {
            var target = document.getElementById('target').value;
            if (target == '') {
                alert('Please select server side connection implementation.');
                return;
            }
            if ('WebSocket' in window) {
                ws = new WebSocket(target);
            } else if ('MozWebSocket' in window) {
                ws = new MozWebSocket(target);
            } else {
                alert('WebSocket is not supported by this browser.');
                return;
            }
            ws.onopen = function () {
                setConnected(true);
                log('Info: WebSocket connection opened.');
            };
            ws.onmessage = function (event) {
                log('Received: ' + event.data);
            };
            ws.onclose = function (event) {
                setConnected(false);
                log('Info: WebSocket connection closed, Code: ' + event.code + (event.reason == "" ? "" : ", Reason: " + event.reason));
            };
        }

        function disconnect() {
            if (ws != null) {
                ws.close();
                ws = null;
            }
            setConnected(false);
        }

        function echo() {
            if (ws != null) {
                var message = document.getElementById('message').value;
                log('Sent: ' + message);
                ws.send(message);
            } else {
                alert('WebSocket connection not established, please connect.');
         oteEndpoint;
        }

        function updateTarget(target) {
            if (window.location.protocol == 'http:') {
                document.getElementById('target').value = 'ws://' + window.location.host + target;
            } else {
                document.getElementById('target').value = 'wss://' + window.location.host + target;
            }
        }

        function log(message) {
            var console = document.getElementById('console');
            var p = document.createElement('p');
            p.style.wordWrap = 'break-word';
            p.appendChild(document.createTextNode(message));
            console.appendChild(p);
            while (console.childNodes.length > 25) {
                console.removeChild(console.firstChild);
            }
            console.scrollTop = console.scrollHeight;
        }


        document.addEventListener("DOMContentLoaded", function() {
            // Remove elements with "noscript" class - <noscript> is not allowed in XHTML
            var noscripts = document.getElementsByClassName("noscript");
            for (var i = 0; i < noscripts.length; i++) {
                noscripts[i].parentNode.removeChild(noscripts[i]);
            }
        }, false);
    ]]></script>
</head>
<body>
<div class="noscript"><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable
    Javascript and reload this page!</h2></div>
<div>
    <div id="connect-container">
        <div>
            <span>Connect to service implemented using:</span>
            <!-- echo example using new programmatic API on the server side -->
            <input id="radio1" type="radio" name="group1" value="/SMPUI/websocket/echoProgrammaticAsync"
                   οnclick="updateTarget(this.value);"/> <label for="radio1">programmatic API</label>
            <!-- echo example using new annotation API on the server side -->
            <input id="radio2" type="radio" name="group1" value="/SMPUI/websocket/echoAnnotation"
                   οnclick="updateTarget(this.value);"/> <label for="radio2">annotation API</label>
        </div>
        <div>
            <input id="target" type="text" size="40" style="width: 350px"/>
        </div>
        <div>
            <button id="connect" onclick="connect();">Connect</button>
            <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
        </div>
        <div>
            <textarea id="message" style="width: 350px">Here is a message!</textarea>
        </div>
        <div>
            <button id="echo" onclick="echo();" disabled="disabled">Echo message</button>
        </div>
    </div>
    <div id="console-container">
        <div id="console"/>
    </div>
</div>
</body>
</html>



2.服务器端:
2.1 ExamplesConfig
package websocket;

import java.util.HashSet;
import java.util.Set;

import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;
import websocket.echo.EchoEndpoint;
import websocket.echo.EchoEndpointAsync;

public class ExamplesConfig implements ServerApplicationConfig {

	@Override
	public Set<ServerEndpointConfig> getEndpointConfigs( Set<Class<? extends Endpoint>> scanned) {

		Set<ServerEndpointConfig> result = new HashSet<ServerEndpointConfig>();

		if (scanned.contains(EchoEndpoint.class)) {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值