被要求一個這樣的需求:要求項目和websocket使用一個端口。經過一周激烈爭論,領導終於同意可以可以開通一個端口,一個月了,端口還沒有開。
正式環境已經通過此方法進行部署,沒有問題。
前言
因涉及到內外網安全問題,被要求使用nginx進行代理,不能直連。即對websocket做一個反向代理即可。
項目信息:springboot(cloud等),yeauty集成的websocket(推薦使用,實現非常方便),nginx,websocket在線測試網站(百度搜有很多)
項目搭建
引入依賴
1 <dependencies> 2 <dependency> 3 <groupId>org.yeauty</groupId> 4 <artifactId>netty-websocket-spring-boot-starter</artifactId> 5 <version>0.7.5</version> 6 </dependency> 7 </dependencies>
添加代碼
有兩個文件,可以直接復制到項目中,如圖:
Websocket.java:
1 package com.example.springboottest.common.websocket; 2 3 4 import org.springframework.stereotype.Component; 5 import org.yeauty.annotation.*; 6 import org.yeauty.pojo.Session; 7 8 import java.util.ArrayList; 9 import java.util.List; 10 11 @Component 12 @ServerEndpoint(port = 9906,path = "/WebSocket") 13 public class WebSocket { 14 15 /** 16 * 存放所有在線的客戶端 17 */ 18 19 20 private static List<Session> clients = new ArrayList<>(); 21 22 @OnOpen 23 public void onOpen(Session session) { 24 System.out.println("open"); 25 System.out.println("remoteAddress" + session.remoteAddress()); 26 //將新用戶存入在線的組 27 clients.add(session); 28 sendMessage("hello Word!"); 29 } 30 31 /** 32 * 客戶端關閉 33 * 34 * @param session session 35 */ 36 37 38 @OnClose 39 public void onClose(Session session) { 40 System.out.println(session.remoteAddress() + ":closeWebSocket"); 41 } 42 43 /** 44 * 發生錯誤 45 * 46 * @param throwable e 47 */ 48 49 50 @OnError 51 public void onError(Throwable throwable) { 52 System.out.println("websocket:error"); 53 throwable.printStackTrace(); 54 } 55 56 /** 57 * 收到客戶端發來消息 58 * 59 * @param message 消息對象 60 */ 61 62 @OnMessage 63 public void onMessage(String message, Session session) { 64 System.out.println(message); 65 sendMessage("hello Word!"); 66 } 67 68 public void sendMessage(String message) { 69 try { 70 if (clients.size() > 0) { 71 for (Session session : clients) { 72 session.sendText(message); 73 } 74 } 75 76 } catch (Exception e) { 77 e.printStackTrace(); 78 } 79 80 } 81 }
WebSocketConfig.java:
1 package com.example.springboottest.common.websocket; 2 3 import org.springframework.context.annotation.Bean; 4 5 import org.springframework.context.annotation.Configuration; 6 import org.yeauty.standard.ServerEndpointExporter; 7 8 9 @Configuration 10 public class WebSocketConfig { 11 @Bean 12 public ServerEndpointExporter serverEndpointExporter() { 13 return new ServerEndpointExporter(); 14 } 15 }
啟動項目測試websocket
nginx代理
NGINX從1.3版本開始支持WebSocket,其可以作為一個反向代理和為WebSocket程序做負載均衡。此博客不涉及內部原理等,放個干貨。
反向代理
先把我自己的nginx配置發出來:
1 #user nobody; 2 worker_processes 1; 3 4 #error_log logs/error.log; 5 #error_log logs/error.log notice; 6 #error_log logs/error.log info; 7 8 #pid logs/nginx.pid; 9 10 11 events { 12 worker_connections 1024; 13 } 14 15 16 http { 17 map $http_upgrade $connection_upgrade { 18 default upgrade; 19 '' close; 20 } 21 22 upstream websocket { 23 #ip_hash; 24 hash $remote_addr consistent; 25 server localhost:9907; 26 } 27 28 # 以下配置是在server上下文中添加,location指用於websocket連接的path。 29 30 server { 31 listen 9908; 32 server_name localhost; 33 #access_log /var/log/nginx/yourdomain.log; 34 35 location /WebSocket { 36 root html; 37 index index.html index.htm; 38 proxy_pass http://websocket/WebSocket; 39 proxy_connect_timeout 4s; #配置點1 40 proxy_read_timeout 60s; #配置點2,如果沒效,可以考慮這個時間配置長一點 41 proxy_send_timeout 12s; #配置點3 42 43 proxy_set_header Host $host; 44 proxy_set_header X-Real-IP $remote_addr; 45 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 46 47 proxy_http_version 1.1; 48 proxy_set_header Upgrade $http_upgrade; 49 proxy_set_header Connection "upgrade"; 50 } 51 } 52 }
代理端口為9908,堅挺的地址就是/WebSocket,下面測試一下:
至此反向代理已經完成。
負載均衡
如果想做負載均衡,可以在upstream websocket {}中加一個server就可以了,比如復制一下項目,將websocket端口改為9907
@ServerEndpoint(port = 9907,path = "/WebSocket")
連上之后發送信息為hello world!2
sendMessage("hello Word!2");
然后連上反向代理的鏈接,連接之后發現連的是9906的鏈接,如圖:
此時我把9906斷掉,再次連接時:
此時遇到問題,當負載均衡時,9906斷了之后,沒有自動轉為9907的鏈接。在客戶端監聽此鏈接,如果查實連接不上則進行重新鏈接,即可使用9907的服務。