瓜子IM智能客服系统的数据架构设计(整理自现场演讲)

本文由ITPub根据封宇在【第十届中国系统架构师大会(SACC2018)】现场演讲内容整理而成。php

一、引言

瓜子业务重线下,用户网上看车、预定到店、成交等许多环节都发生在线下。瓜子IM智能客服系统的目的是要把这些线下的活动搬到线上,对线下行为进行追溯,积累相关数据。系统链接用户、客服、电销、销售、AI机器人、业务后台等多个角色及应用,覆盖网上咨询、浏览、预定看车、到店体验、后服、投诉等众多环节,各个角色间经过可直接操做的卡片传递业务。html

例如,用户有买车意向时,电销或AI机器人会及时给用户推送预定看车的卡片,用户只需选择时间便可完成预定操做。整个系统逻辑复杂,及时性、可靠性要求高,涉及IM消息、业务卡片、各类实时统计。这次演讲,从数据架构层面讲解系统遇到的挑战及解决办法。web

补充说明:本文对应的演讲PPT详见《瓜子IM智能客服系统的数据架构设计(PPT) [附件下载]》。算法

学习交流:数据库

- 即时通信/推送技术开发交流5群: 215477170 [推荐]

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IMwindows

(本文同步发布于:http://www.52im.net/thread-2807-1-1.html后端

二、分享者

封宇:瓜子二手车高级技术专家,中国计算机学会专业会员。2017年2月入职瓜子二手车,主要负责瓜子即时消息解决方案及相关系统研发工做。在瓜子期间,主持自研消息系统用于支持瓜子内效工具呱呱,知足瓜子两万多员工移动办公需求;做为项目经理,负责瓜子服务在线化项目,该项目对瓜子二手车交易模式及流程带来深远影响。缓存

在入职瓜子二手车以前,封宇曾供职于58同城、58到家、华北计算技术研究所,参与到家消息系统、58爬虫系统以及多个国家级军工科研项目的架构及研发工做。在消息系统、后端架构、存储架构等方面有丰富经验。安全

封宇分享的其它IM技术资料:服务器

一套海量在线用户的移动端IM架构设计实践分享(含详细图文)

即时通信安全篇(七):用JWT技术解决IM系统Socket长链接的身份认证痛点

移动端IM中大规模群消息的推送如何保证效率、实时性?

一个低成本确保IM消息时序的方法探讨

从零开始搭建瓜子二手车IM系统(PPT) [附件下载]

瓜子IM智能客服系统的数据架构设计(PPT) [附件下载]

瓜子IM智能客服系统的数据架构设计(整理自现场演讲,有配套PPT)

三、正文概述

今天分享的题目是“瓜子IM智能客服数据架构设计”,这个系统和旺旺比较像。

简单说一下分享的几大部份内容:

第一部分:项目背景,项目背景须要稍微讲一下,有助于你们理解;

第二部分:是系统架构,为何要简单讲一下系统架构呢?由于不讲业务的架构都是耍流氓,因此说咱们讲存储,都要知道系统是怎么回事;

第三部分:重点讲一下存储,在这块咱们讲分享一下咱们的实践经验,以及演进过程,更多的是采到的一些坑,咱们怎么解决的。

四、项目背景

比较熟悉的都知道瓜子二手车没有中间商赚差价,其实瓜子在中间要作不少事情。

首先二手车很难,它不是一个标品,咱们要去收车,收车都是在社会上去收,经过网站收,你要验车。咱们要把它放到咱们的网站上,有些车要收到咱们的场地,有些车多是在用户家里楼下停着。若是一个用户来买车,在网上点了以后,你可能下单要去看这个车,咱们的销售要去跟到线下去陪你看车选车试驾,还有一系列的复检等等,有不少的事情。

如今瓜子这块咱们作的还不够好,为何不够好?咱们站在瓜子内部的角度来看这个事情,有不少的行为发生在线下,咱们很难防就会带来不少问题。好比飞单,有些去跟别的企业串,这个车就没有在平台上卖,这些问题都很难解决。

第二个就是销售到底跟用户在作什么,他没准骂那些用户或者什么的,回头企业发现的投诉很难查。那咱们这个项目的一个重要的问题,就要解决一个线上化,就是把这些线下的行为搬到线上,咱们利用通信,在IM过程中传递一些业务,这样把整个的一些业务线上化固化下来。

第三个是电商化,我比较喜欢用京东,我以为他的物流和客服都很好,瓜子也是但愿作成电商,还有一个就降本增效快一点,若是如今你用瓜子,你会发现你到网上去浏览,就会有电销人员给你打电话,这个是很烦的,对用户的体验很很差,再一个对瓜子来讲成本也很高,咱们也养了不少的电销,因此说咱们就启动了这样一个项目来解决这些问题。

刚才提到了很复杂,整个系统串联了不少角色,有用户、销售、电销、评估师,还有AI和机器人。作系统要善于抽象,咱们先有一个基于通信的即时通信系统。第二咱们是要把通信系统集成到业务,或者叫把这些业务搬到通信系统上面来,核心就是这样子。

这是截了几个图,咱们看第一个图,咱们上边就有一个车,下边有个预定,用户事实上是能够直接在聊天界面里面去点预定了。这个就是我说的跟通常的客服不同的,它能够作一些叫作导购或者叫营销也好,直接经过这样一个途径,由于瓜子的获客成本很高,每一个访问瓜子的用户咱们都但愿及时跟他沟通,这个图有助于你们理解。

五、系统架构

整个系统不是一个单一的系统,它结合了一些业务,咱们能够把它拆分红这么几个层次。最上面是一个端,那端首先就是瓜子的APP,固然还有毛豆的,如今瓜子的业务有好几个,除此以外有些员工用的,好比说电销的、客服的、售后的、金融的、咱们可能都是一些APP或者是一些桌面系统,这是咱们的客户端。

第二是一个路由层,咱们要打通这些业务。要让这些业务在这个系统里边及时的传递,所谓传递刚才前面有个图案,好比说你想约车了,那客服就给你或者电销就给你发一个约车的卡片,你就能够直接选时间约,这是传递业务。再往下边是一些业务层,那就是原来瓜子有不少有什么业务就涉及什么业务,最底下是一个存储。此次后边主要讲的就是站在存储层的角度来看整个系统,重点会讲存储层怎么对路由层进行支持。

六、存储架构

存储这块会讲大概几个点,包括:

1)数据库的拆分;

