GeekOnline

GeekOnline

【有奖调研】Geek Online 2020 编程挑战赛参赛调研

技术交流放肆小青年 回复了问题 • 28 人关注 • 27 个回复 • 551 次浏览 • 2020-10-26 09:33 • 来自相关话题

如何做一个懂产品的程序员?

技术交流王叫兽 发表了文章 • 0 个评论 • 94 次浏览 • 2020-09-16 10:32 • 来自相关话题

两个相爱相杀的岗位,想要更好的达成共识、更好的合作,自然不仅仅是一方的事情。这次Z哥先会带你看看产品经理眼中的程序员是什么样子。然后给出一些我的建议。直接进入正题吧。从产品视角是怎么看程序员的呢?我根据我自己的经历以及与其他产品经理的交流下来看,吐槽的主要是以... ...查看全部

timg.jpg

两个相爱相杀的岗位,想要更好的达成共识、更好的合作,自然不仅仅是一方的事情。这次Z哥先会带你看看产品经理眼中的程序员是什么样子。然后给出一些我的建议。

直接进入正题吧。

从产品视角是怎么看程序员的呢?我根据我自己的经历以及与其他产品经理的交流下来看,吐槽的主要是以下几点:

  1. 这个功能实现不了。

  2. 希望所有产品都不要改版,一次性把现在或未来要做的开发完。

  3. 只关心要写多少代码,不在乎产品体验。

  4. 写完程序从不自测,直接丢给别人测试。

  5. 过分追寻新技术潮流,完全不考虑对产品带来什么价值。

第一点,的确存在一些由于技术限制导致实现成本无限大的需求,比如手机屏幕背景色根据手机壳颜色切换……

但是,国内的技术环境不像老美那的技术味道重,大多还是商业导向的,很少企业里需要用到高精尖的技术,所以,真正实现不了的功能微乎其微。

对于大多数的功能需求来说,无非是一个成本大小、价值高低的问题。从立场上看,程序员自然是站在「成本」一方的,但对大多数人来说,决定这个成本的主要因素往往是自己工作的难度和耗时,费时费力的功能就容易得到“实现不了”的结果。

第二点对大多数产品经理来说是他们的对立面。因为大多数产品经理最喜欢“走一步算一步”地高频迭代,甚至是有一个想法就开始干。而程序员则喜欢来一个大而全的,并且内容要非常详细的,心里的想法是,这样的话我一开始就可以设计一个完美架构来支撑它。

而且,内容越详细,产品经理就越不敢乱调整需求,毕竟“证据在手”嘛:D。

第三点在大多数程序员身上都能看到。毕竟做程序员的还是理科男偏多,对需要有同理心、需要靠感受的事情的确弱了一些。

第四点的原因主要有两个。

一个是对自己的代码过度自信导致,我自己深有体会。我还记得有一次我交付一个功能,那个功能我单元测试都写了不少,对质量很有信心,觉得就算有bug也都是比较深层的bug。结果没想到……第一天就测出来好几个低级的bug。

另一个原因是反正有测试人员在,等他们测出问题我再改不是更轻松。惰性使然,从个人角度的确如此。但是从团队角度来看,徒增了不少的沟通成本。

第五点的原因也有两个。

一个是行业里的新技术迭代的确太快,怕不学新技术被淘汰。

另一个原因是,只有用上新技术才能有谈资,显得自己与众不同、有成就感。

以上就是对这五点的简单分析,那么如何改善呢?继续往下看。

下面这些方法都是我亲测有效的,强烈推荐你也试试。这里的序号与前面被吐槽的五点一一对应。

01  说实现不了之前,先三思

根据先后可以做以下3个思考:

是觉得这个功能没有价值不想做吗?

真的实现不了?我想全了吗?

这些方案里,有成本比价值低的吗?

第一个问题先确定必要性。我们不是说不能推需求,而是要推掉低价值、无价值的需求。当然有没有价值不一定你说了算,但至少这才能算是拒绝的理由。

第二个问题,努力拓宽自己的边界、舒适区。如果我们总是习惯性地从大脑的记忆中找解决方案,那么将会永远在舒适区止步不前。

第三个问题,拒绝需求虽然不用动之以情,但一定需要晓之以理。当你能清楚的阐述利弊、收益比,拒绝需求自然不是一件需要相互扯皮的事情。

经过了这三个问题的思考,不管最终能不能实现,相信可以很好的与产品经理达成共识。

02  明白需求本身也是成本

过度地苛求需求要细、要完整、要全面,这个本身也是在增加产品经理需要投入的时间。你的开发成本是成本,产品经理的也是。

与其等一个“XXXX最终绝对不改版”,不如从已经达成共识的部分开工,在这个过程中再与产品经理「共创」,多一起沟通打磨,此时再让产品完善PRD等文档,形成最终版。

03  刻意练习,多换位到用户视角

平时多去体验一下自家的产品以及竞品,把整个过程中的感受记录下来。比如,哪里感到不太顺手、哪里感受到了小惊喜、哪里感到特别烦人等等。结果不重要,重要的是这个过程,慢慢锻炼自己作为用户的感知力。

有些程序员看起来经常把用户体验挂在嘴上,其实提出来的很多反而是脱离大众习惯的“个性化”需求,就是因为平时缺少对同行、外界的关注。

04  交付的东西是自己的「招牌」

“有人的地方就有江湖,有江湖的地方就有称号”。如果长期报以等测出来bug再去修的心态,你在别人心中的称号就是负面的。

轻则影响自己的口碑,影响与他人之间的协作关系;重则失去未来的晋升机会。一个对自己的东西都不负责的人,如何负责更多的人、更大的事情呢?

在这件事上,除了多自测外,作为过来人,我建议每一个程序员认真对待单元测试。特别把核心、复杂的方法单元测试给做上,这对交付功能的质量的提升非常明显。

05  不产生价值的新技术是“垃圾”

拥抱新技术是值得鼓励的。但是单纯的为了体验某新技术而去使用它,这不但给团队在挖坑,也在给自己挖坑。

比如你花了不少的时间在项目里用了某个新技术,但是对团队没有带来什么价值,你说后续公司还会继续投入资源加大新技术的使用吗?大概率并不会。那么之前了解到的一些知识,就会随着时间的推移而淡忘,投入的时间大多数浪费掉了。

所以,对待新技术Z哥的观点是。对于无法在工作中找到价值点的新技术浅尝辄止即可。相反,遇到可以产生价值的新技术,请全身心投入进去,而不是仅仅在应用层面捣腾,不去深入细节。之前发过一篇讲解新技术选用的文章《程序员与新技术之间的「爱」与「恨」》,可以点击文末的链接阅读。

很多程序员对待新技术的习惯是,打一枪换一个地方,经过了几年,发现技术实力还在原地打转,不免有些可惜。

另外,推荐大家可以阅读一两本心理学、行为学相关的书,特别是我们做程序员的。

这不但可以提高自己对用户体验的感觉,还能提高对人性的洞察力,包括对自我的认知。是一项不管在生活中还是工作中都非常有用的技能。

好了,总结一下。

这篇呢,Z哥和你分享了我对程序员如何更好地与产品经理达成共识这件事的看法。主要是以下五点建议:

1.说实现不了之前,先三思。

2.明白需求本身也是成本。

3.刻意练习,多换位到用户视角。

4.交付的东西是自己的「招牌」

5.不产生价值的新技术是“垃圾”

希望对你有所帮助。


作者:Z哥,公众号“跨界架构师” 

刚刚用鸿蒙跑了个“hello world”!跑通后,我特么开始怀疑人生了....

技术交流大兴 发表了文章 • 0 个评论 • 78 次浏览 • 2020-09-14 10:18 • 来自相关话题

最近华为鸿蒙OS 2.0正式开源!关于鸿蒙的教程其实网上也已经有一些尝鲜的小伙伴分享的相关文章了,编者我按照步骤一步步跑下来,整个流程还是非常简单的,尤其是对Android开发的小伙伴来说,从IDE到项目的创建及项目的编译安装简直是一模一样呀。我特么的都有点怀... ...查看全部

最近华为鸿蒙OS 2.0正式开源!关于鸿蒙的教程其实网上也已经有一些尝鲜的小伙伴分享的相关文章了,编者我按照步骤一步步跑下来,整个流程还是非常简单的,尤其是对Android开发的小伙伴来说,从IDE到项目的创建及项目的编译安装简直是一模一样呀。我特么的都有点怀疑人生了...感兴趣的读者也可以试试哦。

再贴一下鸿蒙的源码地址:https://openharmony.gitee.com

咱们一起来跟着一位网友学习一下如何跑起来第一个“hello world”,原文如下:

1.前序

1.1 官网:

https://www.harmonyos.com 网上搜索第一个不是官方是三方的

1.2.IDE下载位置:

源码编译的下载: https://device.harmonyos.com/cn/ide 

开发应用的下载:https://developer.harmonyos.com/cn/develop/deveco-studio#download

1.3.源码位置:

https://device.harmonyos.com/cn/docs/start/get-code/oem_sourcecode_guide-0000001050769927

2. 开发应用部分

2.1 安装时候启动会提示下载SDK,点击取消,搜索SDK,重新自定义SDK路径。


我把所有的SDK都按照上,打勾会自动下载。

2.2 创建的项目

点击创建项目,发现现在支持有三种类型

2.2.1 TV 设备应用

2.2.2 Wearable 可穿戴设备应用

2.2.3 Lite Wearable 可穿戴设备(Lite)应用

2.2.4 创建TV项目 (Java),选了一个列表模板。

创建中:

下载gradle-5.4.1-all.zip慢得等等。( 默认下载到: 

这个应该有办法直接下载好

设置里面很全面都能进行设置,可以探索探索 ) grade 源已经正确的切换到华为国内

2.2.5 下载模拟器

弹窗点击确认就可以下载了 (此处下载速度略慢,下载失败点击 downloadagain 继续下载 估计下载人太多,失败了十几次把)


刷新以后:

使用自己的华为账号登录(此处注意有坑,如果默认使用Chrome可能授权会失败,使用Windows自带的浏览器进行登录)

(开发者账号实名认证,我填写的是银行卡验证速度很快) 

进行授权:

授权成功:

同意协议:

罗列了可用的模拟器:

TV模拟器启动了

运行就可以看到模拟器了

运行成功:


如何用JAVA设计一个亿级消息量的 IM 系统

技术交流大兴 发表了文章 • 0 个评论 • 45 次浏览 • 2020-09-11 11:01 • 来自相关话题

本文不会给出一套通用的IM方案,也不会评判某种架构的好坏,而是讨论设计IM系统的常见难题跟业界的解决方案。因为也没有所谓的通用方案,不同的解决方案都有其优缺点,只有最满足业务的系统才是一个好的系统。而且,在有限的人力、物力跟时间资源下,通常需要做出很多权衡,此... ...查看全部

