“微服务架构”是现在编程中很流行的概念,未来能和最新趋势保持同步,我也一直在学习了解这个架构,如果具体点说,我一直在寻找一种使用Spring在Java中实现微服务架构的方法。
为什么我会有这个想法,这是有一定的背景的:我们公司很好但是却有一个过时的技术堆栈,基本上没有使用Java 8或者微服务器。所以我想要了解关于微服务器的事情,必须自己去收集学习,为此我创建了一个待办事项系统,记录我的学习经历以供将来参考!
概述
本文的目的是为不同微服务提供源代码演练,不会深入解读概念和工具,主要是介绍一个包含用于开发微服务的模式、工具和技术的应用示例。
既然是一个参考应用程序,我会使它尽量简单,源码易于理解!本文中,这个待办事项系统应用程序由8个部分组成:
- Reminder
- User
- Service discovery server
- Mailer
- OAuth Server
- System integration test
- API Gateway
- Web application client
系统如何与Microservices交互
上图中显示了系统与所有微服务器的交互,用户访问使用Angular 2编写的Web应用程序。然后连接到OAuth授权服务器,OAuth授权服务器将是可以分配用户和权限的中心点。此服务器将返回一个JSON Web令牌,其中包含客户端及其权限的信息以及格式范围。在用户认证并具有令牌之后,Web应用程序与API网关通信。通过JWT来验证它是否来自授权服务器,然后调用微服务器并构建响应。
OAuth服务器使用用户服务来获取用户的身份验证详细信息。此外,API网关使用OAuth服务器来获取用户的信息。
Remainder Service由ToDo功能来提供,,ToDo服务有一个计划工作来检查并通过电子邮件通知用户,电子邮件由邮件服务发送,该事件使用Kafka的事件提醒服务触发。
系统集成测试是一个Java应用程序,负责到达提醒服务的端点。
连接微服务
在微服务体系结构中,我们必须处理许多在不同IP和端口上运行的微服务器。因此,我们需要找到一种无需硬编码来管理每个地址的方式。
这就需要用到Netflix Eureka,它是客户端服务发现,允许服务自动查找和相互通信。我们在系统中使用了Spring Cloud Eureka,如果Eureka查找到服务运行的位置,我们就可以添加实例并应用负载平衡来在微服务器之间分配传入的应用流量。
在系统中,我们使用Netflix Ribbon作为客户端负载均衡器,实现容错,并通过冗余增加可靠性和可用性。我们使用Netflix Foreign编写声明性REST客户端,并集成Ribbon和Eureka来提供负载平衡HTTP客户端。
我们的系统中存在一些依赖。我们正在使用Netflix Hystrix断路器将应用程序与依赖性故障隔离开来。它有助于阻止级联故障,并允许快速恢复或添加后备。Hystrix为每个依赖关系维护一个线程池,如果线程池耗尽,它会拒绝请求。它还提供断路器功能,可以停止对依赖关系的所有请求。当请求失败,拒绝或超时时,还可以实现备用逻辑。
认证
开发任何类型的系统时,安全性都是非常重要的,微服务架构也没什么不同。当“如何保持微服务的安全性?”这个问题出现时,我的第一个答案是OAuth2。OAuth2绝对是一个很好的解决方案:它是一种众所周知的授权技术,广泛应用于Google,Facebook和Github。
当然没有Spring Security的情况下,是不可能谈论安全的。。在讨论分布式系统的安全性时,Spring Security和OAuth2是显而易见的选择。
不过,我们在安全问题上还增加了一个元素:JSON Web Token(JWT)。如果仅使用OAuth,我们需要一个OAuth授权服务器来验证用户,生成令牌,并充当资源服务器的端点,询问该令牌是否有效以及授权的权限。与授权服务器相比,这需要两倍的请求。而JWT提供了一种在访问令牌中传输权限和用户数据的简单方法,一旦所有数据都已经在令牌字符串中,资源服务器就不需要请求令牌检查。所有信息都被序列化为JSON,用base64编码,最后用私有RSA密钥签名。
您可以查看OAuth2授权服务器(OAuth-server)和资源服务器(API Gateway)的代码实现。
REST
在系统中,我们有两种交互方式:同步和异步。异步,我们使用了分布式事件与Kafka,遵循发布/订阅模型。同步,我们支持了JSON和XML的REST风格。
Martin Fowler认为RESTful可以分为四个级别,level 0-level 3。我们的微服务处于level 2,为了简单起见,这里不使用HATEOAS设计模式实现超媒体控件。
因为我们使用了Spring Cloud,所以必须实现开箱即用的可扩展性模式,将其放在HTTP连接中,例如:断路器,负载平衡,连接池,超时和重试等。
分布式事件
如上所述,我们通过使用Kafka 进行提醒服务和 Mailer服务之间的通信,并异步地与其他Microservices进行通信。在提醒服务中,我们有一个计划任务来检查提醒时间,并发布RemainderFound事件。Mailer服务中将会有一个订阅的事件,向用户发送电子邮件。
Event sourcing和 CQRS
单片应用通常具有单个关系数据库。我们可以使用ACID交易。因此,如果出现问题,我们的应用程序可以简单地开始一个事务,更改多个行,并提交事务,如果错误的话,还可以恢复到之前。不幸的是,处理微服务架构中的数据访问要复杂得多。这些数据分布在不同的数据库中,跨多个服务实施业务交易是一个很大的挑战。
在“ToDo”项目中,我们使用事件来处理跨多个服务的业务事务。 您可以查看在Mailer服务中应用的CQRS实施事件。 可以看到如何分离读和写,轻松地缩放每个部分。 我们使用关系数据库作为事件存储,然后使用Kafka分发事件。 当然我们需要将这两个动作定义为原子操作并避免存储事件,这样就不会导致JVM崩溃。 不使用Kafka作为事件存储,是因为从关系数据库构建聚合更简单。
未来的计划
您可能也注意到这个项目中尚且还有很多事情尚未解决,这是一个开发中的项目,未来我们会添加更多的东西,例如Spring云配置,Docker容器,与Jenkins的持续集成,Spring Sleuth的分布式跟踪,ELK的日志管理等。
本文作者:田晓旭
来源:51CTO