简介
最近在iHeartRadio ,我们决定我们的一个整体的Java后端服务迁移到多个微服务,更具体地说,我们决定使用akka来实现这些微服务然后使用akka集群来实现我们的微服务集群。
这篇文章就是解释我们如何做出这样的决定的。主要分为三个主要的部分:首先对我们要实现的微服务的目标进行了讨论,第二,我们认为akka应用没有http接口是最有意义的追求目标,第三,简单介绍一下最初的架构设计。
我们要实现的目标
目标一:更容易更快的发布周期
庞大的基于java的后端代码是阻止我们细化服务的主要原因。多个服务的代码变化不得不作为一个整体进行测试或质量控制,这意味着一个小小的变化不得不等待其他所有的变化(相关或不相关的),否则就不能够发布。
我们想通过微服务的方式解决这个问题:每个微服务都基于另一部分微服务进行发布,每个微服务都有自己的发布时间表。因此我们可以以更快的速度提供新功能或补丁给客户。
目标二:提高开发效率,宽松清晰的模块依赖
在我们基于java的庞大后端代码中,不同的功能区之间相互依赖紧密。这种相关性很难于进行代码跟踪,从而使代码更难于管理。这些过度的依赖是整个代码的改变变得非常繁琐-在一个地方改变代码,可能需要在多个地方进行相应的改变。这种变化的含义是很难理解的。通过微服务将代码转换成更为明显的依赖关系,则之间的信息传递要简单的多,配置文件也更易于检查。
目标三:更高的复用性和组合性
一个庞大后台的多个类,从多个不同的功能区是逻辑聚集变得越来越大,这使得重用的困难也越来越大。我们希望借此机会重新设计模块,这样每一个微服务有一个更小的接口,并且责任明确。这将使他们更易于作为模块复用或由低层次的服务组成更高层次的微服务。
目标四:更简单的团队整合
庞大的后端代码库规模巨大并且难于理解。这成为了外部开发者效力专业后台开发的一座屏障。每个微服务中的代码规模一方面更温和也更易于学习。这将为不同的组织打开大门,比如客户端开发人员直接对后台代码库或更多个垂直方向的的团队做出贡献。
为什么我们选择了以Akka集群为核心的架构作为我们的微服务
现在我来解释一下做出这样选择的原因:
- 开箱即装即用的集群基础设施
- 松散耦合且没有Json解析的消耗成本
- 微服务内部透明的编程模型
- 强大的社区和商业支持
- 高弹性、高性能、高扩展性
开箱即装即用的集群基础设施
一个微服务的成本是你需要构建集群的基础设施,包括且不限于 - 负载均衡、监控、故障转移和微服务分割。虽然有第三方工具同样可以实现这些功能,但是很难于集成以及复杂的协议栈。akka集群提供了这些集群基础架构组件的开箱即用工具。从部署到运行集群只需要很少的配置文件。
松散耦合且没有Json解析的消耗成本
微服务通常采用的协议是包含json的http接口。这种协议有json解析的性能成本和写作json解析器的开发成本。基于二进制的akka消息沟通性能则更加优化并且不需要写额外的解析代码。
微服务内部透明的编程模型
Akka的actor编程模型,在内部和跨微服务都是透明的。所有的呼叫都是基于异步的消息传递而不管呼叫者和相应者是否在同一个服务内。这种开发模型有两大好处:1) 他使开发经验一致 - 写客户端时不用担心服务端是本地还是远程。2) 它可以更容易的修改涉及到的逻辑。
高弹性、高性能、高扩展性
Apache Spark使用akka作为driver-worker的沟通系统。他们的团队正在建设用阿卡集群无主独立触发模式。它能够轻松满足我们的性能要求。
基于akka集群的微服务
最后一部分,回顾一下整个架构平台。
所有的微服务基于akka实现并运行于一个akka集群。在akka集群层之上,REST层使用Play Framework提供Http作为微服务的公共接口。他们通过部署在akka集群内部的带有路由actor的微服务进行沟通,我们称为agent。这些web程序还处理一些跨部门的功能,比如安全性和缓存。下面的akka簇提供基本的数据库沟通。
微服务实例通过需求可以加入或离开集群,当微服务的冗余实例加入集群时,所有成员都会得到通知,并且自动启动对服务的负载均衡。同样当一个实例离开集群时,所有的成员也会得到通知。这样我们可以根据服务水平扩大缩小服务。