WebSocket实现app扫描二维码登录以及ws应用进行负载均衡?

来源:互联网 发布:中信淘宝联名信用卡 编辑:程序博客网 时间:2024/05/16 07:05
最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。当这个功能完成了后,我决定将整个实现思路整理出来,方便自己以后查看也方便其他有类似需求的程序猿些。
要实现扫码登录我们需要解决两个问题:
1.  在没有输入用户名及密码的情况下,如何解决权限安全问题?换句话讲,如何让服务器知道扫码二维码的客户端是一个合法的用户?
2.  服务器根据用户在客户端的选择如何实时在网页上作出相应的响应?

首先我们先理一下微信的实现思路,来方便我们理解解决这一难题的思路方向。微信登录的二维码实际上是将一个URL转换成二维码的形式,而通过微信客户端扫码后,无非就是打开了这个url, 我捕捉到的微信二维码的url为https://login.weixin.qq.com/l/YdmTu30I5A== ,这个url里的YdmTu30I5A==代表的是本次会话的唯一ID, 这个有点儿类似浏览器里的session id,通过这个ID,微信就能定向将确认结果反馈到网页上。使用微信二维码登录功能,需要有两个前提:一是客户端上需要安装微信app。 二是用户需要登录到到微信app。https://wx.qq.com/

 

 WebsocketWeb实时消息后台服务器推送技术

 

为什么要有这两个条件呢?那是因为微信在确认是否允许登录到网页版的时候,微信需要提取当前app的登录信息并将上面的session ID一并发给服务器,这样服务器收到了登录信息和sessionID后就可以确认两件事:一是用来确认登录的客户端的用户是验证过的;二是通过session ID服务器知道将反馈结果推送到哪个网页。

   所以针对第一点,我们的关键在于,在扫描前要确保用户是已经被验证过且合法的用户(验证方式可以是用户名+密码,也可以是一个secure key),在选择是否登录时将这个结果一并推送到服务器端,就好了。如果用户没有验证是否合法,可以像微信的处理方式一样直接告诉用户二维码不可识别或提示请先登录到app。

  有了身份验证,那么现在就解决第二个问题,如何将反馈结果实时地显示在网页上呢?有朋友可能会说,客户端这边很简单发一个请求到后台就好了,而网页上用ajax定时发送到服务器端看是否有反馈。我不赞成这种做法,因为ajax轮询方式十分消耗客户端和服务器端资源!这里涉及到另一个技术-web实时推送技术,使用推送技术可以节约服务器端和客户端的资源,可以稳定地推送和接收任何消息。我在实现的过程中我采用了第三方推送服务-GoEasy推送,用它是实现非常简单,我们项目里的其他功能也用到了GoEasy web实时推送服务,所以在此我直接就用的GoEasy推送来将登录反馈结果推送到服务器。我的实现步骤非常简单,将传送的session ID作为客户端与网页端的通信channel,网页端订阅用session ID作为值得channel,客户端将验证结果和session ID发送到服务器端,服务器端可以通过这个channel主动将结果推送给网页版!如果客户端也需要做相应的反馈的话,那么客户端也只需要订阅这个channel,然后服务器端会同时将结果推送给网页版和客户端,收到消息后,就可以根据需求在goeasy的回调函数里做你想做的事情了。关于goeasy推送的使用,大家可以参考这篇博客: http://www.cnblogs.com/jishaochengduo/articles/5552645.html, 另外GoEasy推送官网上也有一个demo:GoEasy二维码扫码登录demo,大家可以去看看效果.


话不多说,直接上代码,上代码,上代码。项目整起!!!!!

后台框架采用SpringMVC,不同的框架可根据逻辑更改即可:

【思路】- PC端生成二维码,二维码包含uuid(全局唯一标识符),且打通websocket通道,等待服务器返回登录成功信息;APP扫描二维码,获取uuid及登录信息,推送给服务端,处理后的登录信息通过websocket返回给PC端,PC端得到登录信息后保存即登录成功。APP扫描确认登录的信息可以采用ActiveMQ进行推送。

生成二维码部分引入依赖文件

