基于nchan打造百万用户的聊天室

大家好,我是烤鸭:

   这次介绍下nchan,nginx的一个module。

nchan

源码: https://github.com/slact/nchan
官网: https://nchan.io/
nginx 配置说明文档: https://nchan.io/documents/nginxconf2016-slides.pdf

测试环境搭建

4 台linux centos 7,都安装了nginx和nchan。

安装可以参考下这篇文章。

https://www.cnblogs.com/rongfengliang/p/7866122.html

目前使用的是4台nginx做测试,1台模拟上游的转发服务器(类似 keepalived),后3台是安装了nchan的nginx,用来 pub/sub
看下配置。

nginx_master.conf

#master
upstream ws {server 192.168.1.1:8080 weight=1 max_fails=2;server 192.168.1.2:8080 weight=1 max_fails=2;server 192.168.1.3:8080 weight=1 max_fails=2;
}server {listen       80;server_name test.xxx.xxx.com;root  /usr/local/nginx/chat;#error_page 404 /404.html;error_page 500 502 503 504 /50x.html;location = 50x.html {root /usr/local/nginx/html;}#masterlocation / {proxy_pass http://ws;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";}access_log  logs/barrage.access.log;error_log  logs/nchan_error.log;
}

nginx_salve.conf

nchan_shared_memory_size 256M;upstream redis_cluster {nchan_redis_server redis://:password@10.168.1.2:3001;nchan_redis_server redis://:password@10.168.1.2:3002;nchan_redis_server redis://:password@10.168.1.3:3001;nchan_redis_server redis://:password@10.168.1.3:3002;nchan_redis_server redis://:password@10.168.1.1:3001;nchan_redis_server redis://:password@10.168.1.1:3002;# you don't need to specify all the nodes, they will be autodiscovered# however, it's recommended that you do specify at least a few master nodes.}server {listen       8080;server_name localhost;root  /usr/local/nginx/chat;location = /sub {add_header x-hit-from 127 always;nchan_subscriber;nchan_channel_id $arg_vid;nchan_use_redis on;nchan_redis_pass redis_cluster;}location = /pub{nchan_publisher;nchan_channel_id $arg_id;nchan_redis_pass redis_cluster;nchan_message_timeout 5m;}#..location = /private/status {nchan_stub_status;}
}

websocket测试网站:
http://www.websocket-test.com/

ws 建联 sub接口用来接收消息,vid是nginx配置里的channel_id

在这里插入图片描述

postman模拟消息发送到 nchan, /pub 接口

在这里插入图片描述

redis 存消息数据

在这里插入图片描述

如果自己写前端页面的话:可以是创建原生的websocket,也可以使用官方提供的NchanSubscriber.js。

正常 websocket:

var ws =new WebScoket("ws://xxx.yyy.com/sub?id=demo")
ws.onMessage=funciton(data){console.log(data)}

引入 NchanSubscriber.js:

var sub = new NchanSubscriber('http://xxx.yyy.com/sub?id=demo', 'websocket');
sub.on("message",
function(message, message_metadata) {alert(message);
});

架构

# 共享内存,单机的时候取决于单机的内存,redis集群下取决于 单个节点的内存
nchan_shared_memory_size 32000M;

单机nginx的worker通过channel的hash把数据同步到当前worker的内存,再同步到共享内存,同时刷到第二个worker。(关于内存操作其实调用的是nginx的api)

右边的redis模拟的场景是不同的nchan节点,共享层用redis来实现。

在这里插入图片描述

如果量小的话,简单的做个im或者聊天室,单机就足够了,使用的是nginx的机器内存。(和 springboot 直接加个 @WebSocket 注解差不多)
在这里插入图片描述

支持水平扩展的集群架构:(理论上支持不限数量的横向扩展,瓶颈在redis集群,得做好容灾方案。)

