什么是长连接、短连接?
在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。
而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:Connection:keep-alive
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。
HTTP协议的长连接和短连接,实质上就是TCP协议的长连接和短连接。
TCP 长连接和短连接,TCP在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立通过三次握手,释放则需要四次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的。
TCP长/短连接的应用场景:
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。
每个TCP连接都需要三次握手,这需要时间,如果每个操作都是先连接,
再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,
再次处理时直接发送数据包就OK了,不用建立TCP连接。
例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,
而且频繁的socket 创建也是对资源的浪费。而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,
而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,
如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,
那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。目前实现长连接的方式无论是从兼容性从高到低,还是实时性从低到高,亦或是服务器资源占用从高到低,排列顺序都是:
短轮询 -> 长轮询 -> 长连接(sse,comet4j) -> websocket
sse:
sse(Server Sent Event
),直译为服务器发送事件,顾名思义,也就是客户端可以获取到服务器发送的事件
我们常见的 http 交互方式是客户端发起请求,服务端响应,然后一次请求完毕;但是在 sse 的场景下,客户端发起请求,连接一直保持,服务端有数据就可以返回数据给客户端,这个返回可以是多次间隔的方式
sse 是单通道,只能服务端向客户端发消息;而 webscoket 是双通道,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息
【SpringBoot WEB 系列】SSE 服务器发送事件详解 - 一灰灰Blog - 博客园
//map
HashMap<String, Thread> jobMap = new HashMap<>();
//线程池
ExecutorService newExecutorService = Executors.newCachedThreadPool();
@GetMapping("/query/status/{lastCount}")
public SseEmitter query(@PathVariable Integer lastCount){
SseEmitter sseEmitter = new SseEmitter(0L);
Thread todo = jobMap.get("todo");
if (ObjectUtil.isNotEmpty(todo)){
todo.interrupt();
}
String token = SecurityUtils.getToken();
LoginUser loginUser = AuthUtil.getLoginUser(token);
AtomicInteger recordDataToDo = new AtomicInteger();
if (ObjectUtil.isNotEmpty(loginUser)){
newExecutorService.execute(()->{
try {
Thread thread = Thread.currentThread();
jobMap.put("todo",thread);
while (true){
recordDataToDo.set(alarmRecordService.selectAlarmStatus());
thread.sleep(2000);
if (lastCount == recordDataToDo.get() &&ObjectUtil.isNotEmpty(AuthUtil.getLoginUser(token))){
continue;
}else {
break;
}
}
if ( lastCount != recordDataToDo.get()){
sseEmitter.send(recordDataToDo);
System.out.println("信息发送成功");
System.out.println(recordDataToDo);
sseEmitter.complete();
System.out.println("完成");
}
} catch (IOException | InterruptedException e ) {
sseEmitter.completeWithError(e);
}
});
}else {
throw new InterfaceException(1001,"参数异常");
}
return sseEmitter;
}
Come4J:
WebSocket:
见自己博客:【长链接】WebSocket实现数据库更新前台实时显示_keep one's resolveY的博客-CSDN博客_websocket实现数据库更新时前端页面实时刷新