2)消息怎么存,消息里边也会特别提一下群的模型,大规模的群是比较麻烦的;

3)还有一些存储逻辑以及业务怎么在上边run起来;

4)最后是统计分析,实时计算这样一些设备。

6.1 数据库的拆分

这个图是如今的一个数据库的图,咱们看着这些就是数据库已经分得很好了,好比通信的数据库,有调度的有卡片有分析。

我在这里介绍一下“调度”这个新出现的名词,它是干吗的?就是你在这个系统里边点开一个车也好,点开的人也好,聊天时用户看到的是一个瓜子的客服,后边瓜子内部其实是一个瓜子的客户团队很是大的团队来支持你,因此说到底你跟哪一个客服聊天,是有一些策略的。

咱们感受这样一个拆分其实是瓜熟蒂落的,可是事实上根本就不是。我举个例子,2015年大概是京东内部有一个分享,刘强东分享流露出来了,说有一个二手车企业一年仍是一个月,我忘了,卖了两辆车出去,估值就到了2亿美金。简直不敢相信。

我分享这个例子并非说话有什么不对,固然我相信他也不是说的瓜子,由于瓜子A轮它不止这个数,我想说最初企业是很小的,业务量是很小的,咱们根本就不多是这样一个数据库的结构,就跟沈老师说的同样,其实它就是一个库也没有什么IM,没有什么调度,这些卡片可能就是一个卖车的一个数据库。

我是去年的2月份进入瓜子的,快两年了,那时候瓜子的业务量很是小,具体我也不知道,就是一个数据库,一个数据库实际上是很是好的。由于不少人来了以后就说要拆库,一个数据库的好处是写业务很快,十几我的快速的就把系统就搭起来了。

咱们事实上库拆成这么几个也经历了一个过程,最初是作IM只有一个IM的库,后来有了调度加了一个库,再后来有什么卡片,有分析逐步得往外扩。

这个库它实际上是有必定的成本,若是你拆分得很差,你会去作不少接口,好比说你像关联查一下,发现不是我团队的库也要作各类各样接口,产生了分布式的事物的一致性的问题,都产生了。因此说数据库的拆分,尤为垂直拆分,其实是随着你不一样的阶段,你选择不一样的拆分方式,之后随着系统的扩大瓜子业务扩大这个系统它会拆的更多数据库,但拆的更多,对你的运维监控这些团队的挑战都会带来一些成本,也会带来一些挑战。咱们如今把它拆成了这样一个库,各司其职。

(原图来自《现代IM系统中聊天消息的同步和存储方案探讨》)

6.2 消息怎么存

下面就重点说一下消息,这块咱们怎么存?

咱们看一下左边这个图,左边这个图是通常来讲很容易理解的消息,怎么存的方式?

之前桌面系统常常这么干:好比说A要给B发一个消息,他怎么发?他就说A用户端,A这个端我发一个消息,若是B在线,我就把消息直接发给他,他给我一个确认,这个过程就存储好就结束了。

