使用Eclipse MicroProfile(更新版)构建您的下一个微服务

本快速教程将向您展示如何使用最新版本的Eclipse MicroProfile API构建您的下一个微服务。这是一篇基于以前John D Ament 的文章的修订版,更新了MicroProfile 1.3的一些新功能。

Eclipse MicroProfile旨在为由多个微服务组成的Java应用程序提供一个增长型的API集。该项目最近引起了很多关注,也包括Oracle和IBM在内的越来越多的企业支持者。现在有很多提供API的服务器和框架,这意味着您可以继续使用相同的API和性能来选择最好的工具来运行您的微服务。本文是一个使用MicroProfile API构建下一个微服务的快速教程。

MicroProfile由JavaEE的核心技术构建,现在称为Jakarta EE技术:

向他们添加一组可以让你的微服务准备好云计算的规范,其中包括:

这些规范组在一起成了Eclipse MicroProfile 1.3。

初始项目设置

那么你如何利用这些项目呢?这个快速指南可以教你编写你的第一个应用程序。MicroProfile仅指定了API和行为,但不包含指定的功能。这是由Payara Micro提供的功能实现的。使用Payara Micro,您可以从命令行运行WAR文件,但也可以组装单个可执行JAR文件。还有很多其他的实现,你可以在MicroProfile实现列表中找到它们。

如果您选择使用Payara Micro运行微服务,请首先创建一个可生成WAR文件的Web项目。如果你的项目使用Maven或Gradle,你可以设置一个标准的Web应用程序项目(使用war打包或war插件)。构建WAR文件后,您可以从https://www.payara.fish/downloads下载Payara Micro,并通过以下命令从命令行运行应用程序:

java -jar payara-micro.jar application.war

然后,将MicroProfile附属项添加到您的项目中。

Maven的:

<dependency>
  <groupId>org.eclipse.microprofile</groupId>
  <artifactId>microprofile</artifactId>
  <version>1.3</version>
  <type>pom</type>
  <scope>provided</scope>
</dependency>

Gradle:

dependencies {  providedCompile 'org.eclipse.microprofile:microprofile:1.3'}
}

这个附属项引入了所有必需的API来构建您的应用程序。那么典型的微服务是什么样的?

  1. 一个JAX-RS控制器。由于我们公开了REST API,我们希望控制器能够处理API调用。
  2. 某种服务。您需要一些支持组件来生成或使用数据。现在,我们将使用一些模拟数据来解释范例。
  3. 可配置性。我们希望以声明方式进行,而不是客户端指定数据量。
  4. 安全。需要声明式和业务逻辑驱动的安全性来知道如何响应请求。
  5. 容错。我们关心我们消耗的任何服务,并确保我们可以快速失败或从失败中恢复过来
  6. 监测。我们想知道这个服务被调用的频率以及每个请求需要多长时间。

REST控制器和服务

首先,我们有我们的rest控制器,这对Java EE开发人员来说应该非常熟悉:

@Path("/api/books") // just a basic JAX-RS resource
@Counted // track the number of times this endpoint is invoked
@RequestScoped
public class BooksController {
 @Inject //use CDI to inject a service
 private BookService bookService;
 @GET
 @RolesAllowed("read-books")
 // uses common annotations to declare a role required
 public Books findAll() {
  return bookService.getAll();
 }
}

对于小型服务器,控制器也可以包含服务逻辑。但是,在我们的示例中,它通常会将业务逻辑的处理委托给另一个服务bean,如bookService。

如果我们进一步深入图书服务,我们可以开始看到可配置性如何工作:

@ApplicationScoped
public class BookService {
 @Inject
 // JPA is not provided out of the box, but most providers support it at
 // some level.  Worst case, create your own producer for the field
 private EntityManager entityManager;
 @Inject
 // use configuration to control how much data you want to supply at
 // a given time
 @ConfigProperty(name = "max.books.per.page", defaultValue = "20")
 private int maxBooks;
 public Books getAll() {
  List < Book > bookList = entityManager
   .createQuery("select b from Book b", Book.class)
   .setMaxResults(maxBooks) // use that configuration to do a paginated look up
   .getResultList();
  return new Books(bookList);
 }
}

可配置性

可以使用注入点上的@ConfigProperty注释将配置值简单地注入到服务中。该配置是基于配置名称提供的,该配置名称被用作从容器中检索配置值的关键字。其他可选属性也可以被提供,例如defaultValue,如果给定名称没有配置,则使用该属性。即使是名字属性也是可选的。如果未提供,则将根据类和字段名称生成,以便稍后可以提供配置值。

所以配置也可以像这样注入:

@Inject
@ConfigProperty
private int maxBooks

如果未提供默认值,则在应用程序启动时,必须使用根据指定算法生成的名称配置。

配置与bookService分离,可以由应用程序内部的配置提供,甚至可以在应用程序启动时由外部源(例如系统属性)提供。

