nginx服务器 部署websocket项目 连接中断 报错 :Error in connection establishment: net::ERR_NAME_NOT_RESOLVED

项目需要弹窗告警,原本采用comet4j的方式进行,但是后来发现不支持tomcat8.5,于是打算使用webSocket的方式实现,webSocket是浏览器客户端和服务器后台实现的一种全双工通信方式,许多网页聊天工具都是采用该方式进行。

本地开发的时候都可以正常使用,但是在部署到nginx代理服务器的时候发现报了错误,连不上

Error in connection establishment: net::ERR_NAME_NOT_RESOLVED

后来发现是nginx服务器默认是不打开webSocket的功能的,这需要我们在nginx服务器上配置:

 location /test/ {
                proxy_pass http://test.com;
                proxy_redirect default;
                proxy_set_header Upgrade $http_upgrade; # allow websockets
                proxy_set_header Connection "upgrade";
                proxy_http_version 1.1;
                }

另外nginx设置了连接超时时间或者读取超时时间的时候,websoket会中断,那么需要我们维护socket连接,断线自动重连,代码如下,

websocket后台:

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import net.sf.json.JSONObject;


@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {
	
	static Logger log=LoggerFactory.getLogger(WebSocketServer.class);
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    //接收sid
    private String sid="";
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session,@PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
        this.sid=sid;
        try {
        	 sendMessage("连接成功");
        } catch (IOException e) {
            log.error("websocket IO异常");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
    	log.info("收到来自窗口"+sid+"的信息:"+message);
        //群发消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
            	log.error(e.toString());
                e.printStackTrace();
            }
        }
    }

	/**
	 * 
	 * @param session
	 * @param error
	 */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }
	/**
	 * 实现服务器主动推送
	 */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
    
    /**
     * 发送告警消息
     * */
   public void sendMessage(Map dataMap)throws IOException{
	   try{if(dataMap!=null){
			JSONObject jsonObject = JSONObject.fromObject(dataMap);
			StringBuilder builder = new StringBuilder(jsonObject.toString());		
			//发送告警到前台
			this.session.getBasicRemote().sendText(builder.toString());
			log.info("发送成功");
			}	
	   }catch(Exception e) {
		   log.error(e.toString());
	   }
   }
    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message, String sid) throws IOException {
    	log.info("推送消息到窗口"+sid+",推送内容:"+message);
        for (WebSocketServer item : webSocketSet) {
            try {
            	//这里可以设定只推送给这个sid的,为null则全部推送
            	if(sid==null) {
            		item.sendMessage(message);
            	}else if(item.sid.equals(sid)){
            		item.sendMessage(message);
            	}
            } catch (IOException e) {
            	log.error(e.toString());
                continue;
            }
        }
    }
    /**
     * 群发自定义消息
     * */
    public static void sendInfoMap(Map dataMap, String sid) throws IOException {
    	log.info("推送消息到窗口"+sid+",推送内容:"+dataMap.toString());
        for (WebSocketServer item : webSocketSet) {
            try {
            	item.sendMessage(dataMap);
            	//这里可以设定只推送给这个sid的,为null则全部推送暂时全部放开
            	/*if(sid==null) {
            		item.sendMessage(dataMap);
            	}else if(item.sid.equals(sid)){
            		item.sendMessage(dataMap);
            	}*/
            } catch (IOException e) {
            	log.error(e.toString());
                continue;
            }
        }
    }
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}


前台页面以及js:

var websocket_connected_count = 0;
var onclose_connected_count = 0;
/**websocekt*/
function webSocketClient(){
	var socket;  
	if(typeof(WebSocket) == "undefined") {  
	    console.log("您的浏览器不支持WebSocket");  
	}else{  
	    console.log("您的浏览器支持WebSocket");  
	    	//httprequest请求id
	    	var sid = "<%=requestId%>";
	    	//实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接  
	        socket =new WebSocket("ws://127.0.0.1:8080/butlerBf/websocket/"+sid);   
	        //打开事件  
	        socket.onopen = function() {  
	            console.log("Socket 已打开"); 
	            //socket.send("这是来自客户端的消息" + location.href + new Date());  
	        };  
	        //获得消息事件  
	        socket.onmessage = function(msg) {  
	            if(msg.data!="您的浏览器支持WebSocket"&&msg.data!="Socket 已打开"&&msg.data!="连接成功"&&msg.data!="ping"&&msg.data!=""){
	            	console.log(msg);
	            	checkAuthority(msg.data);
	            	heartCheck.reset().start();
	            }
	        };  
	        //关闭事件  
	        socket.onclose = function(e) {  
	            console.log("Socket已关闭"); 
	            console.log(e);
	        };
	        //发生了错误事件  
	        socket.onerror = function() {  
	        	websocket_connected_count++;
	            if(websocket_connected_count <= 5){
	            	webSocketClient()
	            }
	            console.log("Socket发生了错误");  
	            //此时可以尝试刷新页面
	        }  
	        //离开页面时,关闭socket
	        //jquery1.8中已经被废弃,3.0中已经移除
	         $(window).unload(function(){  
	             socket.close();  
	         });  
	}
	// 心跳检测, 每隔一段时间检测连接状态,如果处于连接中,就向server端主动发送消息,来重置server端与客户端的最大连接时间,如果已经断开了,发起重连。
    var heartCheck = {
        timeout: 60000,        // 60s发一次心跳,比server端设置的连接时间稍微小一点,在接近断开的情况下以通信的方式去重置连接时间。
        serverTimeoutObj: null,
        reset: function(){
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            return this;
        },
        start: function(){
            var self = this;
            this.serverTimeoutObj = setInterval(function(){
                if(socket.readyState == 1){
                    console.log("连接状态,发送消息保持连接");
                    socket.send("ping");
                    heartCheck.reset().start();    // 如果获取到消息,说明连接是正常的,重置心跳检测
                }else{
                    console.log("断开状态,尝试重连");
                    webSocketClient();
                }
            }, this.timeout)
        }
    }

}

此外补充一点,如果nginx没有设置如下读取超时时间,websocket会一直断线重连,比较消耗内存,建议设置长一点:

location /test{
            root html;
	    proxy_pass  http://test.com;
	    proxy_set_header Upgrade $http_upgrade; # allow websockets
    	proxy_set_header Connection "upgrade";
    	proxy_http_version 1.1;
        proxy_connect_timeout 60s;#l连接超时时间,不能设置太长会浪费连接资源
	    proxy_read_timeout 500s;#读超时时间
	    proxy_send_timeout 500s;#写超时时间
            index  index.html index.htm;
        }

你可能感兴趣的:(nginx)