春节前后,我实践了一个项目, 就是模拟一个类微信的系统。
希望能做到以下几点,
当然 你真的用这套代码 去支持上千万用户 那时间还太早了点。
发这篇文章的目的
有问题也可以提在后面,我也可以回答
网页版 wiki 在这里
https://github.com/xiaojiaqi/fakewechat/wiki/Design
pdf 在这里
https://github.com/xiaojiaqi/fakewechat/blob/master/doc/cn/doc.pdf
项目在这里
https://github.com/xiaojiaqi/fakewechat/
下面是文章的前几部分
#构建一个你自己的类微信系统 ##可扩展通信系统实践
##前言
正如你们所知的那样,微信是一个非常成功的在线服务系统,由几万台服务器组成的系统为几亿人提供着稳定的业务服务。可惜作为一个普通的工程师基本上不可能有整体设计这样一个系统的机会,即使加入 xx 也基本是个螺丝钉,只见树木不见树林。看着 inforQ 上面高大上的架构设计,高来高去,个人资质驽钝,看过以后,所得有限,仍然大脑空空,没有太多收获。
2016 年春节前后,我个人做了一个可扩展的系统实践,试图构建一个可扩展的类微信系统,本文记录了构建一个设计,实践可扩展通信系统的过程。
本文只是从外部反推后的一个设计,实践,模拟,实现的过程,和真实环境也不存在任何可比性,本人也和 tx 公司没有任何交集。文中必然充满了臆想,猜测以及错误,不能对本文的任何正确性做任何保证,也不对可能造成的损失负责。如果你愿意给出任何建议意见欢迎 email ppmsn2005#gmail.com. 本次设计开发,实验所使用的所有代码和文档都可以在 http://www.github.com/xiaojiaqi/fakewechat 里找到。
##1. 目标及局限性 ###1.1 核心目标:
这个系统可扩展,可抗压,稳定并且正确服务。
注意 因为服务器负载和网络的原因,消息延迟到达是允许的。
###1.2 本文及实验的局限性
局限性... 太多了,初始数据是随机的,数据集合大小是随机的,系统的架构是理想状况的,所以里面所有的结论都可以认为是不可信。
##2. 技术要求
##3. 环境准备 ###3.1 开发语言选择
我选择的语言是 Go, 版本 1.4/1.5 而不是 CPP 。主要的原因 在前文 c1000k practice guide 一文中,我认为 go 是很好的后台开发语言。 在此次测试中,我也期待使用 go 进行一次实践。 go 开发速度快,编译更是便捷,事实也证明的确很好用。
但是 GO 可能存在以下的问题
###3.2 系统优化设置 我觉得以下几篇文章很好 可以参照进行设置,过程略
###3.3 脚本语言 python
###3.4 数据库比较及选型 数据库可以分为关系型的 mysql/PG 和非关系型的 Redis /memcached
关系型的数据库 Mysql 和 pg 都能满足业务要求, mysql 我更熟悉,所以选 Mysql 非关系型数据库 Redis/ memcached
设计需要支持原子操作,也就是 CAS 操作
Redis 天生单线程,不存在这个问题,而 memcached 也满足这个要求。
内存情况
Redis 存在 fork 问题,对内存使用有要求 memcached 的缺点数据不能持久化,数据存在被淘汰的问题。 所以选 Redis
Mysql 和 Redis 之间进行对比
CAS mysql 支持 Redis 支持
吞吐量
mysql 强 Redis 很强
数据管理
mysql 可以有完整的数据类型,可以严格保证数据安全,分表机制比较复杂,对硬件要求高 Redis 没有完整的数据类型约束,完全靠客户程序自己保证,风险很大,对硬件要求低
数据估算
以最后 1000 万用户在线考虑 以每人有 50 条相互好友关系,每个用户 id 为 4 字节 1000*10*1000*50*4/1024/1024/1024 = 1.8G ,数据量并不大。同样情况下, Redis 操作更简单,可控性很强
所以 最终选 Redis
###3.5 缓存系统 暂无,
##4. 系统分析以及设计 ###4.1. 账号
首先考虑 一个用户的信息 简单的看 微信的界面,头像 昵称 微信号。没有类似 QQ 这样的数字 ID ,而且微信号可以不设置。我个人觉得从系统设计的角度看,微信自己也需要一个唯一的可以标识用户的标识。这个唯一的标记应该是什么?没有资料描述,我将它设计为一个整数了,我在系统里会用一个唯一的整数表述一个用户。
###4.2 账户间的好友关系
微信不存在单向好友,所以好友链就是 2 个数字 在用户 100 的关系链表里存在 100,200 这样一个记录,表明 用户 100 和用户 200 互为好友。则在用户 200 的关系链里也应该存在 200,100 这样一个记录。
###4.3. 多 IDC 以及账号区域问题 ####4.3.1 单 IDC
这个可以从腾讯大讲堂中看到一些端倪, QQ 也有这样的发展历程。最早所有的客户端是连到深圳一个 IDC 区域的,由单个点来完成。这样的好处是业务简单
但是单 IDC 也带来了很多问题
1.单点问题,如果过于集中接入,一旦出现节点故障,可能导致大量的不可服务。这就是所谓的鸡蛋在一个篮子里。 2.服务距离太远影响服务质量,设想一下,服务器在深圳,有一个用户,身在在东北,那么他每个消息都是要穿过整个中国大陆。要为他提供很好的服务的质量,代价会比为一位广州的用户大很多。距离的增加必然带来延迟上的增加。 3.单节点是中心节点 压力太大 继续做扩展很难,有技术和物理的上限。你见过 10 吨的卡车, 100 吨的卡车,但是你见过 1000 吨的卡车吗?扩展有难度
###4.3.2 多 IDC
虽然多年以前,我认为互联网是传统电信是不一样的东西,但是多年以后,我发现世界上很多东西都是类似的。电信的网络和 Internet 本身就是解决这个问题最好的例子。
你的电话是有区域的,也就是本地电话和国内长途和国际长途。你拨打的电话,绝大多数的电话是在本地的,有一些是国内的,更少一些是国际的。对绝大多数人来说本地的朋友要比远方的朋友多一些额。如果你和你的朋友都在一个 IDC ,那么肯定能更好的服务。( 8/2 原则,至少我如此认为)
所以在用户的属性里应该有一项很重要的属性 Region, Region 代表了你这个用户属于哪个 IDC ,类似于你电话区号,唯一的不同是,你的区号是可以变化的,只是这种变化不是很经常。平均每人每年的变化都不大的。并且这种改变不是由客户端发起,而是由服务器决定
多 IDC 的优势:
多 IDC 好,但是 IDC 带来一系列问题,需要仔细考虑。本次实践没有做这些部分,这是个大工程。实践过程中,将所有的用户按照不同的号段,分配到不同的 Region, 模拟出多 IDC 的情况。而更普遍的情况,则是每个 Region 内用户的号段是不连续,并且离散的。这需要一个专门的服务来解决,我会慢慢实现。
###4.3.3 IDC 内部的用户如何分布到均匀地服务器上
假设有 IDC 内部有 1000 台服务器可以服务 1000 万用户,如何将用户均衡的分布到所有的用户上?
原始的办法有按照号段来区分,或者用户 id 取余来做,但是这样都存在数目不均衡的问题,一旦存在服务器宕机无法快速迁徙的问题。
可以利用一个一致性 hash 算法,将用户 id 映射成另一个 id 值,解决分布不集中的问题。然后做 sharding.如果用户数目足够多的话,应该非常均衡。(需要慢慢实践这一想法)
##4.4. 多设备消息的支持
这个主要依靠后面的设计
##5 核心服务器设计 以及通信保证
这部分是系统的核心,我是这样理解和设计它们的。
###5.1 背景和需求
首先看前提, 这是一个大型的系统,里面任何一个服务器都是不稳定的,可以崩溃的。原因可以是软件故障, bug ,操作系统,断电,操作失误。任何一个服务器的崩溃都是在预期之中的。(这又是云计算的一个典型特征)
然后看消息的传输, 这里的消息可以暂时看作用户之间的聊天消息
首先看丢失,一个消息在这样的系统里传输,是随时可能被丢弃的。虽然我们使用了 TCP 协议,但是需要途径多个服务器,而每个服务器的崩溃都是可以无法预测的,所以任何一个消息都可能丢弃。也就是传输不可靠
再看重传,因为整个系统的服务器是不稳定的,一条消息发出去以后,是否到达了目的服务器,这不可知,所以一定是存在重传机制的,那么对方在收到 2 个重复的消息,一定要能区别出来。一条消息重复收到 100 次 和处理 1 次没有区别。这也就是所谓的幂等性
最后是乱序,同样因为服务器是不稳定的,那么先传的消息,可能因为途经一个缓慢的服务器,会后到目的地,而后传的消息,也许会因为网络优化而先到目的地,这一点接受消息的服务器也需要能区分出来。
后面的贴不了了 请去 最大同性交友网站 看吧。
https://github.com/xiaojiaqi/fakewechat/
有问题 可以提,能回答我就回答一下
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.