架构设计理念

### 服务化背景
经济学的经典著作[国富论]上讲到,从农业社会过渡到工业社会的标志是分工。如果把公司做类比,在最开始创业阶段大家都是全栈式,很像农民自给自足的生产。随着公司规模的增大,各种业务越来越复杂,研发人员逐步增多,分工越来越细,很多开发人员专注在某个模块自身的业务逻辑上,对系统其它部分则了解甚少。开发人员每天面临大量沟通与协调、代码冲突的问题,整体研发效率急剧下降。
在这种情况下单一系统已经不能满足业务需求,需要对系统进行拆分,在此过程中遇到了诸多挑战,例如各个模块的重用,一致性的保证,高可用性的保证等等。
因此提出了服务化,服务化的核心概念是将大量的核心业务逻辑以RPC的方式暴露给各个前端业务系统,并在底层框架层面实现服务发现、分组、治理。各个系统之间约定接口和规范,使用RPC进行通信调用。
服务化思想将促使开发人员在做系统分析的时候开始思考系统本身的边界,尽管在一开始肯定是不完善的,但是至少使得事情朝着正确的方向来推进。
### 整体设计
核心业务逻辑:什么才算核心业务逻辑?这是一个需要仔细斟酌的问题。一般来说,如果一段逻辑比较固定,有着确切的业务含义,并且存在被其它系统调用的可能,那么就认定其为核心业务逻辑。
这些逻辑均以标准的Java Interface接口对外暴露,接口输入一般是简单类型或者DTO对象,输出同理。DTO(DataTransferObject)对象一般包含了大量数据,意在减少RPC调用次数,一次性把可能会用到的数据都传输过去。
接口必须有相关的文档说明,开发人员不允许将未文档化的接口发布出去。
这样一来各个系统之间就形成比较松的耦合,业务逻辑保持在各自的单元中而不是四处泄漏,项目的边界和依赖也会随之变的清晰。
#### RPC:
由于公司历史原因,存在大量非Java的系统(主要是Python),因此在RPC选型中跨语言调用是一个硬性要求。另外原来使用的RPC的性能问题也是要着重解决掉的。
跨语言是一个比较棘手的问题,不同的语言之间由于数据类型的差异,导致RPC框架本身需要做大量的额外处理。为了保持RPC核心功能的稳定,将Java和非Java的处理分开来,非Java调用是以Java调用加上一层Json映射的方式实现。而Java调用是基于标准的序列化。公司趋势是核心业务逐步迁移到Java,因此整体上也是符合公司战略的。
RPC底层通信则是基于Netty实现,性能上非常有保证。
#### 服务发现:
服务提供者暴露了接口之后,服务消费者需要有感知。并且当某个服务提供者失联后,自动将其从可用的节点中剔除。
这里需要一个全局的配置中心,我们选用的是zookeeper,一方面是看中其稳定可靠;另一方面是文档和社区内容丰富,代码本身也是Java,具备较高的可维护性。最重要的一点,zk提供了强大的功能保证,完全满足服务发现的需求。
服务提供者在启动时,将自身的服务元数据(service metadata)注册到zk中,元数据包含了自身的IP地址,RPC端口,所暴露的Interfaces集合,启动时间等信息。以EPHEMERAL的方式创建zk节点,这样一来一旦当前这台机器挂掉,zk就会自动将该节点删除。
服务消费者在启动时创建一个守护线程,该线程从zk读取节点列表,并在该zk路径下创建一个watch,当列表有变时,zk会自动回调通知该线程。此时再重复读取一次,拿到最新可用节点列表。需要注意的,zk的watch机制对网络要求较高,如果网络不是非常稳定,则watch可能存在失效的问题,因此还有一套方案就是简单粗暴的轮询,即每隔一段时间如10秒,从zk获取列表,然后同上一次的做比较,将差集更新。
#### 服务分组:
同样的服务可能会被多个不同的消费者调用,某些消费者可能调用量非常大,而某些则很少,这样将会导致整个提供者集群都被这些调用量大的消费者拖慢,性能严重下降。解决这个问题的最佳方法是将服务提供者集群切分成多个组,每个组的服务能力各不相同,例如重要并且量大的服务,其分组的节点数和节点质量(硬件条件)都将比其余分组高。
在实现上,服务提供者自身对分组是无感知的,打包部署等操作都是无差别对待。分组只对服务消费者有意义,消费者在编译打包时确定欲调用的服务组,并在启动后从配置中心(zk)拿到运维人员事先配置好与之对应的分组节点列表来初始化。
服务分组对整个系统的稳定性有着重大的意义。在业务层面可以预见到的是类似于短时间内聚集大量流量的营销活动将不会给整站带来过大的压力。
#### 服务治理:
当服务越来越多的时候,服务之间的相互依赖将会非常复杂。不同的服务级别要求也不一样,例如有的非核心服务如果挂掉,不能影响业务主流程,因此强弱依赖是一个关键点。服务治理的另外一个方面就是动态负载均衡与服务性能审计。
对服务依赖的梳理,建立在一套分布式调用跟踪系统之上。目前这套系统还是处在比较初级的阶段,但是已经基本上可以满足需求。核心思想是RPC框架在调用时自动将这次调用的token传递给下游(服务消费者),下游将此token放入ThreadLocal中。每一次调用都将当前上下文(如token、执行时间、执行方法名、执行是否成功)等信息打到日志。
日志采集系统定时将各个节点上的调用链日志统一拉取到HDFS。最后通过离线分析工具如Hive,得到调用链的上下游关系和执行时间。这样一来服务依赖关系和性能审计都得到了解决。
关于方法级别的调用链跟踪,请参考我的另一篇博文。
整个过程暂时无法做到实时,据了解taobao的鹰眼(类似的分布式调用跟踪系统)也未做到完全实时,而是有一定延迟的准实时。这个就要看具体的场景和业务需求了。
动态负载均衡是一个比较大的领域,根据当前公司的业务发展情况来看并不是一个特别紧急的需要。因此就简单做了一下负载均衡,即单纯的round-robin。后面甚至可以考虑引入独立的或第三方的均衡模块来辅助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值