其实我服务当中不须要存这个消息,若是是A发给一个C这个C不在线怎么办?

咱们也有策略:A把这个消息发给了C,C若是没有确认说我收到这个消息,我就把这个下边的第二步,我就把它存到一个离线的数据库里边等着你C何时上线,你就把这个消息拉回去,这个过程就完结了,这个消息我就给送到了,因此说这个时候的存储很是简单,我就一个离线库,存一下某我的的消息就行了。

可是这种模式实际上是有不少问题的,真正使用的时候,不少产品如今是移动端的手机端,网络首先是不稳定,长期处于一个C的状态,若是你都去监控它的状态,送没送到,再存储性能会不好。

(原图来自《现代IM系统中聊天消息的同步和存储方案探讨》)

第二个如今的端有不少:有桌面的、有手机的、有APP端、还有PAD端,有好几个端。若是都用这种模式,须要为每一个端都这里判断去看传输数据其实也是很困难的。

咱们就变了一个方式:第1步来了消息,咱们就把消息存到存储库,你只要发消息我就先给你存下来,第2步,同时我还存到一个同步库里边。

这两个库要稍微解释一下,存储和同步库分别来作什么?

存储库比较好理解,你何时都能从库里边还原你的消息,把它读回去,好比说你换了手机,你均可以把消息拉回来。

这个同步库是什么意思?同步库就是说你没有换手机,也没有从新装系统,就是你可能有一段时间离线,离线起来以后,就说我好比假设一个消息序列,1到100发给你了,就A发给C,1到100了。结果可是C从第70号消息的时候,他就离线了,他就不在线。这样子,C这个端上线后,第四步把70号消息传给这个服务端,说我有70消息同步库就知道,把70到100的消息发给C。这样子消息就是能够送达这个端,这两个概念稍微是会有一点模糊,可是没有关系,后边我会接着展开来说。

也就说一个消息,咱们会存一个消息同步库和一个存储库,这个其实是一个消息同步库,它是一个模型,我下一张PPT应该会讲用什么东西来存它。

若是咱们把它理解成一个邮件,你很好理解。咱们的邮件,有一个收件箱,同步库就像一个收件箱,无论是谁发给你的邮件,群发地也好,单发的也好,反正我都给你放一份,在收件箱里面放一份,A把这个邮件放进去了消息,你从另外一个要取出来,你无论用这个手机也好用你的苹果系统笔记本或者windows本也好,也都要去收这个邮件,收的过程就是什么?好比说刚才说第一苹果系统笔记本,B1说我以前收了前两封B游标,它本地有一个消息最大的,说我在2号消息,我收到了,那他把2号消息传给服务端,服务端就说好,后边2到20号消息均可以收走了,这样子能够保证这个消息不重不漏地送给客户端去。

B2说我以前这个端其实收了十封了,我就从11分开始收,B3说就收过一封,我就从第二封邮件开始收就行了,这样子就解决了,消息就送过去了。这里有几个问题,咱们同步的过程就是理论上是能够了,可是有几个问题,第一个就是扩散写扩散读,在这块跟存储很相关,扩散写和扩散读有不少讨论。

举个例子:是什么地方产生的?好比我若是是一个单聊,咱们两我的聊天没有问题,我确定把消息都写给你了。可是事实上不少时候咱们是在一个群里边聊天,给咱们销售,咱们的评估师或者机器人,他们都在里边聊,用户也在里边聊,这个消息我是为每人写一份,仍是我为整个会话也就是这个群,我只写只存一份,大家都来读。一个会话的消息,或者说你本身手里的收件箱,我先说结论,咱们是在消息的同步库里边采用的扩散写,后边还有一个存储库,存储库里边咱们是采用的扩散读的方式,在同步库里边,咱们每人都写了一份,这样子读的时候很方便。而在存储的时候,因为咱们的存储速度慢一些,咱们是只写了一份数据,这一个会话只有一条消息。

第二个点是事实上在同步过程当中,咱们遇到了一些问题,有不少的策略须要咱们考虑。就是一个消息TimeLine,有时候不一样的端有不一样的同步策略,好比说有些场景下,咱们要求它的每个端都收到这一条消息,那就是咱们的通知,在公司发的优惠券什么的,每一个端都要送达。有些场景人他是但愿说你的手机收了,那你打开桌面,咱们就再也不给你送这个消息了,按照刚才这一个同步模型,有一些困难的每一个端可能都会去搜这个消息,因此说咱们就准备了三个这样的存储的号。

那好比说这个图上的B1B2B3,当前消息的咱们另外存在一个最大消息的号。第三个号你全部的里边搜的消息最靠前的一块就说比较像B2这样经过这三个位置的组合,咱们能够肯定你收取消息的位置或者一个策略,这是这个模型,咱们存储采用什么?咱们采用了Redis cluster,咱们用了SortedSet结构。