本文不会给出一套通用的IM方案,也不会评判某种架构的好坏,而是讨论设计IM系统的常见难题跟业界的解决方案。因为也没有所谓的通用方案,不同的解决方案都有其优缺点,只有最满足业务的系统才是一个好的系统。而且,在有限的人力、物力跟时间资源下,通常需要做出很多权衡,此时,一个能够快速迭代、方便扩展的系统才是一个好的系统。

 

IM核心概念

用户:系统的使用者

消息:是指用户之间的沟通内容。通常在IM系统中,消息会有以下几类:文本消息、表情消息、图片消息、视频消息、文件消息等等

会话:通常指两个用户之间因聊天而建立起的关联

群:通常指多个用户之间因聊天而建立起的关联

终端:指用户使用IM系统的机器。通常有Android端、iOS端、Web端等等

未读数:指用户还没读的消息数量

用户状态:指用户当前是在线、离线还是挂起等状态

关系链:是指用户与用户之间的关系,通常有单向的好友关系、双向的好友关系、关注关系等等。这里需要注意与会话的区别,用户只有在发起聊天时才产生会话,但关系并不需要聊天才能建立。对于关系链的存储,可以使用图数据库(Neo4j等等),可以很自然地表达现实世界中的关系,易于建模

单聊:一对一聊天

群聊:多人聊天

客服:在电商领域,通常需要对用户提供售前咨询、售后咨询等服务。这时,就需要引入客服来处理用户的咨询

消息分流:在电商领域,一个店铺通常会有多个客服,此时决定用户的咨询由哪个客服来处理就是消息分流。通常消息分流会根据一系列规则来确定消息会分流给哪个客服,例如客服是否在线(客服不在线的话需要重新分流给另一个客服)、该消息是售前咨询还是售后咨询、当前客服的繁忙程度等等

信箱:本文的信箱我们指一个Timeline、一个收发消息的队列

 

读扩散 vs 写扩散

读扩散

我们先来看看读扩散。如上图所示,A与每个聊天的人跟群都有一个信箱(有些博文会叫Timeline),A在查看聊天信息的时候需要读取所有有新消息的信箱。这里的读扩散需要注意与Feeds系统的区别,在Feeds系统中,每个人都有一个写信箱,写只需要往自己的写信箱里写一次就好了,读需要从所有关注的人的写信箱里读。但IM系统里的读扩散通常是每两个相关联的人就有一个信箱,或者每个群一个信箱。

读扩散的优点:

写操作(发消息)很轻量,不管是单聊还是群聊,只需要往相应的信箱写一次就好了

每一个信箱天然就是两个人的聊天记录,可以方便查看聊天记录跟进行聊天记录的搜索

读扩散的缺点:

读操作(读消息)很重

写扩散

接下来看看写扩散。

e38c316470c48ab4e7f8492ec20398672f5e14.jpg.png

在写扩散中,每个人都只从自己的信箱里读取消息,但写(发消息)的时候,对于单聊跟群聊处理如下:

单聊:往自己的信箱跟对方的信箱都写一份消息,同时,如果需要查看两个人的聊天历史记录的话还需要再写一份(当然,如果从个人信箱也能回溯出两个人的所有聊天记录,但这样效率会很低)。

群聊:需要往所有的群成员的信箱都写一份消息,同时,如果需要查看群的聊天历史记录的话还需要再写一份。可以看出,写扩散对于群聊来说大大地放大了写操作。

写扩散优点:

读操作很轻量

可以很方便地做消息的多终端同步

写扩散缺点:

写操作很重,尤其是对于群聊来说

注意,在Feeds系统中:

写扩散也叫:Push、Fan-out或者Write-fanout

读扩散也叫:Pull、Fan-in或者Read-fanout

 

唯一ID设计

通常情况下,ID的设计主要有以下几大类:

UUID

基于Snowflake的ID生成方式

基于申请DB步长的生成方式

基于Redis或者DB的自增ID生成方式

特殊的规则生成唯一ID

具体的实现方法跟优缺点可以参考之前的一篇博文:分布式唯一 ID 解析

在IM系统中需要唯一Id的地方主要是:

会话ID

消息ID

 

消息ID

我们来看看在设计消息ID时需要考虑的三个问题。

 

消息ID不递增可以吗

我们先看看不递增的话会怎样:

使用字符串,浪费存储空间,而且不能利用存储引擎的特性让相邻的消息存储在一起,降低消息的写入跟读取性能

使用数字,但数字随机,也不能利用存储引擎的特性让相邻的消息存储在一起,会加大随机IO,降低性能;而且随机的ID不好保证ID的唯一性

因此,消息ID最好是递增的。

 

全局递增 vs 用户级别递增 vs 会话级别递增

全局递增:指消息ID在整个IM系统随着时间的推移是递增的。全局递增的话一般可以使用Snowflake(当然,Snowflake也只是worker级别的递增)。此时,如果你的系统是读扩散的话为了防止消息丢失,那每一条消息就只能带上上一条消息的ID,前端根据上一条消息判断是否有丢失消息,有消息丢失的话需要重新拉一次。

用户级别递增:指消息ID只保证在单个用户中是递增的,不同用户之间不影响并且可能重复。典型代表:微信。如果是写扩散系统的话信箱时间线ID跟消息ID需要分开设计,信箱时间线ID用户级别递增,消息ID全局递增。如果是读扩散系统的话感觉使用用户级别递增必要性不是很大。

会话级别递增:指消息ID只保证在单个会话中是递增的,不同会话之间不影响并且可能重复。典型代表:QQ。

 

连续递增 vs 单调递增

连续递增是指ID按 1,2,3...n 的方式生成;而单调递增是指只要保证后面生成的ID比前面生成的ID大就可以了,不需要连续。

据我所知,QQ的消息ID就是在会话级别使用的连续递增,这样的好处是,如果丢失了消息,当下一条消息来的时候发现ID不连续就会去请求服务器,避免丢失消息。此时,可能有人会想,我不能用定时拉的方式看有没有消息丢失吗?当然不能,因为消息ID只在会话级别连续递增的话那如果一个人有上千个会话,那得拉多少次啊,服务器肯定是抗不住的。

对于读扩散来说,消息ID使用连续递增就是一种不错的方式了。如果使用单调递增的话当前消息需要带上前一条消息的ID(即聊天消息组成一个链表),这样,才能判断消息是否丢失。

总结一下就是:

写扩散:信箱时间线ID使用用户级别递增,消息ID全局递增,此时只要保证单调递增就可以了

读扩散:消息ID可以使用会话级别递增并且最好是连续递增

 

会话ID

我们来看看设计会话ID需要注意的问题:

其中,会话ID有种比较简单的生成方式(特殊的规则生成唯一ID):拼接 from_user_id 跟 to_user_id:

如果 from_user_id 跟 to_user_id 都是32位整形数据的话可以很方便地用位运算拼接成一个64位的会话ID,即: conversation_id = ${from_user_id} << 32 | ${to_user_id} (在拼接前需要确保值比较小的用户ID是 from_user_id,这样任意两个用户发起会话可以很方便地知道会话ID)

如果from_user_id 跟 to_user_id 都是64位整形数据的话那就只能拼接成一个字符串了,拼接成字符串的话就比较伤了,浪费存储空间性能又不好。

前东家就是使用的上面第1种方式,第1种方式有个硬伤:随着业务在全球的扩展,32位的用户ID如果不够用需要扩展到64位的话那就需要大刀阔斧地改了。32位整形ID看起来能够容纳21亿个用户,但通常我们为了防止别人知道真实的用户数据,使用的ID通常不是连续的,这时,32位的用户ID就完全不够用了。因此,该设计完全依赖于用户ID,不是一种可取的设计方式。

因此,会话ID的设计可以使用全局递增的方式,加一个映射表,保存from_user_id、to_user_id跟conversation_id的关系。

 

推模式 vs 拉模式 vs 推拉结合模式

在IM系统中,新消息的获取通常会有三种可能的做法:

推模式:有新消息时服务器主动推给所有端(iOS、Android、PC等)

拉模式:由前端主动发起拉取消息的请求,为了保证消息的实时性,一般采用推模式,拉模式一般用于获取历史消息

推拉结合模式:有新消息时服务器会先推一个有新消息的通知给前端,前端接收到通知后就向服务器拉取消息

推模式简化图如下:

04cfb696364aeffcfdd83382d29a45bfefb11a.jpg.png

如上图所示,正常情况下,用户发的消息经过服务器存储等操作后会推给接收方的所有端。但推是有可能会丢失的,最常见的情况就是用户可能会伪在线(是指如果推送服务基于长连接,而长连接可能已经断开,即用户已经掉线,但一般需要经过一个心跳周期后服务器才能感知到,这时服务器会错误地以为用户还在线;伪在线是本人自己想的一个概念,没想到合适的词来解释)。因此如果单纯使用推模式的话,是有可能会丢失消息的。

推拉结合模式简化图如下:

6760f5926b20f8763ec72480c1328e859795dd.jpg.png

可以使用推拉结合模式解决推模式可能会丢消息的问题。在用户发新消息时服务器推送一个通知,然后前端请求最新消息列表,为了防止有消息丢失,可以再每隔一段时间主动请求一次。可以看出,使用推拉结合模式最好是用写扩散,因为写扩散只需要拉一条时间线的个人信箱就好了,而读扩散有N条时间线(每个信箱一条),如果也定时拉取的话性能会很差。

 

业界解决方案

前面了解了IM系统的常见设计问题,接下来我们再看看业界是怎么设计IM系统的。研究业界的主流方案有助于我们深入理解IM系统的设计。以下研究都是基于网上已经公开的资料,不一定正确,大家仅作参考就好了。

微信

虽然微信很多基础框架都是自研,但这并不妨碍我们理解微信的架构设计。从微信公开的《从0到1:微信后台系统的演进之路》这篇文章可以看出,微信采用的主要是:写扩散 + 推拉结合。由于群聊使用的也是写扩散,而写扩散很消耗资源,因此微信群有人数上限(目前是500)。所以这也是写扩散的一个明显缺点,如果需要万人群就比较难了。

从文中还可以看出,微信采用了多数据中心架构:

 62f39582378e16672d3132062133f47142ce8e.jpg.png

 


微信每个数据中心都是自治的,每个数据中心都有全量的数据,数据中心间通过自研的消息队列来同步数据。为了保证数据的一致性,每个用户都只属于一个数据中心,只能在自己所属的数据中心进行数据读写,如果用户连了其它数据中心则会自动引导用户接入所属的数据中心。而如果需要访问其它用户的数据那只需要访问自己所属的数据中心就可以了。同时,微信使用了三园区容灾的架构,使用Paxos来保证数据的一致性。

从微信公开的《万亿级调用系统:微信序列号生成器架构设计及演变》这篇文章可以看出,微信的ID设计采用的是:基于申请DB步长的生成方式 + 用户级别递增。如下图所示:

