面向服务的架构SOA
任何大型网站的发展都伴随着网站架构的演进。网站架构一般最初是单应用设计,然后逐渐经历面向对象设计和模块化设计的架构,最终发展到面向服务的服务化架构。在单应用设计架构体系当中,我们关注的是方法和实体;而在面向服务的服务化架构中,我们则关注的是服务和API。网站架构演进图如下图所示:
传统应用开发中会面临研发成本高,运维效率低等挑战。
研发成本高主要体现在:
- 代码重复率高:在实际项目分工时,开发都是各种负责几个功能,即使开发之间存在功能重叠,往往也是选择自己实现,而不是类库共享。
- 需求变更困难:代码重复率高后,已有功能变更和新需求加入时都会非常困难。所有重复开发的功能都需要重新修改和测试,很容易出现修改不一致或者被遗漏。
- 无法满足新业务的快速迭代和交互等问题。
运维效率低主要体现在: - 测试、部署成本高:业务运行在一个进程中,因此系统中任何程序的改变,都需要对整个系统重新测试并部署
- 可伸缩性差:水平扩展只能基于整个系统进行扩展,无法针对某一功能模块按需扩展
- 可靠性差:某个应用BUG,会导致整个进程宕机,影响其他应用
为了解决单体架构面临的挑战,会对系统进行拆分、解耦、独立、分层。将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心;同时将公共API抽取出来,作为独立的公共服务供其他调用者消费,以实现服务的共享和重用,降低开发和运维成本。传统的单应用设计逐渐演进为了面向服务的服务化架构。
既然系统都是由成千上万大大小小的服务组成,各服务部署在不同的机器上,由不同的团队负责。这时就会遇到两个问题:
1. 要搭建一个新服务,免不了需要依赖他人的服务,而现在他人的服务都在远端,怎么调用?
2. 其他团队要使用我们的新服务,我们的服务该怎么发布以便他人调用?
服务调用
由于各服务部署在不同机器,服务间的调用免不了网络通信过程,服务消费方每调用一个服务都要写一坨网络通信相关的代码,不仅复杂而且极易出错。
如果有一种方式能让我们像调用本地服务一样调用远程服务,并且让调用者对网络通信这些细节透明,那么将大大提高生产力,比如服务消费方在调用本地 的一个接口时,实质上调用的是远端的服务。这种方式其实就是RPC(Remote Procedure Call Protocol),如今rpc在各大互联网公司中被广泛使用,如阿里巴巴的hsf、dubbo(开源)、Facebook的thrift(开源)、Google grpc(开源)、Twitter的finagle(开源)等。当然,也可以通过HTTP + JSON的方式来进行服务间的调用,本质上HTTP的方式也是属于RPC的一种实现,但是HTTP的方式并不会像调用本地服务一样那么直观,同时也有一定的性能问题。
服务调用方式
服务的调用方式,主要有三种:
- 同步服务调用:最常见、简单的服务调用,即同步等待服务方返回。为了防止服务端长时间不返回应答消息导致客户端线程被挂死,用户线程等待的时候需要设置超时时间。
- 异步服务调用:异步服务调用有两种实现方式:一种是只通过Future来实现,还有一种是通过构造Listener对象并将其添加到Future中,用于服务端应答的异步回调。通过Future方式时,线程会阻塞在get结果的操作上;而使用Listener的方式是监听器异步的获取执行结果。
RPC通信细节
要让网络通信细节对使用者透明,我们需要对通信细节进行封装,我们先看下一个RPC调用的流程涉及到哪些通信细节:
1. 服务消费方(client)调用以本地调用方式调用服务;
2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
3. client stub找到服务