我专门把它提出来了,由于咱们其实在这还踩过一个坑,我要存这个消息了,怎么都得知道这个结构的效率,咱们查了一下SortedSet效率还能够,就说它是一个ln这样子一个效率,因此说在同步库,若是咱们每一个用户只存一部分消息,它的性能是很是高的。这个结构本质上是个跳表,跳表结构其实很复杂,我想在这会上讲清楚很难,最后放了两个图,就是一本书。

咱们知道这些结构,好比像二叉树或者一些红黑树,检索都有比较好的索引策略,跳表也相似。它比较相似什么,就像咱们好比说一本书上有一千页,我想翻到856页怎么翻?其实咱们有一种方法去前面去找索引去定位什么东西,还有我大概翻到800页,逐步修正。跳表结构本质上比较像翻书,我以为是翻到一个大的页,先翻800页,翻到850,在逐渐翻到860页。

下面分享一下这块咱们遇到了一个什么问题!咱们SortedSet存储,存消息,而咱们存消息为了全局一致性,用了一个思路。这个算法咱们消息是一个长整型,就是上班卡,我先讲的是不要紧,先讲下面精度丢失的问题,咱们的消息是一个长整型。这个场景下总共是64位,因此说snowflake这个算法,它的前面第一位不用,它其实表示正整数它是有意义的。用41表示一个时间区间,这里面产生一些ID表明了大概有六七十年或者三四十年,反正是确定是够用了。

中间十位是一个工做机编号,他能够支持1024个台机器,咱们现阶段用不了这么多机器,最后的12位是一个毫秒内的一个序号,因此说构成了咱们消息的ID所以消息是很长的一串,算下来得18位的整数。

放到SortedSet里边以后,咱们后来就发现一些问题,发现这个时间靠的近的消息,咱们区分不出来它的前后顺序。就这深挖下去,发现SortedSet它十个字其实是个double类型的,下边这个图是double类型的描述,它的精度只有52位,上边长整型它是有63位的精度,这里边就有11位的差距。因此说在那个毫时间很接近的消息,它的精度丢失,咱们检索拉取的时候,这些顺序就出了问题。

所以咱们采用了一个策略,也能够借鉴一下,根据咱们的当时的负载量以及机器数,这个最终保证了咱们几乎遇不到这种精度丢失的问题,就把精度主动的转换下降了。这个case上说明就是咱们选择存储的时候,数据类型很重要,你得根据你的业务类型看一下。

咱们还在这同步的时候遇到一些问题,就是这个问题更多出如今咱们内部的一个工具,咱们有不少的人数比较大的群,由于咱们的消息像一个收件箱,他的大小是有限制的,有些大群它疯狂的刷消息,那这样子这个群里边可能就有成百上千上万的消息,由于咱们收件箱大小有限制,咱们就会把以前更早的消息淘汰掉,致使一些单聊比较重要的消息就丢失了,这个是咱们的遇到的问题,后边的PPT会有解决方案。

第二个问题就是还有一些web端,咱们web端,其实本地的缓存是很难用的,这个就是咱们用户一打开以后,它有多少未读数,只能先经过咱们把消息拉回去算一下,新拉到多少消息,才能计算出它有多少未读数。这个实际上对咱们也是一个挑战,很不友好。

6.3 消息的落库存储

接下来咱们讲一下存储这块,咱们存储消息要落库了,咱们怎么存消息?

咱们刚才提到了,是按照每一个会话你看到的每一个人跟你聊天的一个维度来存储。咱们也是采用了分库的策略,分库比较简单。

这举个例子:事实上这个库不止这么多个,咱们把一个他的消息绘画的ID除以四,取它的模来肯定它到底放到哪一个库里边,刚才提到了咱们不少ID是用snowflake算法来生成的,咱们有个方法来防止它的生存不均,看一下。这是一个咱们防止它的ID分布不均的一个方案。咱们看到最后有一个12位的序列号,若是你不加任何干预,他每次都从零开始,事实上当你并发比较小的时候,你会发现它后边都是零,就最后几位都是你这样子,若是都是零,你用是你用固定的取模的算法,他就绝对是不平均了。

更多有关消息ID的算法和策略,能够详细读读如下文章:

美团技术分享:深度解密美团的分布式ID生成算法

融云技术分享:解密融云IM产品的聊天消息ID生成策略

微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)

好比说你的数据库不够了,你到时要扩容的时候你就发现很困难,你不知道之前的数据,你只能把之前的数据所有翻出来,简直是灾难,因此说咱们须要人为的干预,咱们就是用取模的方式,把分库进行特殊的处理,加上一些分库的行为,在这个里边咱们用了一个ID生成的时间,给他一个最后八位的一个遮罩,他在128个数据库的时候,它分布会很平均。