1.png

微信的序列号生成器由仲裁服务生成路由表(路由表保存了uid号段到AllocSvr的全映射),路由表会同步到AllocSvr跟Client。如果AllocSvr宕机的话会由仲裁服务重新调度uid号段到其它AllocSvr。

 

钉钉

钉钉公开的资料不多,从《阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处》这篇文章我们只能知道,钉钉最开始使用的是写扩散模型,为了支持万人群,后来貌似优化成了读扩散。

但聊到阿里的IM系统,不得不提的是阿里自研的Tablestore。一般情况下,IM系统都会有一个自增ID生成系统,但Tablestore创造性地引入了主键列自增,即把ID的生成整合到了DB层,支持了用户级别递增(传统MySQL等DB只能支持表级自增,即全局自增)。具体可以参考:《如何优化高并发IM系统架构》

 

Twitter

什么?Twitter不是Feeds系统吗?这篇文章不是讨论IM的吗?是的,Twitter是Feeds系统,但Feeds系统跟IM系统其实有很多设计上的共性,研究下Feeds系统有助于我们在设计IM系统时进行参考。再说了,研究下Feeds系统也没有坏处,扩展下技术视野嘛。

Twitter的自增ID设计估计大家都耳熟能详了,即大名鼎鼎的Snowflake,因此ID是全局递增的。


从这个视频分享《How We Learned to Stop Worrying and Love Fan-In at Twitter》可以看出,Twitter一开始使用的是写扩散模型,Fanout Service负责扩散写到Timelines Cache(使用了Redis),Timeline Service负责读取Timeline数据,然后由API Services返回给用户。

但由于写扩散对于大V来说写的消耗太大,因此后面Twitter又使用了写扩散跟读扩散结合的方式。如下图所示:



 2.png

对于粉丝数不多的用户如果发Twitter使用的还是写扩散模型,由Timeline Mixer服务将用户的Timeline、大V的写Timeline跟系统推荐等内容整合起来,最后再由API Services返回给用户。

 

58到家

58到家实现了一个通用的实时消息平台:

3.png

可以看出,msg-server保存了应用跟MQ主题之间的对应关系,msg-server根据这个配置将消息推到不同的MQ队列,具体的应用来消费就可以了。因此,新增一个应用只需要修改配置就可以了。

58到家为了保证消息投递的可靠性,还引入了确认机制:消息平台收到消息先落地数据库,接收方收到后应用层ACK再删除。使用确认机制最好是只能单点登录,如果多端能够同时登录的话那就比较麻烦了,因为需要所有端都确认收到消息后才能删除。

看到这里,估计大家已经明白了,设计一个IM系统很有挑战性。我们还是继续来看设计一个IM系统需要考虑的问题吧。

 

IM需要解决的问题

如何保证消息的实时性

在通信协议的选择上,我们主要有以下几个选择:

使用TCP Socket通信,自己设计协议:58到家等等

使用UDP Socket通信:QQ等等

使用HTTP长轮循:微信网页版等等

不管使用哪种方式,我们都能够做到消息的实时通知。但影响我们消息实时性的可能会在我们处理消息的方式上。例如:假如我们推送的时候使用MQ去处理并推送一个万人群的消息,推送一个人需要2ms,那么推完一万人需要20s,那么后面的消息就阻塞了20s。如果我们需要在10ms内推完,那么我们推送的并发度应该是:人数:10000 / (推送总时长:10 / 单个人推送时长:2) = 2000

因此,我们在选择具体的实现方案的时候一定要评估好我们系统的吞吐量,系统的每一个环节都要进行评估压测。只有把每一个环节的吞吐量评估好了,才能保证消息推送的实时性。

如何保证消息时序

以下情况下消息可能会乱序:

发送消息如果使用的不是长连接,而是使用HTTP的话可能会出现乱序。因为后端一般是集群部署,使用HTTP的话请求可能会打到不同的服务器,由于网络延迟或者服务器处理速度的不同,后发的消息可能会先完成,此时就产生了消息乱序。解决方案:

前端依次对消息进行处理,发送完一个消息再发送下一个消息。这种方式会降低用户体验,一般情况下不建议使用。

带上一个前端生成的顺序ID,让接收方根据该ID进行排序。这种方式前端处理会比较麻烦一点,而且聊天的过程中接收方的历史消息列表中可能会在中间插入一条消息,这样会很奇怪,而且用户可能会漏读消息。但这种情况可以通过在用户切换窗口的时候再进行重排来解决,接收方每次收到消息都先往最后面追加。

通常为了优化体验,有的IM系统可能会采取异步发送确认机制(例如:QQ)。即消息只要到达服务器,然后服务器发送到MQ就算发送成功。如果由于权限等问题发送失败的话后端再推一个通知下去。这种情况下MQ就要选择合适的Sharding策略了:

按to_user_id进行Sharding:使用该策略如果需要做多端同步的话发送方多个端进行同步可能会乱序,因为不同队列的处理速度可能会不一样。例如发送方先发送m1然后发送m2,但服务器可能会先处理完m2再处理m1,这里其它端会先收到m2然后是m1,此时其它端的会话列表就乱了。

按conversation_id进行Sharding:使用该策略同样会导致多端同步会乱序。

按from_user_id进行Sharding:这种情况下使用该策略是比较好的选择

通常为了优化性能,推送前可能会先往MQ推,这种情况下使用to_user_id才是比较好的选择。

用户在线状态如何做

很多IM系统都需要展示用户的状态:是否在线,是否忙碌等。主要可以使用Redis或者分布式一致性哈希来实现用户在线状态的存储。

看上面的图可能会有人疑惑,为什么每次心跳都需要更新Redis?如果我使用的是TCP长连接那是不是就不用每次心跳都更新了?确实,正常情况下服务器只需要在新建连接或者断开连接的时候更新一下Redis就好了。但由于服务器可能会出现异常,或者服务器跟Redis之间的网络会出现问题,此时基于事件的更新就会出现问题,导致用户状态不正确。因此,如果需要用户在线状态准确的话最好通过心跳来更新在线状态。

由于Redis是单机存储的,因此,为了提高可靠性跟性能,我们可以使用Redis Cluster或者Codis。

使用分布式一致性哈希需要注意在对Status Server Cluster进行扩容或者缩容的时候要先对用户状态进行迁移,不然在刚操作时会出现用户状态不一致的情况。同时还需要使用虚拟节点避免数据倾斜的问题。

 

多端同步怎么做

读扩散

前面也提到过,对于读扩散,消息的同步主要是以推模式为主,单个会话的消息ID顺序递增,前端收到推的消息如果发现消息ID不连续就请求后端重新获取消息。但这样仍然可能丢失会话的最后一条消息,为了加大消息的可靠性,可以在历史会话列表的会话里再带上最后一条消息的ID,前端在收到新消息的时候会先拉取最新的会话列表,然后判断会话的最后一条消息是否存在,如果不存在,消息就可能丢失了,前端需要再拉一次会话的消息列表;如果会话的最后一条消息ID跟消息列表里的最后一条消息ID一样,前端就不再处理。这种做法的性能瓶颈会在拉取历史会话列表那里,因为每次新消息都需要拉取后端一次,如果按微信的量级来看,单是消息就可能会有20万的QPS,如果历史会话列表放到MySQL等传统DB的话肯定抗不住。因此,最好将历史会话列表存到开了AOF(用RDB的话可能会丢数据)的Redis集群。这里只能感慨性能跟简单性不能兼得。

 

写扩散

对于写扩散来说,多端同步就简单些了。前端只需要记录最后同步的位点,同步的时候带上同步位点,然后服务器就将该位点后面的数据全部返回给前端,前端更新同步位点就可以了。

如何处理未读数

在IM系统中,未读数的处理非常重要。未读数一般分为会话未读数跟总未读数,如果处理不当,会话未读数跟总未读数可能会不一致,严重降低用户体验。

读扩散

对于读扩散来说,我们可以将会话未读数跟总未读数都存在后端,但后端需要保证两个未读数更新的原子性跟一致性,一般可以通过以下两种方法来实现:

使用Redis的multi事务功能,事务更新失败可以重试。但要注意如果你使用Codis集群的话并不支持事务功能。

使用Lua嵌入脚本的方式。使用这种方式需要保证会话未读数跟总未读数都在同一个Redis节点(Codis的话可以使用Hashtag)。这种方式会导致实现逻辑分散,加大维护成本。

写扩散

对于写扩散来说,服务端通常会弱化会话的概念,即服务端不存储历史会话列表。未读数的计算可由前端来负责,标记已读跟标记未读可以只记录一个事件到信箱里,各个端通过重放该事件的形式来处理会话未读数。使用这种方式可能会造成各个端的未读数不一致,至少微信就会有这个问题。

如果写扩散也通过历史会话列表来存储未读数的话那用户时间线服务跟会话服务紧耦合,这个时候需要保证原子性跟一致性的话那就只能使用分布式事务了,会大大降低系统的性能。

如何存储历史消息

读扩散

对于读扩散,只需要按会话ID进行Sharding存储一份就可以了。

 

写扩散

对于写扩散,需要存储两份:一份是以用户为Timeline的消息列表,一份是以会话为Timeline的消息列表。以用户为Timeline的消息列表可以用用户ID来做Sharding,以会话为Timeline的消息列表可以用会话ID来做Sharding。

 

数据冷热分离

对于IM来说,历史消息的存储有很强的时间序列特性,时间越久,消息被访问的概率也越低,价值也越低。如果我们需要存储几年甚至是永久的历史消息的话(电商IM中比较常见),那么做历史消息的冷热分离就非常有必要了。数据的冷热分离一般是HWC(Hot-Warm-Cold)架构。对于刚发送的消息可以放到Hot存储系统(可以用Redis)跟Warm存储系统,然后由Store Scheduler根据一定的规则定时将冷数据迁移到Cold存储系统。获取消息的时候需要依次访问Hot、Warm跟Cold存储系统,由Store Service整合数据返回给IM Service。

 

接入层怎么做

实现接入层的负载均衡主要有以下几个方法:

硬件负载均衡:例如F5、A10等等。硬件负载均衡性能强大,稳定性高,但价格非常贵,不是土豪公司不建议使用。

使用DNS实现负载均衡:使用DNS实现负载均衡比较简单,但使用DNS实现负载均衡如果需要切换或者扩容那生效会很慢,而且使用DNS实现负载均衡支持的IP个数有限制、支持的负载均衡策略也比较简单。

DNS + 4层负载均衡 + 7层负载均衡架构:例如 DNS + DPVS + Nginx 或者 DNS + LVS + Nginx。有人可能会疑惑为什么要加入4层负载均衡呢?这是因为7层负载均衡很耗CPU,并且经常需要扩容或者缩容,对于大型网站来说可能需要很多7层负载均衡服务器,但只需要少量的4层负载均衡服务器即可。因此,该架构对于HTTP等短连接大型应用很有用。当然,如果流量不大的话只使用DNS + 7层负载均衡即可。但对于长连接来说,加入7层负载均衡Nginx就不大好了。因为Nginx经常需要改配置并且reload配置,reload的时候TCP连接会断开,造成大量掉线。

