Одна машина на основе веб-сокета поддерживает систему распределенного чата (IM) с миллионами подключений.

WebSocket

В этой статье будет рассказано, как реализовать систему распределенного чата (IM) на основе веб-сокета.

Используя golang для реализации связи через веб-сокет, одна машина может поддерживать миллионы подключений, используя структуру gin, загрузку nginx, горизонтальное развертывание, взаимную связь внутри программ и протокол связи grpc.

Содержание этой статьи относительно длинное. Если вы хотите напрямую познакомиться с проектом клонирования, введите его напрямую.Опыт проекта скачать проект goWebSocket, Текст начинается с введения в то, что такое webSocket, затем начинается введение в этот проект и настройка доменного имени в Nginx для переадресации webSocket, а затем рассказывается, как построить распределенную систему.

содержание

  • 1. Описание проекта
    • 1.1 goWebSocket
    • 1.2 Опыт проекта
  • 2. Представляем веб-сокет
    • 2.1 Что такое веб-сокет
    • 2.2 Совместимость веб-сокета
    • 2.3 Зачем использовать веб-сокет
    • 2.4 Процесс создания веб-сокета
  • 3. Как реализовать систему длинных соединений на основе webSocket
    • 3.1 Используйте go для реализации сервера webSocket
      • 3.1.1 Начать прослушивание порта
      • 3.1.2 Протокол обновления
      • 3.1.3 Управление клиентскими подключениями
      • 3.1.4 Зарегистрировать асинхронный обработчик, написанный клиентским сокетом
      • 3.1.5 Зарегистрировать асинхронный обработчик для чтения клиентского сокета
      • 3.1.6 Получение клиентских данных и обработка
      • 3.1.7 Использование маршрутизации для обработки данных запроса клиента
      • 3.1.8 Предотвратить переполнение памяти и не перезапускать Goroutine
    • 3.2 Реализовать клиент webSocket с помощью javaScript
      • 3.2.1 Запустите и зарегистрируйте прослушиватель
      • 3.2.2 Отправка данных
  • 4. проект goWebSocket
    • 4.1 Описание проекта
    • 4.2 Зависимости проекта
    • 4.3 Старт проекта
  • 5, конфигурация Nginx проекта webSocket
    • 5.1 Зачем настраивать Nginx
    • 5.2 Конфигурация nginx
    • 5.3 Устранение неполадок
  • 6. Измерение давления
    • 6.1 Оптимизация ядра Linux
    • 6.2 Подготовка к стресс-тестам
    • 6.3 Данные измерения давления
  • 7. Как реализовать распределенный Im на базе webSocket
    • 7.1 Описание
    • 7.2 Архитектура
    • 7.3 Развертывание распределенной системы
  • 8. Обзор и размышление
    • 8.1 Применение в других системах
    • 8.2 Необходимо улучшить и оптимизировать
    • 8.3 Резюме
  • 9. Ссылки

1. Описание проекта

1.1 goWebSocket

В этой статье рассказывается, как реализовать распределенную систему на основе веб-чата (IM).

Используйте golang для реализации связи через веб-сокет, одна машина поддерживает миллионы подключений, использует структуру gin, загрузку nginx, может быть развернута горизонтально, взаимодействует друг с другом внутри программы и использует протокол связи grpc.

  • Схема архитектуры, используемая webSocket в общих проектах
    网站架构图

1.2 Опыт проекта

2. Представляем веб-сокет

2.1 Что такое веб-сокет

Протокол WebSocket родился в 2008 году и стал международным стандартом в 2011 году. Все браузеры уже поддерживают его.

Его самая большая особенность заключается в том, что сервер может активно передавать информацию клиенту, а клиент также может активно отправлять информацию серверу.Это настоящий двусторонний равноправный диалог и относится к разновидности серверной технологии push.

  • Сравнение HTTP и WebSocket в процессе коммуникации

    HTTP协议和WebSocket比较

  • И HTTP, и webSocket поддерживают настройку сертификатов,ws://нет сертификатаwss://Настройте идентификатор протокола сертификата

    HTTP协议和WebSocket比较

2.2 Совместимость веб-сокета

  • Совместимость с браузером, версия, которая начинает поддерживать webSocket

