Lagom框架

发布于:2021-01-21 14:38:11

0

220

0

lagom 教程 框架

完全不同,但仍然很容易-这就是新的开源微服务框架Lagom试图创建的二分法。它与其他框架有什么区别?处理起来有多容易?这个名字到底是什么意思?

关于这个名字的意思的问题不容易回答,因为人们不能从字面上翻译瑞典成语Lagom。根据维基百科,这个词的意思是:“足够,足够,足够,恰到好处。”在我们的例子中,这不应该是自我表扬,而是对微服务概念的批评声明。Lagom建议,我们不要把重点放在“微”上,而要固执地遵循“代码越少越好”的概念,而是从领域驱动的设计中考虑“有界上下文”的概念,以找到服务的边界。领域驱动设计和微服务的概念接近性可以在Lagom框架的不同位置找到。

Lagom入门

使用Lagom开发应用程序的最简单方法是借助Maven项目模板。

$ mvn archetype:generate -DarchetypeGroupId=com.lightbend.lagom  -DarchetypeArtifactId=maven-archetype-lagom-java  -DarchetypeVersion=1.1.0

在回答了有关名称的问题并切换到新创建的目录后,您将发现此处显示的目录结构。

cassandra-config hello-api hello-impl integration-tests pom.xml stream-api stream-impl

正如微服务所应该的那样,已经生成了两个服务,而不是一个服务。毕竟,服务之间的交互和通信至少与单个on的实现同等重要(而且常常是更大的挑战)。下面是服务“hello”和“stream”;每个实现都分为两个子项目(“api”和“impl”)。

要启动应用程序,只需一个简单的mvn lagom:runAll。

下载几次之后,它应该在端口9000上运行。这可以通过HTTPIE这样的命令行工具轻松检查:

$ http localhost:9000/api/hello/Lagom HTTP/1.1 200 OK Content-Type: text/plainHello, Lagom!

开发所需的所有组件都有一个特殊性,即它们——项目的服务、服务注册中心、API网关,甚至数据库Cassandra(在嵌入式版本中)——都是通过Maven插件启动的。不需要在项目外部设置服务或数据库。Lagom强调为开发人员提供一个交互的环境的重要性-检查项目并开始工作。这包括代码更改将在重新加载后立即生效,而不需要构建/部署/重新启动周期。

服务API-类型安全和异步

从文件夹结构可以看出,每个服务都分为一个实现(“-”)和一个API定义(“-”)。后者以编程方式定义服务的HTTP接口。

public interface HelloService extends Service {

 ServiceCallhello(String id);

 default Descriptor descriptor() {
   return named("hello").withCalls(
       pathCall("/api/hello/:id",  this::hello),
     );
 }
}

在生成器的帮助下,将创建服务描述,其中请求的路径将映射到方法调用上。

此接口不仅是实现的模板;Lagom还生成适当的客户机库。在其他Lagom服务中,这可以通过Google的Guice的依赖注入来实现。这样,在选择相应的服务时提供类型安全接口。可以省略手动构造HTML请求和直接使用通用http客户机。

不过,使用客户机库并不是强制性的,因为框架将方法调用映射到HTTP调用上,HTTP调用也可以直接调用,特别是非Lagom服务。
顺便说一句,我们的“hello”小方法不直接传递响应,而是一个ServiceCall。这是一个功能接口。也就是说,我们创建的不是一个简单的对象,而是一个函数,这个函数将由相应的请求执行。我们将类型作为请求(因为user GET call不提交任何数据,在本例中为“NotUsed”)和响应(在本例中为一个简单的字符串)的类型参数来传递。请求的处理总是异步的–函数的结果必须是CompletionStage。Lagom广泛使用java8特性。简单的实现如下所示:

public class HelloServiceImpl implements HelloService {
 @Override
 public ServiceCallhello(String id) {
   return request -> {
     CompletableFuture.completedFuture("Hello, " + id);
   };
 }
}

对于一个简单的GET请求,服务描述符的增益是有限的。当我们想要在服务之间异步发送事件时,它变得更有趣。我们可以在Lagom中通过为ServiceCall选择不同的类型参数来实现这一点。如果我们的请求和响应类型被定义为source(来自Akka streams库的类型),那么框架将初始化一个WebSocket链接。在这里,服务抽象可以得分,因为它简化了WebSockets的工作。就未来版本而言,有计划支持额外的“发布/订阅”模式,以便消息可以放置在总线上,其他服务可以订阅它。