DNS + 4层负载均衡:4层负载均衡一般比较稳定,很少改动,比较适合于长连接。

对于长连接的接入层,如果我们需要更加灵活的负载均衡策略或者需要做灰度的话,那我们可以引入一个调度服务,如下图所示:

 4.png

Access Schedule Service可以实现根据各种策略来分配Access Service,例如:

根据灰度策略来分配

根据就近原则来分配

根据最少连接数来分配

 

架构心得

最后,分享一下做大型应用的架构心得:

灰度!灰度!灰度!

监控!监控!监控!

告警!告警!告警!

缓存!缓存!缓存!

限流!熔断!降级!

低耦合,高内聚!

避免单点,拥抱无状态!

评估!评估!评估!

压测!压测!压测!

 

来源:InfoQ 


【Geek Online 2020 编程挑战赛】有人组队吗?

活动梅川酷子 回复了问题 • 3 人关注 • 2 个回复 • 113 次浏览 • 2020-09-10 18:25 • 来自相关话题

【领取见面礼】限量 100份 GeekOnline加油包!等你来拿

活动雨山 回复了问题 • 61 人关注 • 62 个回复 • 1835 次浏览 • 2020-09-08 17:35 • 来自相关话题

开发者真的喜欢开源吗?

技术交流大兴 发表了文章 • 0 个评论 • 47 次浏览 • 2020-09-07 10:03 • 来自相关话题

作者 | Matt Asay译者 | 风车云马,责编 | 屠敏出品 | CSDN(ID:CSDNnews)FaunaDB创始人埃文·韦弗(Evan Weaver)有一个疯狂的想法:只要给开发者一个开放的API和一个适合他们开发的模型,那么云是最好的选... ...查看全部
作者 | Matt Asay

译者 | 风车云马,责编 | 屠敏
出品 | CSDN(ID:CSDNnews)

timg.jpg

FaunaDB创始人埃文·韦弗(Evan Weaver)有一个疯狂的想法:只要给开发者一个开放的API和一个适合他们开发的模型,那么云是最好的选择,即便是Linux和Kubernetes这样的开源平台。也许他们不想为任何代码做“手术”。那么,他们感兴趣的是云,而不是代码?

这是一个大胆的想法,倒也存在合理的地方。然而,当我向业界的重量级人物提出这个想法时,他们却因为各种不同的原因否定了。根据Lightspeed的投资者瓜拉夫•古普塔(Guarav Gupta)的说法,“开发人员对开源有着深深的喜爱和欣赏,几乎像是上瘾了”,而这是开发人员对API所没有的感觉。

有没有一种方法既能方便地使用API,又不会失去开发人员对开源社区的归属感?答案似乎是肯定的,但实现起来有点复杂。

不要忘记数据

StrAPI提供一种开源headless CMS,它的联合创始人兼CPO Aurélien Georget认为,开源的持久魅力不仅在于代码。例如,StrAPI的许多客户都想要定制他们的CMS。在这种情况下,云服务不能满足他们的需求。

即使他们不想修改代码,数据也会驱使他们这样做:“我们的用户对他们的代码的所有权不感兴趣,而是对他们的数据感兴趣。出于数据隐私的原因,或者从法律的角度出发(例如银行、保险、公共管理等部门),他们需要在自己的数据中心运行他们的代码——并保存他们的数据。”当然,这并不是说Weaver坚持以API为中心的方法是错误的,“每个解决方案都应该是面向API的,”Georget赞同道,“因为它让开发人员富有创造力,想象新的用例,并进行创新。”

Georget表示,使用云服务或许会更方便,但对于某些类别的应用程序或客户来说,这并不总是可行的。

DataStax为ApacheCassandra数据库社区提供代码和操作技巧,其首席宣传官PatrickMcFadin认为,API可能会成为一扇单向的门,过去开放的API随着时间的推移会被锁起来,或者被挡在付费墙后面。未来数据之争只会带来严重的专利化。相比之下,代码可以免费提供,因为它不是大多数企业(包括软件供应商)的核心“业务”。

Instaclustr的首席技术官本•布罗姆海德(BenBromhead)有一个理想的折中方案。Instaclustr将ApacheKafka等开源软件作为托管服务运行。只要建立的云服务严格遵守开源标准,他们就不会真正失去代码或数据的独立性。

开源数据层技术保证公司完全控制自己的数据和流程。通过选择100%的开源技术,公司可以拥有自己的代码,并保持不受供应商或技术的限制。比代码本身更重要的是,真正的开源技术确保了公司的关键信息流程不会被提供专有解决方案的供应商所破坏,也不会在任何情况下干扰公司充分利用自己数据的能力。

核心在于信任

这让我们回到了古普塔的观点,即开源有一些不同的,也许更好的东西。尽管“人们最终还是想以云服务的形式消费”,但是他认为,代码对于营造真正的技术亲和力——甚至对技术的喜爱和欣赏——是至关重要的。

您能通过API实现这种“亲密”(cosmic closeness)吗?古普塔认为不会。他说,“如果你是开源的,那么很容易构建社区,而不是带API的黑箱云服务”。因为,“作为一名开发者,你想通过自己的感觉和理解来开发它,并成为团队的一部分。”

这种开源运动的核心是信任:开放代码、开放路线图、开放交流、开放决策。这就是像Instaclustr这样的云供应商的成功之处:云的易用性以及对开源的信任和控制,可以为客户提供他们想要的东西。好的开源公司会建立起令人不可思议的信任。明智的云计算公司不甘示弱,一定不会让开发人员缺失在开源社区中的那种信任和喜爱。

原文:https://www.infoworld.com/article/3572324/do-developers-really-care-about-open-source.html


数十家技术社区联名推荐的GeekOnline来了!

科技前线融云那些事 发表了文章 • 2 个评论 • 136 次浏览 • 2020-07-09 10:21 • 来自相关话题

极客是一群什么样的人?他们大智若愚而富有科学精神,对一切常规的东西天然反感;他们天生热爱探索和创造,对于跟随和人云亦云深恶痛绝;他们特立独行,从不自我设置禁区;他们信仰自由,对于人为的限制极其不屑并热衷于挑战权威;在工作中他们推崇化繁为简,相信技术的力量并追求... ...查看全部

极客是一群什么样的人?他们大智若愚而富有科学精神,对一切常规的东西天然反感;他们天生热爱探索和创造,对于跟随和人云亦云深恶痛绝;他们特立独行,从不自我设置禁区;他们信仰自由,对于人为的限制极其不屑并热衷于挑战权威;在工作中他们推崇化繁为简,相信技术的力量并追求产品美学……


现在,由通信云技术领导者融云推出的开发者社区 GeekOnline 正式与全球极客们见面啦!


崇尚科技、自由和创造力的极客精神,GeekOnline 致力于成为一个创意与价值兼备、兴趣和温度并存的技术社区。


这是一个开发者们的技术乐园,也是国内首个全面覆盖了 IM、RTC 等通信云技术的极客社区。在移动互联网概念中,设想所有的客户端都“永远在线”,即每条消息发出必有回响。Online 既是一种态度,也是一种精神,正如 IM 所连接的每一个用户,极客永远在线。

极客在线微信头图.png


全球首个互联网通信云领域技术社区


不同于其他技术类社区体系的繁杂,GeekOnline 社区更专注于 IM 即时通讯、RTC 实时音视频以及与通信云相关领域的技术交流。在内容上 GeekOnline 社区提供了 IM、RTC、5G、出海等上百个热门话题让开发者们讨论和学习。目前 GeekOnline 社区与融云账号系统已全面打通,融云用户使用邮箱密码即可直接登录。


此外,关于更多互联网通信云领域的技术问题,开发者还可以通过社区中的“帮助”板块寻求解决。融云将 GeekOnline 社区打造成内容坚实的技术资料库,整理了针对通信云场景的各项技术资料,涵盖 IM 即时通讯、实时音视频、小程序、客服以及内容审核等众多维度。经过融云多年的技术积累,几乎所有关于通信云领域的常见问题都可以通过“帮助”板块来解决。


全球数十家技术社区联合推荐


GeekOnline 社区上线初期就已经获得了国内外几十家社区及媒体伙伴的认可并建立了联系,其中包括开源中国、51CTO、SegmentFault、LiveVideoStack、ThoughtWorks 等国内外知名技术社区。

社区合作伙伴.png


除此以外,还有多个活跃在技术社区的 KOL 已经入驻 GeekOnline,分享自己的经验观点并与用户们互动交流。还有一个秘密提前透露给你,一场由中国最专业的技术社区们共同发起的开发者活动,不久后也将与开发者朋友们见面。


开发者互助,每一个问题都必有所得


相关调研数据显示,在技术开发过程中,大约 76% 的开发者都希望获得代码、数据的支持,而超过 81% 的开发者则需要更多技术经验和课程的学习。


在 GeekOnline 社区,开发者可以自行搜索想了解的问题,在技术大牛的文章或者问题回复中找到所需的答案,亦或是从其他人的讨论中获得新的灵感。如果在搜索结果中没有收获,还可以针对性地提出问题,@业界技术大牛来回复和解答。此外在疫情期间,不少开发者都面临着一些工作上的难题,在这里大家还可以“抱团取暖”,找寻新的合作点或者互相推荐工作机会。


在 GeekOnline 社区建设中,融云还将陆续注入更多资源,针对专属社区会员定制周边礼品,为开发者们提供专业技术类书籍、技术大会演讲 PPT 等资料下载,打造丰富多彩的开发者活动,并定期邀请来自于各领域的技术专家与开发者们在线上线下进行互动,期待有更多的开发者伙伴加入 GeekOnline,一起打造中国最 Geek 的技术社区。


福利时间到


点击下方传送门参与活动即可获得融云精心准备的“GeekOnline加油包”!惊喜礼品在等你,快来参加吧!


传送门https://geekonline.rongcloud.cn/question/701

条新动态, 点击查看
1、还记得第一次访问的社区论坛是哪个吗?当时的场景是? 第一次上网是访问nike的basketball的网站,初中二年级的时候家里买了第一台电脑,那时候还是拨号的,时不时会断掉,然后需要手动重连。那时候我连打字都不熟练,却没成想在十几年后在蚂蚁开始写代码。时... 显示全部 »
1、还记得第一次访问的社区论坛是哪个吗?当时的场景是? 第一次上网是访问nike的basketball的网站,初中二年级的时候家里买了第一台电脑,那时候还是拨号的,时不时会断掉,然后需要手动重连。那时候我连打字都不熟练,却没成想在十几年后在蚂蚁开始写代码。时间过得好快,我也渐渐找到了方向,坚定的走下去。 2、用你擅长的编程语言输出“hello world” #include int main() { printf("Hello, World!"); return 0; } 3、你对GeekOnline社区的期待有哪些? 希望可以认识更多的Geek爱好者