说一下怎么扩容:刚才以前提到的最开始业务量不多,可是前几天瓜子一天已经卖出1万辆的车了,因此说这个量如今咱们是逐步的会在起来,当成交1万辆,用户量是很是大的。咱们怎么扩容,这就是扩容的基本方法。咱们最初有db0123这样几个库,咱们看一下左边有就是这个图的左边,一个msg:chatid=100和msg:chatid=104,之前chatid除以四的时候,100和104这两个数据,这两个数据它都会并重db0这个库。

咱们分库的时候怎么作?第一步咱们把db0123这样的库同时进项,我就要主从同步,反正在搞db4567,db0和db4数据同样的db1和db5数据也同样,相对应的同样。咱们把分库策略改为除以八求余,以后的结果就出现什么?

咱们就发现按照新的分库策略,chatid104还在db0里面,chatid100它因为除了以后他就到db4了,这样子咱们就至关因而从四个库直接就变成了八个库,以后的过程就是分库规则上线以后,咱们再由DBA把不属于库的其余数据给删掉,这个库的扩容就搞定了,因此说业务能够接着跑,可是这样子是否是就解决问题了?

其实没有简单,远远没有,由于你像咱们的数据库前面的数据库是MySQL,为了安全,他有一重两重可能还有扩库,简直这个数据库愈来愈多,它运维和DBA他就不干了,说你这库愈来愈多,我怎么维护,这个受不了了。

因此说还有一个就是这种关系型数据库,它能够支持像事务有好多事务关联性查询这些很是丰富的逻辑,但事实上咱们一个消息都最多的一个数据量的应用,它用不上这么复杂,它是很简单的,我要么根据消息ID查某一条消息,要么根据一个消息要检索这个范围之间的消息,真的用不上这么复杂的一些逻辑,因此说存储用关系型数据库并非说特别适合,那咱们就去研究了。

咱们首先一查发现有一个时序型的数据库是一个OpenTSDB,他的应用场景跟这个业务很类似。OpenTSDB它内部是用Hbase来实现的,咱们以为Hbase就很好。咱们为何选择Hbase?由于有团队维护,很是现实。选用了Hbase以后,这个接下来若是用过Hbase的同窗就知道,除了咱们要分片,这些作好了以后,最关键的是要设计Rowkey设计是须要结合业务,并且须要设计得很是精妙,咱们的Rowkey结果就是一个会话chatid ID,Chatid能够分散region,msgid 时间有序用于范围检索。

而若是咱们用这个你去咨询,你会常常的一个场景,我打开了看到的最新的消息,我往下划一划才是加载更老的消息,这个结构正好一来了以后,检索你最新的消息,你往下滑的时候,咱们就接着去查后边的消息,这样子很是快,而若是当地什么都没有,你从新新装一个很是方便,你直接来Hbase里边查询最新的Rowkey你就找到你最新的消息了,这个就解决了。

还有一点就是region,就像分库同样,Hbase作的比较好,它能够本身帮你维护这个分片,可是咱们不建议这么搞本身维护分片,当你像这种消息的数据它存储量是很小的,它很小会致使默认给你一个region,可是这样一个读写瓶颈就来了,因此说咱们须要提早规划咱们分库的region.

下面是一些群,群有一些特殊的地方,在咱们的二手车APP上,这种大规模的群比较小,可是我想分享的是咱们在内部通信里边群遇到的一些问题也带上来,就一块儿把它跟你们交流一下。

第一个就是刚才提到的减小存储量。这个是下面的存储库,好比有不少群,可能有2000多人有,若是我发一条消息就存2000份,那简直是灾难,因此说咱们只能存一份,所以咱们看这个图就是左边的蓝色以后,咱们只存了一份,标明了这个消息ID,标明了这是哪一个会话或者是哪一个群的。

由于存了一份,第二个问题就带来了:若是今天加群的人,昨天加群的人其实看到的消息应该是不同的。

正常业务是这样,有时候你还能够看到最近多少条的逻辑怎么实现,就是咱们在再给它扩展一个数据库表,这个表是关系型数据库里的,记录上群的号码,记录上这我的的ID,记录上他加群的时间,加群的时间咱们能够经过一个函数把它运算。因此说msg ID的策略很重要,咱们通过加群时间,因为它是一个时间的函数,咱们能够跟这个加群的时间进行一个映射关系,这样子我经过加群时间可以大概定位到他从哪条消息能够检索,若是你须要去作策略,也能够说上面看多少条,下边看多少条均可以作。第三个就是有一个会话排序的问题,这种对话的场景里边,咱们能够看到会有不少的会话,因此说这是一个策略的选择。