[html] view plain copy
  1.     <dependency>  
  2.     <groupId>com.google.zxing</groupId>  
  3.     <artifactId>core</artifactId>  
  4.     <version>3.1.0</version>  
  5. </dependency>  
  6. <dependency>    
  7.         <groupId>com.google.zxing</groupId>    
  8.         <artifactId>javase</artifactId>    
  9.         <version>3.1.0</version>    
  10.     </dependency>   

二维码登录后台控制层Controller

[java] view plain copy
  1. /** 
  2.  * 项目名称:dream_user 
  3.  * 项目包名:org.fore.user.controller 
  4.  * 创建时间:2017年8月8日下午5:29:41 
  5.  * 创建者:Administrator-宋发元 
  6.  * 创建地点:杭州 
  7.  */  
  8. package org.fore.user.controller;  
  9.   
  10. import java.io.IOException;  
  11. import java.io.OutputStream;  
  12. import java.util.HashMap;  
  13. import java.util.Map;  
  14. import java.util.UUID;  
  15.   
  16. import javax.servlet.ServletException;  
  17. import javax.servlet.http.HttpServletRequest;  
  18. import javax.servlet.http.HttpServletResponse;  
  19.   
  20. import org.slf4j.Logger;  
  21. import org.slf4j.LoggerFactory;  
  22. import org.fore.model.user.UserAccount;  
  23. import org.fore.model.user.UserModel;  
  24. import org.fore.user.qrcode.websocket.WebSocketHandler;  
  25. import org.fore.user.service.UserAccountService;  
  26. import org.fore.user.service.UserService;  
  27. import org.fore.utils.jms.JmsSender;  
  28. import org.fore.utils.mvc.TokenUtil;  
  29. import org.fore.utils.mvc.annotation.LimitLess;  
  30. import org.springframework.beans.factory.annotation.Autowired;  
  31. import org.springframework.beans.factory.annotation.Qualifier;  
  32. import org.springframework.stereotype.Controller;  
  33. import org.springframework.web.bind.annotation.RequestMapping;  
  34. import org.springframework.web.bind.annotation.ResponseBody;  
  35.   
  36. import com.alibaba.fastjson.JSONObject;  
  37. import com.google.zxing.BarcodeFormat;  
  38. import com.google.zxing.EncodeHintType;  
  39. import com.google.zxing.MultiFormatWriter;  
  40. import com.google.zxing.WriterException;  
  41. import com.google.zxing.client.j2se.MatrixToImageWriter;  
  42. import com.google.zxing.common.BitMatrix;  
  43. import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;  
  44.   
  45.   
  46. /** 
  47.  * 描述:控制层 
  48.  * @author songfayuan 
  49.  * 2017年8月8日下午5:29:41 
  50.  */  
  51. @Controller  
  52. @RequestMapping("/qrcodelogin")  
  53. public class QrCodeLoginController {  
  54.   
  55.     private Logger logger = LoggerFactory.getLogger(QrCodeLoginController.class);  
  56.       
  57.     public static int defaultWidthAndHeight=260;  
  58.       
  59.     @Autowired  
  60.     private WebSocketHandler webSocketHandler;  
  61.     @Autowired  
  62.     private UserService userService;  
  63.     @Autowired  
  64.     private UserAccountService userAccountService;  
  65.     @Autowired  
  66.     @Qualifier(value = "qrCodeLoginSender")  
  67.     private JmsSender jmsSender;  
  68.       
  69.     /** 
  70.      * 描述:PC获取二维码 
  71.      * @param uuid 
  72.      * @param request 
  73.      * @param response 
  74.      * @throws ServletException 
  75.      * @throws IOException 
  76.      * @author songfayuan 
  77.      * 2017年8月11日上午9:04:43 
  78.      */  
  79.     @RequestMapping("/getLoginQrCode")  
  80.     @ResponseBody  
  81.     @LimitLess  
  82.     public void getLoginQrCode(String uuid, HttpServletRequest request,  
  83.             HttpServletResponse response) throws ServletException, IOException {  
  84.         //生成参数  
  85.         //String uuid = generateUUID();  
  86.         String host = request.getHeader("Host");  
  87.         JSONObject data = new JSONObject();  
  88.         data.put("code"200);  
  89.         data.put("msg""获取二维码成功");  
  90.         data.put("uuid", uuid);  
  91.         data.put("host", host);  
  92.         logger.info("【二维码内容】:{}",data);  
  93.           
  94.         //生成二维码  
  95.         Map<EncodeHintType, Object>  hints=new HashMap<EncodeHintType, Object>();  
  96.         // 指定纠错等级    
  97.         hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);    
  98.         // 指定编码格式    
  99.         hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");    
  100.         hints.put(EncodeHintType.MARGIN, 1);  
  101.         try {  
  102.             BitMatrix bitMatrix = new MultiFormatWriter().encode(data.toString(),BarcodeFormat.QR_CODE, defaultWidthAndHeight, defaultWidthAndHeight, hints);  
  103.             OutputStream out = response.getOutputStream();  
  104.             MatrixToImageWriter.writeToStream(bitMatrix, "png", out);//输出二维码  
  105.             out.flush();  
  106.             out.close();  
  107.               
  108.         } catch (WriterException e) {  
  109.             // TODO Auto-generated catch block  
  110.             e.printStackTrace();  
  111.         }  
  112.     }  
  113.       
  114.     /** 
  115.      * 描述:app确认请求处理 
  116.      * @param uuid 
  117.      * @param host 
  118.      * @param userid 
  119.      * @author songfayuan 
  120.      * 2017年8月11日上午9:05:56 
  121.      */  
  122.     @RequestMapping("/sendCodeLoginInfo")  
  123.     @ResponseBody  
  124.     @LimitLess  
  125.     public void sendCodeLoginInfo(String uuid, String host, Integer userid) {  
  126.         // 注册成功后 或 登录,需要同步账户信息,获取用户基本信息  
  127.         UserAccount account = userAccountService.findCurrentUserAccount(userid);  
  128.         userAccountService.syncAccount(account);  
  129.   
  130.         UserModel userModel = userService.findUserById(userid);  
  131.         userModel = changeUserForShow(userModel);  
  132.         JSONObject token = TokenUtil.generateTokenByQrCodeLogin(userid, host);  
  133.         JSONObject object = new JSONObject();  
  134.         object.put("code"10086);  
  135.         object.put("uuid", uuid);  
  136.         object.put("userinfo", userModel);  
  137.         object.put("token", token);  
  138.         object.put("msg""登录成功");  
  139.         //this.webSocketHandler.forwardQrCode(object.toString());  
  140.         jmsSender.sendMessage(object.toString()); //采用ActiveMQ进行推送,也可以直接注入websocket进行发送  
  141.     }  
  142.     //处理用户登录信息  
  143.     private UserModel changeUserForShow(UserModel userModel) {  
  144.         UserModel user = new UserModel();  
  145.         user.setId(userModel.getId());  
  146.         user.setUserName(userModel.getUserName());  
  147.         user.setUserSex(userModel.getUserSex());  
  148.         user.setUserPortrait(userModel.getUserPortrait());  
  149.         return user;  
  150.     }  
  151.       
  152.     /** 
  153.      * 描述:唯一标识符 
  154.      * @return 
  155.      * @author songfayuan 
  156.      * 2017年8月11日上午9:06:12 
  157.      */  
  158.     public static String generateUUID() {  
  159.         String uuid = UUID.randomUUID().toString();  
  160.         uuid = uuid.replace("-""");  
  161.         Long currentTime = System.currentTimeMillis();  
  162.         String currentDate = String.valueOf(currentTime);  
  163.         return uuid + currentDate;  
  164.     }  
  165.       
  166. }  

