# 基于websocket单台机器支持百万连接分布式聊天(IM)系统
本文将介绍如何实现一个基于websocket分布式聊天(IM)系统。
使用golang实现websocket通讯,单机可以支持百万连接,使用gin框架、nginx负载、可以水平部署、程序内部相互通讯、使用grpc通讯协议。
本文内容比较长,如果直接想clone项目体验直接进入[项目体验](#12-项目体验) [goWebSocket项目下载](#4goWebSocket-项目) ,文本从介绍webSocket是什么开始,然后开始介绍这个项目,以及在Nginx中配置域名做webSocket的转发,然后介绍如何搭建一个分布式系统。
## 目录
- [1、项目说明](#1项目说明)
- [1.1 goWebSocket](#11-goWebSocket)
- [1.2 项目体验](#12-项目体验)
- [2、介绍webSocket](#2介绍webSocket)
- [2.1 webSocket 是什么](#21-webSocket-是什么)
- [2.2 webSocket的兼容性](#22-webSocket的兼容性)
- [2.3 为什么要用webSocket](#23-为什么要用webSocket)
- [2.4 webSocket建立过程](#24-webSocket建立过程)
- [3、如何实现基于webSocket的长连接系统](#3如何实现基于webSocket的长连接系统)
- [3.1 使用go实现webSocket服务端](#31-使用go实现webSocket服务端)
- [3.1.1 启动端口监听](#311-启动端口监听)
- [3.1.2 升级协议](#312-升级协议)
- [3.1.3 客户端连接的管理](#313-客户端连接的管理)
- [3.1.4 注册客户端的socket的写的异步处理程序](#314-注册客户端的socket的写的异步处理程序)
- [3.1.5 注册客户端的socket的读的异步处理程序](#315-注册客户端的socket的读的异步处理程序)
- [3.1.6 接收客户端数据并处理](#316-接收客户端数据并处理)
- [3.1.7 使用路由的方式处理客户端的请求数据](#317-使用路由的方式处理客户端的请求数据)
- [3.1.8 防止内存溢出和Goroutine不回收](#318-防止内存溢出和Goroutine不回收)
- [3.2 使用javaScript实现webSocket客户端](#32-使用javaScript实现webSocket客户端)
- [3.2.1 启动并注册监听程序](#321-启动并注册监听程序)
- [3.2.2 发送数据](#322-发送数据)
- [3.3 发送消息](#33-发送消息)
- [3.3.1 文本消息](#331-文本消息)
- [3.3.2 图片和语言消息](#332-图片和语言消息)
- [4、goWebSocket 项目](#4goWebSocket-项目)
- [4.1 项目说明](#41-项目说明)
- [4.2 项目依赖](#42-项目依赖)
- [4.3 项目启动](#43-项目启动)
- [4.4 接口文档](#44-接口文档)
- [4.4.1 HTTP接口文档](#441-HTTP接口文档)
- [4.4.1.1 接口说明](#4411-接口说明)
- [4.4.1.2 聊天页面](#4412-聊天页面)
- [4.4.1.3 获取房间用户列表](#4413-获取房间用户列表)
- [4.4.1.4 查询用户是否在线](#4414-查询用户是否在线)
- [4.4.1.5 给用户发送消息](#4415-给用户发送消息)
- [4.4.1.6 给全员用户发送消息](#4416-给全员用户发送消息)
- [4.4.2 RPC接口文档](#442-RPC接口文档)
- [4.4.2.1 接口说明](#4421-接口说明)
- [4.4.2.2 查询用户是否在线](#4422-查询用户是否在线)
- [4.4.2.3 发送消息](#4423-发送消息)
- [4.4.2.4 给指定房间所有用户发送消息](#4424-给指定房间所有用户发送消息)
- [4.4.2.5 获取房间内全部用户](#4425-获取房间内全部用户)
- [5、webSocket项目Nginx配置](#5webSocket项目Nginx配置)
- [5.1 为什么要配置Nginx](#51-为什么要配置Nginx)
- [5.2 nginx配置](#52-nginx配置)
- [5.3 问题处理](#53-问题处理)
- [6、压测](#6压测)
- [6.1 Linux内核优化](#61-Linux内核优化)
- [6.2 压测准备](#62-压测准备)
- [6.3 压测数据](#63-压测数据)
- [7、如何基于webSocket实现一个分布式Im](#7如何基于webSocket实现一个分布式Im)
- [7.1 说明](#71-说明)
- [7.2 架构](#72-架构)
- [7.3 分布式系统部署](#73-分布式系统部署)
- [8、回顾和反思](#8回顾和反思)
- [8.1 在其它系统应用](#81-在其它系统应用)
- [8.2 需要完善、优化](#82-需要完善优化)
- [8.3 总结](#83-总结)
- [9、参考文献](#9参考文献)
## 1、项目说明
#### 1.1 goWebSocket
本文将介绍如何实现一个基于websocket聊天(IM)分布式系统。
使用golang实现websocket通讯,单机支持百万连接,使用gin框架、nginx负载、可以水平部署、程序内部相互通讯、使用grpc通讯协议。
- 一般项目中webSocket使用的架构图
![网站架构图](img/%E7%BD%91%E7%AB%99%E6%9E%B6%E6%9E%84%E5%9B%BE.png)
#### 1.2 项目体验
- [项目地址 gowebsocket](https://github.com/link1st/gowebsocket)
- [IM-聊天首页](http://im.91vh.com/home/index) 或者在新的窗口打开 http://im.91vh.com/home/index
- 打开连接以后进入聊天界面
- 多人群聊可以同时打开两个窗口
## 2、介绍webSocket
### 2.1 webSocket 是什么
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
- HTTP和WebSocket在通讯过程的比较
![HTTP协议和WebSocket比较](img/HTTP%E5%8D%8F%E8%AE%AE%E5%92%8CWebSocket%E6%AF%94%E8%BE%83.png)
- HTTP和webSocket都支持配置证书,`ws://` 无证书 `wss://` 配置证书的协议标识
![HTTP协议和WebSocket比较](img/HTTP%E5%8D%8F%E8%AE%AE%E5%92%8CWebSocket%E6%AF%94%E8%BE%83.jpeg)
### 2.2 webSocket的兼容性
- 浏览器的兼容性,开始支持webSocket的版本
![浏览器开始支持webSocket的版本](img/%E6%B5%8F%E8%A7%88%E5%99%A8%E5%BC%80%E5%A7%8B%E6%94%AF%E6%8C%81webSocket%E7%9A%84%E7%89%88%E6%9C%AC.jpeg)
- 服务端的支持
golang、java、php、node.js、python、nginx 都有不错的支持
- Android和IOS的支持
Android可以使用java-webSocket对webSocket支持
iOS 4.2及更高版本具有WebSockets支持
### 2.3 为什么要用webSocket
- 1. 从业务上出发,需要一个主动通达客户端的能力
> 目前大多数的请求都是使用HTTP,都是由客户端发起一个请求,有服务端处理,然后返回结果,不可以服务端主动向某一个客户端主动发送数据
![服务端处理一个请求](img/%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%A4%84%E7%90%86%E4%B8%80%E4%B8%AA%E8%AF%B7%E6%B1%82.jpeg)
- 2. 大多数场景我们需要主动通知用户,如:聊天系统、用户完成任务主动告诉用户、一些运营活动需要通知到在线的用户
- 3. 可以获取用户在线状态
- 4. 在没有长连接的时候通过客户端主动轮询获取数据
- 5. 可以通过一种方式实现,多种不同平台(H5/Android/IOS)去使用
### 2.4 webSocket建立过程
- 1. 客户端先发起升级协议的请求
客户端发起升级协议的请求,采用标准的HTTP报文格式,在报文中添加头部信息
`Connection: Upgrade`表明连接需要升级
`Upgrade: websocket`需要升级到 websocket协议
`Sec-WebSocket-Version: 13` 协议的版本为13
`Sec-WebSocket-Key: I6qjdEaqYljv3+9x+GrhqA==` 这个是base64 encode 的值,是浏览器随机生成的,与服务器响应的 `Sec-WebSocket-Accept`对应
```
# Request Headers
Connection: Upgrade
Host: im.91vh.com
Origin: http://im.91vh.com
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: I6qjdEaqYljv3+9x+GrhqA==
Sec-WebSocket-Version: 13
Upgrade: websocket
```
![浏览器 Network](img/%E6%B5%8F%E8%A7%88%E5%99%A8%20Network.png)
- 2. 服务器响应升级协议
服务端接收到升级协议的请求,如果服务端支持升级协议会做如下响应
返回:
`S