【有奖调研】Geek Online 2020 编程挑战赛参赛调研

技术交流放肆小青年 回复了问题 • 28 人关注 • 27 个回复 • 551 次浏览 • 2020-10-26 09:33 • 来自相关话题

【领取见面礼】限量 100份 GeekOnline加油包!等你来拿

活动雨山 回复了问题 • 61 人关注 • 62 个回复 • 1835 次浏览 • 2020-09-08 17:35 • 来自相关话题

【有奖调研】Geek Online 2020 编程挑战赛参赛调研

回复

技术交流放肆小青年 回复了问题 • 28 人关注 • 27 个回复 • 551 次浏览 • 2020-10-26 09:33 • 来自相关话题

【Geek Online 2020 编程挑战赛】有人组队吗?

回复

活动梅川酷子 回复了问题 • 3 人关注 • 2 个回复 • 113 次浏览 • 2020-09-10 18:25 • 来自相关话题

【领取见面礼】限量 100份 GeekOnline加油包!等你来拿

回复

活动雨山 回复了问题 • 61 人关注 • 62 个回复 • 1835 次浏览 • 2020-09-08 17:35 • 来自相关话题

如何做一个懂产品的程序员?

技术交流王叫兽 发表了文章 • 0 个评论 • 94 次浏览 • 2020-09-16 10:32 • 来自相关话题

两个相爱相杀的岗位,想要更好的达成共识、更好的合作,自然不仅仅是一方的事情。这次Z哥先会带你看看产品经理眼中的程序员是什么样子。然后给出一些我的建议。直接进入正题吧。从产品视角是怎么看程序员的呢?我根据我自己的经历以及与其他产品经理的交流下来看,吐槽的主要是以... ...查看全部

timg.jpg

两个相爱相杀的岗位,想要更好的达成共识、更好的合作,自然不仅仅是一方的事情。这次Z哥先会带你看看产品经理眼中的程序员是什么样子。然后给出一些我的建议。

直接进入正题吧。

从产品视角是怎么看程序员的呢?我根据我自己的经历以及与其他产品经理的交流下来看,吐槽的主要是以下几点:

  1. 这个功能实现不了。

  2. 希望所有产品都不要改版,一次性把现在或未来要做的开发完。

  3. 只关心要写多少代码,不在乎产品体验。

  4. 写完程序从不自测,直接丢给别人测试。

  5. 过分追寻新技术潮流,完全不考虑对产品带来什么价值。

第一点,的确存在一些由于技术限制导致实现成本无限大的需求,比如手机屏幕背景色根据手机壳颜色切换……

但是,国内的技术环境不像老美那的技术味道重,大多还是商业导向的,很少企业里需要用到高精尖的技术,所以,真正实现不了的功能微乎其微。

对于大多数的功能需求来说,无非是一个成本大小、价值高低的问题。从立场上看,程序员自然是站在「成本」一方的,但对大多数人来说,决定这个成本的主要因素往往是自己工作的难度和耗时,费时费力的功能就容易得到“实现不了”的结果。

第二点对大多数产品经理来说是他们的对立面。因为大多数产品经理最喜欢“走一步算一步”地高频迭代,甚至是有一个想法就开始干。而程序员则喜欢来一个大而全的,并且内容要非常详细的,心里的想法是,这样的话我一开始就可以设计一个完美架构来支撑它。

而且,内容越详细,产品经理就越不敢乱调整需求,毕竟“证据在手”嘛:D。

第三点在大多数程序员身上都能看到。毕竟做程序员的还是理科男偏多,对需要有同理心、需要靠感受的事情的确弱了一些。

第四点的原因主要有两个。

一个是对自己的代码过度自信导致,我自己深有体会。我还记得有一次我交付一个功能,那个功能我单元测试都写了不少,对质量很有信心,觉得就算有bug也都是比较深层的bug。结果没想到……第一天就测出来好几个低级的bug。

另一个原因是反正有测试人员在,等他们测出问题我再改不是更轻松。惰性使然,从个人角度的确如此。但是从团队角度来看,徒增了不少的沟通成本。

第五点的原因也有两个。

一个是行业里的新技术迭代的确太快,怕不学新技术被淘汰。

另一个原因是,只有用上新技术才能有谈资,显得自己与众不同、有成就感。

以上就是对这五点的简单分析,那么如何改善呢?继续往下看。

下面这些方法都是我亲测有效的,强烈推荐你也试试。这里的序号与前面被吐槽的五点一一对应。

01  说实现不了之前,先三思

根据先后可以做以下3个思考:

是觉得这个功能没有价值不想做吗?

真的实现不了?我想全了吗?

这些方案里,有成本比价值低的吗?

第一个问题先确定必要性。我们不是说不能推需求,而是要推掉低价值、无价值的需求。当然有没有价值不一定你说了算,但至少这才能算是拒绝的理由。

第二个问题,努力拓宽自己的边界、舒适区。如果我们总是习惯性地从大脑的记忆中找解决方案,那么将会永远在舒适区止步不前。

第三个问题,拒绝需求虽然不用动之以情,但一定需要晓之以理。当你能清楚的阐述利弊、收益比,拒绝需求自然不是一件需要相互扯皮的事情。

经过了这三个问题的思考,不管最终能不能实现,相信可以很好的与产品经理达成共识。

02  明白需求本身也是成本

过度地苛求需求要细、要完整、要全面,这个本身也是在增加产品经理需要投入的时间。你的开发成本是成本,产品经理的也是。

与其等一个“XXXX最终绝对不改版”,不如从已经达成共识的部分开工,在这个过程中再与产品经理「共创」,多一起沟通打磨,此时再让产品完善PRD等文档,形成最终版。

03  刻意练习,多换位到用户视角

平时多去体验一下自家的产品以及竞品,把整个过程中的感受记录下来。比如,哪里感到不太顺手、哪里感受到了小惊喜、哪里感到特别烦人等等。结果不重要,重要的是这个过程,慢慢锻炼自己作为用户的感知力。

有些程序员看起来经常把用户体验挂在嘴上,其实提出来的很多反而是脱离大众习惯的“个性化”需求,就是因为平时缺少对同行、外界的关注。

04  交付的东西是自己的「招牌」

“有人的地方就有江湖,有江湖的地方就有称号”。如果长期报以等测出来bug再去修的心态,你在别人心中的称号就是负面的。

轻则影响自己的口碑,影响与他人之间的协作关系;重则失去未来的晋升机会。一个对自己的东西都不负责的人,如何负责更多的人、更大的事情呢?

在这件事上,除了多自测外,作为过来人,我建议每一个程序员认真对待单元测试。特别把核心、复杂的方法单元测试给做上,这对交付功能的质量的提升非常明显。

05  不产生价值的新技术是“垃圾”

拥抱新技术是值得鼓励的。但是单纯的为了体验某新技术而去使用它,这不但给团队在挖坑,也在给自己挖坑。

比如你花了不少的时间在项目里用了某个新技术,但是对团队没有带来什么价值,你说后续公司还会继续投入资源加大新技术的使用吗?大概率并不会。那么之前了解到的一些知识,就会随着时间的推移而淡忘,投入的时间大多数浪费掉了。

所以,对待新技术Z哥的观点是。对于无法在工作中找到价值点的新技术浅尝辄止即可。相反,遇到可以产生价值的新技术,请全身心投入进去,而不是仅仅在应用层面捣腾,不去深入细节。之前发过一篇讲解新技术选用的文章《程序员与新技术之间的「爱」与「恨」》,可以点击文末的链接阅读。

很多程序员对待新技术的习惯是,打一枪换一个地方,经过了几年,发现技术实力还在原地打转,不免有些可惜。

另外,推荐大家可以阅读一两本心理学、行为学相关的书,特别是我们做程序员的。

这不但可以提高自己对用户体验的感觉,还能提高对人性的洞察力,包括对自我的认知。是一项不管在生活中还是工作中都非常有用的技能。

好了,总结一下。

这篇呢,Z哥和你分享了我对程序员如何更好地与产品经理达成共识这件事的看法。主要是以下五点建议:

1.说实现不了之前,先三思。

2.明白需求本身也是成本。

3.刻意练习,多换位到用户视角。

4.交付的东西是自己的「招牌」

5.不产生价值的新技术是“垃圾”

希望对你有所帮助。


作者:Z哥,公众号“跨界架构师” 

刚刚用鸿蒙跑了个“hello world”!跑通后,我特么开始怀疑人生了....

技术交流大兴 发表了文章 • 0 个评论 • 78 次浏览 • 2020-09-14 10:18 • 来自相关话题

最近华为鸿蒙OS 2.0正式开源!关于鸿蒙的教程其实网上也已经有一些尝鲜的小伙伴分享的相关文章了,编者我按照步骤一步步跑下来,整个流程还是非常简单的,尤其是对Android开发的小伙伴来说,从IDE到项目的创建及项目的编译安装简直是一模一样呀。我特么的都有点怀... ...查看全部

最近华为鸿蒙OS 2.0正式开源!关于鸿蒙的教程其实网上也已经有一些尝鲜的小伙伴分享的相关文章了,编者我按照步骤一步步跑下来,整个流程还是非常简单的,尤其是对Android开发的小伙伴来说,从IDE到项目的创建及项目的编译安装简直是一模一样呀。我特么的都有点怀疑人生了...感兴趣的读者也可以试试哦。

再贴一下鸿蒙的源码地址:https://openharmony.gitee.com

咱们一起来跟着一位网友学习一下如何跑起来第一个“hello world”,原文如下:

1.前序

1.1 官网:

https://www.harmonyos.com 网上搜索第一个不是官方是三方的

1.2.IDE下载位置:

源码编译的下载: https://device.harmonyos.com/cn/ide 

开发应用的下载:https://developer.harmonyos.com/cn/develop/deveco-studio#download

1.3.源码位置:

https://device.harmonyos.com/cn/docs/start/get-code/oem_sourcecode_guide-0000001050769927

2. 开发应用部分

2.1 安装时候启动会提示下载SDK,点击取消,搜索SDK,重新自定义SDK路径。


我把所有的SDK都按照上,打勾会自动下载。

2.2 创建的项目

点击创建项目,发现现在支持有三种类型

2.2.1 TV 设备应用

2.2.2 Wearable 可穿戴设备应用

2.2.3 Lite Wearable 可穿戴设备(Lite)应用

2.2.4 创建TV项目 (Java),选了一个列表模板。

创建中:

下载gradle-5.4.1-all.zip慢得等等。( 默认下载到: 

这个应该有办法直接下载好

设置里面很全面都能进行设置,可以探索探索 ) grade 源已经正确的切换到华为国内