websocket实现(本案例采用Spring自带的websocket)

[java] view plain copy
  1. package org.fore.sms.qrcode.websocket;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.context.annotation.Bean;  
  5. import org.springframework.context.annotation.Configuration;  
  6. import org.springframework.web.servlet.config.annotation.EnableWebMvc;  
  7. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;  
  8. import org.springframework.web.socket.config.annotation.EnableWebSocket;  
  9. import org.springframework.web.socket.config.annotation.WebSocketConfigurer;  
  10. import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;  
  11. import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;  
  12.   
  13. @Configuration  
  14. @EnableWebMvc  
  15. @EnableWebSocket  
  16. public class QrCodeLoginWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {  
  17.   
  18.     @Autowired  
  19.     private QrCodeLoginWebSocketEndPoint endPoint;  
  20.     @Autowired  
  21.     private QrCodeLoginHandshakeInterceptor interceptor;  
  22.   
  23.     @Override  
  24.     public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {  
  25.         registry.addHandler(endPoint, "/qrcodelogin.do").addInterceptors(interceptor).setAllowedOrigins("*");  
  26. //       registry.addHandler(endPoint,  
  27. //       "/sockjs.do").addInterceptors(interceptor).setAllowedOrigins("*")  
  28. //       .withSockJS();  
  29.     }  
  30.   
  31.     /** 
  32.      * Each underlying WebSocket engine exposes configuration properties that 
  33.      * control runtime characteristics such as the size of message buffer sizes, 
  34.      * idle timeout, and others. 
  35.      */  
  36.   
  37.     /** 
  38.      * For Tomcat, WildFly, and GlassFish add a 
  39.      * ServletServerContainerFactoryBean to your WebSocket Java config: 
  40.      */  
  41.     @Bean  
  42.     public ServletServerContainerFactoryBean createWebSocketContainer() {  
  43.         ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();  
  44.         container.setMaxTextMessageBufferSize(8192);  
  45.         container.setMaxBinaryMessageBufferSize(8192);  
  46.         return container;  
  47.     }  
  48.   
  49.     /** 
  50.      * For Jetty, you’ll need to supply a pre-configured Jetty 
  51.      * WebSocketServerFactory and plug that into Spring’s 
  52.      * DefaultHandshakeHandler through your WebSocket Java config: 
  53.      */  
  54.     // @Bean  
  55.     // public DefaultHandshakeHandler handshakeHandler() {  
  56.     //  
  57.     // WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);  
  58.     // policy.setInputBufferSize(8192); /* 设置消息缓冲大小 */  
  59.     // policy.setIdleTimeout(600000); /* 10分钟read不到数据的话,则断开该客户端 */  
  60.     //  
  61.     // return new DefaultHandshakeHandler(new JettyRequestUpgradeStrategy(new  
  62.     // WebSocketServerFactory(policy)));  
  63.     // }  
  64.   
  65. }  

