上周公司需要做一个空号检测功能,也就是输入一批手机号码,检测出哪些是空号,哪些是异常号,哪些是正常号,分门别类整理出来,也就是如下图所示的样子,当然我是已经做了好多天,然后今天才分享出来。
最开始我是想着用正常的http请求方式,将一批号码全部导入,等后台全部执行完了,将结果一次性都以json的格式返回,然后在前端显示。想着想着,觉得这样好像没什么技术含量,而且一旦导入的原号码量大的时候(比如说10万),可能前端要等很久,而且就算结果出来了,把号码归类的时候浏览器也可能卡顿,这样的用户体验效果就相当差,我的饭碗也不保了,尽管公司只有我一个技术,但还是要对自己的要求高点,否则自己的不可替代性弱爆了,以后随时会在职场game over。
忘记说了一件事,这个空号检测是对接别人公司的接口,一次请求只能传入一个号码,那么10w手机号,就要调用10w次,没办法咯。正好为了配合他的表演,我就想到用websocket,前端一次性传入所有号码,然后websocket把每个号码依次返回前端,呈现出空号检测的实时性,同时在右边将检测的过程日志打印出来。这就是采用了webSocket一次请求,多次返回的协议特性。
前端页面 numberCheck.html 核心代码:
var websocket = null;
var resTime = 5;
function startCheck(){
if('WebSocket' in window) {
websocket = new WebSocket("ws://"+ wsUrl +"/webSocket.ws/#(session.user.id)");
} else if('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://"+ wsUrl +"/webSocket.ws/#(session.user.id)");
} else {
websocket = new SockJS(wsUrl +"/webSocket.ws/#(session.user.id)");
}
//连接发生错误的回调方法
websocket.onerror = function () {
websocket.close();
};
//连接成功建立的回调方法
websocket.onopen = function () {
$('#message').val($('#message').val() + "WebSocket连接已开启\n");
calRestTime();
//一次性将所有号码传入
var numbers = $("#numbers").val();
websocket.send(numbers);
};
//接收到消息的回调方法
websocket.onmessage = function (event) {
resTime = 5;
//分门别类函数
setMessageHtml(event.data);
};
//连接关闭的回调方法
websocket.onclose = function () {
$('#message').val($('#message').val() +"WebSocket连接已关闭\n");
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
websocket.close();
};
}
//5秒内没有结果返回就关闭websocket
function calRestTime(){
interval = setInterval(function (args) {
resTime--;
if (resTime == 0){
clearInterval(interval);
websocket.close();
console.log("finished");
}
},1000);
}
后端代码 websocket.java
package com.yuzi.controller.numberCon;
import com.alibaba.fastjson.JSONObject;
import com.yuzi.service.numberCheck.NumberService;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value = "/webSocket.ws/{userid}")
public class WebSocket {
private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
private Session session;
private String userid;
@OnOpen
public void onOpen(@PathParam("userid") String userid, Session session) throws IOException {
this.userid = userid;
this.session = session;
//addOnlineCount();
clients.put(userid, this);
System.out.println("已连接");
}
@OnClose
public void onClose() throws IOException {
clients.remove(userid);
System.out.println("已关闭");
//subOnlineCount();
}
@OnMessage
public void onMessage(String message) throws IOException {
dealNumber(message);
}
//业务代码
public void dealNumber (String numbers) throws IOException{
if ("".equals(numbers)){
sendMessageTo("原号码不能为空",userid);
return;
}
String numberArr[] = numbers.split("\n");
for (int i=0;i<numberArr.length;i++){
if ("".equals(numberArr[i]) ){
continue;
}
//将每次检测结果返回前端
String result = NumberService.dao.checkNumber(numberArr[i].trim()).toString();
sendMessageTo(result,userid);
}
}
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
public void sendMessageTo(String message, String To) throws IOException {
// session.getBasicRemote().sendText(message);
//session.getAsyncRemote().sendText(message);
for (WebSocket item : clients.values()) {
if (item.userid.equals(To) )
item.session.getAsyncRemote().sendText(message);
}
}
public void sendMessageAll(String message) throws IOException {
for (WebSocket item : clients.values()) {
item.session.getAsyncRemote().sendText(message);
}
}
}
很多人在JFinal + webSocket配合使用的时候,前端访问websocket的时候,总会出现404路径错误:index:155 WebSocket connection to 'ws://localhost/tmgh/websocket' failed: Error during WebSocket handshake: Unexpected response code: 404,这是因为JFinal默认的Handler会拦截所有请求,所以我们需要将websocket路径从中排除,创建WebSocketHandler.java。
WebSocketHandler.java
package com.yuzi.handler;
import com.jfinal.handler.Handler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class WebSocketHandler extends Handler {
public void handle(String target, HttpServletRequest request,
HttpServletResponse response, boolean[] isHandled) {
//对于websocket 不交予 jfinal 处理
if (target.indexOf("/webSocket") == -1) {
next.handle(target, request, response, isHandled);
}
}
}
然后在主配置中加上该handler。
BaseConfig.java
public void configHandler(Handlers handlers) {
handlers.add(new WebSocketHandler());
}
让我们测试一下吧:
搞定!!!!!!!!!!
但是别高兴的太早了哦,看似没什么问题,因为我们这是本地测试,当然如果你部署在远程服务器上采用tomcat部署,也是没什么问题的。
websocket = new WebSocket("ws://"+ wsUrl +"/webSocket.ws/#(session.user.id)");
但如果在某一个架构中,tomcat是局域网服务器,nginx作为反向代理服务器,那么用域名去访问,依然会出现404错误。
这时候我们需要去代理Nginx中加入两行代码:
server {
listen 80;
server_name yourdomain;
location / {
proxy_connect_timeout 1;
proxy_send_timeout 300;
proxy_read_timeout 300;
proxy_buffer_size 1M;
proxy_buffers 8 1M;
proxy_busy_buffers_size 1M;
proxy_temp_file_write_size 1M;
proxy_pass http://xxx;
proxy_set_header Upgrade $http_upgrade; //这两行
proxy_set_header Connection "upgrade"; //这两行
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
至于为什么大家可以去看看 https://www.8dc.net/news/content/160.html (转载)。

