[关闭]
@levinzhang 2021-10-19T11:27:20.000000Z 字数 15499 阅读 713

通过Quarkus和MicroProfile实现微服务特性

by

摘要:

微服务特性(microservicility)指的是除了业务逻辑之外,服务必须要实现的一组横切性的关注点。这些关注点包括调用、弹性(elasticity)和回弹性(resiliency)等。本文描述了如何使用Quarkus和MicroProfile实现这些关注点。


核心要点

为何需要微服务特性?

在微服务架构中,应用程序是由多个相互连接的服务组成的,这些服务协同工作以实现所需的业务功能。

所以,一个典型的企业级微服务架构如下所示:

最初,我们可能认为使用微服务架构实现一个应用程序是很容易的事情。但是,要恰当地完成这一点并不容易,因为我们会面临一些新的挑战,而这些挑战是单体架构所未曾遇到的。举例来讲,这样的挑战包括容错、服务发现、扩展性、日志和跟踪等。

为了应对这些挑战,每个微服务都需要实现在Red Hat被称为“微服务特性(microservicility)”的内容。这个术语指的是除了业务逻辑之外,服务必须要实现的一个横切性关注点的列表,总结起来如下图所示:

业务逻辑可以使用任何语言(Java、Go或JavaScript)或任何框架(Spring Boot、Quarkus)来实现,但是围绕着业务逻辑,我们应该实现如下的关注点:

API:服务可以通过一组预先定义的API操作进行访问。例如,在采用RESTful Web API的情况下,会使用HTTP作为协议。此外,API还可以使用像Swagger这样的工具实现文档化。

发现(Discovery):服务需要能够发现其他的服务。

调用(Invocation):在服务发现之后,需要使用一组参数来调用它,并且可能会返回一个响应。

弹性(Elasticity):微服务架构很重要的特性之一就是每个服务都是有弹性的,这意味着它可以根据一些参数(比如系统的重要程度或当前的工作负载)独立地进行扩展和伸缩。

回弹性(Resiliency):在微服务架构中,我们在开发时应该要考虑到故障,特别是与其他服务进行通信的时候。在单体架构中,应用会作为一个整体进行启动和关闭。但是,当我们把应用拆分成微服务架构之后,应用就变成由多个服务组成的,所有的服务会通过网络互相连接,这意味着应用的某些部分可能在正常运行,而其他部分可能已经出现了故障。在这种情况下,很重要的一点就是遏制故障,避免错误通过其他的服务进行传播。回弹性(或称为应用回弹性)是指一个应用/服务能够对面临的问题作出反应的能力,在出现问题的时候,依然能够提供尽可能最好的结果。

管道(Pipeline):服务应该能够独立部署,不需要任何形式的部署编排。基于这一点,每个服务应该有自己的部署管道。

认证(Authentication):在微服务架构中,涉及到安全性时,很重要的一个方面就是如何认证/授权内部服务之间的调用。Web token(以及通用的token)是在内部服务之间声明安全性的首选方式。

日志(Logging):在单体应用中,日志是很简单的事情,因为应用的所有组件都在同一个节点中运行。现在,组件以服务的形式分布在多个节点上,因此,为了全面了解日志跟踪的情况,我们需要一个统一的日志系统/数据收集器。

监控(Monitoring):要保证基于微服务的应用正确运行,很重要的一个方面就是衡量系统的运行情况、理解应用的整体健康状况并在出现问题的时候发出告警。监控是控制应用程序的重要方面。

跟踪(Tracing):跟踪用来可视化一个程序的流程和数据进展。当我们需要检查用户在整个应用中的操作时,它对开发人员或运维人员尤其有用。

Kubernetes正在成为部署微服务的事实标准工具。它是一个开源的系统,用来自动化、编排、扩展和管理容器。

但是在我们提到的十个微服务特性中,通过使用Kubernetes只能覆盖其中的三个。

发现(Discovery)是通过Kubernetes Service理念实现的。它提供了一种将Kubernetes Pod(作为一个整体)进行分组的方式,使其具有稳定的虚拟IP和DNS名。要发现一个服务只需要在发送请求的时候使用Kubernetes的服务名作为主机名即可。

