小P的架构生活(下)

小L强烈建议团队使用微服务,并极力推荐了前公司用的一套分布式事务解决方案。
小P经过反复思考查证并做了大量的尝试后,辨证地对微服务架构做了如下分析:

为什么要用微服务,微服务带来了哪些好处?
1、减少相同代码的copy
2、屏蔽底层,不对外暴露DB,避免DB耦合
3、业务特定,适合由专门团队维护
4、保留无限伸缩能力
其中好处1、2、3通过传统模块化的方式,通过一定的研发管理也能达成。而伸缩能力才是传统的模块化方法无法达到的。所以说追求水平扩展能力才是我们实施微服务的本质目标。

事物都有两面性,服务化带来的坏处又有哪些?
1、带来了分布式事务
2、开发变复杂了,原本简单的方法调用,现在跨了N个工程,通过各种RPC调用
3、对Devops的要求变高
总而言之,就是一切都变复杂了,成本提高了。

无论是用Dubbo、OSP还是后来的Spring Cloud作为服务化实现的框架,服务发现、熔断、负载均衡,链路追踪、配置中心、监控,甚至由于服务的拆分引入Docker并辅以运维自动化的手段。这些知识,或许对开发人员而言一段时间后都将被淡忘,小P认为一个完善的服务化框架应该是对开发人员屏蔽掉底层这一切的,就像现在HTTP编程不需要过多关注底层网络协议的细节实现一样。关于微服务的定义,不同的人有不同的理解,似乎很难达成完全的一致。小P认为服务化第一是为了水平伸缩,第二是为了分工。所以服务化的重点和难点并不在于框架,而在于服务的拆分。

那服务的拆分又有好多套方法,从微观到宏观可以有 一个接口一个服务,一个类一个服务、一个模块一个服务、一个系统一个服务,甚至还可以按层拆分,就像传统的MVC一样,每层搞个服务。各种说法都似乎有它的道理,忽略场景而去找一个放之四海皆准的法则似乎也不太可靠。拆细了,充斥着分布式事务,加上开发运维成本的递增;拆粗了,似乎又没达成当初使用微服务的初衷。

微服务开发中容易碰到的那些坑:
循环依赖问题
A服务依赖B,B依赖C,C又依赖A

多网卡多IP
无论是用Zookeeper或者Eureka服务注册,都需要关注多网卡多IP问题造成服务访问不通。

重试
有重试通常就意味着要实现幂等,别小看这一点点工作量,累加起来成本可不低。

RPC比HTTP效率高?
一般而言二进制协议比通用HTTP效率要高,但HTTP有更好的可读性,还要兼顾测试,再者RPC协议接口对外部还需要一层API Gateway层,将HTTP转为RPC。为了提高一点性能,我们付出这么高的代价到底值不值得?

批量接口
以前单体应用,可以方便加本地缓存,批量获取数据可以循环获取,因为每次都是进程内调用。而现在每次调用都是跨网络调用,再用循环获取时间必然成倍增长。

报表
很多时候报表需要跨多个服务,那到底报表归属给谁?

辨证地分析完之后,系统也从原来的集中化架构,改为分布式微服务架构,如下

服务化前

服务化后

跟大神小L的不断的合作、碰撞与冲突也促使小P不断地去思考,不断地去学习流行的互联网架构技术。对新技术而言,小P会更加谨慎保守,小L则更为激进。小P也意识到了团队管理里面有个很重要的问题:团队的成长与更简单实现任务是一对矛盾,同样一项任务,有简单可靠的实现方案,假设能得80分,花的时间是20%。而用另一种方法去实现完美方案,假设能得99分,需要花100%的时间。小P要做的是尽可能地保证技术架构稍微领先于业务

关于MQ
ZeroMQ、ActiveMQ、RabbitMQ、Kafka、RocketMQ
ZeroMQ其实就是0MQ,就是没有MQ的意思啦,实际上它还真的不是一个MQ,可以认为是Socket的增强库,把它放到跟Netty同一归类才是正确。

ActiveMQ和RabbitMQ都是老牌的MQ产品,前者使用Java实现,是JMS规范的参考实现,后者使用Elang实现,是AMQP的一个实现,通常认为RabbitMQ比ActiveMQ更成熟,更高的性能与稳定性。

Kafka最初被设计用来做日志处理,是一个不折不扣的大数据通道,追求高吞吐,存在丢消息的可能。

RocketMQ是阿里参考了Kafka自行实现的高可靠、高可用、高并发、低延迟MQ产品,需要注意的是RocketMQ有部分特性是不开源的。

如何选择合适的MQ产品是摆在架构师前面的一个挑战,一般而言,对于中小公司来说,选择RabbitMQ是比较中庸的选择:性能够用、安全、可靠、可控、维护简单。当然也要结合具体场景综合选择。有一点需要注意:默认参数配置下,出现脑裂后RabbitMQ集群不会自我恢复,需要人工介入恢复,务必加好监控和报警。

