嗨。今天我要讨论和解释如何实现基于微服务的系统。有很多工具和技术可以实现微服务。今天我将专注于使用Netflix堆栈和SpringBoot进行实现。这些天微服务是行业中炙手可热的话题。每个人都需要了解微服务,每个人都需要基于微服务架构来进行他们的项目。
在开始微服务之前,我们必须清楚微服务架构的概念,为什么我们的项目要使用微服务,微服务架构的优缺点是什么。在这里 Krish正在进行关于微服务的非常好的教程系列。如果你想要对微服务架构有非常好的理解,请在开始实现之前通过这些教程进行学习。
主要话题
- 样例项目介绍
- 核心服务
- 发现服务器
- 发现客户端
- 客户端负载均衡器
- API网关
- 安全流程
- 服务弹性和容错
- 无状态服务器
- 公共库
样例项目介绍
首先,我将解释系统的高级架构,以便了解我们要做什么。这只是一个用于说明的样例应用程序。假设这是一个基于电子商务的Web应用程序。所有这些系统设计和实现的解释都是基于我以前的项目经验。有时根据你的要求,你的系统设计可能会有所不同。
核心服务
你可以看到,根据微服务架构,我们将整个应用程序划分为单独的服务。每个服务都是可扩展和独立部署的,并且具有确切的定义范围。这些都是使用SpringBoot项目构建的。我将在本文的结尾提供源代码项目,因此不必担心编码,而是要尝试理解概念。在这个示例中,我们有五个核心服务。
auth-service负责处理系统的身份验证过程,auth-user检索,auth-user存储。它连接到User,Role,Permission db表。在这里我使用了MongoDB。
product-service负责产品存储,产品列表,如库存处理流程。
order-service负责处理买家下单购买产品的订单。然后卖家处理他所售产品的订单。
message-service负责处理系统内每个用户之间的实时消息传递流程。
search-service负责搜索产品、用户、类别。在这些应用程序中,搜索是一项最具挑战性的操作,因此我使用了Redis和Searchbox提供高性能服务。但这只是一个示例项目,你可以选择适合的技术。
发现服务器
在上一节中,我们讨论了系统的核心服务。它们是独立的服务。其中一些部署在单个服务器机器上,而其他一些则可能部署在另一台服务器机器上。有时,某些服务在某些情况下必须与其他服务通信,我将在稍后解释这些情况。但是,如果我们不知道服务在哪里,我们如何与另一个服务通信。这就像我们试图打电话给一个人,但不知道他的电话号码。
解决方案是发现服务器。发现服务器帮助我们发现我们需要的服务。当某个服务需要访问另一个服务时,发现服务器提供所请求服务的所有端点详细信息以建立连接。发现服务器充当服务注册表。所有服务都需要注册到发现服务器,否则发现服务器将不知道该服务。有多个发现服务器的实现,例如Netflix Eureka、Consul、Zookeeper。在这里,我将讨论Netflix Eureka。
我们可以轻松地设置Netflix Eureka作为我们的发现服务。我不会在这里讨论每个编码部分,我专注于重要的配置和实现点。你可以在源代码项目中找到它。要将Springboot应用程序设置为Eureka Discovery Server,你必须在应用程序的启动(main方法)中放置**@EnableEurekaServer**注释。
你必须放置一些配置。在这种情况下,我放在application.yml中(或者你可以使用application.properties)
在这里,我指出这个实例不是一个发现客户端,在这里服务器的端口是从maven属性获取的。但是,你可以放置任何你需要的端口号。默认端口是1111。
发现客户端
在服务发现部分中,我告诉每个服务都必须使用**@EnableDiscoveryClient将其作为发现客户端进行注册。此注释可以与在你的项目中实现的任何发现客户端实现一起使用(Eureka、Consul、Zookeeper)。你还可以使用@EnableEurekaClient**注释,但它仅适用于Eureka Discovery Client实现。
你必须放置一些配置。
在这里,你必须提到spring.application.name。因为那个名称将是其他人用来访问该服务的Service-Id。
我需要指出的另一件事是,我们可以根据服务的需求保持服务的集群,使用相同的Service-Id。实际上,Eureka Server通过使用Instance-Id来跟踪其发现客户端。有人可以向Eureka请求获取特定服务的所有可用端点,提供Service-Id。然后,Eureka能够为该服务客户端提供所有服务端点列表。
Instance-Id = {Server-Host} + ':' {Service-Id} + ':' + {Server-Port}
你可以看到有一些简单的配置。你必须使用defaultZone属性指定Eureka Server的位置。使用leaseRenewalIntervalInSeconds可以更改注册时间。注册需要最多30秒,因为那是默认客户端刷新时间。## 客户端负载均衡器
我已经告诉你了。在某些情况下,一些服务需要从其他服务获取服务或数据。在这种情况下,一个服务成为另一个服务的客户端。客户端服务可以使用服务 ID 调用所需的服务。但是假设所需的服务保留了集群。然后,Eureka 为所请求的服务提供了所有端点。现在客户端服务如何决定选择哪个端点以建立连接。这就是客户端负载均衡器发挥作用的时候。
根据负载均衡算法,客户端负载均衡器将为你从列表中选择最佳端点以建立连接。在我们的情况下,我使用 Netflix Ribbon 作为客户端负载均衡器。
根据你的要求,你可以选择最佳的负载均衡算法。在 Ribbon 中有几种实现。简单的循环负载平衡,加权响应时间负载平衡,区域感知循环负载平衡和随机负载平衡。或者你可以实现自己的 LB 实现。默认的是简单的循环负载平衡。
Spring 框架提供了一种使用 RestTemplate 类轻松访问 REST 端点的方法。
在使用 RestTemplate 之前,你必须在 Spring 上下文中创建实例。@LoadBalanced 注释将有助于将 Ribbon 配置设置到 RestTemplate 中。这是使用 RestTemplate 访问另一个服务的方法。
我希望现在你知道如何使用 Ribbon 和 Eureka 进行服务间通信了。
API 网关
现在我们将实现系统的前门。外部用户(Web 应用程序,移动应用程序)将如何访问我们的服务,或者换句话说,我们如何将微服务暴露给外部用户。是的,解决方案是 API 网关。系统的外部用户通过 API 网关访问我们的核心服务。在这里,我们使用 Netflix Zuul API 网关。我们可以将 Zuul 用作代理和请求过滤器。我将通过查看配置来解释更多细节。
你可以在此处看到,我们必须像在发现客户端部分中所做的那样提到 Eureka Server 的详细信息。原因是当请求进入 Zuul 时,他将使用 Service-Id 访问特定的核心服务。就像我们之前做的那样。我们不需要担心客户端负载平衡,Zuul 正在使用 Ribbon 进行负载平衡。
在 Zuul 属性中,我们可以指定 URL 前缀。在路由属性部分中,我们必须使用相同的名称指定每个核心服务。在其中,我们可以指定用于访问核心服务的 URL 路径。最后,我们必须指定特定服务的 Service-Id。
例如,如果外部用户需要访问核心产品服务。他的请求端点 URL 可能像这些结构一样。假设 Zuul 运行在 8080 上。
http://localhost:8080/api/product-service/{core-product-service-end-point}
http://localhost:8080/api/product-service/products
http://localhost:8080/api/product-service/products/10
安全流程
在本节中,我将讨论如何在我们的应用程序中处理安全性。为此,我主要使用了 JWT(JSON Web Token)和 Spring Security。
登录
你可以在此处看到登录请求进入 auth-service。让我们看看如何生成 auth token。
header = {"alg": "HS256", "typ": "JWT"}payload = {
"exp": "2017-08-09 12:00:00",
"user_name": "user",
"authorities": [
"ROLE_SELLER"
],
...
}secret_key = "quebic_secret"Token = HMAC( base64(header) + "." + base64(payload) , secret_key)
服务检查用户名和密码是否有效。如果凭据正确,则 auth-service 创建有效载荷。有效载荷包含用户名、权限和令牌的过期时间。秘密密钥存储在每个服务的配置(application.yml)中。
在 jwt 配置中,我们可以设置包含 auth-token 的请求标头是什么。为此,我们使用 Authorization 标头。你可以根据需要更改秘密和过期值。在此场景中,Web 应用程序将生成的 auth-token 存储在浏览器存储中。
授权
根据我们的示例项目,产品存储操作仅允许卖家(USER_SELLER)。你可以在此处看到 Web 应用程序将新产品详细信息发送到 product-service。
product_service 不知道任何传入的用户信息,因此他调用 auth-service 获取 auth-object。auth-service 能够解密 auth-token,如果令牌有效,则 auth-service 返回 auth-object。auth-object 包含 userId、用户名和权限。现在根据权限,product_service 将继续处理过程。
在这里,你必须了解我们安全流程的主要关键点。第一件事是我们的任何核心服务都不会维护有关登录用户的会话。所有服务都是无状态的。第二件事是 product-service、order-service、message-service 和 search-service 将身份验证过程委托给 auth-service。在获取 auth-object 后,他们可以处理授权过程,因为每个核心服务都包含使用 Spring Security 实现的权限规则。
Authentication Delegating FilterCommonAuthenticationTokenFilter是Authentication Delegating的实现。它包含在我们的common-lib依赖项中。你可以在com.quebic.common.security包中找到这个过滤器类。通过覆盖OncePerRequestFilter类的doFilterInternal()方法,我实现了Auth Delegating逻辑。
使用Spring Security进行授权
你可以在WebSecurityConfig类中找到授权规则。每个服务都包含其自己的实现,而这个类位于security.config包下。
退出登录
当以无状态方式进行时,在令牌过期之前无法使认证令牌无效。所以当退出登录时,需要从客户端中丢弃令牌。例如,如果客户端是一个Web应用程序,我们可以从浏览器存储中释放认证令牌。
服务弹性和容错性
当我们设计基于微服务的项目时,必须考虑实现服务弹性和容错性机制。有几种方法可以实现这一点,而断路器模式是处理这个问题的一种好方法。Netflix Hystrix是断路器模式的一种实现。
如果readingList()方法在其执行过程中失败,回退方法将立即触发,而不会中断主要流程。
无状态服务器
正如我之前提到的,所有核心服务都必须是可伸缩的和独立部署的。根据需求,我们可以从同一服务中保留多个实例。但是,如果我们保留基于服务器的会话,我们必须在部署新实例时共享这些会话数据。这将破坏微服务架构的自由。因此,所有服务都被设计为无状态服务器。
不要将会话数据或缓存存储在服务器内存中,而是将其存储在分布式内存存储器中。有许多好的解决方案可用。例如:Hazelcast,Redis,Memcache。
公共库
如果你有一些所有服务都共有的东西,请不要在每个服务内部重复。将其放在共同的地方。在这个例子中,我将其放在一个名为common-lib的单独项目中。每个核心服务都必须将common-lib作为一个依赖项添加进来。
好的,现在我们已经讨论到了尽头。我认为你已经对微服务架构的实现有了相当的了解。我希望这将有助于你未来的项目。这是Github源代码项目,请按照Readme中给出的说明运行该项目。你可以将微服务托管在AWS EC2、Pivotal WebServices或Heroku中。我将在未来的文章中更多地介绍托管。感谢阅读,祝你好运。
译自:https://medium.com/@tharanganilupul/microservices-implementation-netflix-stack-ba4f4a57a79f
评论(0)