2.2.5 下载模拟器

弹窗点击确认就可以下载了 (此处下载速度略慢,下载失败点击 downloadagain 继续下载 估计下载人太多,失败了十几次把)


刷新以后:

使用自己的华为账号登录(此处注意有坑,如果默认使用Chrome可能授权会失败,使用Windows自带的浏览器进行登录)

(开发者账号实名认证,我填写的是银行卡验证速度很快) 

进行授权:

授权成功:

同意协议:

罗列了可用的模拟器:

TV模拟器启动了

运行就可以看到模拟器了

运行成功:


如何用JAVA设计一个亿级消息量的 IM 系统

技术交流大兴 发表了文章 • 0 个评论 • 45 次浏览 • 2020-09-11 11:01 • 来自相关话题

本文不会给出一套通用的IM方案,也不会评判某种架构的好坏,而是讨论设计IM系统的常见难题跟业界的解决方案。因为也没有所谓的通用方案,不同的解决方案都有其优缺点,只有最满足业务的系统才是一个好的系统。而且,在有限的人力、物力跟时间资源下,通常需要做出很多权衡,此... ...查看全部

本文不会给出一套通用的IM方案,也不会评判某种架构的好坏,而是讨论设计IM系统的常见难题跟业界的解决方案。因为也没有所谓的通用方案,不同的解决方案都有其优缺点,只有最满足业务的系统才是一个好的系统。而且,在有限的人力、物力跟时间资源下,通常需要做出很多权衡,此时,一个能够快速迭代、方便扩展的系统才是一个好的系统。

 

IM核心概念

用户:系统的使用者

消息:是指用户之间的沟通内容。通常在IM系统中,消息会有以下几类:文本消息、表情消息、图片消息、视频消息、文件消息等等

会话:通常指两个用户之间因聊天而建立起的关联

群:通常指多个用户之间因聊天而建立起的关联

终端:指用户使用IM系统的机器。通常有Android端、iOS端、Web端等等

未读数:指用户还没读的消息数量

用户状态:指用户当前是在线、离线还是挂起等状态

关系链:是指用户与用户之间的关系,通常有单向的好友关系、双向的好友关系、关注关系等等。这里需要注意与会话的区别,用户只有在发起聊天时才产生会话,但关系并不需要聊天才能建立。对于关系链的存储,可以使用图数据库(Neo4j等等),可以很自然地表达现实世界中的关系,易于建模

单聊:一对一聊天

群聊:多人聊天

客服:在电商领域,通常需要对用户提供售前咨询、售后咨询等服务。这时,就需要引入客服来处理用户的咨询

消息分流:在电商领域,一个店铺通常会有多个客服,此时决定用户的咨询由哪个客服来处理就是消息分流。通常消息分流会根据一系列规则来确定消息会分流给哪个客服,例如客服是否在线(客服不在线的话需要重新分流给另一个客服)、该消息是售前咨询还是售后咨询、当前客服的繁忙程度等等

信箱:本文的信箱我们指一个Timeline、一个收发消息的队列

 

读扩散 vs 写扩散

读扩散

我们先来看看读扩散。如上图所示,A与每个聊天的人跟群都有一个信箱(有些博文会叫Timeline),A在查看聊天信息的时候需要读取所有有新消息的信箱。这里的读扩散需要注意与Feeds系统的区别,在Feeds系统中,每个人都有一个写信箱,写只需要往自己的写信箱里写一次就好了,读需要从所有关注的人的写信箱里读。但IM系统里的读扩散通常是每两个相关联的人就有一个信箱,或者每个群一个信箱。

读扩散的优点:

写操作(发消息)很轻量,不管是单聊还是群聊,只需要往相应的信箱写一次就好了

每一个信箱天然就是两个人的聊天记录,可以方便查看聊天记录跟进行聊天记录的搜索

读扩散的缺点:

读操作(读消息)很重

写扩散

接下来看看写扩散。

e38c316470c48ab4e7f8492ec20398672f5e14.jpg.png

在写扩散中,每个人都只从自己的信箱里读取消息,但写(发消息)的时候,对于单聊跟群聊处理如下:

单聊:往自己的信箱跟对方的信箱都写一份消息,同时,如果需要查看两个人的聊天历史记录的话还需要再写一份(当然,如果从个人信箱也能回溯出两个人的所有聊天记录,但这样效率会很低)。

群聊:需要往所有的群成员的信箱都写一份消息,同时,如果需要查看群的聊天历史记录的话还需要再写一份。可以看出,写扩散对于群聊来说大大地放大了写操作。

写扩散优点:

读操作很轻量

可以很方便地做消息的多终端同步

写扩散缺点:

写操作很重,尤其是对于群聊来说

注意,在Feeds系统中:

写扩散也叫:Push、Fan-out或者Write-fanout

读扩散也叫:Pull、Fan-in或者Read-fanout

 

唯一ID设计

通常情况下,ID的设计主要有以下几大类:

UUID

基于Snowflake的ID生成方式

基于申请DB步长的生成方式

基于Redis或者DB的自增ID生成方式

特殊的规则生成唯一ID

具体的实现方法跟优缺点可以参考之前的一篇博文:分布式唯一 ID 解析

在IM系统中需要唯一Id的地方主要是:

会话ID

消息ID

 

消息ID

我们来看看在设计消息ID时需要考虑的三个问题。

 

消息ID不递增可以吗

我们先看看不递增的话会怎样:

使用字符串,浪费存储空间,而且不能利用存储引擎的特性让相邻的消息存储在一起,降低消息的写入跟读取性能

使用数字,但数字随机,也不能利用存储引擎的特性让相邻的消息存储在一起,会加大随机IO,降低性能;而且随机的ID不好保证ID的唯一性

因此,消息ID最好是递增的。

 

全局递增 vs 用户级别递增 vs 会话级别递增

全局递增:指消息ID在整个IM系统随着时间的推移是递增的。全局递增的话一般可以使用Snowflake(当然,Snowflake也只是worker级别的递增)。此时,如果你的系统是读扩散的话为了防止消息丢失,那每一条消息就只能带上上一条消息的ID,前端根据上一条消息判断是否有丢失消息,有消息丢失的话需要重新拉一次。

用户级别递增:指消息ID只保证在单个用户中是递增的,不同用户之间不影响并且可能重复。典型代表:微信。如果是写扩散系统的话信箱时间线ID跟消息ID需要分开设计,信箱时间线ID用户级别递增,消息ID全局递增。如果是读扩散系统的话感觉使用用户级别递增必要性不是很大。

会话级别递增:指消息ID只保证在单个会话中是递增的,不同会话之间不影响并且可能重复。典型代表:QQ。

 

连续递增 vs 单调递增

连续递增是指ID按 1,2,3...n 的方式生成;而单调递增是指只要保证后面生成的ID比前面生成的ID大就可以了,不需要连续。

据我所知,QQ的消息ID就是在会话级别使用的连续递增,这样的好处是,如果丢失了消息,当下一条消息来的时候发现ID不连续就会去请求服务器,避免丢失消息。此时,可能有人会想,我不能用定时拉的方式看有没有消息丢失吗?当然不能,因为消息ID只在会话级别连续递增的话那如果一个人有上千个会话,那得拉多少次啊,服务器肯定是抗不住的。

对于读扩散来说,消息ID使用连续递增就是一种不错的方式了。如果使用单调递增的话当前消息需要带上前一条消息的ID(即聊天消息组成一个链表),这样,才能判断消息是否丢失。

总结一下就是:

写扩散:信箱时间线ID使用用户级别递增,消息ID全局递增,此时只要保证单调递增就可以了

读扩散:消息ID可以使用会话级别递增并且最好是连续递增

 

会话ID

我们来看看设计会话ID需要注意的问题:

其中,会话ID有种比较简单的生成方式(特殊的规则生成唯一ID):拼接 from_user_id 跟 to_user_id:

如果 from_user_id 跟 to_user_id 都是32位整形数据的话可以很方便地用位运算拼接成一个64位的会话ID,即: conversation_id = ${from_user_id} << 32 | ${to_user_id} (在拼接前需要确保值比较小的用户ID是 from_user_id,这样任意两个用户发起会话可以很方便地知道会话ID)

如果from_user_id 跟 to_user_id 都是64位整形数据的话那就只能拼接成一个字符串了,拼接成字符串的话就比较伤了,浪费存储空间性能又不好。

前东家就是使用的上面第1种方式,第1种方式有个硬伤:随着业务在全球的扩展,32位的用户ID如果不够用需要扩展到64位的话那就需要大刀阔斧地改了。32位整形ID看起来能够容纳21亿个用户,但通常我们为了防止别人知道真实的用户数据,使用的ID通常不是连续的,这时,32位的用户ID就完全不够用了。因此,该设计完全依赖于用户ID,不是一种可取的设计方式。

因此,会话ID的设计可以使用全局递增的方式,加一个映射表,保存from_user_id、to_user_id跟conversation_id的关系。

 

推模式 vs 拉模式 vs 推拉结合模式

在IM系统中,新消息的获取通常会有三种可能的做法:

推模式:有新消息时服务器主动推给所有端(iOS、Android、PC等)

拉模式:由前端主动发起拉取消息的请求,为了保证消息的实时性,一般采用推模式,拉模式一般用于获取历史消息

推拉结合模式:有新消息时服务器会先推一个有新消息的通知给前端,前端接收到通知后就向服务器拉取消息

推模式简化图如下:

04cfb696364aeffcfdd83382d29a45bfefb11a.jpg.png

如上图所示,正常情况下,用户发的消息经过服务器存储等操作后会推给接收方的所有端。但推是有可能会丢失的,最常见的情况就是用户可能会伪在线(是指如果推送服务基于长连接,而长连接可能已经断开,即用户已经掉线,但一般需要经过一个心跳周期后服务器才能感知到,这时服务器会错误地以为用户还在线;伪在线是本人自己想的一个概念,没想到合适的词来解释)。因此如果单纯使用推模式的话,是有可能会丢失消息的。

推拉结合模式简化图如下:

6760f5926b20f8763ec72480c1328e859795dd.jpg.png

可以使用推拉结合模式解决推模式可能会丢消息的问题。在用户发新消息时服务器推送一个通知,然后前端请求最新消息列表,为了防止有消息丢失,可以再每隔一段时间主动请求一次。可以看出,使用推拉结合模式最好是用写扩散,因为写扩散只需要拉一条时间线的个人信箱就好了,而读扩散有N条时间线(每个信箱一条),如果也定时拉取的话性能会很差。

 

业界解决方案

前面了解了IM系统的常见设计问题,接下来我们再看看业界是怎么设计IM系统的。研究业界的主流方案有助于我们深入理解IM系统的设计。以下研究都是基于网上已经公开的资料,不一定正确,大家仅作参考就好了。

微信