使用Kubernetes 调用(Invocation)服务是非常容易的,因为平台本身提供了所需的网络来调用任意的服务。

弹性(Elasticity) (或者说扩展性)是Kubernetes从一开始就考虑到的问题,例如,如果运行kubectl scale deployment myservice --replicas=5命令的话,myservice deployment就会扩展至五个副本或实例。Kubernetes平台会负责寻找合适的节点、部署服务并维持所需数量的副本一直处于运行状态。

但是,剩余的微服务特性该怎么处理呢?Kubernetes只涵盖了其中的三个,那么我们该如何实现剩余的哪些呢?

根据所使用的语言或框架,我们有很多可遵循的策略,但是在本文中,我们会看到如何使用Quarkus来实现其中某些微服务特性。

什么是Quarkus?

Quarkus是一个全栈、Kubernetes原生的Java框架,适用于Java虚拟机(JVM)和原生编译环境,针对容器环境对Java的进行了专门的优化,使其成为一个可用于无服务器、云和Kubernetes环境的高效平台。

Quarkus没有重复发明轮子,而是使用了由标准/规范支撑的知名企业级框架,并使它们可以借助GraalVM编译成二进制文件。

什么是MicroProfile?

Quarkus集成了MicroProfile规范,将企业级Java生态系统转移到了微服务架构中。

在下图中,我们可以看到构成MicroProfile规范的所有API。其中有些API是基于Jakarta EE(也就是以前的Java EE)规范的,比如CDI、JSON-P和JAX-RS,其他的则是由Java社区开发的。

接下来,我们就使用Quarkus来实现API、调用、回弹性、认证、日志、监控和跟踪等微服务特性。

如何使用Quarkus实现微服务特性

起步

开始使用Quarkus的最快捷方式就是通过起始页面,在这里我们可以添加所需的依赖。就本例来讲,我们要注册如下的依赖以满足微服务特性的需求:

我们可以手动选择这些依赖,也可以导航至如下的链接Microservicilities Quarkus Generator,在这里所有的依赖都已经选择好了。然后点击“Generate your application”按钮以下载包含脚手架应用的压缩文件。

服务

在本例中,我们会创建一个非常简单的应用,它只包含两个服务。其中一个服务名为Rating service,它会返回给定一本书的评分,另外一个服务名为Book service,它会返回某本书的信息及其评分。服务之间的所有调用必须要进行认证。

在下图中,我们可以看到完整系统的概览:

Rating service已经开发完成并且能够以Linux容器的形式供我们使用。我们可以使用如下的命令在9090端口启动该服务:

  1. docker run --rm -ti -p 9090:8080
  2. quay.io/lordofthejars/rating-service:1.0.0