[java] view plain copy
  1. package org.fore.sms.qrcode.websocket;  
  2.   
  3. import java.util.Map;  
  4.   
  5. import javax.servlet.http.HttpServletRequest;  
  6.   
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9. import org.springframework.http.server.ServerHttpRequest;  
  10. import org.springframework.http.server.ServerHttpResponse;  
  11. import org.springframework.stereotype.Component;  
  12. import org.springframework.web.socket.WebSocketHandler;  
  13. import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;  
  14.   
  15. @Component  
  16. public class QrCodeLoginHandshakeInterceptor extends HttpSessionHandshakeInterceptor {  
  17.     private Logger logger = LoggerFactory.getLogger(QrCodeLoginHandshakeInterceptor.class);  
  18.   
  19.     @Override  
  20.     public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,  
  21.             Map<String, Object> attributes) throws Exception {  
  22.         return super.beforeHandshake(request, response, wsHandler, attributes);  
  23.     }  
  24.   
  25.     @Override  
  26.     public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,  
  27.             Exception ex) {  
  28.         super.afterHandshake(request, response, wsHandler, ex);  
  29.     }  
  30. }  

[java] view plain copy
  1. package org.fore.sms.qrcode.websocket;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.Map;  
  5. import java.util.UUID;  
  6. import java.util.concurrent.ConcurrentHashMap;  
  7.   
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10. import org.fore.model.quota.tcp.ReqCode;  
  11. import org.springframework.stereotype.Component;  
  12. import org.springframework.web.socket.CloseStatus;  
  13. import org.springframework.web.socket.TextMessage;  
  14. import org.springframework.web.socket.WebSocketSession;  
  15. import org.springframework.web.socket.handler.TextWebSocketHandler;  
  16.   
  17. import com.alibaba.fastjson.JSON;  
  18. import com.alibaba.fastjson.JSONObject;  
  19.   
  20. @Component  
  21. public class QrCodeLoginWebSocketEndPoint extends TextWebSocketHandler {  
  22.     private Logger logger = LoggerFactory.getLogger(QrCodeLoginWebSocketEndPoint.class);  
  23.   
  24.     private static Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();  
  25.     private static Map<WebSocketSession,String > sessionMap2 = new ConcurrentHashMap<>();  
  26.   
  27.     @Override  
  28.     public void afterConnectionEstablished(WebSocketSession session) throws Exception {  
  29.         logger.info("WebSocketHandler:客户端{}上线", session.getRemoteAddress());  
  30.         String uuid = generateUUID();  
  31.         sessionMap.put(uuid,session);  
  32.         sessionMap2.put(session,uuid);  
  33.     }  
  34.   
  35.     @Override  
  36.     protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {  
  37.         String msg = message.getPayload();  
  38.         String ipAddress = session.getRemoteAddress().toString();  
  39.         JSONObject requestData = JSON.parseObject(msg);  
  40.         Integer code = requestData.getInteger("code");  
  41.         JSONObject result = new JSONObject();  
  42.         String uuid = sessionMap2.get(session);  
  43.         result.put("code"200);  
  44.         result.put("uuid", uuid);  
  45.         switch (code) {  
  46.         case ReqCode.REQ_QR_CODE:  
  47.             logger.info("WebSocketHandler:客户端{}发送消息{}...", ipAddress, msg);  
  48.             if(session.isOpen())  
  49.             session.sendMessage(new TextMessage(result.toString()));  
  50.             logger.info("WebSocketHandler:客户端{}发送消息{}完成", ipAddress, msg);  
  51.             break;  
  52.         default:  
  53.             break;  
  54.         }  
  55.     }  
  56.   
  57.     @Override  
  58.     public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {  
  59.         String ipAddress = session.getRemoteAddress().toString();  
  60.         logger.info("WebSocketHandler:客户端{}下线", ipAddress);  
  61.         logger.info("WebSocketHandler:删除客户端{}的session...", ipAddress);  
  62.         logger.info("WebSocketHandler:删除sessionMap的客户端{}连接...", ipAddress);  
  63.         String uuid = sessionMap2.get(session);  
  64.         sessionMap.remove(uuid);  
  65.         sessionMap2.remove(session);  
  66.         logger.info("WebSocketHandler:删除sessionMap的客户端{}连接完成", ipAddress);  
  67.         logger.info("WebSocketHandler:删除WebSocket客户端{}连接...", ipAddress);  
  68. //      logger.info("{}", sessionMap);  
  69.         sessionMap.remove(session);  
  70. //      logger.info("{}", sessionMap);  
  71.         logger.info("WebSocketHandler:删除WebSocket客户端{}连接完成", ipAddress);  
  72.         logger.info("WebSocketHandler:删除客户端{}的session完成", ipAddress);  
  73.         if(session.isOpen())  
  74.         session.close();  
  75.     }  
  76.   
  77.     @Override  
  78.     public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {  
  79.         logger.info("WebSocketHandler:客户端{}异常", session.getRemoteAddress(), exception);  
  80.     }  
  81.       
  82.     //发送消息  
  83.     public void sendMessage(String userInfo) throws Exception {  
  84.         JSONObject json = JSONObject.parseObject(userInfo);  
  85.         String uuid = json.getString("uuid");  
  86.         WebSocketSession session = sessionMap.get(uuid);  
  87.         if (session == null) {  
  88.             logger.info("app发送给PC的登录信息:{}参数不正确!",userInfo);  
  89.         }else {  
  90.             logger.info("app发送给PC的登录信息:{}",userInfo);  
  91.             session.sendMessage(new TextMessage(userInfo));  
  92.         }  
  93.     }  
  94.       
  95.     //唯一标识符  
  96.     public static String generateUUID() {  
  97.         String uuid = UUID.randomUUID().toString();  
  98.         uuid = uuid.replace("-""");  
  99.         Long currentTime = System.currentTimeMillis();  
  100.         String currentDate = String.valueOf(currentTime);  
  101.         return uuid + currentDate;  
  102.     }  
  103. }  