浏览器开始支持webSocket的版本

  • поддержка сервера

golang, java, php, node.js, python, nginx имеют хорошую поддержку

  • Поддержка Android и IOS

Android может использовать java-webSocket для поддержки webSocket

iOS 4.2 и выше имеют поддержку WebSockets

2.3 Зачем использовать веб-сокет

    1. С точки зрения бизнеса требуется возможность активного доступа к клиенту.

В настоящее время большинство запросов выполняется с использованием HTTP.Клиент инициирует запрос, который обрабатывается сервером, а затем возвращает результат.Сервер не может активно отправлять данные клиенту.

服务端处理一个请求

    1. В большинстве сценариев нам необходимо активно уведомлять пользователей, например: система чата, пользователи активно информируют пользователей, когда они выполняют задачи, и о некоторых операционных действиях необходимо уведомлять онлайн-пользователей.
    1. Получить онлайн-статус пользователя
    1. Активный опрос клиента для получения данных при отсутствии длительного соединения
    1. Его можно реализовать одним способом и использовать на разных платформах (H5/Android/IOS).

2.4 Процесс создания веб-сокета

    1. Сначала клиент инициирует запрос на обновление протокола.

Клиент инициирует запрос на обновление протокола, используя стандартный формат сообщения HTTP и добавляя информацию заголовка к сообщению.

Connection: UpgradeУказывает, что соединение необходимо обновить

Upgrade: websocketНеобходимо перейти на протокол веб-сокета

Sec-WebSocket-Version: 13Версия протокола 13

Sec-WebSocket-Key: I6qjdEaqYljv3+9x+GrhqA==Это значение кодировки base64, которое генерируется браузером случайным образом и на которое отвечает сервер.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

    1. Протокол обновления ответа сервера

Сервер получает запрос на обновление протокола, и если сервер поддерживает протокол обновления, он ответит следующим образом.

возвращение:

Status Code: 101 Switching ProtocolsУказывает на поддержку протоколов переключения

# Response Headers
Connection: upgrade
Date: Fri, 09 Aug 2019 07:36:59 GMT
Sec-WebSocket-Accept: mB5emvxi2jwTUhDdlRtADuBax9E=
Server: nginx/1.12.1
Upgrade: websocket
    1. После завершения протокола обновления клиент и сервер могут отправлять данные друг другу.

websocket接收和发送数据

3. Как реализовать систему длинных соединений на основе webSocket

3.1 Используйте go для реализации сервера webSocket

3.1.1 Начать прослушивание порта

  • websocket должен слушать порт, поэтому он должен быть вgolangуспешныйmainФункция использует сопрограмму для запуска программы
  • main.goосуществить запуск
go websocket.StartWebSocket()
  • init_acc.goстартовая программа
// 启动程序
func StartWebSocket() {
	http.HandleFunc("/acc", wsPage)
	http.ListenAndServe(":8089", nil)
}

3.1.2 Протокол обновления

  • Клиент отправляется на сервер через http-запрос, нам нужно обновить протокол http до протокола websocket.
  • Обновите библиотеку golang протокола HTTP-запросов.gorilla/websocketЭто уже сделано хорошо, мы можем использовать его напрямую
  • При реальном использовании рекомендуется использовать две сопрограммы для каждого соединения для обработки данных клиентского запроса и отправки данных клиенту.Хотя открытие сопрограммы займет некоторую память, разделение чтения уменьшит возможность блокировки отправки и получения данных.
  • init_acc.go
func wsPage(w http.ResponseWriter, req *http.Request) {

	// 升级协议
	conn, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool {
		fmt.Println("升级协议", "ua:", r.Header["User-Agent"], "referer:", r.Header["Referer"])

		return true
	}}).Upgrade(w, req, nil)
	if err != nil {
		http.NotFound(w, req)

		return
	}

	fmt.Println("webSocket 建立连接:", conn.RemoteAddr().String())

	currentTime := uint64(time.Now().Unix())
	client := NewClient(conn.RemoteAddr().String(), conn, currentTime)

	go client.read()
	go client.write()

	// 用户连接事件
	clientManager.Register <- client
}