第一种作法:你能够为每一个人建一个会话,他每有一条消息,你就把他的最后时间更新一下,这个过程就能知足会话的排序,但事实上咱们能不能这么作?我以为咱们不能这么作,由于有些时候消息不少,并且有些时候用户很大,咱们发一条消息。有一千我的要去更新他的状态,无论你用多少都是扛不住的,因此会话的策略,咱们也是在缓存转存会话的最后一条消息量。当用户要来拉取他的会话列表,或者更新他的会话列表的时候,由服务器端给他预算好了以后返回给他,咱们用的时候正常状况下与客户端它本地是能够收到消息,若是你在线他是本身知道调整这个数据的。拉取会话的行为,当它发生离线了再次打开,这个时候须要更新一下,若是这个频率比较低这样一个取舍,咱们的存储模型也就出来了,因此说其余不少业务都是在发生的时候咱们就跟踪她的状态,而这个会话排序咱们是在好比说读取的时候咱们才能够创建这个过程。

后边的已读未读,这个点再也不细讲,没有什么特征。咱们知道缓存里为每条消息都建了一个存储结构,说这条消息哪些人已读哪些未读,在比较短的时间把它淘汰。消息撤回这块提一下,以前有个小同窗这么干,这个消息怎么撤回?在关系型数据库里边,这个消息要撤回,我在表里边把这条消息标记上,这条消息是撤回来的,这个作法有没有问题?一点都没有问题。

以后他又来了个需求,说我就想看一下这些没有撤回的消息拿出来怎么办?这个同窗也是刚毕业没多久,就调整,就想到了建索引,他就把索引建好了,就能够这么去拉取数据,结果跑一段时间数据库报警了。这不行,怎么回事?由于撤回的消息跟正常没撤回的消息比例是失衡的很是小一间隔索引,因此毫无心义,并且还消耗了写消息的性能,所以咱们撤回消息后来两种作法,第一是把它从这个消息库里边删掉,挪到一个撤回的消息表里边,这是显而易见的。还有一种作法就是咱们也打标记,可是不作索引,我也不支持你过滤接受,而我是无差异的拉出来以后在存储的逻辑层那边把它过滤掉,这样子作。

下边有提到了,咱们讲存储结构不光是一个简单的一个数据库这样一个简单的概念,它其实在db到业务之间还会有一些叫作约定也好,规范也好,或者下降复杂度也好,由于你直接让业务去处理它是很差的,因此咱们有存储的逻辑,这样逻辑层作一些基本的逻辑。

这里跟你们分享一下:瓜子它APP的地位还不够高,我第一次用的时候一点它要登录,所以咱们要作一些匿名的策略,咱们但愿匿名的状态下你已经能创建沟通了,若是你以为能够咱们再接着聊,卖车也好,买车也好,因此说匿名就对咱们这个业务带来一个挑战,匿名的时候,咱们可能给他分了一个ID,他聊着聊着以为能够了,它就登陆了,登陆了以后,他实名的时候,他实名有多是新建立的一个,也可能他以前就登过,可是因为忘了,或者是时间久了过时了,这个时候他在这一次的业务过程中,他就两个ID,若是一直让它成为两个ID其实对后边的电销人员是很郁闷的,说咱们开始跟我聊了一下,过会变了我的实际上是一我的,前面的业务也中断了,因此说咱们对这个消息层面咱们就进行了一个Merge,这个咱们并无说你实名,咱们就把你的数据给搬家,按照这个实名的就是匿名有一个时间序列,实名是否是也有一个,咱们并无这么搞,咱们仍是两个,而是在存储中间的一个层次进行拉取的过程,在须要Merge的时候,咱们在存储逻辑上给他Merge。

可是匿名到实名远没有简单,只是一个延伸,事实上你这个消息里面的匿名很好作,可是你的业务匿名到实名很难,还有咱们常常遇到这个问题,机器人给他发了一个东西,匿名状态,后来他登录了,他一打开,拉回去了以后,这个消息还在他那里,他变成实名了。他进行操做,这个时候业务的匿名到实名实际上是更难的,若是有作这样想法的,提早想好,更多的是业务层面的理论都是。

这个是消息的最后一部分了,实际上还会遇到一些问题,事实上消息同步是很是复杂的一个事情,咱们后来越作越以为它复杂。这个有些人会出现一个什么状况,好比说我用A手机收了几条消息,我在B手机上又收了几条消息,过一段时间我在A手机上又来收几条消息。