JMS实现

[java] view plain copy
  1. package org.fore.sms.qrcode.jms;  
  2.   
  3. import org.fore.utils.jms.Listener;  
  4. import org.fore.sms.qrcode.websocket.QrCodeLoginWebSocketEndPoint;  
  5. import org.slf4j.Logger;  
  6. import org.slf4j.LoggerFactory;  
  7. import org.springframework.beans.factory.annotation.Autowired;  
  8. import org.springframework.stereotype.Component;  
  9.   
  10. import com.alibaba.fastjson.JSONObject;  
  11.   
  12. @Component  
  13. public class QrCodeLoginListener implements Listener {  
  14.     private Logger logger = LoggerFactory.getLogger(QrCodeLoginListener.class);  
  15.     @Autowired  
  16.     private QrCodeLoginWebSocketEndPoint qrCodeLoginWebSocketEndPoint;  
  17.   
  18.     @Override  
  19.     public void onMessage(String message) {  
  20.         logger.info("app确认登录信息:接收app推送的确定PC登录消息{}", message);  
  21.         JSONObject object = JSONObject.parseObject(message);  
  22.         try {  
  23.             qrCodeLoginWebSocketEndPoint.sendMessage(object.toJSONString());  
  24.         } catch (Exception e) {  
  25.             logger.info("app确认登录信息:接收app推送的确定PC登录消息异常", e);  
  26.         }  
  27.     }  
  28.   
  29. }  