3.1.3 Управление клиентскими подключениями

  • Сколько пользовательских подключений имеет текущая программа, и потребность в пользовательских трансляциях, здесь нам нужен менеджер (clientManager) для обработки этих событий:
  • Запишите все подключения, пользователи входа могут пройтиappId+uuidПроверить подключение пользователя
  • Использование хранилища карт связано с проблемой одновременного чтения и записи нескольких сопрограмм, поэтому вам необходимо добавить блокировки чтения-записи.
  • Определите четыре канала для обработки установления клиентского соединения, входа пользователя в систему, отключения и событий полной трансляции соответственно.
// 连接管理
type ClientManager struct {
	Clients     map[*Client]bool   // 全部的连接
	ClientsLock sync.RWMutex       // 读写锁
	Users       map[string]*Client // 登录的用户 // appId+uuid
	UserLock    sync.RWMutex       // 读写锁
	Register    chan *Client       // 连接连接处理
	Login       chan *login        // 用户登录处理
	Unregister  chan *Client       // 断开连接处理程序
	Broadcast   chan []byte        // 广播 向全部成员发送数据
}

// 初始化
func NewClientManager() (clientManager *ClientManager) {
	clientManager = &ClientManager{
		Clients:    make(map[*Client]bool),
		Users:      make(map[string]*Client),
		Register:   make(chan *Client, 1000),
		Login:      make(chan *login, 1000),
		Unregister: make(chan *Client, 1000),
		Broadcast:  make(chan []byte, 1000),
	}

	return
}

3.1.4 Зарегистрировать асинхронный обработчик, написанный клиентским сокетом

  • Предотвратите сбои программы, поэтому вам нужно перехватывать исключения
  • Чтобы отобразить здесь место ненормального сбоя, используйтеstring(debug.Stack())распечатать информацию о стеке вызовов
  • Если запись данных не удалась, возможно проблема с соединением, закройте соединение
  • client.go
// 向客户端写数据
func (c *Client) write() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("write stop", string(debug.Stack()), r)

		}
	}()

	defer func() {
		clientManager.Unregister <- c
		c.Socket.Close()
		fmt.Println("Client发送数据 defer", c)
	}()

	for {
		select {
		case message, ok := <-c.Send:
			if !ok {
				// 发送数据错误 关闭连接
				fmt.Println("Client发送数据 关闭连接", c.Addr, "ok", ok)

				return
			}

			c.Socket.WriteMessage(websocket.TextMessage, message)
		}
	}
}

3.1.5 Зарегистрировать асинхронный обработчик для чтения клиентского сокета

  • Прочитайте данные, отправленные клиентом в цикле, и обработайте их.
  • Если прочитать данные не удается, закройте канал
  • client.go
// 读取客户端数据
func (c *Client) read() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("write stop", string(debug.Stack()), r)
		}
	}()

	defer func() {
		fmt.Println("读取客户端数据 关闭send", c)
		close(c.Send)
	}()

	for {
		_, message, err := c.Socket.ReadMessage()
		if err != nil {
			fmt.Println("读取客户端数据 错误", c.Addr, err)

			return
		}

		// 处理程序
		fmt.Println("读取客户端数据 处理:", string(message))
		ProcessData(c, message)
	}
}

3.1.6 Получение клиентских данных и обработка

  • Согласен на отправку и получение формата данных запроса, для удобства обработки js, использованиеjsonОтправлять и получать данные в формате данных (человекочитаемый формат более удобен для использования в разработке работы)

  • Войдите, чтобы отправить пример данных:

{"seq":"1565336219141-266129","cmd":"login","data":{"userId":"马远","appId":101}}
  • Пример данных ответа на вход:
{"seq":"1565336219141-266129","cmd":"login","response":{"code":200,"codeMsg":"Success","data":null}}
  • Websocket — это двусторонняя передача данных, которую можно отправлять непрерывно.Если отправленные данные требуют ответа от сервера,seqЧтобы определить, на какие данные запроса отвечает сервер

  • cmd используется для определения действия, websocket не имеет URL-адреса, похожего на http, поэтому укажите, что такое действие cmd

  • Текущие действия: login/heartbeat используется для отправки запросов на вход и поддержания соединения (долгие соединения без передачи данных в течение длительного времени легко разрываются браузерами, мобильными посредниками, nginx и серверными программами)

  • Зачем нужен AppId?UserId – это единственное поле, которое представляет пользователя. Чтобы сделать его универсальным, AppId предназначен для указания того, на какой платформе пользователь входит в систему (веб, приложение, ios и т. д.), что удобно для последующее расширение.

  • request_model.goСогласованный формат данных запроса

