一个Java码农的Node之旅

本篇是一个Node新手做完实际项目后的心得总结。写代码之前先理清楚思路和结构,不打没有准备的仗。

【声明:本文经授权转自CSDN,作者:蔡伟伟。未经许可,禁止转载。】

本篇是一个Node新手做完实际项目后的心得总结。

摘要

如果BOSS要求你在短期内快速实现一套聊天云服务平台,实现成本主要是:

  • 要维护社交关系,一大波僵尸POJO正在袭来。
  • 要存储数据库,找个ORM工具。
  • 长连接吧方面是WebSocket标准,亦或Netty、Mina系的框架。
  • 连接断了需要实现下重连机制吧?服务器端写完了还有客户端,也需要帮助指导下实现,问题诸多。

我们的征途是偷懒

如果有现成的轮子偷懒的话,socket.io比较适合:

  • 轻量级、扩展便捷、API简单易用。
  • 周边完善,重连、路由、隔离、单播、广播等都已实现。
  • 丰富的客户端支持,涵盖了浏览器端、Android、iOS。

在仔细研读了Flexi传授如何说服自己的老板采用Node.js,并成功说服BOSS后,正式开始自己的Node之旅。

工欲善其事必先利其器

开发环境

对于开发环境,无论你是使用编辑器/编辑器之神,或是sublime/atom/npp之类,亦或是WebStorm高富帅有钱任性,都是很不错的选择。

Supervisor可以帮你watch代码变更,自动重启服务,从而节省了手工重启程序的时间。

关于调试

  • 高富帅款:WebStorm
  • 高逼格款:原始打断点
  • 屌丝款:node-inspector,可以在Chrome中直接调试,强烈推荐:

图片描述

离不开的中间件

首先,勾勒出一个核心的聊天系统大致的雏形:

基本功能

  • 集群实现
  • 敏感词过滤

好友

  • 加好友,删好友
  • 好友之间聊天,发文字发图片发音频发视频啥的

群组

  • 创建,加入,退出群组
  • 群组内广播聊天

聊天历史记录

扩展与周边

  • 集群实现
  • 敏感词过滤

分析下大致需要的存储层和中间件以及是否有相关的Node实现:

  • MySQL:存储一些重要的元数据,主要是用户关系类的,需要事务支持。(node-mysql)
  • ZooKeeper:用户在线离线状态存储。(node-zookeeper-client)
  • Redis:使用缓存加速一些查询,PubSub特性用于实现集群通讯。(ioredis)
  • HBase:典型的列式存储,用于实现一些非核心数据的快速存储查询。(hbase-rpc-client)
  • LevelDB:本地快速读写一些键值对。(LevelUP)

技术栈

对比

常年滋润在Java这片润土之上,先来做个比较,让我们对陌生的技术栈有所了解。

图片描述

ES6

ES6是个好东西, 我觉得比较好用的三点:

  • const: 可以方便地对不可变的东西进行声明。
  • let: 只JavaScript初学者不用担忧不知不觉把变量提升的问题。
  • lambda表达式: 神器。

核心实现

流程时序

仔细思考,勾勒出大致的时序图:

图片描述

系统架构:

设计下大致的架构:

图片描述

步步为赢,各个击破

状态管理

典型的IM系统中必然存在用户在线离线的状态。每一个在线状态,对于服务器来说,等价于与客户端存在一个Socket连接。所以对于单机环境下,在内存中维护用户和Socket的关系即可,当Socket连接和断开时分别做更新操作。当切换至集群环境时,情况变得稍微复杂,所以我们需要借助ZooKeeper来实现。除了本机每个用户与Socket关联关系,另外以临时节点的方式在ZooKeeper中进行存储,目录结构为根节点/命名空间/用户标识/Socket标识(临时节点)。当socket连接被建立的时候,创建对应的临时节点,socket断开时移除临时节点。当服务器意外退出时,除了socket连接全部断开之外,在其ZooKeeper session上的所有对应的临时节点也会被销毁。SocketIO的重连机制会尝试重连至其他伺服器并重新建立起对应关系。

优点:

  • 判断一个用户是否在线只需判断用户标识节点的numChildren是否大于零即可。
  • 获取用户所有已连接的Socket只需读取用户标识下的所有孩子节点即可。

缺点:

  • 多了额外读写ZooKeeper的开销。

用途:

  • 实现集群的基础
  • 有了状态判定才能实现离线消息推送

好友关系、群组关系

关系表原本存储于HBase,但因为缺乏事务支持,实际效果不佳,经常导致关系不一致。传统关系型数据库在这一方面依旧强势。这块比较简单,即常见的关系模型表,在此略过。

点对点聊天实现

单机

A对B发送消息,除了基础的权限判定,只需查询内存表中对应B的所有socket,然后对其发射消息即可。见下图:

图片描述

集群

由于B可能登录在不同的服务器上,需要借助消息中间件(Redis Pub/Sub),发布消息,每个服务器订阅消息列表,如果存在消息接收者,则找到其注册在本机的socket进行发射消息。流程如下图:

图片描述

广播聊天实现

广播类似以上的点对点实现,只是多了一步查询成员表依次处理的步骤。此处略。

聊天历史记录的实现

考虑到只需要根据时间范围做分页查询的简单需求,这里使用了HBase的宽表。点对点形式的聊天我们可以对两个用户标识进行排序,并结合命名空间生成唯一的哈希值,作为行健,而每个CELL的值则是时间戳,因为我们需要令其自然倒序排列,所以针对时间戳做了LONG。MAX-时间戳的处理。综合起来,大致的存储结构如下:

图片描述

敏感词过滤