核心代码就酱......简短项目就是这些了,好了到了关键发布和部署到服务器环节####

如何对 Websocket 应用进行负载均衡?

#nginx websocket 负载均衡配置(用1.3以后版本的nginx,原生支持websocket反向代理;压力测试可以用jmeter+第三方websocket插件,具体可以到github上搜一下。

#回传消息 需要 uid+serverip+fd 绑定关系 来实现

#压测 可以用jmeter 或者 swoole作者写的 swoole-src/run.php at master · swoole/swoole-src · GitHub

#add 2017 1126

upstream websocket{

server serverip01;

server serverip02

}

map $http_upgrade $connection_upgrade {

default upgrade;

'' close;

}

server {

listen 8020;

location / {

proxy_pass websocket;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "Upgrade";

}

}

客户端链接时负载…就是公布出无数个WS链接点,客户端获取“通过一个计算策略分配的链接点”地址,客户端链接…

nginx负载没用,代理链接数在那儿放着的…

我的简单方案:我后台用PHP跑了6个进程监听六个端口(12322〜12327),然后Nginx部署安装了yaoweibin的ngx_tcp_proxy_module实现了tcp upstream,目前运行良好。

之前项目有过类似实践,一百万个并发连接。

第一层:DNS轮训 ,域名可指向多个同等配置的NGINX

第二层:NGINX,后端配置多个无状态应用进程

DNS轮训主要解决单机NGINX并发连接数瓶颈,不高的话直接NGINX亦可。


NGINX WEBSOCKET和负载均衡可参考:

Note: 基本的 WebSocket 的 Nginx 配置

NGINX负载均衡配置


觉得很容易用到.. Nginx 从 1.3 开始支持 WebSocket, 现在已经是 1.4.4 了
相对 HTTP, 看过例子发现配置其实比较简单,

先用 ws 模块写一个简单的 WebSocket 服务器:

Server = require('ws').Serverwss = new Server port: 3000wss.on 'connection', (ws) ->  console.log 'a connection'  ws.send 'started'console.log 'server started'

然后修改 Hosts, 添加, 比如 ws.repo, 指向 127.0.0.1
然后是 Nginx 配置:

server {  listen 80;  server_name ws.repo;  location / {    proxy_pass http://127.0.0.1:3000/;    proxy_redirect off;    proxy_http_version 1.1;    proxy_set_header Upgrade $http_upgrade;    proxy_set_header Connection "upgrade";  }}

Reload Nginx 然后从浏览器控制台尝试链接, OK

new WebSocket('ws://ws.repo/')

或者通过 Upstream 的写法:

upstream ws_server {  server 127.0.0.1:3000;}server {  listen 80;  server_name ws.repo;  location / {    proxy_pass http://ws_server/;    proxy_redirect off;    proxy_http_version 1.1;    proxy_set_header Upgrade $http_upgrade;    proxy_set_header Connection "upgrade";  }}

WebSocket 先是通过 HTTP 建立连接,
然后通过 101 状态码, 表示切换协议,, 在配置里是 Upgrade

【博主推荐两个比较常用的WS负载组件】

1、Swoole - 面向生产环境的 PHP 异步网络通信引擎 https://www.swoole.com/

2、Java丨PHP丨C#丨Websocket丨Asp.net Web实时消息服务器推送 - GoEasy http://goeasy.io/cn/

 希望对大家有帮助,如有理解错误的地方,还请大家斧正。




原创粉丝点击