/************************  请求数据  **************************/
// 通用请求数据格式
type Request struct {
	Seq  string      `json:"seq"`            // 消息的唯一Id
	Cmd  string      `json:"cmd"`            // 请求命令字
	Data interface{} `json:"data,omitempty"` // 数据 json
}

// 登录请求数据
type Login struct {
	ServiceToken string `json:"serviceToken"` // 验证用户是否登录
	AppId        uint32 `json:"appId,omitempty"`
	UserId       string `json:"userId,omitempty"`
}

// 心跳请求数据
type HeartBeat struct {
	UserId string `json:"userId,omitempty"`
}
  • response_model.go
/************************  响应数据  **************************/
type Head struct {
	Seq      string    `json:"seq"`      // 消息的Id
	Cmd      string    `json:"cmd"`      // 消息的cmd 动作
	Response *Response `json:"response"` // 消息体
}

type Response struct {
	Code    uint32      `json:"code"`
	CodeMsg string      `json:"codeMsg"`
	Data    interface{} `json:"data"` // 数据 json
}

3.1.7 Использование маршрутизации для обработки данных запроса клиента

  • Используйте маршрутизацию для обработки данных запроса, отправленных клиентом.
  • После добавления типа запроса в дальнейшем можно использовать класс для его обработки аналогично http (маршрутизатор-контроллер).
  • acc_routers.go
// Websocket 路由
func WebsocketInit() {
	websocket.Register("login", websocket.LoginController)
	websocket.Register("heartbeat", websocket.HeartbeatController)
}

3.1.8 Предотвратить переполнение памяти и не перезапускать Goroutine

    1. Запланированные задачи для очистки тайм-аута соединений Соединение без входа в систему и соединение с входом в систему будут разорваны, если в течение 6 минут не будет пульса.

client_manager.go

// 定时清理超时连接
func ClearTimeoutConnections() {
    currentTime := uint64(time.Now().Unix())

    for client := range clientManager.Clients {
        if client.IsHeartbeatTimeout(currentTime) {
            fmt.Println("心跳时间超时 关闭连接", client.Addr, client.UserId, client.LoginTime, client.HeartbeatTime)

            client.Socket.Close()
        }
    }
}
    1. Если одна из горутин чтения и записи выходит из строя, они закрывают друг друга.write()Горутине не удалось записать данные, закрытьc.Socket.Close()соединение будет закрытоread()Goroutine read()Горутине не удалось прочитать данные, закрытьclose(c.Send)соединение будет закрытоwrite()Goroutine
    1. Клиент активно закрывается Закройте горутины чтения и записи отClientManagerудалить соединение
    1. Мониторинг пользовательских подключений и количество горутин Девять из десяти переполнений памяти связаны с горутинами Добавьте http-интерфейс для просмотра состояния системы и предотвращения перезапуска горутин.Просмотр состояния системы
    1. Nginx настраивает время освобождения неактивного соединения, чтобы не забыть закрыть соединение.
    1. Используйте pprof для анализа производительности, затрат времени

3.2 Реализовать клиент webSocket с помощью javaScript

3.2.1 Запустите и зарегистрируйте прослушиватель

  • js устанавливает соединение и обрабатывает события успешного соединения, получения данных и отключения.
ws = new WebSocket("ws://127.0.0.1:8089/acc");

 
ws.onopen = function(evt) {
  console.log("Connection open ...");
};
 
ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  data_array = JSON.parse(evt.data);
  console.log( data_array);
};
 
ws.onclose = function(evt) {
  console.log("Connection closed.");
};

3.2.2 Отправка данных

  • Примечание. Данные могут быть отправлены только после успешного установления соединения.
  • Пример отправки данных от клиента на сервер после установления соединения
登录:
ws.send('{"seq":"2323","cmd":"login","data":{"userId":"11","appId":101}}');

心跳:
ws.send('{"seq":"2324","cmd":"heartbeat","data":{}}');