程度员的世界通常只有0和1,也更容易陷入牛角尖里出不来,非比个高下,然后选择性能最高的,小时候我看武侠片也最喜欢问:谁才最厉害,但现在我们已经成年,对于惟性能论我想说的是:
Kafka之所以快是有几个代价的:
第一是用了MMAP,第二是采用顺序文件,第三是ZeroCopy。
MMAP如果要最快,就用异步,反正我消息是写到内存了,操作系统自己决定什么时候写到硬盘,如果没写,那就是人们经常说的Kafka不可靠,可靠吗?不可靠,因为有概率存在。可靠吗?可靠,因为概率极低。而且我还可以配成同步的,但这样就牺牲了性能
顺序文件的话就没有随机操作了,像RocketMq这种也是类似的机制,通常没办法删消息了,只能靠时间到了自己触发。另外其实我觉得对一般的中小公司来说,瓶颈根本就不在消息中间件,你号称一秒100万消息有什么用呢?你的消费者处理能能跟得上吗?如果你直接用Push,结局就是直接把消费者压死,最后还得老老实实的慢慢Pull,那你又得过来反思,我需要那么快的MQ吗?

什么时候考虑用MQ?
上游不关心业务执行结果
上游关心执行结果但要比较长时间才能执行完成

什么时候不用MQ?
上游实时依赖下游的执行结果,而且在业务上也无法妥协为两个步骤,这时候就老实的调用RPC接口吧,如登录。

MQ是如何解耦的?
拿注册送优惠券为例子,紧耦合的做法是在注册完成后加上一段送优惠券的代码,这样就把注册与送券耦合到了一起,哪天营销活动有改动了,不再送券了,则需要修改注册相关代码。
松耦合的做法是注册完成之后发消息给MQ,MQ消费者订阅该队列,在接收到新消息后执行送券逻辑。如果哪天不再送券了,则修改消费者代码不再送券即可。

如何理解MQ能削峰填谷
一般MQ都支持推和拉的模式。如果上游的处理能力远远大于下游的处理能力时,如采用推的方式,则消费者有被压垮的风险。这时消费者可根据自己处理能力,以一定的处理频率拉取消息,避免被压垮。

MQ结合快速失败
对于某些客户期望立即返回处理结果,但生产者和消费者处理能力差异明显的系统,可以使用MQ达到”排队处理”的效果,处理结果使用异步通知。如订票、秒杀业务。生产者在消息体里加上创建时间,消费者在处理消息时判断消息的创建时间与当前系统时间,如果超过了某个值,则直接失败处理。这其实也是业务妥协的结果,即保证系统在高峰期的某个时间段内只有部分用户的请求处理成功优于所有用户的请求都失败。

MQ如何实现消息必达
先本地事务再调用MQ发送消息,则不能保证消息发送成功;先发送MQ做本地事务则不能保证本地事务成功。一般会先本地事务,再调用MQ发送消息,对于发送超时或失败的,有个补偿程序进行补偿,最终一致即可。RocketMQ的事务消息核心原理与之类似,只是中间件做了一部分补偿的活。

MQ消费者幂等设计
消费者要保证收到重复的消息后不会重复处理,一般是使用业务ID,如订单保证惟一,通常使用数据库的惟一索引约束。

常见的解耦手段

数据库中间件
分库分表、读写分离等是常用的优化手段,数据库中间件(在一定程度上)屏蔽了底层的实现,使业务系统不再关注这些细节。

配置中心
各类中间件地址、下游服务地址集中管理,解决使用配置文件时配置私藏引起的诸多问题,另外配置中心更易于运维统一管理。

关于SQL:
尽量由应用去实现join,预留垂直再拆分的空间。通常Mysql的join是通过临时表实现,也更难优化,拆成简单的sql,更容易建索引优化。

常用提升单机性能的常见手段

缓存
难点在于过期数据的淘汰
应用程序角度看:页面缓存、数据缓存
架构的角度看:进程内缓存、分布式(跨网络)缓存
客户端角度看:浏览器缓存、DNS缓存、CDN加速

数据库索引
强烈建议阅读完《高性能Mysql》

减少跨网络调用次数
比如,调用Redis查询用户数据,一次取10个用户数据比10次每次取1个用户数据要快

互联网分布式架构的本质在于分,分完之后最终的瓶颈一定落在CPU、内存、存储和网络上,而后面的一系列的具体战术都是为了减少某个瓶颈。而越是大流量的互联网公司,越容易走在前面,很多中小公司由于流量不够,对大型互联网架构只能停留在纸上谈兵阶段。了解了这些之后,小P觉得自己做为技术的老大,在这里已然很难有技术的提升空间而他要学习的东西实在还有太多,于是毅然选择了某大型互联网公司,不久之后,小L也去了另一家大型互联网公司,全剧终!

posted @ 2017-12-20 23:12  彭彭(moext.com)  阅读(204)  评论(0编辑  收藏  举报