上周公司需要做一个空号检测功能,也就是输入一批手机号码,检测出哪些是空号,哪些是异常号,哪些是正常号,分门别类整理出来,也就是如下图所示的样子,当然我是已经做了好多天,然后今天才分享出来。

最开始我是想着用正常的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 (转载)。

最后修改于 2019-10-16 11:57:54
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付
上一篇