虽然微信很多基础框架都是自研,但这并不妨碍我们理解微信的架构设计。从微信公开的《从0到1:微信后台系统的演进之路》这篇文章可以看出,微信采用的主要是:写扩散 + 推拉结合。由于群聊使用的也是写扩散,而写扩散很消耗资源,因此微信群有人数上限(目前是500)。所以这也是写扩散的一个明显缺点,如果需要万人群就比较难了。

从文中还可以看出,微信采用了多数据中心架构:

 62f39582378e16672d3132062133f47142ce8e.jpg.png

 


微信每个数据中心都是自治的,每个数据中心都有全量的数据,数据中心间通过自研的消息队列来同步数据。为了保证数据的一致性,每个用户都只属于一个数据中心,只能在自己所属的数据中心进行数据读写,如果用户连了其它数据中心则会自动引导用户接入所属的数据中心。而如果需要访问其它用户的数据那只需要访问自己所属的数据中心就可以了。同时,微信使用了三园区容灾的架构,使用Paxos来保证数据的一致性。

从微信公开的《万亿级调用系统:微信序列号生成器架构设计及演变》这篇文章可以看出,微信的ID设计采用的是:基于申请DB步长的生成方式 + 用户级别递增。如下图所示:

1.png

微信的序列号生成器由仲裁服务生成路由表(路由表保存了uid号段到AllocSvr的全映射),路由表会同步到AllocSvr跟Client。如果AllocSvr宕机的话会由仲裁服务重新调度uid号段到其它AllocSvr。

 

钉钉

钉钉公开的资料不多,从《阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处》这篇文章我们只能知道,钉钉最开始使用的是写扩散模型,为了支持万人群,后来貌似优化成了读扩散。

但聊到阿里的IM系统,不得不提的是阿里自研的Tablestore。一般情况下,IM系统都会有一个自增ID生成系统,但Tablestore创造性地引入了主键列自增,即把ID的生成整合到了DB层,支持了用户级别递增(传统MySQL等DB只能支持表级自增,即全局自增)。具体可以参考:《如何优化高并发IM系统架构》

 

Twitter

什么?Twitter不是Feeds系统吗?这篇文章不是讨论IM的吗?是的,Twitter是Feeds系统,但Feeds系统跟IM系统其实有很多设计上的共性,研究下Feeds系统有助于我们在设计IM系统时进行参考。再说了,研究下Feeds系统也没有坏处,扩展下技术视野嘛。

Twitter的自增ID设计估计大家都耳熟能详了,即大名鼎鼎的Snowflake,因此ID是全局递增的。


从这个视频分享《How We Learned to Stop Worrying and Love Fan-In at Twitter》可以看出,Twitter一开始使用的是写扩散模型,Fanout Service负责扩散写到Timelines Cache(使用了Redis),Timeline Service负责读取Timeline数据,然后由API Services返回给用户。

但由于写扩散对于大V来说写的消耗太大,因此后面Twitter又使用了写扩散跟读扩散结合的方式。如下图所示:



 2.png

对于粉丝数不多的用户如果发Twitter使用的还是写扩散模型,由Timeline Mixer服务将用户的Timeline、大V的写Timeline跟系统推荐等内容整合起来,最后再由API Services返回给用户。

 

58到家

58到家实现了一个通用的实时消息平台:

3.png

可以看出,msg-server保存了应用跟MQ主题之间的对应关系,msg-server根据这个配置将消息推到不同的MQ队列,具体的应用来消费就可以了。因此,新增一个应用只需要修改配置就可以了。

58到家为了保证消息投递的可靠性,还引入了确认机制:消息平台收到消息先落地数据库,接收方收到后应用层ACK再删除。使用确认机制最好是只能单点登录,如果多端能够同时登录的话那就比较麻烦了,因为需要所有端都确认收到消息后才能删除。

看到这里,估计大家已经明白了,设计一个IM系统很有挑战性。我们还是继续来看设计一个IM系统需要考虑的问题吧。

 

IM需要解决的问题

如何保证消息的实时性

在通信协议的选择上,我们主要有以下几个选择:

使用TCP Socket通信,自己设计协议:58到家等等

使用UDP Socket通信:QQ等等

使用HTTP长轮循:微信网页版等等

不管使用哪种方式,我们都能够做到消息的实时通知。但影响我们消息实时性的可能会在我们处理消息的方式上。例如:假如我们推送的时候使用MQ去处理并推送一个万人群的消息,推送一个人需要2ms,那么推完一万人需要20s,那么后面的消息就阻塞了20s。如果我们需要在10ms内推完,那么我们推送的并发度应该是:人数:10000 / (推送总时长:10 / 单个人推送时长:2) = 2000

因此,我们在选择具体的实现方案的时候一定要评估好我们系统的吞吐量,系统的每一个环节都要进行评估压测。只有把每一个环节的吞吐量评估好了,才能保证消息推送的实时性。

如何保证消息时序

以下情况下消息可能会乱序:

发送消息如果使用的不是长连接,而是使用HTTP的话可能会出现乱序。因为后端一般是集群部署,使用HTTP的话请求可能会打到不同的服务器,由于网络延迟或者服务器处理速度的不同,后发的消息可能会先完成,此时就产生了消息乱序。解决方案:

前端依次对消息进行处理,发送完一个消息再发送下一个消息。这种方式会降低用户体验,一般情况下不建议使用。

带上一个前端生成的顺序ID,让接收方根据该ID进行排序。这种方式前端处理会比较麻烦一点,而且聊天的过程中接收方的历史消息列表中可能会在中间插入一条消息,这样会很奇怪,而且用户可能会漏读消息。但这种情况可以通过在用户切换窗口的时候再进行重排来解决,接收方每次收到消息都先往最后面追加。

通常为了优化体验,有的IM系统可能会采取异步发送确认机制(例如:QQ)。即消息只要到达服务器,然后服务器发送到MQ就算发送成功。如果由于权限等问题发送失败的话后端再推一个通知下去。这种情况下MQ就要选择合适的Sharding策略了:

按to_user_id进行Sharding:使用该策略如果需要做多端同步的话发送方多个端进行同步可能会乱序,因为不同队列的处理速度可能会不一样。例如发送方先发送m1然后发送m2,但服务器可能会先处理完m2再处理m1,这里其它端会先收到m2然后是m1,此时其它端的会话列表就乱了。

按conversation_id进行Sharding:使用该策略同样会导致多端同步会乱序。

按from_user_id进行Sharding:这种情况下使用该策略是比较好的选择

通常为了优化性能,推送前可能会先往MQ推,这种情况下使用to_user_id才是比较好的选择。

用户在线状态如何做

很多IM系统都需要展示用户的状态:是否在线,是否忙碌等。主要可以使用Redis或者分布式一致性哈希来实现用户在线状态的存储。

看上面的图可能会有人疑惑,为什么每次心跳都需要更新Redis?如果我使用的是TCP长连接那是不是就不用每次心跳都更新了?确实,正常情况下服务器只需要在新建连接或者断开连接的时候更新一下Redis就好了。但由于服务器可能会出现异常,或者服务器跟Redis之间的网络会出现问题,此时基于事件的更新就会出现问题,导致用户状态不正确。因此,如果需要用户在线状态准确的话最好通过心跳来更新在线状态。

由于Redis是单机存储的,因此,为了提高可靠性跟性能,我们可以使用Redis Cluster或者Codis。

使用分布式一致性哈希需要注意在对Status Server Cluster进行扩容或者缩容的时候要先对用户状态进行迁移,不然在刚操作时会出现用户状态不一致的情况。同时还需要使用虚拟节点避免数据倾斜的问题。

 

多端同步怎么做

读扩散

前面也提到过,对于读扩散,消息的同步主要是以推模式为主,单个会话的消息ID顺序递增,前端收到推的消息如果发现消息ID不连续就请求后端重新获取消息。但这样仍然可能丢失会话的最后一条消息,为了加大消息的可靠性,可以在历史会话列表的会话里再带上最后一条消息的ID,前端在收到新消息的时候会先拉取最新的会话列表,然后判断会话的最后一条消息是否存在,如果不存在,消息就可能丢失了,前端需要再拉一次会话的消息列表;如果会话的最后一条消息ID跟消息列表里的最后一条消息ID一样,前端就不再处理。这种做法的性能瓶颈会在拉取历史会话列表那里,因为每次新消息都需要拉取后端一次,如果按微信的量级来看,单是消息就可能会有20万的QPS,如果历史会话列表放到MySQL等传统DB的话肯定抗不住。因此,最好将历史会话列表存到开了AOF(用RDB的话可能会丢数据)的Redis集群。这里只能感慨性能跟简单性不能兼得。

 

写扩散

对于写扩散来说,多端同步就简单些了。前端只需要记录最后同步的位点,同步的时候带上同步位点,然后服务器就将该位点后面的数据全部返回给前端,前端更新同步位点就可以了。

如何处理未读数

在IM系统中,未读数的处理非常重要。未读数一般分为会话未读数跟总未读数,如果处理不当,会话未读数跟总未读数可能会不一致,严重降低用户体验。

读扩散

对于读扩散来说,我们可以将会话未读数跟总未读数都存在后端,但后端需要保证两个未读数更新的原子性跟一致性,一般可以通过以下两种方法来实现:

使用Redis的multi事务功能,事务更新失败可以重试。但要注意如果你使用Codis集群的话并不支持事务功能。

使用Lua嵌入脚本的方式。使用这种方式需要保证会话未读数跟总未读数都在同一个Redis节点(Codis的话可以使用Hashtag)。这种方式会导致实现逻辑分散,加大维护成本。

写扩散

对于写扩散来说,服务端通常会弱化会话的概念,即服务端不存储历史会话列表。未读数的计算可由前端来负责,标记已读跟标记未读可以只记录一个事件到信箱里,各个端通过重放该事件的形式来处理会话未读数。使用这种方式可能会造成各个端的未读数不一致,至少微信就会有这个问题。

如果写扩散也通过历史会话列表来存储未读数的话那用户时间线服务跟会话服务紧耦合,这个时候需要保证原子性跟一致性的话那就只能使用分布式事务了,会大大降低系统的性能。

如何存储历史消息

读扩散

对于读扩散,只需要按会话ID进行Sharding存储一份就可以了。

 

写扩散

对于写扩散,需要存储两份:一份是以用户为Timeline的消息列表,一份是以会话为Timeline的消息列表。以用户为Timeline的消息列表可以用用户ID来做Sharding,以会话为Timeline的消息列表可以用会话ID来做Sharding。

 

数据冷热分离

对于IM来说,历史消息的存储有很强的时间序列特性,时间越久,消息被访问的概率也越低,价值也越低。如果我们需要存储几年甚至是永久的历史消息的话(电商IM中比较常见),那么做历史消息的冷热分离就非常有必要了。数据的冷热分离一般是HWC(Hot-Warm-Cold)架构。对于刚发送的消息可以放到Hot存储系统(可以用Redis)跟Warm存储系统,然后由Store Scheduler根据一定的规则定时将冷数据迁移到Cold存储系统。获取消息的时候需要依次访问Hot、Warm跟Cold存储系统,由Store Service整合数据返回给IM Service。

 