你看刚才那种模型就会致使中间出现不少断层,中间出现了不少,就像Client右边这个图,就345的消息他并无收到,可是服务端实际上是全部消息都有的。这时候咱们作了一些策略,咱们为每一个消息严格的编号,msg index:1,2,3,每一个消息严格的编号。若是是这样子,客户端知道了以后,他就知道我原来少了345这三个号对不对?我就能够到服务端去说,我缺345这几个消息,你给我解索出来。

有没有这么容易?客户端可能以为这个是很容易的,可是到了服务端事情不是这么回事,345是你本身编的一个号,而咱们的消息以前说了snowflake这样一个惟一的编号,那你拿着345并不能找到你究竟是哪一个消息ID,因此说我是否是服务端要用哪一个消息创建这么一个索引,仍是应该是一个编号到msg ID索引?

能够作,可是存储量工做量很是的大,那咱们怎么干?

咱们在逻辑层里边作了一些事情,服务端每次返回客户端的消息,咱们把这个消息把它作成一个链表的结构,当你来拉取,由于是反向消息好可能是吧?拉去2号消息的时候,我就说,这个我告诉你,你的下一条消息是msg7,你拉取,也能够一段一段拉取不要紧。你拉到msg6的时候,我告诉你说你的消息msg5,我客户端说原来少这个消息5,这样子客户端能够经过这个消息ID到服务端来检索消息,因为是消息ID无论咱们是OK也好,或者咱们的关系型数据库是基于索引也好,都很容易作,现成的,也不须要再维护其余的索引关系。因此说这也是一个策略的点。这种断层咱们就解决了。

可是还有问题,有时候消息很是多,若是你一次都把这些就是咱们刚才说的同步库的消息收过去,过程实际上是很慢的,尤为在深度用户的时候这个方案很差。

有一种作法:你把这个消息压缩一下送过去,简直传递。可是其实仍是很差,客户端要渲染,要计算数量很慢,这就是刚才提到的扩散读和扩散写的问题,最先有一个,因此说后来更好的办法是说同步库里边也并非去同步的具体的消息,你能够去作这个用户有哪些变动的会话,这么一个会话,它有多少未接收的数据,记录好这个数字有多少未读。这样客户端能够把这些数据拉到本地,你看到了有多少未读的会话以后,你点进去的时候,你再照这个存储库里边反向的经过这个来拉取你的消息,再加上咱们刚才说的中间空荡的一个补齐的策略,一个列表补齐的策略,这样的体验很是好。

因此说咱们就解决了咱们这个项目,咱们就解决了消息的问题。后边咱们看一下消息解决了尚未完,咱们要推广这个项目,咱们要落地,须要作业务,由于只是传一个消息没有意义,对观众来讲咱们就在作业务了,咱们承载业务的就是叫咱们的业务卡片,最初那个图里边咱们看到的那些传过去能够直接操做的这个东西,应该咱们还申请了专利,当时去查了一下没人这么搞的,可是因为没人这么搞,其实咱们在实施过程当中遇到一些问题,下面咱们看一下这个图。

这个图右边有一个卡片的代理,右图有个绿色的卡片代理是咱们对这个业务设置的一个特殊的东西,咱们在推广的时候遇到不少问题,咱们这些业务原有的业务部门,他们都作得有接口是现成的,因为你把它搬到了IM交互里面有几种方法:第一大家所有给我改一下,这个是很困难,有些业务说他不肯意,咱们就设计了这么一个代理的产出的卡片的全部响应试点,先打到咱们的一个代理模块,代理模块再去适配你原有的业务逻辑,这样子代理模块,知道用户操做行为究竟是什么样,成没成功,成功了或者没成功,他再经过调度,经过他们的通道通知相关业务的各方结果,这是一种策略。

同时它要高可靠,好比说咱们预定看车就至关于下班了,就至关于这个是很重要的业务。你这个不行,链条太长了,风险过高,宁肯咱们加点东西均可以,那就是左边这个逻辑,这业务服务愿意说我本身改一下,可控性我得本身把控,不能说由于通信有问题,个人业务就不跑了。那就是他改一下,来触发调度的一些逻辑。这个是咱们在整个推广的过程中最重要的一个策略,实际上也是探索出来的,由于不光是一个技术问题,仍是个组织结构问题。

简单提一下调度问题,由于不是重点,你怎么知道究竟是哪一个客户来服务你?咱们提出了一个场景的概念,就是你每次从各类入口进到对话界面的时候,这些入口咱们是有状态的。A入口B入口,好比你约车仍是砍价什么之类的,咱们它先把这个场景到调度去注册一下,说我从这儿进来,同时调度会有一些大数据来支撑,原来你是谁谁,你就从这个场景进来,咱们认为你多是要干什么事情。这样给他返回场景里,他再次跟通道间发生关系的时候,带上这个场景,咱们这个调度就知道把你推给具体的谁,是销售也好,机器人也好,是咱们具体的解决投诉的客服或者解决什么电销的客服了。