维护脏词字典,对消息进行字符串替换?为了实现正确辨认“曹操在操场操美女”中的动词“操”,需要实现中文分词和词性判断,于是处理逻辑转变成:

  1. 拉取最新的脏词列表,转换为简体中文并写入LevelDB中。
  2. 使用nodejieba进行中文分词:曹操(n)/在(p)/操场(n)/操(v)/美女(n)
  3. 对分词后的名词和动词转换为简体中文并查询LevelDB,命中则替换。
  4. 返回替换后的字符串得到:曹操在操场*美女

打包部署

PM2

Node本身是单线程的,虽然Node本身提供Cluster模块,但需要修改代码。通过PM2这个工具可以简便地让其多进程部署,充分利用多核CPU资源:

图片描述

Docker

可以使用官方的node镜像。但体积比较大,这里推荐基于alpine-node,体积比较小巧,例如:

FROM mhart/alpine-node:4

RUN apk add --no-cache make gcc g++ python

RUN apk add --no-cache imagemagick

WORKDIR /src
ADD . .
RUN npm install --registry=http://registry.npm.taobao.org/

EXPOSE 3000
CMD ["npm","start"]

C1000K测试

著名的单机100万连接,由于项目是第一个版本,限于各方面原因我们暂时没有完成这个测试。但在这里简短介绍所需的一些配置,以备后续使用:

服务器端

  • 修改tcp连接的最小内存为4k, 编辑/etc/sysctl.conf
net.ipv4.tcp_wmem = 4096 87380 4161536
net.ipv4.tcp_rmem = 4096 87380 4161536
net.ipv4.tcp_mem = 786432 2097152 3145728
  • 修改系统最大文件描述符, 编辑/etc/sysctl.conf
fs.file-max = 1000000
  • 修改进程最大文件描述符, 编辑/etc/security/limits.conf
*         hard    nofile      1000000
*         soft    nofile      1000000
root      hard    nofile      1000000
root      soft    nofile      1000000
  • 重载下配置(sysctl -p)或者重启,检查下当前的设置 cat /proc/sys/fs/file-nr

*客户端

  • 因为每个IP最多可以创建6万多个连接,不可能找很多服务器进行测试。所以客户端除了上述修改,还需要创建多个虚拟IP,这样每个IP可以提供大约6万的连接,如:
 ifconfig eth0:0 192.168.77.10 netmask 255.255.255.0 up
 ifconfig eth0:1 192.168.77.11 netmask 255.255.255.0 up
 ifconfig eth0:2 192.168.77.12 netmask 255.255.255.0 up
 ifconfig eth0:3 192.168.77.13 netmask 255.255.255.0 up
 ifconfig eth0:4 192.168.77.14 netmask 255.255.255.0 up
 ifconfig eth0:5 192.168.77.15 netmask 255.255.255.0 up
 ifconfig eth0:6 192.168.77.16 netmask 255.255.255.0 up
 ifconfig eth0:7 192.168.77.17 netmask 255.255.255.0 up
 ifconfig eth0:8 192.168.77.18 netmask 255.255.255.0 up
 ifconfig eth0:9 192.168.77.19 netmask 255.255.255.0 up
 ifconfig eth0:10 192.168.77.20 netmask 255.255.255.0 up
 ifconfig eth0:11 192.168.77.21 netmask 255.255.255.0 up
 ifconfig eth0:12 192.168.77.22 netmask 255.255.255.0 up
 ifconfig eth0:13 192.168.77.23 netmask 255.255.255.0 up
 ifconfig eth0:14 192.168.77.24 netmask 255.255.255.0 up
 ifconfig eth0:15 192.168.77.25 netmask 255.255.255.0 up
 ifconfig eth0:16 192.168.77.26 netmask 255.255.255.0 up
 ifconfig eth0:17 192.168.77.27 netmask 255.255.255.0 up
 ifconfig eth0:18 192.168.77.28 netmask 255.255.255.0 up
  • 修改本地端口范围,编辑/etc/sysctl.conf
    net.ipv4.ip_local_port_range = 1024 65535
  • 重载配置或重启开始测试

总结

存在即合理,不要卷入无谓的语言之争,本猿觉得干这行的最重要莫过于学习能力。 
写代码之前先理清楚思路和结构,不打没有准备的仗。

良好的代码规范,遵循KISS原则。

 

【原文地址:http://geek.csdn.net/news/detail/66330

最新文章
1客户案例研究:专家安全扫描,守护金融银行小程序安全和私密性 WeTest私有化部署的定制扫描平台让金融银行客户能无成本接入扫描系统并迅速上手使用。客户能方便快捷地根据定制手册进行自助扫描,根据生成的扫描报告,详细洞察漏洞,快速识别并准确定位问题根源。
2客户案例研究:专家渗透测试,洞察电子商务小程序重大交易漏洞 通过WeTest渗透测试服务,某知名零售公司旗下的在线购物类小程序中发现了8处安全风险,我们的安全专家为客户提供了详细的漏洞报告,提供了较为清晰完整的安全加固方案。在回归测试中,中危以上风险均被解决。
3自查小程序4大安全隐患!文末免费赠送小程序安全扫描专业版! 腾讯WeTest现面向小程序开发者开放免费申请使用小程序安全扫描专业版,助您提前发现全面的安全漏洞。扫描文中问卷二维码或点击问卷链接,即可报名参与免费领取活动。
4浅谈渗透测试服务在泛互行业带来的价值 在泛互联网行业中,渗透测试服务对于保障企业的网络安全至关重要。
5云手机卡顿/无特定设备/商店登录受限怎么办?WeTest专有云帮您解决! 公有云满足了大量小微企业、个人的测试需求;随着客户深入使用,也遇到了一系列新问题。本篇将对几个常见问题予以解答
购买
客服
反馈