接入层怎么做

实现接入层的负载均衡主要有以下几个方法:

硬件负载均衡:例如F5、A10等等。硬件负载均衡性能强大,稳定性高,但价格非常贵,不是土豪公司不建议使用。

使用DNS实现负载均衡:使用DNS实现负载均衡比较简单,但使用DNS实现负载均衡如果需要切换或者扩容那生效会很慢,而且使用DNS实现负载均衡支持的IP个数有限制、支持的负载均衡策略也比较简单。

DNS + 4层负载均衡 + 7层负载均衡架构:例如 DNS + DPVS + Nginx 或者 DNS + LVS + Nginx。有人可能会疑惑为什么要加入4层负载均衡呢?这是因为7层负载均衡很耗CPU,并且经常需要扩容或者缩容,对于大型网站来说可能需要很多7层负载均衡服务器,但只需要少量的4层负载均衡服务器即可。因此,该架构对于HTTP等短连接大型应用很有用。当然,如果流量不大的话只使用DNS + 7层负载均衡即可。但对于长连接来说,加入7层负载均衡Nginx就不大好了。因为Nginx经常需要改配置并且reload配置,reload的时候TCP连接会断开,造成大量掉线。

DNS + 4层负载均衡:4层负载均衡一般比较稳定,很少改动,比较适合于长连接。

对于长连接的接入层,如果我们需要更加灵活的负载均衡策略或者需要做灰度的话,那我们可以引入一个调度服务,如下图所示:

 4.png

Access Schedule Service可以实现根据各种策略来分配Access Service,例如:

根据灰度策略来分配

根据就近原则来分配

根据最少连接数来分配

 

架构心得

最后,分享一下做大型应用的架构心得:

灰度!灰度!灰度!

监控!监控!监控!

告警!告警!告警!

缓存!缓存!缓存!

限流!熔断!降级!

低耦合,高内聚!

避免单点,拥抱无状态!

评估!评估!评估!

压测!压测!压测!

 

来源:InfoQ 


开发者真的喜欢开源吗?

技术交流大兴 发表了文章 • 0 个评论 • 47 次浏览 • 2020-09-07 10:03 • 来自相关话题

作者 | Matt Asay译者 | 风车云马,责编 | 屠敏出品 | CSDN(ID:CSDNnews)FaunaDB创始人埃文·韦弗(Evan Weaver)有一个疯狂的想法:只要给开发者一个开放的API和一个适合他们开发的模型,那么云是最好的选... ...查看全部
作者 | Matt Asay

译者 | 风车云马,责编 | 屠敏
出品 | CSDN(ID:CSDNnews)

timg.jpg

FaunaDB创始人埃文·韦弗(Evan Weaver)有一个疯狂的想法:只要给开发者一个开放的API和一个适合他们开发的模型,那么云是最好的选择,即便是Linux和Kubernetes这样的开源平台。也许他们不想为任何代码做“手术”。那么,他们感兴趣的是云,而不是代码?

这是一个大胆的想法,倒也存在合理的地方。然而,当我向业界的重量级人物提出这个想法时,他们却因为各种不同的原因否定了。根据Lightspeed的投资者瓜拉夫•古普塔(Guarav Gupta)的说法,“开发人员对开源有着深深的喜爱和欣赏,几乎像是上瘾了”,而这是开发人员对API所没有的感觉。

有没有一种方法既能方便地使用API,又不会失去开发人员对开源社区的归属感?答案似乎是肯定的,但实现起来有点复杂。

不要忘记数据

StrAPI提供一种开源headless CMS,它的联合创始人兼CPO Aurélien Georget认为,开源的持久魅力不仅在于代码。例如,StrAPI的许多客户都想要定制他们的CMS。在这种情况下,云服务不能满足他们的需求。

即使他们不想修改代码,数据也会驱使他们这样做:“我们的用户对他们的代码的所有权不感兴趣,而是对他们的数据感兴趣。出于数据隐私的原因,或者从法律的角度出发(例如银行、保险、公共管理等部门),他们需要在自己的数据中心运行他们的代码——并保存他们的数据。”当然,这并不是说Weaver坚持以API为中心的方法是错误的,“每个解决方案都应该是面向API的,”Georget赞同道,“因为它让开发人员富有创造力,想象新的用例,并进行创新。”

Georget表示,使用云服务或许会更方便,但对于某些类别的应用程序或客户来说,这并不总是可行的。

DataStax为ApacheCassandra数据库社区提供代码和操作技巧,其首席宣传官PatrickMcFadin认为,API可能会成为一扇单向的门,过去开放的API随着时间的推移会被锁起来,或者被挡在付费墙后面。未来数据之争只会带来严重的专利化。相比之下,代码可以免费提供,因为它不是大多数企业(包括软件供应商)的核心“业务”。

Instaclustr的首席技术官本•布罗姆海德(BenBromhead)有一个理想的折中方案。Instaclustr将ApacheKafka等开源软件作为托管服务运行。只要建立的云服务严格遵守开源标准,他们就不会真正失去代码或数据的独立性。

开源数据层技术保证公司完全控制自己的数据和流程。通过选择100%的开源技术,公司可以拥有自己的代码,并保持不受供应商或技术的限制。比代码本身更重要的是,真正的开源技术确保了公司的关键信息流程不会被提供专有解决方案的供应商所破坏,也不会在任何情况下干扰公司充分利用自己数据的能力。

核心在于信任

这让我们回到了古普塔的观点,即开源有一些不同的,也许更好的东西。尽管“人们最终还是想以云服务的形式消费”,但是他认为,代码对于营造真正的技术亲和力——甚至对技术的喜爱和欣赏——是至关重要的。

您能通过API实现这种“亲密”(cosmic closeness)吗?古普塔认为不会。他说,“如果你是开源的,那么很容易构建社区,而不是带API的黑箱云服务”。因为,“作为一名开发者,你想通过自己的感觉和理解来开发它,并成为团队的一部分。”

这种开源运动的核心是信任:开放代码、开放路线图、开放交流、开放决策。这就是像Instaclustr这样的云供应商的成功之处:云的易用性以及对开源的信任和控制,可以为客户提供他们想要的东西。好的开源公司会建立起令人不可思议的信任。明智的云计算公司不甘示弱,一定不会让开发人员缺失在开源社区中的那种信任和喜爱。

原文:https://www.infoworld.com/article/3572324/do-developers-really-care-about-open-source.html


数十家技术社区联名推荐的GeekOnline来了!

科技前线融云那些事 发表了文章 • 2 个评论 • 136 次浏览 • 2020-07-09 10:21 • 来自相关话题

极客是一群什么样的人?他们大智若愚而富有科学精神,对一切常规的东西天然反感;他们天生热爱探索和创造,对于跟随和人云亦云深恶痛绝;他们特立独行,从不自我设置禁区;他们信仰自由,对于人为的限制极其不屑并热衷于挑战权威;在工作中他们推崇化繁为简,相信技术的力量并追求... ...查看全部

极客是一群什么样的人?他们大智若愚而富有科学精神,对一切常规的东西天然反感;他们天生热爱探索和创造,对于跟随和人云亦云深恶痛绝;他们特立独行,从不自我设置禁区;他们信仰自由,对于人为的限制极其不屑并热衷于挑战权威;在工作中他们推崇化繁为简,相信技术的力量并追求产品美学……


现在,由通信云技术领导者融云推出的开发者社区 GeekOnline 正式与全球极客们见面啦!


崇尚科技、自由和创造力的极客精神,GeekOnline 致力于成为一个创意与价值兼备、兴趣和温度并存的技术社区。


这是一个开发者们的技术乐园,也是国内首个全面覆盖了 IM、RTC 等通信云技术的极客社区。在移动互联网概念中,设想所有的客户端都“永远在线”,即每条消息发出必有回响。Online 既是一种态度,也是一种精神,正如 IM 所连接的每一个用户,极客永远在线。

极客在线微信头图.png


全球首个互联网通信云领域技术社区


不同于其他技术类社区体系的繁杂,GeekOnline 社区更专注于 IM 即时通讯、RTC 实时音视频以及与通信云相关领域的技术交流。在内容上 GeekOnline 社区提供了 IM、RTC、5G、出海等上百个热门话题让开发者们讨论和学习。目前 GeekOnline 社区与融云账号系统已全面打通,融云用户使用邮箱密码即可直接登录。


此外,关于更多互联网通信云领域的技术问题,开发者还可以通过社区中的“帮助”板块寻求解决。融云将 GeekOnline 社区打造成内容坚实的技术资料库,整理了针对通信云场景的各项技术资料,涵盖 IM 即时通讯、实时音视频、小程序、客服以及内容审核等众多维度。经过融云多年的技术积累,几乎所有关于通信云领域的常见问题都可以通过“帮助”板块来解决。


全球数十家技术社区联合推荐


GeekOnline 社区上线初期就已经获得了国内外几十家社区及媒体伙伴的认可并建立了联系,其中包括开源中国、51CTO、SegmentFault、LiveVideoStack、ThoughtWorks 等国内外知名技术社区。

社区合作伙伴.png


除此以外,还有多个活跃在技术社区的 KOL 已经入驻 GeekOnline,分享自己的经验观点并与用户们互动交流。还有一个秘密提前透露给你,一场由中国最专业的技术社区们共同发起的开发者活动,不久后也将与开发者朋友们见面。


开发者互助,每一个问题都必有所得


相关调研数据显示,在技术开发过程中,大约 76% 的开发者都希望获得代码、数据的支持,而超过 81% 的开发者则需要更多技术经验和课程的学习。


在 GeekOnline 社区,开发者可以自行搜索想了解的问题,在技术大牛的文章或者问题回复中找到所需的答案,亦或是从其他人的讨论中获得新的灵感。如果在搜索结果中没有收获,还可以针对性地提出问题,@业界技术大牛来回复和解答。此外在疫情期间,不少开发者都面临着一些工作上的难题,在这里大家还可以“抱团取暖”,找寻新的合作点或者互相推荐工作机会。


在 GeekOnline 社区建设中,融云还将陆续注入更多资源,针对专属社区会员定制周边礼品,为开发者们提供专业技术类书籍、技术大会演讲 PPT 等资料下载,打造丰富多彩的开发者活动,并定期邀请来自于各领域的技术专家与开发者们在线上线下进行互动,期待有更多的开发者伙伴加入 GeekOnline,一起打造中国最 Geek 的技术社区。


福利时间到


点击下方传送门参与活动即可获得融云精心准备的“GeekOnline加油包”!惊喜礼品在等你,快来参加吧!


传送门https://geekonline.rongcloud.cn/question/701