本文试图通过渐进的技术剖析,和大家分享咱们在互联网高并发技术计划抉择上的一些思考和决策;从技术选型的高度为架构师在面对高并发业务设计时须要思考的方方面面做一个参考。
Beautiful Concurrency & Pretty Erlang
==========================================
前言
环信以“连贯人与人,连贯人与商业”为使命,旨在为宽广企业开发者提供最优质的寰球即时通讯 PaaS 服务。如何实现高并发场景下,弹性化的保障服务质量是咱们一贯的业务要求和技术谋求。
本文试图通过渐进的技术剖析,和大家分享咱们在互联网高并发技术计划抉择上的一些思考和决策;从技术选型的高度为架构师在面对高并发业务设计时须要思考的方方面面做一个参考。
接下来,enjoy:
Beautiful Concurrency – 并发的必要性
世界是并发的,软件也应该是并发的
咱们生存在其中的世界,就是一个微小的并发零碎。每时每刻,在世界的每一个角落,人类的每一个个体都在和这个世界进行着频繁的能量交互,信息交互也是其中的重要组成部分;当咱们转换身份,以造物主的视角鸟瞰这个世界里所有的万物(动物,动物,陆地,土壤,机器等等)时,也能够看到它们同样在和这个世界进行着无休止的能量 / 信息交互。
为了与这个世界进行无效的交互,软件也应该是并发的。
世界是散布的,软件也应该是散布的
地球是圆的,世界是平的,不管怎样,从宇宙大爆炸的那个奇点之后,就是散布的宇宙 / 世界。作为与世界万物(包含人类本人)交互的软件,天然也必须满足分布式要求。而这种地理分布(Geo. Distribution)个性,也仅是并发在空间维度下的反映而已。
世界不可预测,软件也应该是容错的
没有完满的世界:抵触,劫难随时在产生,不论在什么样的维度上。作为软件,bug,crash 也是不可躲避的事实挑战。即便存在完满的没有 bug 的程序,运行程序的硬件也可能呈现故障。为了加强软件的容错性,代码的独立性(指一个故障不会影响到故障工作以外的其它工作)和故障检测以及故障解决是要害:这所有都须要并发,因为串行程序的容错性远远不如并发程序。
并发计划概览
“七个模型”来源于 Paul Butcher 著的《Seven Concurrency Models in Seven Weeks》,中文译名《七周七并发模型》,概览的介绍了并发畛域的常见计划,心愿能给架构师提供一个轮廓化的分类形容。自己在其根底上增加了一些本人的拓展思考(_见下文中斜体局部_):
1. 线程与锁_:线程与锁模型有很多家喻户晓的有余,但仍是其余模型的技术根底,也是很多并发软件开发的首选。— _这个计划其实是一个 anti-pattern,在高并发场景下如履薄冰(bug,dead lock 如影随形),让开发者和运维人员胆战心惊。__Ugly Locks to Ugly Concurrency (俊俏的锁, 俊俏的并发):locks and condition variables is fundamentally flawed!
2. 函数式编程_:函数式编程日渐重要的起因之一,是其对并发编程和并行编程提供了良好的反对。函数式编程打消了可变状态,所以从根本上是线程平安的,而且易于并行执行。_— 函数之美,逻辑之美!置信很多从命令式语言(Imperative Programming)转战到函数式编程语言(Functional Programminmg)的时候都会收回这样的感叹。其实这一提高恰好体现了人类在一直的进化过程中,对这个世界认知一直提炼,思维模式逐渐由具象走向形象的演进轨迹。而回到高并发的话题上,函数式编程以不可变状态这一繁难的策略,博得了完满赞美,俨然已是君临天下的明日霸主!
3. 拆散标识与状态:_如果一个线程援用了长久数据结构,那么其余线程对数据结构的批改对该线程就是不可见的。因而长久数据结构对并发编程的意义非比寻 常,其拆散了标识 (identity) 与状态(state)。_— 这又是一个很妙的策略,而大家熟知的 version control system 如 git,包含比特币 / 区块链的机制都是这一思维领导下的具体实际,限于篇幅,本文不做进一步开展。
4.Actor Model:_一种适用性很广的并发编程模型,实用于共享内存模型和分布式内存模型,也适宜解决地理分布型问题,能提供弱小的容错性。_— 最开始接触到 Actor 模型就是通过 Erlang 语言,起初又接触到 Akka(基于 Scala)等基于各种语言实现的框架,也越来越领会到这一模型在高并发场景下的熟能生巧。本文后续局部会开展介绍。另外,做个招聘小广告,环信通信云研发团队正在广纳英才,欢送懂 Erlang,有相干高并发开发教训的小伙伴加盟,虚位以待!点这里间接分割咱们哟!
5.CSP(Communicating Sequential Processes, CSP)_:外表上看,CSP 模型与 Actor Model 很类似,两者都基于消息传递。不过 CSP 模型侧重于传递信息的通道,而 Actor Model 侧重于通道两端的实体,应用 CSP 模型的代码会带有显著不同的格调。_— 这里就是 channel 能够大施拳脚的天地了,go go go!限于篇幅,本文不做进一步开展。同样的,欢送懂 Golang,有相干高并发开发教训的小伙伴加盟,虚位以待!
6. 数据级并行:_每个笔记本电脑里都藏着一台超级计算机——GPU。GPU 利用了数据级并行,不仅能够疾速进行图像处理,也能够用于更 广大的畛域。如果要进行有限元剖析、流体力学计算或其余的大量数字计算,GPU 的性能将是不二抉择。_— 在过来的两年里,华人之光,黄仁勋(Jensen Huang,CEO of Nvidia),从自家的壁炉里一次又一次给大家带来了震撼世界的变革产品,让之前遥不可及的 GPU 飞入寻常百姓家,也带来了一次又一次的算力之争。置信在不久的未来,有了 GPU 算力加持和人工智能算法的普惠应用,会有有数的 AI 利用层出不穷的涌现,它们会从云端落地到边缘,人类可能会比本人设想的更早的进入前途未卜的人机争霸时代。
7.Lambda 架构:_Lambda 架构综合了 MapReduce 和流式解决的特点,是一种能够解决多种大数据问题的架构。_— Lambda 架构也是采纳了数据并行处理技术,然而它把并行算力的宏观场景放大到了一个更大的尺度:将数据和计算散布到成千上万台机器组成的集群上进行,将并发,散布个性整合到一套计划中,通过两层(批处理层 -Batch Layer,减速层 -Speed Layer/Streaming Process)的组合,实现了高计算效率和低提早的“鱼与熊掌兼得”。
Pretty Erlang
环信的寰球即时通讯云的外围网络是基于 Erlang/OTP 开发的,截止目前共服务了几十万 APP 客户,单集群日音讯几十亿量级并还在一直挑战新高。多年的教训积攒,咱们仍在一直的对系统进行优化,榨取计算,网络等资源的内生价值,挑战零碎一个又一个一直晋升的指标要求。感激 Erlang/OTP,有了它,咱们像站在了伟人肩膀上的一名挥动利刃的壮士,可能从容应对各种“黑云压城城欲摧”的业务压力和一直变动,形形色色的业务需要。以下,咱们简略总结了 Erlang/OTP 几点让咱们“迷醉的特质”,给想入坑的小伙伴以参考:
Let it Crash!
再一次,让咱们了解了“思维高度决定人生高度”。当其它语言或者解决方案防御性的,千方百计捕捉异样 / 谬误并尽力挽回“败局”的时候,Erlang 驳回了”Let it crash!”的策略,把重点放在了谬误的检测和谬误告诉体系上,通过谨严设计的单向,双向 link 机制,OTP 基于此设计的 supervisor behaviour 层级(Hierarchical)治理汇报机制,”任其解体“的同时又洁净丑陋的将所有 Actor(过程)在产生异样时的行为进行了简略但无效的治理,取得了意想不到的益处:
- 代码简洁易懂,仅在须要关怀解体解决的层级存在容错代码;
- 因为 Actor Model 的设计,Actor 之间互相独立,也不共享状态,因而任何一个 Actor 的解体并不会影响其它 Actor,遑论它的管理者(Supervisor),因而管理者能够从容的解决被治理 Actor 的解体;
- 管理者也能够不解决解体,仅记录相应解体,继而通过查看解体告诉来进行后续解决。这个策略在后续 Pattern Matching 时候还会见到,咱们能够在_Other(所有匹配均未触发)爱护匹配中记录相应信息,而不用为当时预估不到的场景搜索枯肠!
Actor Model
Actor Model 是 Pure OO 的设计(而 Java 类与办法的设计并不是,意不意外?):每一个 Actor 封装了状态,外界并没有任何办法来操纵(manipulate)对象,它们唯有通过发送音讯告诉 Actor,由 Actor 本人管制对音讯的解决,这种简化同样为高并发解决提供了意料之外的弱小反对。
Actor Model 实用于共享内存模型和分布式内存模型,也适宜解决地理分布型问题,能提供弱小的容错性,用了都说好!
我集体在已经的工作中做过一个网络安全过滤的产品,是靠本人团队设计编写的一套音讯队列解决机制来解耦不同业务对雷同数据的解决,起初回想起来才蓦然发现其实这就是个简陋的 Actor Model 设计,只是那个时候还不晓得 Erlang/OTP,要是早用上这把利器,能节俭我生命中多少个日日夜夜啊!
Functional Programming
介绍函数式编程的书籍,材料曾经很多了,本文不做过多的开展。简略列举下集体的几点领会:
- 程序最终还是要交给机器解决,因而要尽量依照机器的思维模式去编写程序(尽管有时候会让程序员头大),最简略的(a + b)与(+ a b):前者对人类敌对,而后者对机器敌对,因此也能带来更好的程序一致性,继而使得零碎能够设计的更简略;
- 命令式编程的代码由一系列扭转全局状态的语句形成,而函数式编程则是将计算过程形象成表达式求值。这些表达式由纯数学函数形成,而这些数学函数是第一类对象(咱们能够像操作数值一样操作第一类对象)并且没有副作用。因为没有副作用,函数式编程能够更容易做到线程平安,因而特地适宜于并发 / 并行编程。
- 函数之美带来美之并发!原文摘抄:
For me, a beautiful program is one that is so simple and elegant that it obviously has no mistakes, rather than merely having no obvious mistakes. If we want to write parallel programs that work reliably, we must pay particular attention to beauty.
Pattern Matching
又一个让你被美折服的设计:函数在这种 Pattern Matching 的语法结构的描摹下,俨然变成了一道道证明题,你只须要形容(并不需要穷举)你所关怀的场景(Conditon)下本人的想法,剩下的就交给”模式匹配“这个自动化机器帮你实现。你可能想不到的是,”Pattern Matching“施展效用的中央并不只是 case/if 呈现的中央,你的整个代码都是在”Pattern Matching“的魔力下散发它优雅的魅力。
Future Evolution
对高并发的不懈谋求将始终是环信通信云研发团队的指标,咱们也期待在这条起伏山路上一直攀登,厚积薄发,为环信的客户带来一贯的极致产品体验!