6.4 分析统计

接下来再提一下分析统计,像瓜子的规模已经比较大了,咱们如今有超过一千个研发团队,可是在大数据这块投入也比较多,可是事实上如今公司都是数据驱动化,这个团队的力量依然是颇有限的,它支持各类各样的业务线,很是吃力的仍是很忙,因此咱们在分析统计有两块,第一块是T+1的分析是离线的,还有一块是实时的一个分析。

咱们这个项目于对实时统计的要求很高,好比及时回复率这些各类各样的统计要求实时的监控报警,怎么作呢?这是咱们整个系统另外一个角度的一个架构,和咱们一些跟消息相关的或跟一些业务调度相关的,咱们都走了一个kafka,就是通道的以及送给客服的一些销售评估师等等,他们业务线的这些逻辑,咱们经过kafka传递一些比较多的数据。咱们在想能不能用借用kafka来简单的实现技术,事实上是能够,这概念是一个流式数据库,咱们最初的结构就是图左边这一块,整个系统中间走的消息都经过了一个kafka.

咱们能够保证业务在上面跑,其次kafka它缓存的这段数据,咱们是能够对他进行流式计算,咱们总体的架构是上图这样。

七、最后

我简单重复一下咱们的过程:

第一:咱们这个系统经过数据层面展现了一个通信,就是即时通信的这样一个系统,大概是怎么作的,数据库怎么存的;

第二:是把咱们通信的能力应用到业务系统,咱们解决了技术上或者组织上遇到了一些什么困难;

第三:是咱们找一个比较简单的方法,处理咱们的一些离线计算,固然他作T+1也是能够的。

谢谢你们。

附录:更多有关IM架构设计的文章

浅谈IM系统的架构设计

简述移动端IM开发的那些坑:架构设计、通讯协议和客户端

一套海量在线用户的移动端IM架构设计实践分享(含详细图文)

一套原创分布式即时通信(IM)系统理论架构方案

从零到卓越:京东客服即时通信系统的技术架构演进历程

蘑菇街即时通信/IM服务器开发之架构选择

腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT

微信后台基于时间序的海量数据冷热分级架构设计实践

微信技术总监谈架构:微信之道——大道至简(演讲全文)

如何解读《微信技术总监谈架构:微信之道——大道至简》

快速裂变:见证微信强大后台架构从0到1的演进历程(一)

17年的实践:腾讯海量产品的技术方法论

移动端IM中大规模群消息的推送如何保证效率、实时性?

现代IM系统中聊天消息的同步和存储方案探讨

IM开发基础知识补课(二):如何设计大量图片文件的服务端存储架构?

IM开发基础知识补课(三):快速理解服务端数据库读写分离原理及实践建议

IM开发基础知识补课(四):正确理解HTTP短链接中的Cookie、Session和Token

WhatsApp技术实践分享:32人工程团队创造的技术神话

微信朋友圈千亿访问量背后的技术挑战和实践总结

王者荣耀2亿用户量的背后:产品定位、技术架构、网络方案等

IM系统的MQ消息中间件选型:Kafka仍是RabbitMQ?

腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面

以微博类应用场景为例,总结海量社交系统的架构设计步骤

快速理解高性能HTTP服务端的负载均衡技术原理

子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践

知乎技术分享:从单机到2000万QPS并发的Redis高性能缓存实践之路

IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列

微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)

新手入门:零基础理解大型分布式架构的演进历史、技术原理、最佳实践

一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践

阿里技术分享:深度揭秘阿里数据库技术方案的10年变迁史

阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路

社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等

社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进

社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节

社交软件红包技术解密(四):微信红包系统是如何应对高并发的

社交软件红包技术解密(五):微信红包系统是如何实现高可用性的

社交软件红包技术解密(六):微信红包系统的存储层架构演进实践

社交软件红包技术解密(七):支付宝红包的海量高并发技术实践

社交软件红包技术解密(八):全面解密微博红包技术方案

社交软件红包技术解密(九):谈谈手Q红包的功能逻辑、容灾、运维、架构等

即时通信新手入门:一文读懂什么是Nginx?它可否实现IM的负载均衡?

即时通信新手入门:快速理解RPC技术——基本概念、原理和用途

多维度对比5款主流分布式MQ消息队列,妈妈不再担忧个人技术选型了

从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

IM开发基础知识补课(六):数据库用NoSQL仍是SQL?读这篇就够了!

瓜子IM智能客服系统的数据架构设计(整理自现场演讲,有配套PPT)

>> 更多同类文章 ……

(本文同步发布于:http://www.52im.net/thread-2807-1-1.html

相关文章
相关标签/搜索