关于对外websocket端口阿里云高防透传设计
  yx99X8RMvAE0 2023年11月19日 34 0

需求

tcp长连接游戏额外监听一个webscoket端口供微信小程序游戏开发所用,业务方过来后明确提出3点要求:

1,对外的这个websocket端口需要支持wss协议

2,对外的这个websocket端口需要具备一定的防护能力

3,服务端能获取到客户端真实请求ip

4,支持游戏多端,手游为tcp对外,微信小程序游戏为wss对外

对于第四种多端的要求的话算是一种隐性的要求吧,对外wss需要进行额外的配置,如果游戏是纯wss的话那么是可以上云商的k8s集群服务的


什么是ws

在正式配置之前我们需要了解什么是websocket,websocket是一种在单个tcp连接上提供全双供通信的协议,它允许在客户端和服务器之间建立持久的连接,可以在两者之间进行双向实时数据传输,

websocket和http之间的关系

websocket握手过程通常始于一个基于http的握手请求,websocket的握手请求通过http协议的upgrade头部来请求升级到websocket协议

wss和https之间的关系

wss是websocket的安全版本,使用的是加密的传输,它与https一样,都是在传输层上使用加密进行安全通信,wss在websocket握手时会在http upgrade请求的基础上引入安全性,就像https在http的基础上提供安全性一样


思路整理

tcp端口对外

我们可以直接将域名解析到有公网ip的游戏服务器上,那么对外访问就是游戏域名:tcp端口 这种是最简单的方式,但是,该端口没有一定的防护能力,但是现在要求端口具有一定的防护能力,那么可以往上可以添加一个新bgp高防四层端口接入,域名解析到高防实例ip

tcp整体的访问架构为:高防-->源站ip(带公网ip的游戏服务器)

对于没有公网ip的ecs,如果需要对外端口需要在服务器上添加负载均衡clb,整体访问架构变为:

高防--->源站ip(负载均衡公网ip),tcp四层透传--->游戏服务器

websocket端口对外

根据上面对websocket的了解以及websocket和http之前关系我们得知,websocket的握手过程是基于http的,websocket的握手请求需要通过http协议的upgrade头部来升级到websocket协议,如何升级这个http upgrade头,我准备用nginx中间件,同时为了支持wss协议,我需要申请域名证书来保证支持https从而能支持wss,同时对外的wss端口需要具备一定的防护能力,具体设计可以如下:

有公网ip的ecs

高防--->游戏服务器nginx--->游戏服务器websocket

没有公网ip的ecs

高防--->源站ip(七层https监听)--->游戏服务器nginx--->游戏服务器websocket


配置过程

wss不带高防的设计方式

先说下我之前公司透传wss的设计方式,之前的项目可以将一台游戏服服务器理解为一个水桶,一个水桶就能支撑起一个游戏区服,水桶的出水口也就是每个区组对外端口一个是tcp的,一个是webscoket的,如图:

关于对外websocket端口阿里云高防透传设计_websocket

s1区服域名解析到s1游戏服务器, s2区服域名解析到s2游戏服务器...那么tcp访问直接是区服域名:tcp端口,wss设计如下图:

关于对外websocket端口阿里云高防透传设计_wss_02

首先在负载均衡上创建一个https监听,分发到多台ecs上,ecs上安装nginx,由nginx将wss请求转发到对应的游戏服务器的websocket端口上,nginx proxy配置如下,仅供参考

server{
    listen 80;
    listen 443 ssl;
    server_name login登陆域名;  # 这个login登陆域名如login.项目名.域名后缀
    index index.html index.htm index.php;
    root /data/xxx/client;
    ssl_certificate /etc/nginx/key/xxx.pem;
    ssl_certificate_key /etc/nginx/key/xxx.key;
    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;
    ssl_prefer_server_ciphers on;
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;

    location / {
      resolver 223.5.5.5 223.6.6.6 1.2.4.8 114.114.114.114 valid=3600s;
      proxy_redirect off;
      if ( $uri ~ ^\/(.*)\/(.*) ) {
      	proxy_pass http://$1:$2;
    }

      proxy_set_header Host $host;
      proxy_set_header X-Real_IP $remote_addr;
      proxy_set_header X-Forwarded-For $http_x_forwarded_for;
      proxy_set_header Upgrade $http_upgrade; # 升级协议头
      proxy_set_header Connection upgrade;
    }
    access_log /data/logs/nginx/xxx.log nbwl_access;
    error_log /data/logs/nginx/xxx-error.log;
}

上述配置最重要的一点就是升级协议头proxy_set_header Upgrade $http_upgrade;这个配置项,它能将http协议通过Upgradetou升级到ws协议

在此设计模式下wss访问方式:

