老实说,我仍然需要说服微服务。
我可以看出,与单体应用程序相比,它们是一个令人信服的论据,但我认为我需要了解它们面临的一些挑战——第一个想到的是如何有效地定义微服务边界——因为它在我看来,我曾经使用过的很多应用程序都是单一的 ,因为 这些界限非常模糊。
无论如何,我想做一些技术方面的事情,所以决定开始使用 微服务架构模式 构建应用程序,而 Spring Boot 似乎是一个不错的起点。
这是一项正在进行的工作,我正在继续研究应用程序的不同方面,目前实际编写的代码很少(部分原因是 Spring-Boot 提供的简单性)。所有 代码都在 GitHub 中保持最新 ,因此请随时查看。
已经有很多很棒的博客介绍了这方面的内容,所以不会重复他们的工作,下面的文章对 Netflix OSS 和 Spring 集成进行了精彩的介绍,值得一读:
(图片来自: Building Microservices with Spring-Cloud and Netflix OSS )
入门:服务注册中心 - Eureka
需要做的第一件事是中央服务注册中心以允许服务发现——这不是微服务的新概念,而是 SOA 使用的一种方法。开箱即用,Spring-Boot 提供了与 Netflix 的 OSS 应用程序 Eureka 的 集成,后者提供了这一点。我选择为我的注册表创建一个专用应用程序( 代码可以在这里看到 ),它真的很简单,只需将相关依赖项添加到 build.gradle 文件,将 @EnableEurekaServer 注释添加到我们的应用程序配置,然后一个简单的配置文件定义服务器端口/名称等并完成!您可以在该项目中运行 gradle assemble 来构建 JAR 文件,然后运行 java -jar [the new JAR file] 并且应用程序将启动 - 然后您应该能够转到 http://localhost:1111 并且您将看到 Eureka 仪表板(当然没有注册微服务)。
我的第一个微服务
所以,我启动并运行了 Eureka,但它看起来很孤独,没有注册服务。
Spring 中的微服务也非常简单——实际上,它只是一个简单的 Web 应用程序,在其自己的进程中运行,具有有限的域——因此使用单个控制器/端点启动一个 Spring Boot MVC RESTful webapp 就足够了我是一个微服务(即使只是一条推文也可以)。
因此,我们可以创建新的微服务来做任何我们喜欢的事情,在我的例子中,我创建了一个 QuoteService(该应用程序正在慢慢演变为保险引擎)。仅仅拥有独立的应用程序并没有多大帮助,所以我们需要添加一些配置来告诉服务向我们的 Eureka 服务器注册——这将使我们的新微服务可以被其他想要使用它的服务发现。
同样,这非常简单:我们需要告诉我们的应用程序它应该尝试向 Eureka 注册,我们应该添加配置来这样做:
@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
class QuoteServiceApplication {
static void main(String[] args) {
System.setProperty("spring.config.name", "quote-service");
SpringApplication.run QuoteServiceApplication, args
}
}
@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
class QuoteServiceApplication {
static void main(String[] args) {
System.setProperty("spring.config.name", "quote-service");
SpringApplication.run QuoteServiceApplication, args
}
}
你可以看到我们只是在 java 中注释我们的应用程序配置,然后添加一些属性来定义 Eureka 服务器的托管位置,基本上就是这样。
现在,如果我们构建项目 JAR 并再次启动(并且我们的 Eureka 服务注册表仍在运行),那么在 30 秒左右之后,您应该会看到 Quote-Service 已注册并可以使用。
到下一个..
现在,我们有一个微服务,我们有一个注册表可以让它被发现,但仍然 - 只有一个微服务是相当孤独的。所以接下来我创建了另一个虚拟 RESTful 微服务,这次称为 ProductService,它遵循与第一个相同的模式。
一旦启动,Eureka 仪表板开始看起来对注册的两个服务更满意 - 显而易见的下一个挑战是两者之间的无缝交互:将服务拆分到它们自己的进程中是很好的,但如果可以的话就没有意义了'不容易整合它们。我看待它的方式是在阅读服务(或使用微服务的应用程序)的应用程序代码时,它应该看起来就像一个带有服务类的普通应用程序——不应该大肆宣传我的服务类实际上得到了通过 HTTP/AMQP 从专用微服务获取数据,而不是以传统方式直接从数据库获取数据。
因此,仍然只是删除端点,我更新了我的 QuoteService 端点以调用我的 ProductService,然后将该响应插入到我返回的 JSON 响应中:
@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
class QuoteServiceApplication {
static void main(String[] args) {
System.setProperty("spring.config.name", "quote-service");
SpringApplication.run QuoteServiceApplication, args
}
}
如您所见,从这一点来看,它可能是普通单体应用程序中的标准控制器,我们只是在自动装配的 ProductService 类上调用一个方法并返回它。
所以真正有趣的部分在 ProductService 类中——目前这不是一个真正优雅的抽象类,是的,所以仍然有一些样板,但这样做的好处是可以清楚地说明正在发生的事情:
@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
class QuoteServiceApplication {
static void main(String[] args) {
System.setProperty("spring.config.name", "quote-service");
SpringApplication.run QuoteServiceApplication, args
}
}
如您所见,它只是对 Product 微服务进行 REST 调用并返回转换为 Map 的响应 - 但真正好的部分是服务 url 只是服务名称(在本例中为“PRODUCT-SERVICE” ,它被注入到类中)并且 RestTemplate 带有 Spring-Cloud 的 @LoadBalanced 注释,微服务将在 Eureka 中查找(如果有多个 PRODUCT-SERVICE 正在运行,则负载平衡)。
所以我们的设置现在开始成形——我们有两个微服务,都在 Eureka 上注册,并且能够以一种相当干净、松散耦合的方式相互交互。
不要逼我,因为我已经接近边缘了..
随着您的微服务开始激增,您将获得不同级别的服务粒度,毫无疑问,您不会只想将所有微服务公开为公共 API。一种选择是创建一个 RESTful 应用程序并定义您想要公开的命名良好的端点,然后使用上述标准集成来集成它。
幸运的是,有一种更简单的方法——Netflix 提供了一个名为 Zuul 的库,可以简单地配置它以将 URL 模式映射到给定的定义服务名称(并再次在 Eureka 服务注册表中查找)。与 Eureka 非常相似,这非常容易设置,只需要一个注解和再次配置:
@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
class QuoteServiceApplication {
static void main(String[] args) {
System.setProperty("spring.config.name", "quote-service");
SpringApplication.run QuoteServiceApplication, args
}
}
@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
class QuoteServiceApplication {
static void main(String[] args) {
System.setProperty("spring.config.name", "quote-service");
SpringApplication.run QuoteServiceApplication, args
}
}
如您所见,我们只是根据 URL 模式定义服务名称。
现在,一旦所有应用程序都已启动并运行,并且微服务已在 Eureka 上注册,那么您就有了一个 API 接口来开始与服务交互(而不是必须访问其指定端口上的每个服务等)。
结论
这就是我所知道的——我将 QuoteService 连接到 MongoDB,以便所有数据都保存在那里(并添加了一个获取报价端点,它从 mongo 获取相同的数据)并开始使用 JPA 连接产品服务。到目前为止,一切都很愉快,而且事情比我刚开始时更有意义——但仍然有几个问题:
- 似乎在不同的项目中仍然存在重复的服务名称 - 例如 ProductService 名称(“产品服务” - 不区分大小写)在整个过程中激增 - 服务本身定义它,QuoteService 需要知道服务的名称,Zuul 边缘服务器需要知道名称等。我想这是不可避免的,因为这些是内在的依赖关系,但仍然看起来有点不稳定。
- 感觉服务类可以被分解——我们的 ProductService 类允许与产品微服务进行 HTTP REST 交互,可能需要在所有需要使用产品微服务的应用程序/微服务中重复使用