一般单台机器的连接数在 65535(可以改大),所以即便存储使用了redis,单机还是有瓶颈的,当然一般看消息体的大小,可能redis会先崩。所以需要多台nginx机器和一个超大的redis集群,来扛得住百万用户。nginx集群至少20台才能维持这么多人同时在线,如果要考虑消息的话,得看消息内容,预估redis集群大小。

在这里插入图片描述

nchan 和 netty

今天有同事问我,关于分发消息的。nchan和netty有什么区别。

简单说下netty的实现。

用 ConcurrentMap 维护 key(聊天室id)和channelGroup(每一个用户连接成功,就会增加一个channel)。

当有消息需要通知的时候需要调用方法即可。

channelGroup.writeAndFlush(new TextWebSocketFrame(message));

再看下 nchan的源码:

slact/nchan/blob/master/src/subscribers/common.c

ngx_int_t nchan_subscriber_receive_notice(subscriber_t *self, ngx_int_t code, void *data) {if(code == NCHAN_NOTICE_SUBSCRIBER_INFO_REQUEST) {// ... 构建需要通知的内容// 获取需要通知的 channel_id,从这个函数 nchan_get_subscriber_info_response_channel_idngx_str_t *response_channel_id = nchan_get_subscriber_info_response_channel_id(self->request, response_id);// ... 构建msg对象cf->storage_engine->publish(response_channel_id, &msg, cf, NULL, NULL);if(result_allocd) {ngx_http_complex_value_free(&result);}}return NGX_OK;
}

/src/util/nchan_channel_id.c

ngx_str_t *nchan_get_subscriber_info_response_channel_id(ngx_http_request_t *r, uintptr_t request_id) {// 全局的 request_ctx 对象,调用nginx api获得,https://www.nginx.com/resources/wiki/extending/api/http/#ngx-http-get-module-ctxnchan_request_ctx_t    *ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);ngx_str_t *chid = ctx->subscriber_info_response_channel_id;// child 是空的,就新分配内存,并给ctx的subscriber_info_response_channel_id赋值if(!chid) {// nginx 分配内存的函数,https://www.nginx.com/resources/wiki/extending/api/alloc/chid = ngx_palloc(r->pool, sizeof(ngx_str_t));if(chid == NULL) {return NULL;}ctx->subscriber_info_response_channel_id = chid;chid->data = ngx_palloc(r->pool, NCHAN_SUBSCRIBER_INFO_CHANNEL_ID_BUFFER_SIZE);if(chid->data == NULL) {ctx->subscriber_info_response_channel_id = NULL;return NULL;}}u_char *end = ngx_snprintf(chid->data, NCHAN_SUBSCRIBER_INFO_CHANNEL_ID_BUFFER_SIZE, "meta/sr%d", (ngx_int_t )request_id);chid->len = end - chid->data;return chid;}

src/nchan_types.h

看一下 nchan_request_ctx_t 的结构,订阅者的id和channel_id 都存了。

#define NCHAN_MULTITAG_REQUEST_CTX_MAX 4
typedef struct {subscriber_t                  *sub;nchan_reuse_queue_t           *output_str_queue;nchan_reuse_queue_t           *reserved_msg_queue;nchan_bufchain_pool_t         *bcp; //bufchainpool maybe?ngx_str_t                     *subscriber_type;nchan_msg_id_t                 msg_id;nchan_msg_id_t                 prev_msg_id;ngx_str_t                     *publisher_type;ngx_str_t                     *multipart_boundary;ngx_str_t                     *channel_event_name;ngx_str_t                      channel_id[NCHAN_MULTITAG_REQUEST_CTX_MAX];int                            channel_id_count;time_t                         channel_subscriber_last_seen;int                            channel_subscriber_count;int                            channel_message_count;ngx_str_t                     *channel_group_name;ngx_str_t                     *request_origin_header;ngx_str_t                     *allow_origin;ngx_int_t                      subscriber_info_response_id;ngx_str_t                     *subscriber_info_response_channel_id;unsigned                       sent_unsubscribe_request:1;unsigned                       request_ran_content_handler:1;} nchan_request_ctx_t;

其实是从nginx的全局对象获取channel_id的。

总结

优点:

  • 服务不需要考虑和维护链接等,只需要专注处理业务相关逻辑。

  • nchan由于直接在nginx层,性能更好(比起自己搭建pub/sub服务器)。

缺点:

  • 非应用层的,出现问题不好排查。
  • 默认的消息存储时间过长(nchan_message_timeout:1h),消息大量情况下容易拖垮集群。
  • 以channelid为key的话,可能导致redis分配不均匀,单个节点压力过大,影响不止当前的channelid。
  • 集群模式依赖redis,需要考虑容灾方案。
  • 扩展性差,由于不支持多个redis集群,没法根据特定条件分片。(比如某个聊天室人特别多,单独一套redis集群,用netty的话可能比较好实现)(特意给作者留言确认了一下,https://github.com/slact/nchan/issues/619)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/37658.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于MT5的沪深股票回测二 导入历史数据

回测的基础是是历史数据 以导入日线数据为例 1.打开交易品种 2. 找到目标合约 3.选择要处理的数据周期,日线选择daily 然后导入处理后的日K线数据 注意历史数据需要符合MT5的数据格式 此次有一个不是bug的显示,最多显示2000条记录,实际更多…

探索 Google Bard 的 10 大令人兴奋的新功能

这家科技巨头开发的人工智能聊天机器人 Google Bard 最初发布时,收到的评价平平。然而,随着最近在 Google I/O 2023 开发者大会上发布其改进版本,Google Bard 的情况发生了重大变化。全新改进的 Google Bard AI 具有许多令人兴奋的功能。让我们仔细看看谷歌吟游诗人的特点:…

万众瞩目,谷歌的反击来了!全新PaLM 2反超GPT-4,办公全家桶炸裂升级,Bard史诗进化...

Datawhale干货 最新:谷歌 PaLM 2,来源:量子位 万众瞩目,谷歌的反击来了。 现在,谷歌搜索终于要加入AI对话功能了,排队通道已经开放。 当然这还只是第一步。 大的还在后面: 全新大语言模型PaLM …

LaTeX插入图片

基本语法 常用选项[htbp]是浮动格式(参考《LaTeX2e插图指南》16.2节和《Ishort》3.9节): 『h』当前位置。将图形放置在正文文本中给出该图形环境的地方。如果本页所剩的页面不够,这一参数将不起作用。『t』顶部。将图形放置在页…

chatgpt赋能python:Python抓取电脑应用软件数据

Python抓取电脑应用软件数据 随着互联网技术的不断发展,我们的生活方式也在发生着巨大变化,如今,我们已经可以依靠计算机应用软件对生活的各个方面进行掌控。而如何获取这些软件的数据,以及对这些数据进行分析,也成为…

chatgpt赋能python:Python程序如何变成电脑程序

Python程序如何变成电脑程序 简介 Python是一种高级编程语言,它在数据科学、人工智能、Web开发、机器学习、AI等领域非常流行。在Python中编写的程序需要转化为计算机程序才能执行。在这篇文章中,我们将解释Python编写的程序如何变成电脑程序。 什么是…

chatgpt赋能python:Python可以入侵别人电脑吗?

Python可以入侵别人电脑吗? Python自从诞生以来便一直以来备受关注,其简单易学、高效实用的特点让无数人喜爱并成为了开发人员的首选编程语言。但是,你可能会想知道:Python能够入侵别人电脑吗?今天我们就来探讨一下这…

chatgpt赋能python:如何使用Python绕过电脑开机密码?

如何使用Python绕过电脑开机密码? 随着科技的发展,计算机已经成为我们生活和工作中必不可少的工具。当然,在使用计算机时保护自己的隐私也是非常重要的。电脑开机密码是最基本的保护措施之一,但是如果忘记了密码该怎么办呢&#…

chatgpt赋能python:Python模拟操作电脑

Python模拟操作电脑 介绍 Python是一个强大的编程语言,自从引入以来,被广泛地使用在各种领域。其中一个重要的应用场景是模拟操作电脑。这里所说的模拟操作电脑,是指用Python编写程序来模拟用户在计算机上的日常操作,例如键盘输…

chatgpt赋能python:如何用Python阻止电脑关机

如何用Python阻止电脑关机 在使用电脑的过程中,我们经常会遇到电脑因为各种原因自动关机的情况,这给我们的工作和生活带来了很多麻烦。但是,有了Python编程技能,我们可以很容易地使用Python代码来阻止电脑关机。 在本文中&#…

chatgpt赋能python:Python如何打开电脑摄像头

Python如何打开电脑摄像头 Python是一种易于学习和使用的编程语言,因其灵活性和强大的功能而备受欢迎。其中,许多人使用Python来处理图像处理和计算机视觉,这样,了解如何打开电脑摄像头是非常重要的。 在这篇文章中,…

基金投资咨询

针对投资者风险偏好程度高、中、低3种假设情况相应做出了投资建议: 假设情况1:投资者风险偏好程度较高 2021年12月21日,客户小王来到银行,寻求银行的理财经理推荐一款适合自己的基金,来完成自己100万元的投资&#x…

chatgpt赋能python:Python简化手机短信发送

Python 简化手机短信发送 作为一名有10年 Python 编程经验的工程师, 我们经常需要向手机发送提醒短信,例如验证码、通知等。通常情况下,我们需要使用平台 API 或自己的短信网关,这些方式既麻烦又不安全。在本文中,我将…

C#使用SendMessage发送组合键

有时需要出发菜单功能,例如发送ALT F打开应用程序的文件菜单,如何使用SendMessage实现呢? 使用用spy截取的ALTF的消息内容(如何使用spy,请熟悉的高手指点下,我使用spy lite没有得到)&#xff1…

手把手教程:解除AppleID「双重验证」

大家好,我是可夫小子,关注AIGC、读书和自媒体。解锁更多ChatGPT、AI绘画玩法。加:keeepdance,备注:chatgpt,拉你进群。 在前面的文章中,我共享了两个美区ID给大家下载ChatGPT App,可…

Web前端工程师-优秀简历汇总

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴! Web前端工程师-优秀简历汇总 1. http://ww…

xpath爬取智联招聘--大数据开发职位并保存为csv

先上项目效果图: 本次爬取的URL为智联招聘的网址:https://www.zhaopin.com/ 首先先登录上去,为了保持我们代码的时效性,让每个人都能直接运行代码出结果,我们要获取到我们登录上去的cookies,并把他放在表头…

用python爬取前程无忧网大数据岗位信息并分析

爬虫的基本思路 1、在前程无忧官网检索“大数据”的结果中,每条检索结果详情对应的URL存在a标签的href属性中,通过组合选择器可以找到每条检索结果详情的URL。 2、前程无忧的招聘岗位信息数据固定的放在HTML的各个标签内,通过id选择器、标签…

前端工程师简历

总结一下 理解Web,W3C标准 (一淘,SAE,云适配,Zealer,小米,蘑菇街,DNSpod,百姓网) jQuery (云适配,金蚕网络,小米) 跨浏览器适配 (一淘,Zealer,蘑菇街,) HTML5 (云适配,小米,金蚕网络,DNSPod,新浪手机微博) Web语义化 (云适配) 后端语言或经验 (一淘,云适配,小米,金蚕) Ba…

人工智能简历-计算机视觉简历

前言 很多粉丝私我,说面试的事情。 这玩意我不理解,因为如果是计算机科班出身,计算机行业我觉得闭着眼睛找。 简历这玩意我真不会。。。 分享2个东西给大家。 第一个是 出国/在国外找实习/外企的英文简历。 比较流行的是一页。 第二个…