职贝云数AI新零售门户
标题:
企业微信组织架构同步优化的思绪与实操演练
[打印本页]
作者:
rC9s7r2
时间:
2023-1-28 16:55
标题:
企业微信组织架构同步优化的思绪与实操演练
(, 下载次数: 5)
上传
点击文件名下载附件
作者|胡腾 编辑|小智 作为企业级的微信,在业务疾速发展的背景下,迭代优化的要求也越发急迫。企业微信初版的全量同步方案在疾速的业务增长面前曾经捉襟见肘,针对其遇到的成绩,怎样做好组织架构同步优化?这是又一篇来自微信团队的技术实战。 写在后面
企业微信在疾速发展过程中,陆续有大企业加入运用,企业微信初版采用全量同步方案,该方案在大企业下存在流量和功能两方面的成绩,每次同步耗费大量流量,且在 iPhone 5s 上拉取 10w+ 成员架构包解压时会提示 memory warning 而运用崩溃。
全量同步方案难以支撑业务的疾速发展,优化同步方案越来越有必要。本文针对全量同步方案遇到的成绩停止分析,提出组织架构增量同步方案,并对移动端完成增量同步方案的思绪和重难点停止了讲解。
企业微信业务背景
在企业微信中,组织架构是非常重要的模块,用户可以在首页的 tab 上选择"通讯录"查看到本公司的组织架构,并且可以经过"通讯录"找到本公司的一切成员,并与其发起会话或者视频语音通话。
组织架构是非常重要且敏感的信息,企业微信作为企业级产品,一直把用户隐私和安全放在重要地位。针对组织架构信息,企业管理员具有高粒度隐私保护操作权限,不只支持个人信息隐藏,也支持通讯录查看权限等操作。
(, 下载次数: 4)
上传
点击文件名下载附件
在企业微信中,组织架构特征有:
1、多叉树结构。叶子节点代表成员,非叶子节点代表部门。部门最多只要一个父部门,但成员可属于多个部门。
(, 下载次数: 4)
上传
点击文件名下载附件
2、架构隐藏操作。企业管理员可以在管理后台设置白名单和黑名单,白名单可以查看残缺的组织架构,其他成员在组织架构里看不到他们。黑名单的成员只能看到本人所在小组和其一切的父部门,其他人可以看到黑名单的成员。
3、组织架构操作。企业管理员可以在 web 端和 app 端添加 / 删除部门,添加 / 删除 / 移动 / 编辑成员等操作,并且操作结果会及时同步给本公司一切成员。
全量同步方案的成绩
本节大致讲解下全量同步方案完成以及遇到的成绩。
全量同步方案原理
企业微信在 1.0 时代,从波动性以及疾速迭代的角度思索,延用了企业邮通讯录同步方案,采取了全量架构同步方案。
核心思想为服务端下发全量节点,客户端对比本地数据找出变更节点。此处节点可以是用户,也可以是部门,将组织架构视为二叉树结构体,其下的用户与部门均为节点,若同一个用户存在多个部门下,被视为多个节点。
全量同步方案分为初次同步与非初次同步:
初次同步服务端会下发全量的节点信息的紧缩包,客户端解压后得到全量的架构树并展现。
非初次同步分为两步:
服务端下发全量节点的 hash 值。客户端对比本地数据找到删除的节点保存在内存中,对比找到新增的节点待央求详细信息。
客户端央求新增节点的详细信息。央求详细信息成功后,再停止本地数据库的插入 / 更新 / 删除处理,保证同步流程的原子性。
用户反馈
初版上线后,收到了大量的组织架构相关的 bug 赞扬,次要集中在:
流量耗费过大。
客户端架构与 web 端架构不分歧。
组织架构同步不及时。
这些成绩在大企业下更分明。
(, 下载次数: 4)
上传
点击文件名下载附件
成绩分析
深究全量同步方案难以支撑大企业同步的背后缘由,皆是由于采取了服务端全量下发 hash 值方案的缘由,方案存在以下成绩:
拉取大量冗余信息。即便只要一个成员信息的变化,服务端也会下发全量的 hash 节点。针对几十万人的大企业,这样的流量耗费是相当大的,因此在大企业要尽能够的减少更新的频率,但是却会导致架构数据更新不及时。
大企业拉取信息容易失败。全量同步方案中初次同步架构会一次性拉取全量架构树的紧缩包,而超大企业这个包的数据有几十兆,解压后几百兆,对内存不足的低端设备,初次加载架构能够会出现内存不足而 crash。非初次同步在对比出新增的节点,央求详细信息时,能够遇到数据量过大而央求超时的状况。
客户端无法过滤有效数据。客户端不了解 hash 值的详细含义,导致在本地对比 hash 值时不能过滤掉有效 hash 的状况,能够出现组织架构展现错误。
优化组织架构同步方案越来越有必要。
寻觅优化思绪
寻求同步方案优化点,我们要找准原来方案的痛点以及不合理的地方,经过方案的调整来避免这个成绩。
组织架构同步难点
准确且耗费最少资源同步组织架构是一件很困难的事情,难点次要在:
组织架构架构数据量大。音讯 / 联络人同步一次的数据量普通状况不会过百,而企业微信活跃企业中有许多上万甚至几十万节点的企业,意味着架构一次同步的数据量很轻松就会上千上万。移动端的流量耗费是用户非常在乎的,且内存有限,减少流量的耗费以及减少内存运用并保证架构树的残缺同步是企业微信追求的目的。
架构规则复杂。组织架构必须同步到残缺的架构树才能展现,而且企业微信里的触及到复杂的隐藏规则,为了安全思索,客户端不应该拿到隐藏的成员。
修正频繁且改动大。组织架构的调整存在着新建部门且移动若干成员到新部门的状况,也存在解散某个部门的状况。而员工离任也会经过组织架构同步上去,意味着超大型企业基本上每天都会有改动。
技术选型-提出增量更新方案
上述提到的成绩,在大型企业下会变得更分明。在几轮方案讨论后,我们给原来的方案添加了两个特性来完成增量更新:
增量。服务端记录组织架构修正的历史,客户端经过版本号来增量同步架构。
分片。同步组织架构的接口支持传阈值来分片拉取。
在新方案中,服务端针对某个节点的存储结构可简化为:
(, 下载次数: 4)
上传
点击文件名下载附件
vid 是指节点用户的独一标识 id,departmentid 是指节点的部门 id,is_delete 表示该节点能否已被删除。
若节点被删除了,服务端不会真正的删除该节点,而将 is_delete 标为 true。
若节点被更新了,服务端会增大记录的 seq,下次客户端来停止同步便能同步到。
其中,seq 是自增的值,可以了解成版本号。每次组织架构的节点有更新,服务端添加相应节点的 seq 值。客户端经过一个旧的 seq 向服务器央求,服务端前往这个 seq 和 最新的 seq 之间一切的变更给客户端,完成增量更新。
图示为:
(, 下载次数: 3)
上传
点击文件名下载附件
经过提出增量同步方案,我们从技术选型层面处理了成绩,但是在实践操作中会遇到许多成绩,下文中我们将针对方案原理以及实践操作中遇到的成绩停止讲解。
增量同步方案
本节次要讲解客户端中增量同步架构方案的原理与完成,以及基础概念讲解。
增量同步方案原理
企业微信中,增量同步方案核心思想为:
服务端下发增量节点,且支持传阈值来分片拉取增量节点,若服务端计算不出客户端的差量,下发全量节点由客户端来对比差异。
增量同步方案可笼统为四步完成:
客户端传入本地版本号,拉取变更节点。
客户端找到变更节点并拉取节点的详细信息。
客户端处理数据并存储版本号。
判别残缺架构同步能否完成,若尚未完成,反复步骤 1,若完成了残缺组织架构同步,肃清掉本地的同步形态。
忽略掉各种边界条件和异常状况,增量同步方案的流程图可以笼统为:
(, 下载次数: 4)
上传
点击文件名下载附件
接上去我们再看看增量同步方案中的关键概念以及残缺流程是怎样的。
版本号
同步的版本号是由多个版本号拼接成的字符串,版本号的详细含义对客户端透明,但是对服务端非常重要。
版本号的组成部分为:
(, 下载次数: 4)
上传
点击文件名下载附件
版本号回退
增量同步在实践操作过程中会遇到一些成绩:
服务端不能够永世存储删除的记录,删除的记录对服务端是毫有意义的而且永世存储会占用大量的硬盘空间。而且有效数据过多也会影响架构读取速度。当 is_delete 节点的数目超过一定的阈值后,服务端会物理删除掉一切的 is_delete 为 true 的节点。此时客户端会重新拉取全量的数据停止本地对比。
一旦架构隐藏规则变化后,服务端很难计算出增量节点,此时会下发全量节点由客户端对比出差异。
理想状况下,若服务端下发全量节点,客户端铲掉旧数据,并且去拉全量节点的信息,并且用新数据覆盖即可。但是移动端这样做会耗费大量的用户流量,这样的做法是不可接受的。所以若服务端下发全量节点,客户端需求本地对比出增删改节点,再去拉变更节点的详细信息。
增量同步状况下,若服务端下发全量节点,我们在本文中称这种状况为版本号回退,效果相似于客户端用空版本号去同步架构。从统计结果来看,线上版本的同步中有 4% 的状况会出现版本号回退。
阈值分片拉取
若客户端的传的 seq 过旧,增量数据能够很大。此时若一次性前往全部的更新数据,客户端央求的数据量会很大,工夫会很长,成功率很低。针对这种场景,客户端和服务端需求商定阈值,若央求的更新数据总数超过这个阈值,服务端每次最多前往不超过该阈值的数据。若客户端发现服务端前往的数据数量等于阈值,则再次到服务端央求数据,直到服务端下发的数据数量小于阈值。
节点结构体优化
在全量同步方案中,节点经过 hash 独一标示。服务端下发的全量 hash 列表,客户端对比本地存储的全量 hash 列表,若有新的 hash 值则央求节点详细信息,若有删除的 hash 值则客户端删除掉该节点信息。
在全量同步方案中,客户端并不能了解 hash 值的详细含义,并且能够遇到 hash 碰撞这种极端状况导致客户端无法正确处理下发的 hash 列表。
而增量同步方案中,运用 protobuf 结构体代替 hash 值,增量更新中节点的 proto 定义为:
(, 下载次数: 4)
上传
点击文件名下载附件
在增量同步方案中,用 vid 和 partyid 来独一标识节点,完全废弃了 hash 值。这样在增量同步的时分,客户端完全了解了节点的详细含义,而且也从方案上避免了曾经在全量同步方案遇到的 hash 值反复的异常状况。
并且在节点结构体里带上了 seq 。节点上的 seq 来表示该节点的版本,每次节点的详细信息有更新,服务端会提高节点的 seq,客户端发现服务端下发的节点 seq 比客户端本地的 seq 大,则需求去央求节点的详细信息,避免有效的节点信息央求。
判别残缺架构同步完成
由于 svr 接口支持传阈值批量拉取变更节点,一次网络操作并不意味着架构同步曾经完成。那么怎样判别架构同步完成了呢?这里客户端和服务端商定的方案是:
若服务端下发的(新增节点+删除节点)小于客户端传的阈值,则以为架构同步结束。
当残缺架构同步完成后,客户端需求肃清掉缓存,并停止一些额外的业务工作,譬如计算部门人数,计算成员搜索热度等。
增量同步方案 - 残缺流程图
思索到各种边界条件和异常状况,增量同步方案的残缺流程图为:
(, 下载次数: 4)
上传
点击文件名下载附件
增量同步方案难点
在加入增量和分片特性后,针对几十万人的超大企业,在版本号回退的场景,怎样保证架构同步的残缺性和方案选择成为了难点。
前文提到,隐藏规则变更以及后台物理删除有效节点后,客户端若用很旧的版本同步,服务端算不出增量节点,此时服务端会下发全量节点,客户端需求本地对比一切数据找出变更节点,该场景可以了解为版本号回退。在这种场景下,对于几十万节点的超大型企业,若服务端下发的增量节点过多,客户端央求的工夫会很长,成功率会很低,因此需求分片拉取增量节点。而且拉取上去的全量节点,客户端处理不能央求全量节点的详细信息覆盖旧数据,这样的话每次版本号回退的场景流量耗费过大。
因此,针对几十万节点的超大型企业的增量同步,客户端难点在于:
断点续传。增量同步过程中,若客户端遇到网络成绩或运用中止了,在下次网络或运用恢复时,可以接着上次同步的进度继续同步。
同步过程中不影响正常展现。超大型企业同步的耗时能够较长,同步的时分不应影响正常的组织架构展现。
控制同步耗时。超大型企业版本号回退的场景同步非常耗时,但是我们需求想办法加快处理速度,减少同步的耗费工夫。
思绪
架构同步末尾,将架构树缓存在内存中,加快处理速度。
若服务端端下发了需求版本号回退的 flag,本地将 db 中的节点信息做一次备份操作。
将服务端端下发的一切 update 节点,在架构树中查询,若找到了,则将备份数据转为正式数据。若找不到,则为新增节点,需求拉取详细信息并保存在架构树中。
当残缺架构同步结束后,在 db 中找到并删除掉一切备份节点,肃清掉缓存和同步形态。
若服务端下发了全量节点,客户端的处理时序图为:
(, 下载次数: 3)
上传
点击文件名下载附件
服务端下发版本号回退标记
从时序图中可以看出,服务端下发的版本号回退标记是很重要的信号。
而版本号回退这个标记,仅仅在同步的初次会随着新的版本号而下发。在残缺架构同步时期,客户端需求将该标记缓存,并且跟着版本号一同存在数据库中。在残缺架构同步结束后,需求根据能否版本号回退来决议删除掉数据库中的待删除节点。
备份架构树方案
架构树备份最直接的方案是将 db 中数据 copy 一份,并存在新表里。假如在数据量很小的状况下,这样做是完全没有成绩的,但是架构树的节点往往很多,采取这样简单粗暴的方案在移动端是完全不可取的,在几十万人的企业里,这样做会形成极大的功能成绩。
经过思索后,企业微信采取的方案是:
若同步架构时,后台下发了需求版本号回退的 flag,客户端将缓存和 db 中的一切节点标为待删除(时序图中 8,9 步)。
针对服务端下发的更新节点,在架构树中肃清掉节点的待删除标记(时序图中 10,11 步)。
在残缺架构同步结束后,在 db 中找到并删除掉一切标为待删除的节点(时序图中 13 步),并且肃清掉一切缓存数据。
而且,在增量同步过程中,不应该影响正常的架构树展现。所以在架构同步过程中,若有下层来央求 db 中的数据,则需求过滤掉有待删除标记的节点。
缓存架构树
方案决议客户端避免不了全量节点对比,将重要的信息缓存到内存中会大大加快处理速度。内存中的架构树节点体定义为:
此处我们用 std::map 来缓存架构树,用 std::pair 作为 key。我们在比较节点的时分,会触及到很多查询操作,运用 map 查询的工夫复杂度仅为 O(logn)。
增量同步方案关键点
本节单独将优化同步方案中关键点拿出来写,这些关键点不只仅适用于本文架构同步,也适用于大多数同步逻辑。
保证数据处理完成后,再储存版本号
在几乎一切的同步中,版本号都是重中之重,一旦版本号乱掉,后果非常严重。
在架构同步中,最最重要的一点是:
保证数据处理完成后,再储存版本号。
在组织架构同步的场景下,为什么不能先存版本号,再存数据呢?
这触及到组织架构同步数据的一个重要特征:架构节点数据是可反复拉取并覆盖的。
思索下实践操作中遇到的真实场景:
若客户端曾经向服务端央求了新增节点信息,客户端此时刚刚插入了新增节点,还未储存版本号,客户端运用中止了。
此时客户端重新启动,又会用相反版本号拉下刚刚曾经处理过的节点,而这些节点跟本地数据对比后,会发现节点的 seq 并未更新而不会再去拉节点信息,也不会形成节点反复。
若一旦先存版本号再存详细数据,一定会有概率丢失架构更新数据。
同步的原子性
正常状况下,一次同步的逻辑可以简化为:
(, 下载次数: 4)
上传
点击文件名下载附件
在企业微信的组织架构同步中存在异步操作,若停止同步的过程不保证原子性,极大能够出现下图所示的状况:
(, 下载次数: 4)
上传
点击文件名下载附件
该图中,同步的途中插入了另外一次同步,很容易形成成绩:
输入结果不波动。若两次同步几乎同时末尾,但由于存在网络波动等状况,前往结果能够不同,给调试形成极大的困扰。
中间形态错乱。若同步中处理服务端前往的结果会依赖于央求同步时的某个中间形态,而新的同步发起时又会重置这个形态,很能够会惹起匪夷所思的异常。
时序错乱。整个同步流程应该是原子的,若中间插入了其他同步的流程会形成整个同步流程时序混乱,引发异常。
怎样保证同步的原子性呢?
我们可以在末尾同步的时分记一个 flag 表示正在同步,在结束同步时,肃清掉该 flag。若另外一次同步到来时,发现正在同步,则可以直接舍弃掉本次同步,或者等本次同步成功后再停止一次同步。
此外也可将同步串行化,保证同步的时序,多次同步的时序应该是 FIFO 的。
缓存数据分歧性
移动端同步过程中的缓存多分为两种:
内存缓存。加入内存缓存的目的是减少文件 IO 操作,加快程序处理速度。
磁盘缓存。加入磁盘缓存是为了防止程序中止时丢失掉同步形态。
内存缓存多缓存同步时的数据以及同步的中间形态,磁盘缓存用于缓存同步的中间形态防止缓存形态丢失。
在整个同步过程中,我们都必须保证缓存中的数据和数据库的数据的更改需求逐一对应。在增量同步的状况中,我们每次需求更新 / 删除数据库中的节点,都需求更新相应的缓存信息,来保证数据的分歧性。
优化数据对比 内存运用
测试方法:运用工具 Instrument,用同一账号监控全量同步和增量同步分别在初次加载架构时的 App 内存峰值。
内存峰值测试结果
(, 下载次数: 4)
上传
点击文件名下载附件
分析
随着架构的节点增多,全量同步方案的内存峰值会不断攀升,在极限状况下,会出现内存不足运用程序 crash 的状况(实践测试中,30w 节点下,iPhone 6 全量同步方案必 crash)。而增量同步方案中,总节点的多少并不会影响内存峰值,仅仅会添加同步分片的次数。
优化后,在腾讯域下,增量同步方案的 App 总内存运用仅为全量同步方案的 53.1%,且企业越大优化效果越分明。并且不论架构的总节点数有多少,增量同步方案都能将残缺架构同步上去,达到了预期的效果。
流量运用
测试方法:在管理端对成员做添加操作五次,经过日志分析客户端耗费流量,取其平均值。日志会打印出央求的 header 和 body 大小并估算出流量运用值。
测试结果
(, 下载次数: 3)
上传
点击文件名下载附件
分析
添加成员操作,针对增量同步方案仅仅会新拉单个成员的信息,所以无论架构里有多少人,流量耗费都是相近的。异样的操作针对全量同步方案,每次央求变更,服务端都会下发全量 hash 列表,企业越大耗费的流量越多。可以看到,当企业的节点数达到 20w 级别时,全量同步方案的流量耗费是增量同步方案的近 500 倍。
优化后,在腾讯域下,每次增量同步流量耗费仅为全量同步方案的 0.4%,且企业越大优化效果越分明。
写在最后
增量同步方案从方案上避免了架构同步不及时以及流量耗费过大的成绩。经过用户反馈和数据分析,增量架构同步上线后运转波动,达到了理想的优化效果。
作者引见
胡腾,腾讯工程师,参与企业微信从无到有的整个过程,目前次要担任企业微信移动端组织架构和外部联络人等模块的开发工作。
昔日荐文
点击下方图片即可阅读
(, 下载次数: 4)
上传
点击文件名下载附件
技术漫谈:为何 KPI 毁了索尼,而 OKR 却成就了谷歌?
欢迎光临 职贝云数AI新零售门户 (https://www.taojin168.com/cloud/)
Powered by Discuz! X3.5