1. 概述
本文将介绍 Armeria —— 一个用于高效构建微服务的灵活框架。我们将探讨它的核心功能、应用场景以及使用方法。
简单来说,Armeria 提供了便捷的方式构建支持多种协议的微服务客户端和服务器,包括 REST、gRPC、Thrift 和 GraphQL。此外,它还深度集成了多种技术栈:
✅ 服务发现:支持 Consul、Eureka、Zookeeper
✅ 分布式追踪:集成 Zipkin
✅ 框架整合:兼容 Spring Boot、Dropwizard、RESTEasy
2. 依赖配置
使用 Armeria 前需引入依赖,当前最新版本为 1.29.2。
核心依赖位于 com.linecorp.armeria:armeria
,Maven 配置如下:
<dependency>
<groupId>com.linecorp.armeria</groupId>
<artifactId>armeria</artifactId>
<version>1.29.2</version>
</dependency>
根据具体需求,还可添加其他集成依赖(如 gRPC、GraphQL 等)。
2.1. 使用 BOM 管理依赖
Armeria 依赖众多,推荐使用 Maven BOM 统一管理版本:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.linecorp.armeria</groupId>
<artifactId>armeria-bom</artifactId>
<version>1.29.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置后,引入依赖时无需指定版本:
<dependency>
<groupId>com.linecorp.armeria</groupId>
<artifactId>armeria</artifactId>
</dependency>
⚠️ 单依赖时优势不明显,但项目规模扩大后能显著简化版本管理。
3. 启动服务器
配置好依赖后,我们先从 HTTP 服务器入手。Armeria 通过 ServerBuilder
配置服务器,构建后启动:
ServerBuilder sb = Server.builder();
sb.service("/handler", (ctx, req) -> HttpResponse.of("Hello, world!"));
Server server = sb.build();
CompletableFuture<Void> future = server.start();
future.join();
这段代码会启动一个监听随机端口的服务器,并注册一个硬编码处理器。 启动日志会显示监听地址:
07:36:46.508 [main] INFO com.linecorp.armeria.common.Flags -- verboseExceptions: rate-limit=10 (default)
07:36:46.957 [main] INFO com.linecorp.armeria.common.Flags -- useEpoll: false (default)
07:36:46.971 [main] INFO com.linecorp.armeria.common.Flags -- annotatedServiceExceptionVerbosity: unhandled (default)
07:36:47.262 [main] INFO com.linecorp.armeria.common.Flags -- Using Tls engine: OpenSSL BoringSSL, 0x1010107f
07:36:47.321 [main] INFO com.linecorp.armeria.common.util.SystemInfo -- hostname: k5mdq05n (from 'hostname' command)
07:36:47.399 [armeria-boss-http-*:49167] INFO com.linecorp.armeria.server.Server -- Serving HTTP at /[0:0:0:0:0:0:0:0%0]:49167 - http://127.0.0.1:49167/
3.1. 服务器配置
启动前可通过多种方式配置服务器:
最常用的是指定监听端口,否则会随机选择可用端口:
ServerBuilder sb = Server.builder();
sb.http(8080); // HTTP 端口
若需 HTTPS,需先配置 TLS 证书。Armeria 提供自签名证书生成工具:
ServerBuilder sb = Server.builder();
sb.tlsSelfSigned(); // 自动生成自签名证书
sb.https(8443); // HTTPS 端口
3.2. 添加访问日志
默认不记录请求日志(通常由负载均衡器处理)。如需启用,通过 ServerBuilder.accessLogWriter()
配置:
// Apache Common Log 格式
sb.accessLogWriter(AccessLogWriter.common(), true);
// Apache Combined Log 格式
sb.accessLogWriter(AccessLogWriter.combined(), true);
日志通过 SLF4J 输出,示例:
07:25:16.481 [armeria-common-worker-kqueue-3-2] INFO com.linecorp.armeria.logging.access -- 0:0:0:0:0:0:0:1%0 - - 17/Jul/2024:07:25:16 +0100 "GET /#EmptyServer$$Lambda/0x0000007001193b60 h1c" 200 13
07:28:37.332 [armeria-common-worker-kqueue-3-3] INFO com.linecorp.armeria.logging.access -- 0:0:0:0:0:0:0:1%0 - - 17/Jul/2024:07:28:37 +0100 "GET /unknown#FallbackService h1c" 404 35
4. 添加服务处理器
服务器需注册处理器才能处理请求。 Armeria 原生支持 HTTP 处理器,gRPC/Thrift/GraphQL 需额外依赖。
4.1. 简单处理器
最基础的方式是使用 ServerBuilder.service()
注册处理器:
sb.service("/handler", handler);
HttpService
是 SAM 接口,可直接用 lambda 实现:
sb.service("/handler", (ctx, req) -> HttpResponse.of("Hello, world!"));
处理器需实现 HttpResponse HttpService.serve(ServiceRequestContext, HttpRequest)
方法,参数提供请求上下文和内容,返回响应对象。
4.2. URL 模式
Armeria 支持多种 URL 匹配模式:
- 精确匹配:
/handler
- 路径参数:
sb.service("/curly/{name}", (ctx, req) -> HttpResponse.of("Hello, " + ctx.pathParam("name"))); sb.service("/colon/:name", (ctx, req) -> HttpResponse.of("Hello, " + ctx.pathParam("name")));
- Glob 匹配(需
glob:
前缀):
匹配sb.service("glob:/base/*/glob/**", (ctx, req) -> HttpResponse.of("Hello, " + ctx.pathParam("0") + ", " + ctx.pathParam("1")));
/base/a/glob
、/base/a/glob/b
等,*
匹配单段,**
匹配多段。 - 正则表达式(需
regex:
前缀):sb.service("regex:^/regex/[A-Za-z]+/[0-9]+$", (ctx, req) -> HttpResponse.of("Hello, " + ctx.path())); // 命名捕获组 sb.service("regex:^/named-regex/(?<name>[A-Z][a-z]+)$", (ctx, req) -> HttpResponse.of("Hello, " + ctx.pathParam("name")));
4.3. 精细化路由配置
通过 ServerBuilder.route()
可精确匹配 HTTP 方法、头信息等:
sb.route()
.methods(HttpMethod.GET)
.path("/get")
.produces(MediaType.PLAIN_TEXT)
.matchesParams("name")
.build((ctx, req) -> HttpResponse.of("Hello, " + ctx.path()));
仅匹配:
- GET 方法
- 接受
text/plain
响应 - 包含
name
查询参数
不匹配时自动返回标准错误码(如 405 Method Not Allowed)。
5. 注解式处理器
Armeria 支持通过注解自动映射方法到处理器,简化复杂服务开发。
使用 ServerBuilder.annotatedService()
注册:
sb.annotatedService(new AnnotatedHandler());
类中方法用 @Get
/@Post
/@Put
/@Delete
等注解标记:
@Get("/handler")
public String handler() {
return "Hello, World!";
}
方法签名更灵活,参数自动映射,返回值自动转换。
5.1. 处理器参数
以下类型参数自动注入:
ServiceRequestContext
、HttpRequest
、RequestHeaders
、QueryParams
、Cookies
@Get("/handler")
public String handler(ServiceRequestContext ctx) {
return "Hello, " + ctx.path();
}
@Param
注解自动注入路径/查询参数:
@Get("/handler/{name}")
public String handler(@Param String name) {
return "Hello, " + name;
}
⚠️ 默认参数必填,可通过 Optional<>
、@Nullable
或 @Default
设为可选。
5.2. 请求体处理
支持多种请求体接收方式:
原始字节:
byte[]
或HttpData
@Post("/byte-body") public String byteBody(byte[] body) { return "Length: " + body.length; }
解码文本:
String
或CharSequence
@Post("/string-body") public String stringBody(String body) { return "Hello: " + body; }
JSON 反序列化(需 Jackson)
@Post("/json-body") public String jsonBody(JsonBody body) { return body.name + " = " + body.score; } record JsonBody(String name, int score) {}
自定义请求转换器: 实现 RequestConverterFunction
接口
public class UppercasingRequestConverter implements RequestConverterFunction {
@Override
public Object convertRequest(ServiceRequestContext ctx, AggregatedHttpRequest request,
Class<?> expectedResultType, ParameterizedType expectedParameterizedResultType)
throws Exception {
if (expectedResultType.isAssignableFrom(String.class)) {
return request.content(StandardCharsets.UTF_8).toUpperCase();
}
return RequestConverterFunction.fallthrough();
}
}
通过 @RequestConverter
启用:
@Post("/uppercase-body")
@RequestConverter(UppercasingRequestConverter.class)
public String uppercaseBody(String body) {
return "Hello: " + body;
}
5.3. 响应处理
返回值自动转换为 HTTP 响应:
null
→ HTTP 204 No Contentbyte[]
/HttpData
→application/octet-stream
CharSequence
(含String
)→text/plain
(UTF-8)JsonNode
→application/json
JSON 响应(需 @ProducesJson
):
@Get("/json-response")
@ProducesJson
public JsonBody jsonResponse() {
return new JsonBody("Baeldung", 42);
}
自定义响应转换器: 实现 ResponseConverterFunction
public class UppercasingResponseConverter implements ResponseConverterFunction {
@Override
public HttpResponse convertResponse(ServiceRequestContext ctx, ResponseHeaders headers,
@Nullable Object result, HttpHeaders trailers) {
if (result instanceof String) {
return HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8,
((String) result).toUpperCase(), trailers);
}
return ResponseConverterFunction.fallthrough();
}
}
通过 @ResponseConverter
启用:
@Post("/uppercase-response")
@ResponseConverter(UppercasingResponseConverter.class)
public String uppercaseResponse(String body) {
return "Hello: " + body;
}
5.4. 异常处理
默认异常映射:
IllegalArgumentException
→ HTTP 400 Bad RequestHttpStatusException
/HttpResponseException
→ 对应 HTTP 状态码- 其他异常 → HTTP 500 Internal Server Error
自定义异常处理器: 实现 ExceptionHandlerFunction
public class ConflictExceptionHandler implements ExceptionHandlerFunction {
@Override
public HttpResponse handleException(ServiceRequestContext ctx, HttpRequest req, Throwable cause) {
if (cause instanceof IllegalStateException) {
return HttpResponse.of(HttpStatus.CONFLICT);
}
return ExceptionHandlerFunction.fallthrough();
}
}
通过 @ExceptionHandler
启用:
@Get("/exception")
@ExceptionHandler(ConflictExceptionHandler.class)
public String exception() {
throw new IllegalStateException();
}
6. GraphQL 支持
需添加依赖:
<dependency>
<groupId>com.linecorp.armeria</groupId>
<artifactId>armeria-graphql</artifactId>
</dependency>
使用 GraphqlService
暴露 GraphQL 接口:
sb.service("/graphql",
GraphqlService.builder().graphql(buildSchema()).build());
buildSchema()
返回 GraphQL Java 库的 GraphQL
实例。
7. 客户端调用
Armeria 提供 WebClient
调用 HTTP 服务:
WebClient webClient = WebClient.of();
AggregatedHttpResponse response = webClient.get("http://localhost:8080/handler")
.aggregate()
.join();
WebClient.get()
发起 GET 请求,返回流式响应。 aggregate()
将响应聚合为 AggregatedHttpResponse
。
访问响应内容:
System.out.println(response.status());
System.out.println(response.headers());
System.out.println(response.content().toStringUtf8());
指定基础 URL:
WebClient webClient = WebClient.of("http://localhost:8080");
AggregatedHttpResponse response = webClient.get("/handler")
.aggregate()
.join();
POST 请求示例:
WebClient webClient = WebClient.of();
AggregatedHttpResponse response = webClient.post("http://localhost:8080/uppercase-body", "baeldung")
.aggregate()
.join();
7.1. 复杂请求
底层通过 execute()
方法实现:
WebClient webClient = WebClient.of("http://localhost:8080");
HttpRequest request = HttpRequest.of(
RequestHeaders.builder()
.method(HttpMethod.POST)
.path("/uppercase-body")
.contentType(MediaType.PLAIN_TEXT_UTF_8) // 类型安全替代 add()
.build(),
HttpData.ofUtf8("Baeldung"));
AggregatedHttpResponse response = webClient.execute(request)
.aggregate()
.join();
7.2. 客户端配置
通过 ClientFactory
配置底层参数:
ClientFactory clientFactory = ClientFactory.builder()
.connectTimeout(Duration.ofSeconds(10)) // 连接超时
.idleTimeout(Duration.ofSeconds(60)) // 空闲超时
.build();
WebClient webClient = WebClient.builder("http://localhost:8080")
.factory(clientFactory)
.build();
8. 总结
本文简要介绍了 Armeria 的核心功能。这个框架远不止于此,建议动手实践探索更多特性!