本文为阿里云容器服务spring cloud应用开发系列文章的第一篇。
一、在阿里云容器服务上开发spring cloud微服务应用(本文)
单体应用通常指在一个程序中满足多个业务或技术领域的需求,不同的需求领域内化为模块。假定我们要开发一个web应用,通常的mvc模式可以满足要求。针对不同领域有不少代码生成工具可以快生成代码框架,从0到1创建一个应用非常容易。放在一个应用里处理所有的事情的好处是非常大的,比如程序的调试相对容易,执行效率高。
单体的应用在规模变大后其前述好处会很快衰减。随着业务的增长,需求的调整和变更,应用内部会以模块为基础进行重构,增加和删减、改变模块的能力。应用逐渐超越开发人员所能掌控的范围,代码死角开始出现,重构变得困难。变更经常是牵一发而动全身。针对任何一个模块的扩容或升级都是对整个应用的所有模块的扩容和升级。程序对外界的依赖越来越复杂,自动化测试的覆盖率低。
如何避免单体应用的问题引入了微服务的概念。每个服务只处理一件事情,应用由多个服务构成。服务之间通过web协议进行通信,例如http/json。代码要易于抛弃:出问题的代码可以更容易地重写。运行的服务也要易于抛弃,升级时用一个新的服务实例替代旧的实例。
把单体应用分解为一些列微服务,开发团队也可以进行“重构”,每个小团队负责一个服务,维护和学习成本下降了。由于服务间通过接口进行连接,每个服务的内部实现机制可以根据领域选择更合适的技术,混合编程是很自然的事情,试错的成本降低、试错的频率加快,从而意味着创新的速度可以提高。
如此美好的微服务也不是“银弹”。它只是说了“微”是好的,但没有说出如何变“微”。如果对业务领域的理解不全,对需求的把控不准,服务可能只是小的单体应用而已。
另外的问题来源于分布式计算。单体服务中两个模块的调用是高效、可靠的,而两个服务之间的网络通信是低效且不可靠的。分布式计算中的很多经典问题无法绕开,比如服务的可用性、数据的一致性等。
服务实例数目的不断变化,数据流量的波动导致监控和日志分析的复杂度大大提升。现在开源的监控和日志方案很多,但如何搭建并定制一套自己的方案并不是一件容易的事。
当然我们不能因噎废食,如果有一个平台已经能够提供其中大部分非功能需求,比如高可用、监控和日志等,那么在其上构建和运营一个微服务应用很容易了。阿里云的容器服务就是这样一个平台。
微服务的实现需要有高度标准化的交付技术来支撑,容器技术很好地满足了这个需求。利用容器技术把应用及其依赖做成一个标准的镜像,从开发到测试到生产环境都用同样的镜像,devops把开发和运维中间的鸿沟弥补起来。容器技术正逐渐成为微服务和devops领域的最佳实践,成为这些领域创新的基石。
在本系列文章中,我们探讨在阿里云的容器服务上,如何利用spring cloud来构造一个微服务应用。
示例模拟的场景是企业内部应用服务化,服务之间通过api相互访问。foo服务和bar服务是两类基本服务,允许内网其他服务通过api访问,但不对外网提供服务。foobar对外提供服务,在处理时需要调用foo服务和bar服务。
服务发现机制由discovery-server提供,基于eureka实现。由于discovery-server的业务无关性,开发人员可以直接使用docker镜像。
foobar服务通过注册到智能应用路由gateway服务上对外提供服务,gateway基于zuul,其代码业务无关,开发人员可以直接使用其docker镜像,但由于zuul是通过配置文件来描述不同的服务对应的url访问模式,所以在实际使用中需要改变镜像中的配置文件,或者通过挂载volume实现配置文件的共享和修改。
foo, bar和foobar的代码中都包含了eureka和ribbon client,实现了向eureka的自注册和查询eureka的能力,ribbon实现了客户端的负载均衡。本示例中没有引入hystrix。
服务间的逻辑架构如下图所示。

每个服务在运行时都可以根据负载水平弹性扩展,每个服务可能由多个运行中的实例构成。所有服务实例在启动时自动注册到eureka server,服务之间的发现也是通过eureka server获取实例信息,并由ribbon client自动判断,选取一个实例进行访问。在这个过程中没有用到集中式的负载均衡,而是通过客户端发现和负载均衡。
在下一篇文章中我们会讨论如何编译和部署这个应用。