用Scala和Lagom构建微服务
立即解锁
发布时间: 2025-08-19 00:05:42 阅读量: 1 订阅数: 3 


Scala 2.13编程实战与进阶
### 用 Scala 和 Lagom 构建微服务
#### 1. 微服务项目构建回顾
在之前的实践中,我们构建了两个具有相似功能但技术栈不同的小型微服务示例。
第一个项目采用纯函数式方法,将效果封装在 IO 单子和函数流中。通过对象关系映射(ORM)方法,将系统状态映射到数据库表,并根据需要的更改对其进行修改。最后,使用 http4s 客户端构建集成测试来测试整个系统。
第二个项目基于 “官方” 的 Lightbend 栈。研究了 Akka - HTTP 和 Akka Persistence 的协同工作方式,事件溯源方法可通过从持久事件中重新组合来在内存中重建状态,从而避免编写 SQL 语句。还展示了如何使用 Akka - HTTP 测试套件在不启动真实 HTTP 服务器的情况下测试路由。
#### 2. 为何选择 Lagom
微服务架构虽降低了单个服务的复杂性,但系统整体复杂性并未消失,而是从单个微服务内部转移到了它们之间。在基于 Akka 的解决方案中,需要选择合适的数据库存储事件、定义和应用迁移以获得正确的数据库架构,手动读取和验证配置,以及决定如何传递依赖项。
Lagom 框架基于现有技术,采用 “约定优于配置” 的方法,提供项目 “模板”,减轻了重复机械任务的负担,并提供了微服务系统特有的额外功能。其预配置功能如下:
| 功能 | 详情 |
| ---- | ---- |
| 分布式持久化 | 使用事件溯源机制,推荐数据库为 Apache Cassandra,因其具有出色的可扩展性和对 CQRS 原则读端的自然支持。 |
| 异步通信 | 利用 Akka Streams 实现响应式流,以 Apache Kafka 作为消息代理支持消息传递风格的异步通信。 |
| 透明通信协议支持 | 允许将复杂的 API 调用抽象为简单的方法调用。 |
| 表达性服务描述 DSL | 可灵活简洁地定义 API。 |
| 动态可扩展性 | 借助 Akka Cluster 实现。 |
| 依赖注入框架选择 | 可在编译或运行时连接应用程序。 |
| 开发模式 | 支持热代码重新加载,可通过单个命令启动所有服务和所需的基础设施组件,包括开发服务注册表和服务网关。 |
| 默认配置 | 提供基础设施的默认配置和预配置日志。 |
#### 3. 面包店项目概述与设置
##### 3.1 面包店项目
面包店有几个员工共同制作美味的饼干,每个员工在特定领域有专长:
- 经理:推动流程,接收各参与者的工作结果并传递到下一步,创建初始购物清单。
- 男孩:拿到购物清单后去杂货店购买杂货。
- 厨师:用杂货制作面团,通过创建有限容量的搅拌器并行处理大量杂货。
- 厨师助手:用面团制作生饼干。
- 面包师:用烤箱将生饼干烘焙成美味的饼干。
这次将每个员工建模为一个微服务,搅拌器和烤箱将成为所属服务的实现细节。
##### 3.2 项目设置
使用 Lagom 框架实现微服务,由于 SBT 为 Scala 项目提供了更好的用户体验,因此选择 SBT 作为构建工具。
创建 Lagom 项目的步骤如下:
1. 创建项目文件夹并进入:
```bash
slasch@void:~$ mkdir bakery
slasch@void:~$ cd bakery
```
2. 添加 Lagom SBT 插件:
```bash
slasch@void:~$ echo 'addSbtPlugin("com.lightbend.lagom" % "lagom-sbt-plugin" % "1.4.7")' > plugins.sbt
```
3. 指定 SBT 版本:
```bash
slasch@void:~$ echo 'sbt.version=1.1.5' > build.properties
```
4. 设置 SBT 选项:
```bash
slasch@void:~$ cd ..
slasch@void:~$ echo '-J-Xms1024M
> -J-Xmx4096M
> -J-Xss2M
> -J-XX:MaxMetaspaceSize=1024M' > .sbtopts
```
`build.sbt` 文件定义项目的一般属性、共享模型和五个微服务,部分代码如下:
```scala
organization in ThisBuild := "packt"
version in ThisBuild := "1.0-SNAPSHOT"
scalaVersion in ThisBuild := "2.12.6"
val macwire = "com.softwaremill.macwire" %% "macros" % "2.3.0" % Provided
val scalaTest = "org.scalatest" %% "scalatest" % "3.0.5" % Test
val defaultDependencies = Seq(lagomScaladslTestKit, macwire, scalaTest)
lazy val `shared-model` = (project in file("shared-model"))
.settings(libraryDependencies += lagomScaladslApi)
lazy val bakery = (project in file("."))
.aggregate(
`boy-api`, `boy-impl`,
`chef-api`, `chef-impl`,
`cook-api`, `cook-impl`,
`baker-api`, `baker-impl`,
`manager-api`, `manager-impl`)
lazy val `boy-api` = (project in file("boy-api"))
.settings(libraryDependencies += lagomScaladslApi)
// other APIs defined the same way
lazy val `boy-impl` = (project in file("boy-impl"))
.enablePlugins(LagomScala)
.settings(libraryDependencies ++= defaultDependencies)
.dependsOn(`boy-api`)
// other implementations defined the same way
lazy val `chef-impl` = (project in file("chef-impl"))
.enablePlugins(LagomScala)
.settings(
libraryDependencies ++= Seq(
lagomScaladslPersistenceCassandra,
lagomScaladslKafkaBroker,
lagomScaladslTestKit,
lagomScaladslPubSub,
macwire
)
)
.settings(lagomForkedTestSettings: _*)
.dependsOn(`chef-api`)
lazy val `manager-impl` = (project in file("manager-impl"))
.enablePlugins(LagomScala)
.settings(libraryDependencies ++= defaultDependencies)
.dependsOn(`manager-api`, `boy-api`, `chef-api`, `cook-api`, `baker-api`)
```
最终项目的文件结构如下:
```plaintext
slasch@void:~/ch15 [ch15-lagom]$ tree --dirsfirst
.
├── baker-api
├── baker-impl
├── boy-api
├── boy-impl
├── chef-api
├── chef-impl
├── cook-api
├── cook-impl
├── manager-api
├── manager-impl
├── project
│ ├── build.properties
│ └── plugins.sbt
└── build.sbt
```
为简化示例,定义一个包含所有服务使用的 case 类定义的模块,并将其作为依赖项添加到其他模块中。模块中定义的 case 类如下:
```scala
package ch15
object model {
final case class ShoppingList(eggs: Int, flour: Int, sugar: Int, chocolate: Int)
final case class Groceries(eggs: Int, flour: Int, sugar: Int, chocolate: Int)
final case class Dough(weight: Int)
final case class RawCookies(count: Int)
final case class ReadyCookies(count: Int)
}
```
同时,使用 Play 的宏定义序列化规
0
0
复制全文
相关推荐










