出发点

最近公司需要有一个推送前端消息的需求
首选websocket进行推送,也做过客服系统接触过,最近看了一点websocket的相关内容记录总结一下。

什么是websocket

websocket简单来说就是一个服务端可以向客户端发送请求的一个协议,因为现在企业级开发大部分还是由客户端向服务器发送请求索要请求数据,但是在聊天,消息推送等场景下,需要服务器向web前端或者IOS,Android,小程序推送一些消息,就需要借助websocket工具了

整合springboot

由于博主是需要服务端向web前端发送消息,故需首先整合websocket到后端项目。 Java项目,springboot框架下,首先整合websocket导入自动配置类。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

然后交给spring容器处理

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

springboot的拦截器无法对ws请求进行拦截。

进行nginx反向代理转发wss请求

公司项目走的是HTTPS协议,故前端转发的是https请求到nginx代理服务器,ws协议只能直接访问后端websocket接口调用,但是不能满足前端建立websocket请求,故需要使用wss协议进行调用,wss默认端口与https一致为443端口,故可以与https使用同一套加密证书以及端口,这对后面整合wss协议提供了便利。

具体nginx配置

下面就是通过nginx反向代理websocket请求的nginx配置,不做过多赘述 ,具体参考以下两篇文章
wss协议配置解析
wss协议nginx配置

server {
    listen 443 ssl; 
    #监听前端转发的接口
    server_name 0.0.0.0;
    #配置nginx的server_name时,_; 或 0.0.0.0为无效匹配,所有未匹配的域名默认解析到 http的第一个server网站,这点务必注意!
    
    #并且listen配置配置后此配置失效,也就是只通过端口进行请求匹配。
    ssl_certificate /etc/nginx/secret/xxx.crt;#ssl证书
    ssl_certificate_key  /etc/nginx/secret/xxx.pem;#ssl私钥
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;
    charset utf-8;

location = / {
	root    /usr/share/nginx/html;
	index index.html;
}
location ~ /.*\.(gif|jpg|ico|png|js|css|woff|otf|ttf|TTF|svg|json|xlsx|html|htm|mp4|zip|pdf)$ {
	root   /usr/share/nginx/html;
	index index.html;
}
#原来的代理地址
location / {
	proxy_pass http://172.xx.xx.xx:8099/;
}
#!!!!!这里为websocket的代理配置也是踩坑的地方
location /websocket/  {
	proxy_pass http://172.xx.xx.xx:8099;        #通过配置端口指向部署websocker的项目
	proxy_http_version 1.1;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";
	proxy_set_header X-real-ip $remote_addr;
	proxy_set_header X-Forwarded-For $remote_addr;
	proxy_set_header Host $host;
	proxy_set_header X-Forwarded-Proto   $scheme;
}

整合wss协议对于本身是https项目无需过多证书配置,证书通用,端口通用

踩坑

博主踩坑就是location配置时,在网上看到的需要将 proxy_pass 配置为http://172.28.5.10:8099/; 这就是坑的所在,由于用的请求路径不同导致此配置一直代理不到。

proxy_pass转发规则

让我们来看 proxy_pass 如何转发,首先看 proxy_pass 的url 配置。
proxy_pass 只是HOST,不包含任何路径,比如
这种情况下,会把匹配到的所有路径直接穿透转发。比如以下的配置

 location /websocket/ {
    proxy_pass http://127.0.0.1:3000;
 }

访问 http://127.0.0.1:80/websocket/cc, 后端结果为 您的 请求 地址是/websocket/cc,此时不会丢弃/websocket/的uri
但是以下情况
proxy_pass 包含路径,这里的路径哪怕只是一个 / 也是存在的
这种情况下,url 里面会去掉 location 匹配的字符串,拼接到 proxy_pass 再进行转发。

 location /websocket/ {
    proxy_pass http://127.0.0.1:3000/;
 }

也就是访问 http://127.0.0.1:81/websocket/cc, 后端结果为 您的 请求 地址是/cc

踩坑解决

也就是http://172.28.5.10:8099/; 加上location /websocket/ 的nginx配置
最后代理访问的路径是http://172.28.5.10:8099/{userId};(下面是方法签名路径)
@ServerEndpoint(“/websocket/{userId}”)
public class WebSocket {}

这样就无法转发到对应websocket服务器接口,就一直连接失败
改为http://172.28.5.10:8099; 把后面的/去掉后,实际访问路径为http://172.28.5.10:8099/websocket/{userId};就可以连接到websocket服务器。

wss请求以及模板引擎html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket通讯</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
    var socket;

    function openSocket() {
        if(typeof(WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        }else{
            console.log("您的浏览器支持WebSocket");
            //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
            //等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
            /*
            *根据自己ip端口来配置
            */
            var socketUrl="wss://10.0.0.175:9001/websocket/"+$("#userId").val();
            //var socketUrl="wss://kitchen.demo.wiseinfocloud.com"+$("#username").val()+"/"+$("#userId").val();
            console.log(socketUrl);
            if(socket!=null){
                socket.close();
                socket=null;
            }
            socket = new WebSocket(socketUrl);
            //打开事件
            socket.onopen = function() {
                console.log("websocket已打开");
                //socket.send("这是来自客户端的消息" + location.href + new Date());
            };
            //获得消息事件
            socket.onmessage = function(msg) {
                console.log(msg.data);
                setMessageInHtml(msg.data);
                //发现消息进入    开始处理前端触发逻辑
            };
            //关闭事件
            socket.onclose = function() {
                console.log("websocket已关闭");
            };
            //发生了错误事件
            socket.onerror = function() {
                console.log("websocket发生了错误");
            }
        }

    }
    function setMessageInHtml(message){
        document.getElementById('message').innerHTML += message + "<br/>" ;
    }

    function sendMessage() {
        if(typeof(WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        }else {
            socket.send('{"userId":"'+$("#userId").val()+'","dishesName":"'+$("#dishesName").val()+'"}');
        }
    }
</script>
<body>
<p>【用户名】:<div><input id="username" name="username" type="text" value="10"></div>
<p>【userId】:<div><input id="userId" name="userId" type="text" value="20"></div>
<p>【菜品名称】:<div><input id="dishesName" name="dishesName" type="text" value="hello websocket"></div>
<p>【操作】:<div><a onclick="openSocket()">开启socket</a></div>
<p>【操作】:<div><a onclick="sendMessage()">发送消息</a></div>
<div id="message"></div>
</body>
</html>

总结

需要知道具体的nginx的location配置的规则,对nginx配置有详细了解,故又去看了一遍nginx的配置详解。
问题基本都有解决办法,不要放弃,需要理解每个东西的原理,具体运行流程,基本所有问题都能迎刃而解。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