ping 查看服务是否正常:
ws.send('{"seq":"2325","cmd":"ping","data":{}}');

关闭连接:
ws.close();

4. проект goWebSocket

4.1 Описание проекта

  • Этот проект представляет собой распределенную систему обмена мгновенными сообщениями, основанную на реализации веб-сокетов.

  • Клиент случайным образом назначает имена пользователей, и каждый входит в комнату чата, чтобы реализовать функцию группового чата.

  • Одна машина (24 ядра и 128 ГБ памяти) поддерживает миллионы клиентских подключений.

  • Поддержка горизонтального развертывания, развернутые машины могут взаимодействовать друг с другом

  • Диаграмма архитектуры проекта

    网站架构图

4.2 Зависимости проекта

  • В этом проекте нужно использовать только Redis и Golang.
  • Этот проект использует govendor для управления зависимостями, клонируйте этот проект, и вы можете использовать его напрямую
# 主要使用到的包
github.com/gin-gonic/gin@v1.4.0
github.com/go-redis/redis
github.com/gorilla/websocket
github.com/spf13/viper
google.golang.org/grpc
github.com/golang/protobuf

4.3 Старт проекта

  • клонировать проект
git clone git@github.com:link1st/gowebsocket.git
# 或
git clone https://github.com/link1st/gowebsocket.git
  • Изменить конфигурацию проекта
cd gowebsocket
cd config
mv app.yaml.example app.yaml
# 修改项目监听端口,redis连接等(默认127.0.0.1:3306)
vim app.yaml
# 返回项目目录,为以后启动做准备
cd ..
  • Описание файла конфигурации
app:
  logFile: log/gin.log # 日志文件位置
  httpPort: 8080 # http端口
  webSocketPort: 8089 # webSocket端口
  rpcPort: 9001 # 分布式部署程序内部通讯端口
  httpUrl: 127.0.0.1:8080
  webSocketUrl:  127.0.0.1:8089


redis:
  addr: "localhost:6379"
  password: ""
  DB: 0
  poolSize: 30
  minIdleConns: 30
  • Стартовый проект
go run main.go
  • Введите адрес чатаhttp://127.0.0.1:8080/home/index
  • Здесь вы можете испытать систему обмена мгновенными сообщениями на основе веб-сокета.

5, конфигурация Nginx проекта webSocket

5.1 Зачем настраивать Nginx

  • Используйте nginx для разделения внутренней и внешней сетей и открывайте внешнему миру только IP-адрес Nginx (обычные интернет-компании добавят уровень LVS перед nginx для балансировки нагрузки), чтобы уменьшить вероятность вторжения.
  • Использование Nginx может использовать функцию загрузки Nginx.Когда внешний интерфейс используется снова, необходимо подключить только фиксированное доменное имя, а трафик распределяется по разным машинам через Nginx.
  • При этом мы также можем использовать разные стратегии загрузки Nginx (опрос, вес, ip_hash)

5.2 Конфигурация nginx

  • Используйте доменное имяim.91vh.comДля примера обратитесь к конфигурации
  • каталог первого уровняim.91vh.com/accОн используется для webSocket, и он использует функцию переадресации потока nginx (nginx 1.3.31 начинает ее поддерживать, и конфигурация Tengine такая же), и перенаправляет его на порт golang 8089 для обработки.
  • Другие каталоги используются для HTTP и перенаправляются на порт golang 8080 для обработки.
upstream  go-im
{
    server 127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=10s;
    keepalive 16;
}

upstream  go-acc
{
    server 127.0.0.1:8089 weight=1 max_fails=2 fail_timeout=10s;
    keepalive 16;
}


server {
    listen       80 ;
    server_name  im.91vh.com;
    index index.html index.htm ;


    location /acc {
        proxy_set_header Host $host;
        proxy_pass http://go-acc;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Connection "";
        proxy_redirect off;
        proxy_intercept_errors on;
        client_max_body_size 10m;
    }

    location /
    {
        proxy_set_header Host $host;
        proxy_pass http://go-im;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_redirect off;
        proxy_intercept_errors on;
        client_max_body_size 30m;
    }

    access_log  /link/log/nginx/access/im.log;
    error_log   /link/log/nginx/access/im.error.log;
}