安全

接下来,我们假设我们也想要处理书籍的创建,出版过程。我们希望确保服务的安全,以便只允许具有特定角色的呼叫者执行此过程。

根据JWT标准,MicroProfile提供基于JSON令牌的解决方案。我们可以将JsonWebToken对象注入到我们的服务中,并通过调用getClaim方法轻松找出调用者是否具有所需的角色:

@Inject
 private JsonWebToken jsonWebToken;

然后在一个方法中:

  boolean createAny = jsonWebToken.getClaim("create.books.for.other.authors");
   if (!createAny) {
      throw new NotAuthorizedException("Cannot create book, wrong author");
   }

然后调用者需要添加一个有效的JWT令牌和所需的声明到REST调用的头部。

用一个完整的发布服务来支持可能看起来像这样:

@RequestScoped
public class PublishBookService {
 @Inject
 // we can inject a JsonWebToken, a Principal specific to the JWT specification
 private JsonWebToken jsonWebToken;
 // we could also inject individual ClaimValue objects.
 @Inject
 private AuthorService authorService;
 @Inject
 private EntityManager entityManager;
 @Timeout(500)
 // we want to limit how long it takes to publish and if it
 // exceeds, return an exception to the caller.
 public BookId publish(PublishBook publishBook) {
  // we check the standard claim of subject
  if (!publishBook.getAuthor().equals(jsonWebToken.getSubject())) {
   // as well as a custom claim as a boolean
   boolean createAny = jsonWebToken.getClaim("create.books.for.other.authors");
   if (!createAny) {
    throw new NotAuthorizedException("Cannot create book, wrong author");
   }
  }
  Author author = authorService.findAuthor(publishBook.getAuthor());
  if (author == null) {
    throw new NotAuthorizedException("The list author is not an author");
  }
  Book book = entityManager.merge(new Book(publishBook.getIsbn(),
                                           publishBook.getAuthor()));
  return new BookId(book.getIsbn(), book.getAuthor());
 }
}

为了上述所有工作,还需要使用LoginConfig批注在JAX-RS应用程序类上启用JWT安全性。将该类转换为CDI bean也很重要,例如通过添加ApplicationScoped注释,因为JAX-RS类不会自动启用CDI。

这是它在代码中的样子:

@LoginConfig(authMethod = "MP-JWT", realmName = "admin-realm")
@ApplicationScoped
@ApplicationPath("/")
public class BookServiceConfig extends javax.ws.rs.Application {
}

添加容错

如果我们认为管理作者是一个单独的有限语境,我们则希望这个服务是谨慎的。因此,我们将以与书籍服务相同的方式将其作为单独的REST服务实施。因此,我们希望书籍服务通过连接到新的作者REST服务来检查作者是否存在。以下是连接到外部作者服务的完整代码:

@ApplicationScoped
public class AuthorService {
 @Inject
 @RestClient
 AuthorConnector authorConnector;
 // inject a REST proxy for a URL given by a generated config property
 private ConcurrentMap < String, Author > authorCache = new ConcurrentHashMap < > ();
 @Retry
 // Retry indicates that this should trigger retry the method call several times in case the remote server call results in an exception
 @CircuitBreaker
 // CircuiBreaker wraps the call in a circuit breaker which opens after several failures and closes again after some time
 @Fallback(fallbackMethod = "getCachedAuthor")
 // Fallback indicates that we should fall back to the local cache
 // if the method fails even after several retries
 // or the circuit is open
 public Author findAuthor(String id) {
  // call to an external Author service
  Author author = authorConnector.get(id); 
  // Ideally we want to read from the remote server. 
  // However, we can build
  // a cache as a fallback when the server is down
  authorCache.put(id, author);
  return author;
 }
 public Author getCachedAuthor(String id) {
  return authorCache.get(id);
 }
}

重注释,断路器,自动中断等触发拦截器,在基本动作失败的情况下会实施相应的容错模式。它们用于单独的方法或类中,以将其应用于所有方法。Fallback注释指定如果拦截器无法从故障中恢复,应调用哪个方法。此方法可以提供替代结果或通知有关错误。

容错性注解也完全支持可配置性。注释的属性可以通过我们之前使用的相同配置机制来覆盖。当为方法启用任何拦截器时,它将从类和字段名称生成的配置名称中读取配置。例如,要指定方法findAuthor的重试次数,我们可以使用名称ws.ament.microprofile.gettingstarted.AuthorService / findAuthor / Retry / maxRetries来指定配置属性。

这也意味着您可以在代码中使用没有任何属性的注释,并在稍后为每个环境配置不同的值。

在代码中,我们还看到由MicroProfile容器提供的REST客户端代理。该URL由生成的配置名称的外部配置指定,类似于容错注释。剩下的只是在代理上调用一个方法来完成远程调用的所有工作并返回一个Author实例。

