在上一篇文章中我们主要介绍 tomcat nio 中的流式上传文件,其本质是直接拿到上传文件的网络输入流,然后在应用程序中读取并操作。避免了以磁盘作为中转,从而提高了效率。这里我们主要介绍服务端事件 SSE。
SSE 的简称是 Server-Sent Events,其本质是浏览器发送一个请求到服务端建立一个长连接,在这个长连接基础上客户端多次发送 http 请求,来轮询服务端是否有数据需要返回给浏览器。所以 SSE 是建立在浏览器和服务端长连接基础上的多次 http 轮询请求,但是 SSE 支持断开重连(由浏览器自己实现),简化了连接关闭情况下操作,但是 SSE 对浏览器和服务器都有要求:
浏览器要支持 SSE,这一点基本支持 html5 的浏览器都可以。
服务器端要支持长连接,因为 SSE 是基于长连接的。
服务端返回数据 content-type 要为 text/event-stream。
服务端返回数据要以 data: 开头,以\n\n 结尾的格式才能被解析。
下面我们示例简单的服务端代码和客户端代码:
@GetMapping(path="/test-sse", produces="text/event-stream;charset=UTF-8")
@ResponseBody
public String testServerSentEvent(HttpServletRequest request) throws Exception{
Thread.sleep(300);
//Logic to impl here
return "data:"+ Math.random()+"\n\n";
}
<!DOCTYPE html>
<html>
<head>
<title>Test SSE Page</title>
</head>
<script type="text/javascript">
var source = new EventSource('http://127.0.0.1:1033/basic-service/test-controller/test-sse');
source.onmessage = function(event){
console.info(event.data);
document.getElementById('result').innerText=event.data;
}
</script>
<body class="blank">
<div id="result"></div>
</body>
</html>
服务端代码只是 sleep 当前线程 300 毫秒,然后返回随机数。
客户端代码利用 EventSource SSE API 对象调用服务端,并显示返回的随机数。
SSE运行效果:
由上图发现,SSE 的浏览器端会不间断的发请求来轮询服务端。
请求头和响应头中的 content-type 项都是 text/event-stream 类型。
响应体就是我们的数据,用 event.data 可以取到并加以显示。
SSE基于长连接:
查看网络连接状态,发现浏览器端用的62672端口和服务端的1033端口通信。
我们多次查看(这里是2次),发现连接始终不变,所以SSE是基于长连接的。
根据以前文章,对于 tomcat 来说,一个长连接默认最多支持100个请求,所以当请求多于100个的时候,一定关闭当前连接,然后由 SSE 再次建立另一个长连接。
上图就是请求超过100个的情况,我们发现浏览器的62672端口连接出现了 time_wait 状态,说明是浏览器主动关闭连接,发起 tcp 关闭的4次挥手。然后在浏览器端出现了62884端口新的连接,说明 SSE 又自动建立了新的长连接。
过一段时间再次查看如上图,发现 time_wait 状态的连接消失回收,只有新的62884端口的长连接,符合 tcp time_wait 需要等待 2MSL 的回收策略。
客户端主动关闭:
细心的同学可能发现,不是说 tomcat 默认长连接超过100个请求就会关闭么,但是主动关闭方的 time_wait 却出现在浏览器端,这是为什么呢。
上图可以发现在默认长连接超过100个请求的时候,tomcat 返回给浏览器的响应头里有 Connection: close项。
根据 http 标准,浏览器接到这个响应头之后,就发起了对这个长连接的关闭,所以主动关闭的 time_wait 状态在浏览器端。
还可以间接证明,tomcat 默认长连接超过100个请求的时候,就会返回给客户端 Connection: close 的响应头,由客户端根据这个响应头实现关闭。当然返回这个响应头之后,tomcat 也会主动关闭服务端的 socket。
对于 SSE 总结如下:
SSE 需要浏览器本身的支持,只要支持 html5 的浏览器都可以。
SSE 对于服务端需要支持长连接。
SSE 对于请求头和响应头都应该是 text/event-stream 类型。
SSE 要求服务端返回数据以 data: 开头,以\n\n 结尾。
SSE是基于一个长连接的多次 http 请求轮询。
SSE支持长连接关闭之后的自动重连。
目前先写到这里,下一篇文章里我们继续介绍 tomcat 中的异步请求。