今天有人问腾讯云CDN加速支持websocket吗?虽然大家都知道支持。但是也说不出个所以然来,今天就介绍下腾讯云如何开启websocket支持。
1. WebSocket简介
Websocket是用于服务端主动向客户端推送消息的技术。传统的HTTP/HTTPS只能由客户端向服务端发起请求,服务端对请求一一响应。在需要获取服务端状态变化的场景下,如:提交的后台任务是否执行成功,只能通过客户端轮询向服务端发起请求,不仅效率低,还浪费资源(HTTP1.0下每次轮询都需要经过TCP三次握手重新建立连接)。而WebSocket的出现较好的解决了这个问题,在TCP首次建立完连接之后,该连接不自动关闭,在有效期内客户端可以继续向服务端发送消息,服务端也能主动给客户端发送消息。
2. 腾讯云CDN对WebSocket的支持
腾讯云CDN依靠全球广泛部署的CDN节点,高效的网络存储优化方案和精准的调度策略,有效提升下载速度、降低响应时间,提供流畅的用户体验。腾讯云CDN节点自研服务器在提供静态资源访问的能力下,同时支持WebSocket访问,兼容动态资源的极速上云服务。本文将介绍如果验证腾讯云CDN节点支持WebSocket。
2.1 配置源站支持WebSocket
由于WebSocket属于动态资源,不适用于缓存服务,所有请求必定回源,所以首先需要源站支持WebSocket。以下以Python+Nginx为例介绍如何配置代理支持WebSocket。
- 2.1.1 Websocket服务端
服务端示例代码:
#!/usr/bin/python #coding=utf8 """ # Created Time : 2020-05-02 11:07:02 # File Name: websocket-server.py # Description: """ import socket import base64 import hashlib import struct from threading import Thread def parse_headers(data): headers = {} method = url = protocol = '' raw_headers = data.split('\r\n\r\n')[0].split('\r\n') for i in range(0, len(raw_headers)): if i == 0: elements = raw_headers[i].split(' ') if len(elements) == 3: method, url, protocol = elements else: k, v = raw_headers[i].split(':', 1) headers[k] = v.strip() print headers return method, url, protocol, headers def calc_sign(data): message = data + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' #不要问为什么是这个字符串,rfc6455中定义的! return base64.b64encode(hashlib.sha1(message).digest()) def encode(data): token = b"\x81" length = len(data) if length < 126: token += struct.pack("B", length) elif length <= 0xFFFF: token += struct.pack("!BH", 126, length) else: token += struct.pack("!BQ", 127, length) return token + data def decode(raw_data): payload_len = ord(raw_data[1]) & 127 if payload_len == 126: extend_payload_len = raw_data[2:4] mask = raw_data[4:8] decoded = raw_data[8:] elif payload_len == 127: extend_payload_len = raw_data[2:10] mask = raw_data[10:14] decoded = raw_data[14:] else: extend_payload_len = None mask = raw_data[2:6] decoded = raw_data[6:] data = bytearray() for i in range(len(decoded)): chunk = ord(decoded[i]) ^ ord(mask[i % 4]) data.append(chunk) return str(data) def handle_request(conn): data = conn.recv(1024) print data method, url, protocol, headers = parse_headers(data) response = "HTTP/1.1 101 Switching Protocols\r\n" \ "Upgrade:websocket\r\n" \ "Connection:Upgrade\r\n" \ "Sec-WebSocket-Accept:%s\r\n" \ "WebSocket-Location:ws://%s%s\r\n\r\n" % (calc_sign(headers['Sec-WebSocket-Key']), headers['Host'], url) conn.send(response) while True: try: raw_data = conn.recv(1024) except Exception as e: raw_data = None if not raw_data: break data = decode(raw_data) print('recv data', data) if not conn.send(encode(data)): break def run(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('0.0.0.0', 8888)) sock.listen(10) while True: conn, address = sock.accept() print('new client %s:%s' % address) Thread(target=handle_request, args=(conn,)).start() sock.close() if __name__ == '__main__': run()
该服务通过socket响应客户端连接,打印出每个连接的头部,读取客户端发送的消息,并将其发回给客户端。注意websocket通信发送的不是原始消息,需要经过编码,如encode,decode函数。
- 2.1.2 Websocket客户端
客户端同样需要编解码,也可以直接利用现有工具简化测试步骤:https://pypi.org/project/websocket_client/
- 2.1.3 本地测试
分两次分别发送两次消息,123和456。

请求的抓包HTTP头部内容如下:


可以看到服务端总共只收到了一个TCP建立连接请求,一组HTTP头部,两组消息共用一个连接。websocket实际上发送和响应的也是标准的http头部格式,只是额外带上了鉴权头部。
- 2.1.4 配置Nginx代理WebSocket
一般我们会在最终的服务前面挂一个代理服务支持路由+负载均衡等,如Nginx等。Nginx需要启用额外配置支持Websocket,修改Nginx配置文件配置如下:
worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; resolver 119.29.29.29; # websocket map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream websocket { server 127.0.0.1:8888; } sendfile on; keepalive_timeout 65; server { listen 80; server_name _; root /usr/local/nginx/nginx/http/; access_log logs/all-http.log; # websocket location ~ ^/websocket { proxy_pass http://websocket; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } }
重启nginx服务:
nginx -s reload
客户端重新请求80端口即可。
wsdump.py ws://127.0.0.1:80/websocket
- 2.1.5 测试CDN支持WebSocket访问。

可以看到腾讯云CDN确实无需特殊配置即可支持WebSocket访问并透传源站。有一点在实际使用过程中需要注意的是节点默认支持10s的保活时间,10s内如果没有消息传递,将默认关闭连接。
腾讯云CDN开启websocket方法。
CDN控制台创建域名,在协议处选择“协议跟随”

腾讯云CDN websocket协议设置
腾讯云WS协议额外收费吗?
目前腾讯云并没有额外对WS协议收费。且免费用户的20GB月流量包也可以使用WS协议。
目前新注册的腾讯云用户可享受免费CDN额度
1、个人用户120GB。分6个月赠送,每个月20GB。
2、企业用户360GB。分6个月赠送,每个月60GB。