public interface StreamService extends Service {

 ServiceCall<Source, Source> stream();

 @Override
 default Descriptor descriptor() {
   return named("stream").withCalls(namedCall("stream", this::stream));
 }
}

内置断路器

假设我们的服务在另一个服务的每个HTTP请求中请求信息。这在预期的时间范围内没有响应,这意味着将有一个超时。对这个服务器的请求不应该不断重复,因为我们从应用程序请求了不必要的空闲时间:如果我们很可能得不到响应,为什么要等待超时?此外,还会有请求累积到服务。一旦它再次可用,它将受到大量未决请求的炮轰,以至于它将立即屈服。

解决这个问题的可靠方法是断路器模式[6]。断路器知道三种状态:

  • 只要一切正常运行,它就会关闭

  • 如果达到规定的错误限制(超时、异常),它将在规定的时间段内打开。其他请求将以“CircuitBreakerException”失败。对于客户端来说,不会有额外的等待时间,外部服务甚至不会注意到请求。

  • 一旦设置的时间段结束,断路器将切换到“半开”状态。现在将有一个请求通过。如果成功,断路器将关闭-外部系统似乎再次可用。如果失败,下一轮将以“open”状态开始。
    这样的断路器已经集成到Lagom服务客户端中。参数可通过配置文件进行调整。

Lagom持久性

证明Lagom与其他微观框架非常不同的一个方面是事件源框架和CQRS的集成。
对于许多开发人员来说,使用关系数据库仍然是“默认情况”,可能与ORM工具有关。即使这可以在Lagom中实现,但是用户被引导到另一个方向。Lagom中的标准是使用“持久实体”(对应于域驱动设计中的“聚合根”)。这些持久实体接收消息(命令)。

public class HelloEntity extends PersistentEntity{

 @Override
 public Behavior initialBehavior(OptionalsnapshotState) {

   /*
    * Das Behavior definiert, wie die Entity auf Kommandos reagiert.
    */
   BehaviorBuilder b = newBehaviorBuilder(
       snapshotState.orElse(new HelloState("Hello", LocalDateTime.now().toString())));

   /*
    * Command handler für UseGreetingMessage.
    */
   b.setCommandHandler(UseGreetingMessage.class, (cmd, ctx) ->ctx.thenPersist(new GreetingMessageChanged(cmd.message),
       evt -> ctx.reply(Done.getInstance())));

   /*
    * Event handler für GreetingMessageChanged..
    */
   b.setEventHandler(GreetingMessageChanged.class,
       evt -> new HelloState(evt.message, LocalDateTime.now().toString()));

   return b.build();
 }
}

您可以清楚地看到这在代码中是如何表示的。我们非常简单的实体允许我们更改服务的欢迎文本。我们扩展了超类PersistentEntity,它需要三个类型参数:命令类型、事件类型和状态类型。在本例中,我们将命令定义为一个类UseGreetingMessage,它实现了接口HelloCommand,其实例是不可变的。出于保存类型的目的,可以从Immutables库返回命令、事件和状态。为了节省自己的一些击键,您可以利用一个库,例如命令、事件和状态的不可变项。

实体响应命令的方式是由行为定义的。这在运行时可能会改变。通过这种方式,实体可以实现有限状态机——在运行时用一种行为替换另一种行为与机器转换到另一种状态相关。

框架通过initialBevahior获得初始行为。为了构造它,我们将使用builder模式。

首先,我们将CommandHandler定义为我们的命令。如果命令有效并要求更改实体,例如,如果将属性设置为新值,则不会立即发生更改。相反,将创建、保存和发出一个事件。持久实体的EventHandler(我们还将其与构建器一起添加到行为中)对事件作出反应并执行实际更改。

与关系数据库中的“更新”相比,一个显著的区别是持久实体的当前状态不一定要保存。这将仅仅保存在内存中(内存图像)。如果有必要恢复状态,例如在重新启动应用程序之后,将通过回放事件来重建状态。当前状态的可选保存在模型中称为“快照”,并不替换事件历史,只表示“预处理”。如果一个实体在其生命周期内经历了数千次状态变化,则不需要从一开始就回放所有事件。可以通过从最新快照开始并仅重复以下事件来创建快捷方式。