监视发生了什么事

所以你得有它!几个休息控制器,服务器,并且您有一个使用Eclipse MicroProfile构建的微服务来管理书籍。

最后一件事是找出你的应用程序中发生了什么。MicroProfile容器中的度量和健康检查功能提供了很多开箱即用的信息,它可以通过REST端点获得。

在应用程序的生命周期中收集的各种度量标准可以通过基于/ metrics基本路径的HTTP上的REST,以JSON或Prometheus格式自动公开。它提供了有关JVM,线程,加载的类和操作系统的常用指标。其他自定义指标可以由实现提供。应用程序还可以使用方法拦截器或生产者方法非常轻松地收集度量标准。

例如,如果服务在本地主机和端口8080上运行,则可以简单地使用HTTP头Accept = application / json 访问http:// localhost:8080 / metrics,您将得到如下所示的内容:

{
    "base": {
        "classloader.totalLoadedClass.count": 16987,
        "cpu.systemLoadAverage": 1.22,
        "thread.count": 141,
        "classloader.currentLoadedClass.count": 16986,
        "jvm.uptime": 52955,
        "memory.committedNonHeap": 131727360,
        "gc.PS MarkSweep.count": 3,
        "memory.committedHeap": 503316480,
        "thread.max.count": 143,
        "gc.PS Scavenge.count": 20,
        "cpu.availableProcessors": 8,
        "thread.daemon.count": 123,
        "classloader.totalUnloadedClass.count": 2,
        "memory.usedNonHeap": 117340624,
        "memory.maxHeap": 503316480,
        "memory.usedHeap": 139449848,
        "gc.PS MarkSweep.time": 428,
        "memory.maxNonHeap": -1,
        "gc.PS Scavenge.time": 220
    }
}

您还可以访问http:// localhost:8080 / health,以确定服务运行良好还是存在一些错误。这是一个简单的是/否检查,如果一切正常,则提供HTTP 200状态码。这适用于可以自动检测并重新启动服务的系统,例如Kubernetes。

MicroProfile 1.3还有一些组件,例如Open API和Open Tracing。我们不会在这里介绍它们,您可以在microprofile.io上找到它们来了解API和文档。您可以在Payara MicroProfile文档中找到更多关于Microprofile API的文档,包括由Payara Micro添加的其他增强功能。

您也可以在GitHub上下载本文中使用的完整示例代码。

本文的版权归 陈良莉 所有,如需转载请联系作者。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏SpringSpace.cn

RHEL 4.7 (64bit) 环境安装 GCC 4.6 测试记录 (更新至gcc-4.6.1)

Red Hat Enterprise Linux AS release 4 (Nahant Update 7)

1202
来自专栏CodeSheep的技术分享

Pipeline As Code With Jenkins2.0

3399
来自专栏Python中文社区

使用python实现后台系统的JWT认证

專 欄 ❈ 茶客furu声,Python中文社区专栏作者 博客: http://www.jianshu.com/p/537b356d34c9 ❈ ...

4505
来自专栏微信小程序开发

小程序如何区分转发好友和转发群

知晓程序员,专注微信小程序开发的程序员! 前言:群内隔段时间就有同学问,小程序的转发,是否可以区分转发给好友,还是转发给微信群,今天给大家说说如何区分。顺便说一...

2985
来自专栏恰同学骚年

.NET Core微服务之基于EasyNetQ使用RabbitMQ消息队列

  “消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中,“消息队列”是在消息的传...

1755
来自专栏惨绿少年

OpenStack云计算之路-Mitaka 版本

1.1 云计算简介 云计算(英语:cloud computing ),是一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息可以按需求提供给计算机各种...

4028
来自专栏喵了个咪的博客空间

phalapi-进阶篇5(数据库读写分离以及多库使用)

#phalapi-进阶篇5(数据库读写分离以及多库使用)# ? ##前言## 先在这里感谢phalapi框架创始人@dogstar,为我们提供了这样一个优秀的开...

3469
来自专栏木宛城主

SharePoint 2013 Disaster Recovery——迁移内容数据库

安装和配置SharePoint Farm时,一定要注意将内容数据库不要放在C盘,除非你的C盘能足够承受起日益增长的数据。由于在安装SQL SERVER中没有注...

18610
来自专栏coding...

基于55-go的二次开发GUI版本Mac&Win&Linux写在前面项目简介代码Tips&工具最后

一直想搞win版本的,毕竟win的用户量是大头。 由于没有win的电脑,加上C#版本的55实在看不懂,只能去考虑跨平台了。 于是,golang来了~也是因为...

1226
来自专栏Ryan Miao

gradle中使用嵌入式(embedded) tomcat, debug 启动

在gradle项目中使用embedded tomcat。 最开始部署项目需要手动将web项目打成war包,然后手动上传到tomcat的webapp下,然后启动t...

3519

扫码关注云+社区