为了校验该服务,我们可以发送请求到http://localhost:9090/rate/1

  1. curl localhost:8080/rate/1 -vv
  2. > GET /rate/1 HTTP/1.1
  3. > Host: localhost:8080
  4. > User-Agent: curl/7.64.1
  5. > Accept: */*
  6. >
  7. < HTTP/1.1 401 Unauthorized
  8. < www-authenticate: Bearer {token}
  9. < Content-Length: 0

这里的状态码是401 Unauthorized,这是因为我们没有在请求中以bearer token(JWT)的形式提供认证信息。带有group Echoer的合法token才能访问rating service

API

Quarkus使用大家熟知的JAX-RS规范来定义RESTful web API。在底层,Quarkus使用了RESTEasy实现,直接与Vert.X框架协作,而不是使用Servlet相关的技术。

现在我们为book service定义API,实现最常见的操作:

  1. import javax.ws.rs.Consumes;
  2. import javax.ws.rs.DELETE;
  3. import javax.ws.rs.GET;
  4. import javax.ws.rs.POST;
  5. import javax.ws.rs.Path;
  6. import javax.ws.rs.PathParam;
  7. import javax.ws.rs.Produces;
  8. import javax.ws.rs.QueryParam;
  9. import javax.ws.rs.core.MediaType;
  10. import javax.ws.rs.core.Response;
  11. import javax.ws.rs.core.UriBuilder;
  12. @Path("/book")
  13. public class BookResource {
  14. @GET
  15. @Path("/{bookId}")
  16. @Produces(MediaType.APPLICATION_JSON)
  17. public Book book(@PathParam("bookId") Long bookId) {
  18. // logic
  19. }
  20. @POST
  21. @Consumes(MediaType.APPLICATION_JSON)
  22. public Response getBook(Book book) {
  23. // logic
  24. return Response.created(
  25. UriBuilder.fromResource(BookResource.class)
  26. .path(Long.toString(book.bookId))
  27. .build())
  28. .build();
  29. }
  30. @DELETE
  31. @Path("/{bookId}")
  32. public Response delete(@PathParam("bookId") Long bookId) {
  33. // logic
  34. return Response.noContent().build();
  35. }
  36. @GET
  37. @Produces(MediaType.APPLICATION_JSON)
  38. @Path("search")
  39. public Response searchBook(@QueryParam("description") String description) {
  40. // logic
  41. return Response.ok(books).build();
  42. }
  43. }

我们要注意的第一件事情就是这里定义了四个不同的端点:

要注意的第二件事就是返回的类型,有时候我们返回的是一个Java对象,有时候返回的是javax.ws.rs.core.Response的实例。当使用Java对象的时候,我们会将Java编组为@Produces注解所设置的媒体类型。具体到本服务中,输出是JSON文档。如果使用Response对象的话,对于返回什么内容给调用者,我们会有更细粒度的控制,例如,我们可以设置返回给调用者的HTTP状态码、头信息或内容。至于该优选选择哪种方式,这取决于具体的使用场景。

调用

定义完访问book service的API之后,我们就该开发调用rating service服务以获取图书评分信息的代码了。

Quarkus使用MicroProfile Rest Client规范来访问外部的(HTTP)服务。它提供了一种类型安全的方式借助HTTP协议访问RESTful服务,在这个过程中,它会使用JAX-RS 2.0的一些API以实现一致性和更简单的重用。

我们要创建的第一个元素是代表远程服务的接口,它会用到JAX-RS的注解。

  1. import javax.ws.rs.GET;
  2. import javax.ws.rs.Path;
  3. import javax.ws.rs.PathParam;
  4. import javax.ws.rs.Produces;
  5. import javax.ws.rs.core.MediaType;
  6. import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
  7. @Path("/rate")
  8. @RegisterRestClient
  9. public interface RatingService {
  10. @GET
  11. @Path("/{bookId}")
  12. @Produces(MediaType.APPLICATION_JSON)
  13. Rate getRate(@PathParam("bookId") Long bookId);
  14. }

getRate()方法被调用的时候,会触发一个对/rate/{bookId}的远程HTTP调用,在这个过程中bookId会被替换为方法参数中的值。很重要的一点就是,要为接口添加@RegisterRestClient注解。

然后,RatingService接口需要注入到BookResource中以执行远程调用。

  1. import org.eclipse.microprofile.rest.client.inject.RestClient;
  2. @RestClient
  3. RatingService ratingService;
  4. @GET
  5. @Path("/{bookId}")
  6. @Produces(MediaType.APPLICATION_JSON)
  7. public Book book(@PathParam("bookId") Long bookId) {
  8. final Rate rate = ratingService.getRate(bookId);
  9. Book book = findBook(bookId);
  10. return book;
  11. }

@RestClient注解会注入对应接口的一个代理实例,从而提供了客户端的实现。

最后需要配置的就是服务的位置(hostname部分)。在Quarkus中,配置属性是在src/main/resources/application.properties文件中设置的。要配置服务的位置,我们需要使用Rest Client接口的全限定名并结合URL作为键,然后使用实际的位置作为值:

  1. org.acme.RatingService/mp-rest/url=http://localhost:9090

要想正确访问rating服务并摆脱401 Unauthorized的问题,我们还需要解决相互认证的问题。

认证

基于token的认证机制允许系统基于一个安全token进行认证、授权和身份验证。Quarkus集成了MicroProfile JWT RBAC Security规范,以使用JWT Bearer Token来保护服务。

要使用MicroProfile JWT RBAC Security来保护一个端点,我们只需要为方法添加@RolesAllowed注解即可。

  1. @GET
  2. @Path("/{bookId}")
  3. @RolesAllowed("Echoer")
  4. @Produces(MediaType.APPLICATION_JSON)
  5. public Book book(@PathParam("bookId") Long bookId)

随后,我们还需要在application.properties文件中配置token的issuer以及公钥文件的位置,以便于校验token的签名:

  1. mp.jwt.verify.publickey.location=https://raw.githubusercontent.com/redhat-developer-demos/quarkus-tutorial/master/jwt-token/quarkus.jwt.pub
  2. mp.jwt.verify.issuer=https://quarkus.io/using-jwt-rbac

该扩展会执行如下的校验:token是合法的;issuer是正确的;token没有被修改过;签名是合法的;它还没有过期。

现在,book servicerating service都使用相同的JWT issuer和秘钥进行保护,所以服务之间的通信需要用户进行认证,这是通过在Authentication头信息中提供一个合法的bearer token实现的。

rating service运行起来之后,我们就可以使用如下的命令启动book service

  1. ./mvnw compile quarkus:dev

最后,我们可以发送请求来获取图书信息并提供一个合法的JSON Web Token作为bearer token。

至于token如何生成超出了本文的范围,我们假设token已经能够生成:

  1. curl -H "Authorization: Bearer eyJraWQiOiJcL3ByaXZhdGVLZXkucGVtIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJqZG9lLXVzaW5nLWp3dC1yYmFjIiwiYXVkIjoidXNpbmctand0LXJiYWMiLCJ1cG4iOiJqZG9lQHF1YXJrdXMuaW8iLCJiaXJ0aGRhdGUiOiIyMDAxLTA3LTEzIiwiYXV0aF90aW1lIjoxNTcwMDk0MTcxLCJpc3MiOiJodHRwczpcL1wvcXVhcmt1cy5pb1wvdXNpbmctand0LXJiYWMiLCJyb2xlTWFwcGluZ3MiOnsiZ3JvdXAyIjoiR3JvdXAyTWFwcGVkUm9sZSIsImdyb3VwMSI6Ikdyb3VwMU1hcHBlZFJvbGUifSwiZ3JvdXBzIjpbIkVjaG9lciIsIlRlc3RlciIsIlN1YnNjcmliZXIiLCJncm91cDIiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamRvZSIsImV4cCI6MjIwMDgxNDE3MSwiaWF0IjoxNTcwMDk0MTcxLCJqdGkiOiJhLTEyMyJ9.Hzr41h3_uewy-g2B-sonOiBObtcpkgzqmF4bT3cO58v45AIOiegl7HIx7QgEZHRO4PdUtR34x9W23VJY7NJ545ucpCuKnEV1uRlspJyQevfI-mSRg1bHlMmdDt661-V3KmQES8WX2B2uqirykO5fCeCp3womboilzCq4VtxbmM2qgf6ag8rUNnTCLuCgEoulGwTn0F5lCrom-7dJOTryW1KI0qUWHMMwl4TX5cLmqJLgBzJapzc5_yEfgQZ9qXzvsT8zeOWSKKPLm7LFVt2YihkXa80lWcjewwt61rfQkpmqSzAHL0QIs7CsM9GfnoYc0j9po83-P3GJiBMMFmn-vg" localhost:8080/book/1 -v

响应再次提示禁止访问的错误:

  1. < HTTP/1.1 401 Unauthorized
  2. < Content-Length: 0

你可能会想在提供了合法的token之后,为何还会遇到这个错误。如果我们探查一下book service的控制台,就会看到如下的异常:

  1. org.jboss.resteasy.client.exception.ResteasyWebApplicationException: Unknown error, status code 401
  2. at org.jboss.resteasy.client.exception.WebApplicationExceptionWrapper.wrap(WebApplicationExceptionWrapper.java:107)
  3. at org.jboss.resteasy.microprofile.client.DefaultResponseExceptionMapper.toThrowable(DefaultResponseExceptionMapper.java:21)

出现这个异常的原因在于我们已经认证并授权访问book service,但是这个bearer token并没有传递到rating service中。

为了让Authorization头信息能够从传入的请求自动传播至rest-client请求,我们需要进行两项修改。

第一项修改是更新Rest Client接口并为其添加org.eclipse.microprofile.rest.client.inject.RegisterClientHeaders注解。

  1. @Path("/rate")
  2. @RegisterRestClient
  3. @RegisterClientHeaders
  4. public interface RatingService {}

第二项修改是配置哪些头信息要在请求之间进行传递,这是在application.properties文件中进行配置:

  1. org.eclipse.microprofile.rest.client.propagateHeaders=Authorization

我们再次使用相同的curl,就会得到正确的输出了:

  1. < HTTP/1.1 200 OK
  2. < Content-Length: 39
  3. < Content-Type: application/json
  4. <
  5. * Connection #0 to host localhost left intact
  6. {"bookId":2,"name":"Book 2","rating":1}* Closing connection 0

回弹性

在微服务架构中,服务具备容错性是非常重要的,这样可以避免一个故障从某个服务传播至它的所有直接和间接的调用者。Quarkus将MicroProfile Fault Tolerance规范与如下的注解集成到了一起,以便于处理故障相关的问题:

●    @Timeout:定义在抛出异常之前,某个服务最长的持续时间。

●    @Retry:如果调用失败的话,会再次进行尝试执行。

●    @Bulkhead:并发执行的限制,这样的话,该区域出现的故障不会导致整个系统超载。

●    @CircuitBreaker:当执行反复失败时,该服务会自动地快速失败。

●    @Fallback:当执行失败的时候,提供一个替代方案/默认值。

在访问rating service的时候,如果出现错误,我们会进行三次重试并在每次重试之间添加一秒钟的睡眠计时器。

  1. @Retry(maxRetries = 3, delay = 1000)
  2. Rate getRate(@PathParam("bookId") Long bookId);

现在,我们关掉rating service并执行请求,将会抛出如下的异常:

  1. org.jboss.resteasy.spi.UnhandledException: javax.ws.rs.ProcessingException: RESTEASY004655: Unable to invoke request: org.apache.http.conn.HttpHostConnectException: Connect to localhost:9090 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused

显然,这里会有错误,但是需要注意在抛出异常之前,我们经历了三秒钟的时间,这是因为执行了三次重试,并且每次重试间有一秒钟的延迟。

在这种情况下,rating service已经被我们停掉了,所以不可能恢复,但是在现实世界的例子中,rating service可能只会停机很短的时间,或者服务部署了多个副本,这样的话,简单的重试操作可能就足以恢复并提供一个合法的响应。

但是,当重试不足以解决问题并且抛出异常的时候,我们可以将错误传播至调用者,也可以为调用提供一个替代值。这个替代值可以来自对其他系统的调用(如分布式缓存),也可以是一个静态值。

就本例来讲,当连接rating service失败的时候,我们会返回一个值为0的评分值。

为了实现这个回退(fallback)逻辑,我们首先要做的就是实现org.eclipse.microprofile.faulttolerance.FallbackHandler接口,并将返回值设置为相同的类型,因为回退策略方法的作用是返回一个替代值。在本例中,会返回一个默认的Rate对象。

  1. import org.eclipse.microprofile.faulttolerance.ExecutionContext;
  2. import org.eclipse.microprofile.faulttolerance.FallbackHandler;
  3. public class RatingServiceFallback implements FallbackHandler&lt;Rate&gt; {
  4. @Override
  5. public Rate handle(ExecutionContext context) {
  6. Rate rate = new Rate();
  7. rate.rate = 0;
  8. return rate;
  9. }
  10. }

最后要做的就是为getRating()方法添加@org.eclipse.microprofile.faulttolerance.Fallback注解,配置在无法进行恢复的时候要执行的回退类。

  1. @Retry(maxRetries = 3, delay = 1000)
  2. @Fallback(RatingServiceFallback.class)
  3. Rate getRate(@PathParam("bookId") Long bookId);

如果我们重复前面的请求,此时不会抛出异常,而是会返回一个合法的输出,其中评分字段的值被设置成了0。

  1. * Connection #0 to host localhost left intact
  2. {"bookId":2,"name":"Book 2","rating":0}* Closing connection 0

相同的方式也可以用于该规范提供的其他模式。比如,如果使用断路器模式的话::

  1. @CircuitBreaker(requestVolumeThreshold = 4,
  2. failureRatio=0.75,
  3. delay = 1000)

如果在滚动时间窗口中,四个连续的请求中有三个(即4 x 0.75)出现了故障,那么断路器就会打开1000毫秒的时间,然后会回到半开状态。当断路器处于半开状态时,如果调用成功了,那么会再次关闭。否则的话,它会继续保持打开的状态。

日志

在微服务架构中,推荐将所有服务的日志收集到一起,以便于高效使用和理解。

其中有个解决方案就是使用Fluentd,这是一个开源的数据收集器,能够用来实现Kubernetes中统一的日志层。Quarkus使用Graylog扩展日志格式(Graylog Extended Log Format,GELF)与Fluentd进行了集成。

具体的集成是非常简单的。首先,像其他的Quarkus应用那样使用日志逻辑:

  1. import org.jboss.logging.Logger;
  2. private static final Logger LOG = Logger.getLogger(BookResource.class);
  3. @GET
  4. @Path("/{bookId}")
  5. @RolesAllowed("Echoer")
  6. @Produces(MediaType.APPLICATION_JSON)
  7. public Book book(@PathParam("bookId") Long bookId) {
  8. LOG.info("Get Book");

接下来,启用GELF格式并设置Fluentd服务器的地址:

  1. quarkus.log.handler.gelf.enabled=true
  2. quarkus.log.handler.gelf.host=localhost
  3. quarkus.log.handler.gelf.port=12201

最后,我们可以发送请求给实现日志功能的端点:

  1. curl -H "Authorization: Bearer ..." localhost:8080/book/1
  2. {"bookId":1,"name":"Book 1","rating":3}

在输出方面并没有任何变化,但是日志已经被传输到了Fluentd上。如果我们使用Kibana来可视化数据的话,就会看到如下所示的日志行:

监控

监控是另外一个我们需要在微服务架构中实现的微服务特性。Quarkus集成了Micrometer实现应用监控。Micrometer为几乎所有流行的监控系统提供了一个简单的入口,从而能够让我们在避免供应商锁定的前提下instrument基于JVM的应用。

对于本例来讲,我们使用Prometheus格式作为监控输出,但是Micrometer(和Quarkus)也支持其他的格式,比如Azure Monitor、Stackdriver、SignalFx、StatsD和DataDog。

我们可以注册如下的Maven依赖以提供Prometheus输出:

  1. <dependency>
  2. <groupId>io.quarkus</groupId>;
  3. <artifactId>quarkus-micrometer-registry-prometheus</artifactId>
  4. </dependency>

Micrometer扩展默认会注册一些与系统、JVM或HTTP相关的指标。所收集到的指标的一个子集可以通过/q/metrics端点进行访问,如下所示:

  1. curl localhost:8080/q/metrics
  2. jvm_threads_states_threads{state="runnable",} 22.0
  3. jvm_threads_states_threads{state="blocked",} 0.0
  4. jvm_threads_states_threads{state="waiting",} 10.0
  5. http_server_bytes_read_count 1.0
  6. http_server_bytes_read_sum 0.0

但是,我们还可以使用Micrometer API实现应用特定的指标。

在这里,我们实现一个自定义的指标来衡量评分最高的图书。

要注册一个指标,也就是本例中的一个gauge,是通过使用io.micrometer.core.instrument.MeterRegistry类来完成的。

  1. private final MeterRegistry registry;
  2. private final LongAccumulator highestRating = new LongAccumulator(Long::max, 0);
  3. public BookResource(MeterRegistry registry) {
  4. this.registry = registry;
  5. registry.gauge("book.rating.max", this,
  6. BookResource::highestRatingBook);
  7. }

我们发送一些请求并校验gauge是否被正确地更新了。

  1. curl -H "Authorization: Bearer ..." localhost:8080/book/1
  2. {"bookId":1,"name":"Book 1","rating":3}
  3. curl localhost:8080/q/metrics
  4. # HELP book_rating_max
  5. # TYPE book_rating_max gauge
  6. book_rating_max 3.0

我们还可以设置一个计时器,记录从rating service获取评分信息所耗费的时间。

  1. Supplier<Rate> rateSupplier = () -> {
  2. return ratingService.getRate(bookId);
  3. };
  4. final Rate rate = registry.timer("book.rating.test").wrap(rateSupplier).get();

我们发送一些请求并校验是否收集了评分服务的耗时。

  1. # HELP book_rating_test_seconds
  2. # TYPE book_rating_test_seconds summary
  3. book_rating_test_seconds_count 4.0
  4. book_rating_test_seconds_sum 1.05489108
  5. # HELP book_rating_test_seconds_max
  6. # TYPE book_rating_test_seconds_max gauge
  7. book_rating_test_seconds_max 1.018622001

Micrometer使用MeterFilter实例来自定义由MeterRegistry实例所发出的指标。Micrometer扩展将会探测到MeterFilter CDI bean并使用它们来初始化MeterRegistry实例。

例如,我们可以定义一个通用的标签来设置应用运行的环境(prod、testing、staging等)。

  1. @Singleton
  2. public class MicrometerCustomConfiguration {
  3. @Produces
  4. @Singleton
  5. public MeterFilter configureAllRegistries() {
  6. return MeterFilter.commonTags(Arrays.asList(
  7. Tag.of("env", "prod")));
  8. }
  9. }

发送一个新的请求并校验指标是否添加了标签。

  1. http_client_requests_seconds_max{clientName="localhost",env="prod",method="GET",outcome="SUCCESS",status="200",uri="/rate/2",} 0.0

请注意,标签env包含的值为prod

跟踪

Quarkus应用使用OpenTracing规范来为互相交互的Web应用提供分布式跟踪能力。

接下来,我们配置OpenTracing连接一个Jaeger服务器,并将服务的名字设置为book-service以标识跟踪信息:

  1. quarkus.jaeger.enabled=true
  2. quarkus.jaeger.endpoint=http://localhost:14268/api/traces
  3. quarkus.jaeger.service-name=book-service
  4. quarkus.jaeger.sampler-type=const
  5. quarkus.jaeger.sampler-param=1

现在,我们发送一个请求:

  1. curl -H "Authorization: Bearer ..." localhost:8080/book/1
  2. {"bookId":1,"name":"Book 1","rating":3}

访问Jaeger UI来校验调用过程被进行了跟踪:

结论

开发和实现微服务架构要比开发单体应用更具挑战性。我们相信,微服务特性能够促使你在应用基础设施方面正确地开发服务。

我们在这里所阐述的微服务特性(除API和管道之外)都是新的理念,或者说在单体应用中会以不同的方式来实现。其中的原因在于,现在应用被拆分成了多个组成部分,所有的这些组成部分需要在网络中进行相互连接。

如果你打算开发微服务并将它们部署到Kubernetes的话,那么Quarkus是一个很好的解决方案,因为它可以很平滑地与Kubernetes进行集成,实现大多数的微服务特性都非常简单,只需要几行代码就能实现。

用来阐述本文的源码都可以在github上找到。

关于作者

Alex Soto是红帽公司的开发者体验总监。他对Java领域、软件自动化充满热情,他相信开源软件模式。Soto是Manning的《Testing Java Microservices》O’Reilly的《Quarkus Cookbook》两本书的共同作者,他还是多个开源项目的贡献者。自2017年以来,他一直是Java Champion,是国际演讲者和Salle URL大学的教师。你可以在Twitter上关注他(Alex Soto ⚛️),随时了解Kubernetes和Java领域的动态。

查看英文原文:Implementing Microservicilities with Quarkus and MicroProfile

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注