5.3 Устранение неполадок

  • Запустите тестовую команду nginx, чтобы проверить правильность файла конфигурации.
/link/server/tengine/sbin/nginx -t

  • Если есть ошибка
nginx: [emerg] unknown "connection_upgrade" variable
configuration file /link/server/tengine/conf/nginx.conf test failed
  • Подход
  • существуетnginx.comДобавить к
http{
	fastcgi_temp_file_write_size 128k;
..... # 需要添加的内容

    #support websocket
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

.....
    gzip on;
    
}

  • Причина: Nginx столкнется с проблемами дизайна, когда прокси-сервер Nginx webSocketEnd-to-end and Hop-by-hop Headers

6. Измерение давления

6.1 Оптимизация ядра Linux

  • Установите количество дескрипторов открытия файла
ulimit -n 1000000
  • Установить параметры подключения к сокетам
vim /etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0

6.2 Подготовка к стресс-тестам

  • Чтобы пройти испытание под давлением, если у вас есть результаты испытания под давлением, добро пожаловать, чтобы добавить
  • В будущем будут специальные туториалы, от подачи заявки на машины, написания кейсов стресс-тестов, оптимизации ядра и получения данных стресс-тестов.

6.3 Данные измерения давления

  • Когда проект фактически используется, каждое соединение занимает около 24 КБ памяти, а одна горутина занимает около 11 КБ.
  • 22G памяти требуется для поддержки одного миллиона подключений
Количество онлайн-пользователей cup ОЗУ I/O net.out
1W
10W
100W

7. Как реализовать распределенный Im на базе webSocket

7.1 Описание

  • Обратитесь к исходному коду этого проекта

  • gowebsocket v1.0.0 автономная версия Im система

  • gowebsocket v2.0.0 распределенная система Im

  • Для удобства демонстрации система обмена мгновенными сообщениями и система webSocket(acc) объединены в одну систему.

  • Интерфейс системы обмена мгновенными сообщениями: Получить всех пользователей онлайн, запросить всех пользователей сервиса перед заказом + всех пользователей сервиса в кластере Для отправки сообщения здесь используется http-интерфейс (веб-версия WeChat также является http-интерфейсом для отправки сообщений), здесь следует учитывать два основных момента: 1. Разделение сервисов, сделать систему акк максимально простой, не смешивая другую бизнес-логику 2. Отправка сообщений осуществляется через http интерфейс, вместо использования webSocket соединения используется метод разделения данных от получения и отправки, что позволяет ускорить эффективность отправки и получения данных.

7.2 Архитектура

  • Схема регистрации запуска проекта и последовательности подключения пользователей

用户连接时序图

  • Схема последовательности отправки сообщений из других систем (IM, задачи) пользователям, подключенным к системе webSocket (acc)

分布是系统随机给用户发送消息

7.3 Развертывание распределенной системы

  • Демонстрация подразвертывания с горизонтальным развертыванием двух проектов (gowebsocket и gowebsocket1)
  • Как общаться друг с другом между проектами: после запуска проекта зарегистрируйте Ip проекта и rpcPort в Redis, чтобы другие проекты могли его найти, и используйте gRpc для связи, когда требуется связь.
  • gowebsocket
# app.yaml 配置文件信息
app:
  logFile: log/gin.log
  httpPort: 8080
  webSocketPort: 8089
  rpcPort: 9001
  httpUrl: im.91vh.com
  webSocketUrl:  im.91vh.com

# 在启动项目
go run main.go 

  • gowebsocket1
# 将第一个项目拷贝一份
cp -rf gowebsocket gowebsocket1
# app.yaml 修改配置文件
app:
  logFile: log/gin.log
  httpPort: 8081
  webSocketPort: 8090
  rpcPort: 9002
  httpUrl: im.91vh.com
  webSocketUrl:  im.91vh.com

# 在启动第二个项目
go run main.go 
  • Конфигурация Nginx

Добавьте IP и порт второй машины в предыдущий элемент конфигурации Nginx.

upstream  go-im
{
    server 127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=10s;
    server 127.0.0.1:8081 weight=1 max_fails=2 fail_timeout=10s;
    keepalive 16;
}