比如我想访问某一区服的websocket端口,那么我可以这样访问 wss://login登陆域名/游戏区服域名/ws端口

优点

配置过程简单,而且我可以扩展多台nginx proxy,可以大大提高并发能力,同时因为负载均衡监听了https,配置了证书,所以可以支持wss协议的要求

缺点

对外tcp端口和webscoket端口都没有防护能力,只依靠负载均衡基础防护能力太弱了,别人一打就炸


wss带高防的设计方式

说下现在公司的透传wss的设计方式,现在的项目没有滚服概念,游戏服务池是由多台网关服,逻辑服,功能服架设而起的,就像一个大水池一样(图我就不画了,我知道我画图画不好),对外有tcp和websocket端口,随着业务访问量增加可以通过在服务池中添加机器节点来达到扩展能力,有的机器节点需要开放对外端口,这样随着机器节点越添越多,随之对外的端口也会越来越多,那么我只需要一个域名多个端口的方式来实现对外访问

tcp对外:因为我们ecs没有公网,所以对外的话需要加负载均衡,同时想要对外端口有防护能力目前使用的是阿里云的新bgp高防,端口接入非常方便,域名解析到阿里云高防实例上,访问架构:

高防(4层端口接入)--->负载均衡(tcp监听)-->游戏服务器池的tcp监听端口

那么游戏服务器池中有多少个需要对外的tcp端口,负载均衡上就需要配置多少个tcp监听


wss设计如图:

关于对外websocket端口阿里云高防透传设计_wss_03

为了支持wss,源站负载均衡是一定要配置https监听的,如果配置tcp监听,ws访问是不通的,这个也验证了之前我们对于ws的理解

在配置的过程中我们发现,如果slb配置https,最上层新bgp高防如果配置四层端口接入的话会出现拿不到客户端真实请求ip的情况,

而且我们对外的websocket都是非标端口(非80,443),新bgp高防必须是增加版才能支持非标端口七层接入,具体配置如下:

高防

关于对外websocket端口阿里云高防透传设计_wss_04

负载均衡

关于对外websocket端口阿里云高防透传设计_websocket_05

游戏服务器nginx配置:

主配置文件

user  ;
worker_processes  auto;
events {
    worker_connections  65535;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    set_real_ip_from 0.0.0.0;
    real_ip_header X-Forwarded-For;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$proxy_add_x_forwarded_for"';
    access_log  logs/access.log  main;
    access_log  "pipe:rollback logs/access_log interval=1d baknum=7 maxsize=2G"  main;
    sendfile        on;
    keepalive_timeout  65;
    include vhost/*.conf;
}

子配置文件

server {
        listen  8206;
        server_name  xxxxx.com;
         
        access_log  logs/ws.access.log  main;
        error_log  logs/ws.error.log;
        location / { 
            proxy_pass http://127.0.0.1:ws端口;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header X-Real-IP $http_x_true_ip;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
}

然后服务端通过抓取请求头X-Real-IP的值来获取客户端真实请求ip


优点

能同时满足前面的4条要求

缺点

随着游戏服务器池中的服务器节点增加,对外websocket端口会越来越多,而新bgp高防域名接入非标端口最多是一个站点最多转发10个非标端口,后续要加多个必然要加多个站点,站点接入越多越不好管理,端口规划也都乱套,按照现在游戏模式应该是一个域名对多个对外端口,这和我们游戏业务需求模式不相匹配,如果只是测试用,开放低于10对外ws端口的话是够用的,如果正式上线的话肯定不行


ws通信测验方式

目前通过js脚本来测试对外wss端口通信

<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
   <title>WebSocket实例</title>
      <script type="text/javascript">
         function WebSocketTest()
         {
            if ("WebSocket" in window)
            {
               alert("您的浏览器支持 WebSocket!");      
               // 打开一个 web socket
               var ws = new WebSocket("wss://域名:对外ws端口");
               ws.onopen = function()
               {
                  // Web Socket 已连接上,使用 send() 方法发送数据
                  ws.send("发送数据");
                  alert("数据发送中...");
               };
                 
               ws.onmessage = function (evt)
               {
                  var received_msg = evt.data;
                  alert("数据已接收...");
               };
               ws.onclose = function()
               {
                  // 关闭 websocket11111
                  alert("连接已关闭...");
               };
            }
            else
            {
               // 浏览器不支持 WebSocket
               alert("您的浏览器不支持 WebSocket!");
            }
         }
      </script>
   </head>
   <body>
      <div id="sse">
         <a href="javascript:WebSocketTest()">运行 WebSocket</a>
      </div>
   </body>
</html>

当然你如果没有其他的测试方法的话也可以让业务方打服务器日志或者其他方式都行,由运维测验完毕后将整个成果交付给业务方

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月19日 0

暂无评论

yx99X8RMvAE0