Lagom对行为的类型和结构给出的严格规范是为了方便开发人员转换为这个原则,称为事件源。我的想法是,我被迫为每个实体指定一个明确的协议:哪些命令可以被处理,哪些事件可以被触发,哪些值定义了我的类的状态?

包括群集

我可以使用的持久实体的数量不受单个服务器的主内存的限制。相反,每个Lagom应用程序都可以用作分布式应用程序。在一个额外实例的启动过程中,我只需要添加一个已经运行的实例的地址,然后它将在那里注册并与当前实例形成一个集群。持久实体由框架管理,并将在集群内自动分布(集群分片)。如果节点被添加到集群或从集群中移除,框架将重新分发实例。同样,它可以恢复从内存中删除的实例(钝化)。

顺便说一句,Lagom最初还没有开发出这样的内置特性,即以这种方式将应用程序状态保存在内存中,也可以进行扩展。为此,拉贡依赖阿克卡。这肯定已经在任务关键型应用程序中使用,因此任何关于年轻框架可靠性的担忧都是没有根据的。

写作和阅读分开

虽然在SQL数据库中很容易从数据模型请求任何信息,但在事件源的情况下却不可能。我们只能访问实体并使用主键请求状态。因为我们只有一个事件日志而没有关系数据模型,所以通过二级索引进行查询是不可能的。

为了实现这一点,应用了CQRS体系结构(命令查询责任分离,用于进一步阅读:CQRS旅程)。这里的基本原理是不同的数据模型用于读写。在我们的例子中,这意味着我们的事件日志是写端。。它可以用来重建实体,但我们不会对此执行任何查询。相反,我们还从事件生成一个read sidefrom。Lagom已经提供了ReadSideProcessor。与persistentities类结合发生的每个事件也将被处理并用于创建读取端。这是为阅读而优化的,不允许直接写。

这种体系结构方法不仅具有技术优势,因为在许多应用程序中,读取和写入频率非常不同,并且使用这种方法可以独立地进行缩放。它还提供了一些新的可能性。由于从不删除保存的事件,因此可以在读取端添加新的结构,即所谓的投影。这些可以被历史事件填满,因此不仅可以提供未来的信息,也可以提供过去的信息。

CQRS允许在读取端使用不同的技术,并根据用例进行调整。可以想象的是,虽然Lagom还不支持,但是可以构建一个SQL read side并继续使用可用的工具,但同时提供一个ElasticSearch数据库来进行快速搜索,并将事件发送到Spark Streaming进行分析。

重要的是要记住,读取端将被异步刷新,并带有延迟(写入端和读取端之间的“最终一致性”)。强一致性仅在持久性级别上在该模型中可用。

不幸的是,目前它只适用于商业闭源产品导体。开源社区正在为Kubernetes和Consul开发实现,或者可以使用基于静态配置的ServiceLocator,但不建议在生产环境中使用。

结论

Lagom遵循一条有趣的道路,是一个非凡的框架。它在技术基础上有着根本的不同:一切都是异步的,它基于发送命令,而持久化是根据事件源来完成的。这为服务的可伸缩性带来了巨大的优势,但对于大多数开发人员(包括来自javaee领域的每个人)来说,这意味着需要重新思考。随着编程语言的变化,总是会担心生产力会暂时下降,因为开发人员无法恢复熟悉的实践和资源。我们的情况也是如此。

Lagom试图通过给开发者一条清晰的路径来防止这种情况。如果我遵循Lagom中服务实现和持久化的教科书方法的文档,我将能够构建一个反应式系统——完全基于消息传递并能够集群,甚至可能在没有意识到的情况下。

在相对较新的微服务领域,标准尚未建立。我们必须看看哪些框架能够经得起时间的考验。与javaee和Spring的老熟人不同的是,Lagom为这个系统注入了新的活力,并使一个完全不同的体系结构处于平衡状态。那些希望尝试一些新东西并且对可伸缩分布式系统感兴趣的人会发现Lagom很有用。