upstream  go-acc
{
    server 127.0.0.1:8089 weight=1 max_fails=2 fail_timeout=10s;
    server 127.0.0.1:8090 weight=1 max_fails=2 fail_timeout=10s;
    keepalive 16;
}
  • Перезапустите Nginx после завершения настройки.
  • После перезапуска запроса убедитесь, что он соответствует ожиданиям:

Посмотрите, падает ли запрос на оба проекта Поэкспериментируйте, могут ли два пользователя также отправлять сообщения друг другу, подключившись к разным проектам (gowebsocket и gowebsocket1).

  • О распределенном развертывании

Этот проект просто демонстрирует, как проект развертывается распределенным образом и как модули взаимодействуют друг с другом после распределенного развертывания. Полностью решить систему без единой точки отказа, но также нужен кластер Nginx, кластер Redis и т. д.

8. Обзор и размышление

8.1 Применение в других системах

  • Первоначальная цель системного дизайна состоит в том, чтобы поддерживать длительное соединение с клиентом и иметь два интерфейса с внешней системой (для проверки того, находится ли пользователь в сети, и для отправки сообщений онлайн-пользователям), чтобы реализовать разделение услуг.
  • Он может использоваться несколькими службами только в том случае, если он отделен от бизнеса, вместо установления длинного соединения для каждого бизнеса.

8.2 Реализованные функции

  • журнал журнала gin (журнал запросов + журнал отладки)
  • чтение файла конфигурации сделано
  • Временный скрипт для очистки просроченных неподдерживаемых подключений Завершено
  • http интерфейс, получить количество логинов и подключений Готово
  • http-интерфейс, отправка push-уведомлений, запрос количества людей в сети Завершено
  • Внутренняя связь программы grpc, отправка сообщения завершена
  • appIds, когда пользователь входит в систему на нескольких платформах
  • интерфейс, собрать всех онлайн-людей в группу, отправить сообщение, завершить
  • чат один на один, групповой чат завершен
  • Распределенное горизонтальное расширение Завершено
  • Скрипт стресс-теста
  • Документация
  • Каталог документов, реализация миллионов длинных подключений, зачем внедрять IM, как реализовать IM
  • Схема архитектуры и расширение

Детали реализации IM:

  • Определить структуру текстового сообщения Готово
  • html отправить текстовое сообщение готово
  • Интерфейс получает текстовое сообщение и отправляет его всей группе Готово
  • html получает сообщение и отображает его в завершенном интерфейсе
  • Оптимизация интерфейса требует постоянной оптимизации
  • После того, как кто-то присоединяется, вся трансляция завершается.
  • Определите структуру сообщения присоединения к чату
  • Представляем робота, который будет определен

8.2 Необходимо улучшить и оптимизировать

  • Войдите в систему, используйте WeChat для входа в систему, чтобы получить никнейм, аватар и т. д.
  • Система учета, система данных
  • Оптимизация интерфейса, адаптация под мобильные телефоны
  • Сообщение Текстовое сообщение (поддержка смайлика), изображение, голос, видеосообщение
  • Регистрация микросервисов, обнаружение, автоматический выключатель и т. д.
  • Добавить элементы конфигурации, максимальное количество подключений на машину

8.3 Резюме

  • Несмотря на то, что реализован распределенный обмен мгновенными сообщениями, есть много деталей, которые не были обработаны (логин не аутентифицирован, интерфейс еще не оптимизирован и т. д.), но вы можете узнать из этого примера: Решить многие бизнес-задачи через WebSocket
  • Хотя в этой статье утверждается, что одна машина может иметь миллионы длинных подключений (которые могут быть удовлетворены в памяти), реальная ситуация намного сложнее, чем это (процессор несколько загружен).Конечно, если у вас такой большой бизнес объем, вы можете купить больше машин.Хорошо для поддержки вашего бизнеса, эта программа просто демонстрирует, как использовать webSocket на практике.
  • Со ссылкой на эту статью вы можете реализовать программу, соответствующую вашим потребностям

9. Ссылки

Веб-сокет Википедии

Руан Ифэн Учебное пособие по WebSocket

Протокол WebSocket: 5 минут от входа до освоения

link1st gowebsocket

поиск на github: link1st Посмотреть проект gowebsocket

GitHub.com/Lincoln1Body/Дай мне…