跳到主要内容

6.29 配置 HTTP 服务器

HTTP 服务器有许多配置选项。它们被定义在 NettyHttpServerConfiguration 配置类中,它扩展了 HttpServerConfiguration

下面的例子显示了如何通过 application.yml 来调整服务器的配置选项:

配置 HTTP 服务器设置

micronaut:
server:
maxRequestSize: 1MB
host: localhost (1)
netty:
maxHeaderSize: 500KB (2)
worker:
threads: 8 (3)
childOptions:
autoRead: true (4)
  1. 默认情况下,Micronaut 绑定了所有网络接口。使用 localhost 只绑定到回环网络接口。
  2. 报头的最大尺寸
  3. Netty 工作线程的数量
  4. 自动读取请求正文

表 1.为 NettyHttpServerConfiguration 配置属性

属性类型描述
micronaut.server.netty.child-optionsjava.util.Map设置 Netty 的子 worker 选项。
micronaut.server.netty.optionsjava.util.Map设置 channel 选项。
micronaut.server.netty.max-initial-line-lengthint设置 HTTP 请求的最大初始行长。默认值(4096)。
micronaut.server.netty.max-header-sizeint设置任何一个报头的最大尺寸。默认值(8192)。
micronaut.server.netty.max-chunk-sizeint设置任何单一请求块的最大尺寸。默认值(8192)。
micronaut.server.netty.max-h2c-upgrade-request-sizeint设置HTTP1.1请求主体的最大尺寸,用于将连接升级到HTTP2明文(h2c)。这个初始请求不能被流化,而是被完全缓冲,所以默认值(8192)是相对较小的。如果这个值对你的使用情况来说太小,可以考虑使用一个空的初始 "升级请求"(例如{@code OPTIONS /}),或者切换到正常的 HTTP2。 不影响正常的HTTP2(TLS)。
micronaut.server.netty.chunked-supportedboolean设置是否支持分块传输编码。默认值(true)。
micronaut.server.netty.validate-headersboolean设置是否验证传入的头文件。默认值(true)。
micronaut.server.netty.initial-buffer-sizeint设置初始缓冲区大小。默认值(128)。
micronaut.server.netty.log-levelio.netty.handler.logging.LogLevel设置 Netty 日志级别。
micronaut.server.netty.compression-thresholdint设置请求体的最小尺寸,以便进行压缩。默认值(1024)。
micronaut.server.netty.compression-levelint设置压缩级别(0-9)。默认值(6)。
micronaut.server.netty.use-native-transportboolean设置是否使用 netty 的本地传输(epoll或kqueue),如果有的话。默认值(false)。
micronaut.server.netty.fallback-protocoljava.lang.String设置通过 ALPN 谈判时使用的回避协议。
micronaut.server.netty.keep-alive-on-server-errorboolean是否在内部服务器错误时发送连接保持活力。默认值({@value DEFAULT_KEEP_ALIVE_ON_SERVER_ERROR})。
micronaut.server.netty.pcap-logging-path-patternjava.lang.String用于记录传入的连接到 pcap 的路径模式。这是一个不支持的选项:行为可能会改变,也可能完全消失,恕不另行通知。
micronaut.server.netty.listenersjava.util.List设置显式的netty监听器配置,如果它们应该是隐式的,则设置{@code null}。

使用本地传输

与基于 NIO 的传输相比,本地 Netty 传输增加了特定平台的功能,产生更少的垃圾,并普遍提高了性能。

要启用本地传输,首先要添加一个依赖项:

对于 x86 的 macOS:

runtimeOnly("io.netty:netty-transport-native-kqueue::osx-x86_64")

对于 M1 的 macOS:

runtimeOnly("io.netty:netty-transport-native-kqueue::osx-aarch_64")

对于 x86 的 Linux:

runtimeOnly("io.netty:netty-transport-native-epoll::linux-x86_64")

对于 ARM64 的 Linux:

runtimeOnly("io.netty:netty-transport-native-epoll::linux-aarch_64")

然后配置默认的事件循环组,使其倾向于本地传输:

配置默认的事件循环组,使其更倾向于本地传输

micronaut:
netty:
event-loops:
default:
prefer-native-transport: true
注意

Netty 启用了简单的抽样资源泄漏检测,报告是否有泄漏,其代价是少量的开销。你可以通过设置属性 netty.resource-leak-detector-level 来禁用它或启用更高级的检测:SIMPLE (默认)、DISABLEDPARANOIDADVANCED

6.29.1 配置服务器线程池

HTTP 服务器是建立在 Netty 上的,它被设计成一个事件循环模型的非阻塞 I/O 工具箱。

Netty worker 的事件循环使用 "default" 命名的事件循环组。这可以通过 micronaut.netty.event-loops.default 进行配置。

危险

micronaut.server.netty.worker 下的事件循环配置只在事件循环组被设置为不对应于任何 micronaut.netty.event-loops 配置的名称时使用。这一行为已被废弃,并将在未来的版本中被移除。使用 micronaut.netty.event-loops.* 进行任何事件循环组配置,而不是通过 event-loop-group 设置名称。这不适用于父事件循环配置(micronaut.server.netty.parent)。

表 1.为 Worker 配置属性

属性类型描述
micronaut.server.netty.workerNettyHttpServerConfiguration$Worker设置 worker 事件循环配置。
micronaut.server.netty.worker.event-loop-groupjava.lang.String设置使用的名字。
micronaut.server.netty.worker.threadsint设置事件循环组的线程数量。
micronaut.server.netty.worker.io-ratiojava.lang.Integer设置 I/O 率。
micronaut.server.netty.worker.executorjava.lang.String设置执行器名字。
micronaut.server.netty.worker.prefer-native-transportboolean设置是否偏爱本地传输(如果有的话)
micronaut.server.netty.worker.shutdown-quiet-periodjava.time.Duration设置关机静默期
micronaut.server.netty.worker.shutdown-timeoutjava.time.Duration设置关机超时(必须>= shutdownQuietPeriod)。
提示

父事件循环可以用 micronaut.server.netty.parent 配置,配置选项相同。

服务器也可以被配置为使用不同的命名 worker 事件循环:

为服务器使用不同的事件循环

micronaut:
server:
netty:
worker:
event-loop-group: other
netty:
event-loops:
other:
num-threads: 10
注意

线程数的默认值是系统属性 io.netty.eventLoopThreads 的值,如果没有指定,则是可用处理器 × 2。

关于配置事件循环,见下表:

表 2. DefaultEventLoopGroupConfiguration 的配置属性

属性类型描述
micronaut.netty.event-loops.*.num-threadsint
micronaut.netty.event-loops.*.io-ratiojava.lang.Integer
micronaut.netty.event-loops.*.prefer-native-transportboolean
micronaut.netty.event-loops.*.executorjava.lang.String
micronaut.netty.event-loops.*.shutdown-quiet-periodjava.time.Duration
micronaut.netty.event-loops.*.shutdown-timeoutjava.time.Duration

6.29.1.1 阻塞操作

当处理阻塞操作时,Micronaut 默认将阻塞操作转移到一个非绑定的、缓存的 I/O 线程池。你可以使用名为 ioExecutorConfiguration 来配置 I/O 线程池。比如说:

配置服务器 I/O 线程池

micronaut:
executors:
io:
type: fixed
nThreads: 75

上述配置创建了一个有 75 个线程的固定线程池。

6.29.1.2 @Blocking

你可以使用 @Blocking 注解来标记方法为阻塞。

如果你将 micronaut.server.thread-selection 设置为 AUTO,Micronaut 框架就会将带有 @Blocking 注解的方法的执行卸载到 IO 线程池中(参阅:TaskExecutors)。

注意

@Blocking 只有在你使用 AUTO 线程选择时才有效。从 Micronaut 2.0 开始,Micronaut 框架默认为手动选择线程。我们推荐使用 @ExecuteOn 注解来在不同的线程池中执行阻塞操作。@ExecutesOnMANUALAUTO 线程选择都有效。

有一些地方,Micronaut 框架内部使用了 @Blocking

阻塞类型描述
BlockingHttpClient用于测试,为 HttpClient 操作的一个子集提供阻塞版本。
IOUtils以阻塞方式读取一个 BufferedReader 的内容,并将其作为一个 String 返回。
BootstrapPropertySourceLocator解决当前 Environment 的远程或本地 PropertySource 实例。
提示

Micronaut Data 也在内部利用 @Blocking 来进行一些交易操作、CRUD 拦截器和仓库。

6.29.2 配置 Netty 客户端管道

你可以通过编写一个监听 Registry 创建的 Bean 事件监听器来定制 Netty 客户端管道。

ChannelPipelineCustomizer 接口为 Micronaut 注册的各种处理程序的名称定义了常量。

作为一个例子,下面的代码样本演示了注册 Logbook 库,其中包括额外的 Netty 处理程序来执行请求和响应的记录:

为 Logbook 定制 Netty 服务器管道

import io.micronaut.context.annotation.Requires;
import io.micronaut.context.event.BeanCreatedEvent;
import io.micronaut.context.event.BeanCreatedEventListener;
import io.micronaut.http.client.netty.NettyClientCustomizer;
import io.micronaut.http.netty.channel.ChannelPipelineCustomizer;
import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import jakarta.inject.Singleton;
import org.zalando.logbook.Logbook;
import org.zalando.logbook.netty.LogbookClientHandler;

@Requires(beans = Logbook.class)
@Singleton
public class LogbookNettyClientCustomizer
implements BeanCreatedEventListener<NettyClientCustomizer.Registry> { // (1)
private final Logbook logbook;

public LogbookNettyClientCustomizer(Logbook logbook) {
this.logbook = logbook;
}

@Override
public NettyClientCustomizer.Registry onCreated(
BeanCreatedEvent<NettyClientCustomizer.Registry> event) {

NettyClientCustomizer.Registry registry = event.getBean();
registry.register(new Customizer(null)); // (2)
return registry;
}

private class Customizer implements NettyClientCustomizer { // (3)
private final Channel channel;

Customizer(Channel channel) {
this.channel = channel;
}

@Override
public NettyClientCustomizer specializeForChannel(Channel channel, ChannelRole role) {
return new Customizer(channel); // (4)
}

@Override
public void onStreamPipelineBuilt() {
channel.pipeline().addLast( // (5)
"logbook",
new LogbookClientHandler(logbook)
);
}
}
}
  1. LogbookNettyClientCustomizer 监听一个 Registry,并要求定义一个 Logbook Bean。
  2. 根定制器在没有通道的情况下被初始化并注册
  3. 实际的自定义器实现了 NettyClientCustomizer
  4. 当一个新的通道被创建时,一个新的、专门的自定义器将为该通道创建。
  5. 当客户端发出信号说流管道已经完全构建完成时,日志处理程序被注册。
危险

LogBook 有一个重大的错误,限制了它在 netty 上的作用。

6.29.3 配置 Netty 服务器管道

你可以通过编写一个监听 Registry 创建的 Bean 事件监听器 来定制 Netty 服务器管道。

ChannelPipelineCustomizer 接口为 Micronaut 注册的各种处理程序的名称定义了常量。

作为一个例子,下面的代码示例演示了注册 Logbook 库,其中包括额外的 Netty 处理程序,以执行请求和响应日志:

为 Logbook 定制 Netty 服务器管道

import io.micronaut.context.annotation.Requires;
import io.micronaut.context.event.BeanCreatedEvent;
import io.micronaut.context.event.BeanCreatedEventListener;
import io.micronaut.http.netty.channel.ChannelPipelineCustomizer;
import io.micronaut.http.server.netty.NettyServerCustomizer;
import io.netty.channel.Channel;
import org.zalando.logbook.Logbook;
import org.zalando.logbook.netty.LogbookServerHandler;

import jakarta.inject.Singleton;

@Requires(beans = Logbook.class)
@Singleton
public class LogbookNettyServerCustomizer
implements BeanCreatedEventListener<NettyServerCustomizer.Registry> { // (1)
private final Logbook logbook;

public LogbookNettyServerCustomizer(Logbook logbook) {
this.logbook = logbook;
}

@Override
public NettyServerCustomizer.Registry onCreated(
BeanCreatedEvent<NettyServerCustomizer.Registry> event) {

NettyServerCustomizer.Registry registry = event.getBean();
registry.register(new Customizer(null)); // (2)
return registry;
}

private class Customizer implements NettyServerCustomizer { // (3)
private final Channel channel;

Customizer(Channel channel) {
this.channel = channel;
}

@Override
public NettyServerCustomizer specializeForChannel(Channel channel, ChannelRole role) {
return new Customizer(channel); // (4)
}

@Override
public void onStreamPipelineBuilt() {
channel.pipeline().addBefore( // (5)
ChannelPipelineCustomizer.HANDLER_HTTP_STREAM,
"logbook",
new LogbookServerHandler(logbook)
);
}
}
}
  1. LogbookNettyServerCustomizer 监听 Registry,并要求定义一个 Logbook bean
  2. 根定制器在没有通道的情况下被初始化并注册
  3. 实际的自定义器实现了 NettyServerCustomizer
  4. 当一个新的通道被创建时,一个新的、专门的自定义器将为该通道创建。
  5. 当服务器发出信号说流管道已经完全构建完成时,日志处理程序被注册。
危险

LogBook 有一个重大的错误,限制了它在 netty 上的作用。

6.29.4 高级监听器配置

你也可以不配置单个端口,而是手动指定每个监听器。

micronaut:
server:
netty:
listeners:
httpListener: # listener name can be an arbitrary value
host: 127.0.0.1 # optional, by default binds to all interfaces
port: 8086
ssl: false
httpsListener:
port: 8087
ssl: true

如果你手动指定监听器,其他配置如 micronaut.server.port 将被忽略。

可以为每个听众单独启用或禁用 SSL。当启用时,SSL 将被配置成上述的样子

嵌入式服务器还支持使用 netty 绑定到 unix 域套接字。这需要以下的依赖:

implementation("io.netty:netty-transport-native-unix-common")

服务器还必须被配置为使用本地传输(epoll 或 kqueue)。

micronaut:
server:
netty:
listeners:
unixListener: # listener name can be an arbitrary value
family: UNIX
path: /run/micronaut.socket
ssl: true
注意

要使用抽象域套接字而不是普通的套接字,在路径前加一个 NUL 字符,如 "\0/run/micronaut.socket"

6.29.5 配置 CORS

Micronaut 支持开箱即用的 CORS(跨源资源共享)。默认情况下,CORS 请求被拒绝。

6.29.5.1 通过配置实现 CORS

要启用 CORS 请求的处理,请修改你的配置。例如用 application.yml

CORS 配置实例

micronaut:
server:
cors:
enabled: true

通过只启用 CORS 处理,采取了一个 "广泛开放 "的策略,允许来自任何来源的请求。

要改变所有来源或特定来源的设置,改变配置以提供一个或多个 "配置"。通过提供任何配置,默认的"广泛开放"配置就没有被配置。

COR S配置

micronaut:
server:
cors:
enabled: true
configurations:
all:
...
web:
...
mobile:
...

在上面的例子中,提供了三个配置。他们的名字(allwebmobile)并不重要,在 Micronaut 中没有任何意义。它们的存在纯粹是为了能够轻松识别配置的目标用户。

相同的配置属性可以应用于每个配置。参见 CorsOriginConfiguration,了解可以定义的属性。每个配置提供的值将默认为相应字段的默认值。

当一个 CORS 请求被提出时,配置被搜索允许的源,这些源完全匹配或通过正则表达式匹配请求的源。

6.29.5.2 允许的源

要想让一个给定的配置允许任何来源,不要在你的配置中包括 allowedOrigins 键。

对于多个有效的起源,将配置的 allowedOrigins 键设置为一个字符串的列表。每个值可以是一个静态值(http://www.foo.com)或一个正则表达式(^http(|s)://www.google\.com$)。

正则表达式被传递给 Pattern#compile,并通过 Matcher#matches 与请求来源进行比较。

CORS 配置实例

micronaut:
server:
cors:
enabled: true
configurations:
web:
allowedOrigins:
- http://foo.com
- ^http(|s):\/\/www\.google\.com$

6.29.5.3 允许的方法

要允许任何给定配置的请求方法,不要在你的配置中包括 allowedMethods 键。

对于多个允许的方法,将配置的 allowedMethods 键设置为一个字符串列表。

CORS 配置实例

micronaut:
server:
cors:
enabled: true
configurations:
web:
allowedMethods:
- POST
- PUT

6.29.5.4 允许的头

要允许任何给定配置的请求头,不要在你的配置中包括 allowedHeaders 键。

对于多个允许的头,将配置的 allowedHeaders 键设置为一个字符串列表。

CORS 配置示例

micronaut:
server:
cors:
enabled: true
configurations:
web:
allowedHeaders:
- Content-Type
- Authorization

6.29.5.5 暴露的头

要配置通过 Access-Control-Expose-Headers 头在 CORS 请求的响应中发送的头信息,在你的配置中包括 exposedHeaders 键的字符串列表。默认情况下,没有一个是暴露的。

CORS 配置示例

micronaut:
server:
cors:
enabled: true
configurations:
web:
exposedHeaders:
- Content-Type
- Authorization

6.29.5.6 允许的凭证

CORS 请求默认允许凭证。要禁止凭证,请将 allowCredentials 选项设置为 false

CORS 配置示例

micronaut:
server:
cors:
enabled: true
configurations:
web:
allowCredentials: false

6.29.5.7 最大年龄

默认情况下,预检请求可以被缓存的最大年龄是 30 分钟。要改变这种行为,请指定一个以秒为单位的值。

CORS 配置示例

micronaut:
server:
cors:
enabled: true
configurations:
web:
maxAge: 3600 # 1 hour

6.29.5.8 多个标头值

默认情况下,当一个头有多个值时,会发送多个头,每个都有一个值。可以通过设置一个配置选项来改变这种行为,即发送一个带有逗号分隔的数值列表的单一头文件。

micronaut:
server:
cors:
single-header: true

6.29.6 用 HTTPS 保证服务器安全

Micronaut 支持开箱即用的 HTTPS。默认情况下,HTTPS 被禁用,所有的请求都使用 HTTP。要启用 HTTPS 支持,请修改你的配置。例如,使用 application.yml

HTTPS 配置示例

micronaut:
server:
ssl:
enabled: true
buildSelfSigned: true (1)
  1. Micronaut 将创建一个自签名的证书。
提示

默认情况下,支持 HTTPS 的 Micronaut 在 8443 端口启动,但你可以通过 micronaut.server.ssl.port 属性改变端口。

对于生成自签名的证书,Micronaut HTTP 服务器将使用 netty。netty 使用两种方法中的一种来生成证书。

如果你使用一个预先生成的证书(为了安全,你应该这样做),这些步骤是不必要的。

  • Netty 可以使用 JDK 内部的 sun.security.x509 包。在较新的 JDK 版本中,这个包受到限制,可能无法使用。你可能需要添加 --add-exports=java.base/sun.security.x509=ALL-UNNAMED 作为 VM 参数。

  • 另外,netty 将使用 Bouncy Castle BCPKIX API。这需要一个额外的依赖:

implementation("org.bouncycastle:bcpkix-jdk15on")
警告

这种配置将在浏览器中产生一个警告。

https-warning

使用有效的 x509 证书

也可以将 Micronaut 配置为使用现有的有效 x509 证书,例如用 Let's Encrypt 创建的一个。你将需要 server.crtserver.key 文件,并将它们转换为 PKCS #12 文件。

$ openssl pkcs12 -export \
-in server.crt \ (1)
-inkey server.key \ (2)
-out server.p12 \ (3)
-name someAlias \ (4)
-chain -CAfile ca.crt -caname root
  1. 原始的 server.crt 文件
  2. 原始的 server.key 文件
  3. 要创建的 server.p12 文件
  4. 证书的别名

在创建 server.p12 文件的过程中,有必要定义一个密码,以后在 Micronaut 中使用该证书时将需要这个密码。

现在修改你的配置:

HTTPS 配置示例

micronaut:
ssl:
enabled: true
key-store:
path: classpath:server.p12 (1)
password: mypassword (2)
type: PKCS12
  1. p12 文件。它也可以被引用为 file:/path/to/the/file
  2. 输出时定义的密码

有了这个配置,如果我们启动 Micronaut 并连接到 https://localhost:8443,我们仍然会在浏览器中看到警告,但如果我们检查证书,我们可以检查到它是由 Let's Encrypt 生成的。

https-certificate

最后,我们可以通过在 /etc/hosts 文件中为域名添加一个别名来测试该证书对浏览器是否有效:

$ cat /etc/hosts
...
127.0.0.1 my-domain.org
...

现在我们可以连接到 https://my-domain.org:8443

https-valid-certificate


使用 Java Keystore (JKS)

不推荐使用这种类型的证书,因为其格式是专有的 —— PKCS12 格式是首选。不管怎么样,Micronaut也支持它。

p12 证书转换为 JKS 证书:

$ keytool -importkeystore \
-deststorepass newPassword -destkeypass newPassword \ (1)
-destkeystore server.keystore \ (2)
-srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass mypassword \ (3)
-alias someAlias (4)
  1. 有必要定义 keystore 的密码
  2. 要创建的文件
  3. 之前创建的 PKCS12 文件,以及创建时定义的密码
  4. 之前使用的别名

如果 srcstorepassaliasp12 文件中定义的不一样,转换将失败。

现在修改你的配置:

HTTPS 配置示例

micronaut:
ssl:
enabled: true
key-store:
path: classpath:server.keystore
password: newPassword
type: JKS

启动 Micronaut,应用程序将使用 keystore 中的证书在 https://localhost:8443 上运行。


刷新/重载 HTTPS 证书

过期后保持 HTTPS 证书的更新可能是一个挑战。一个很好的解决方案是自动化证书管理环境(ACME)和 Micronaut ACME 模块,它提供了对自动刷新证书机构的支持。

如果不可能使用一个证书颁发机构,你需要从磁盘中手动更新证书,那么你应该使用 Micronaut 对应用事件的支持,发射一个 RefreshEvent,其中包含定义 HTTPS 配置的密钥,Micronaut 将从磁盘中重新加载证书并将新的配置应用到服务器上。

注意

你也可以使用刷新管理端点,但这只适用于磁盘上证书的物理位置发生变化的情况。

例如,下面将从磁盘上重新加载先前列出的 HTTPS 配置,并将其应用于新传入的请求(例如,这段代码可以为一个轮询证书变化的计划工作而运行):

手动刷新 HTTPS 配置

import jakarta.inject.Inject;
import io.micronaut.context.event.ApplicationEventPublisher;
import io.micronaut.runtime.context.scope.refresh.RefreshEvent;
import java.util.Collections;
...

@Inject ApplicationEventPublisher<RefreshEvent> eventPublisher;

...

eventPublisher.publishEvent(new RefreshEvent(
Collections.singletonMap("micronaut.ssl", "*")
));

6.27 启用 HTTP 和 HTTPS

Micronaut 支持同时绑定 HTTP 和 HTTPS。要启用双协议支持,请修改你的配置。例如使用 application.yml

双协议配置示例

micronaut:
server:
ssl:
enabled: true
build-self-signed: true (1)
dual-protocol : true (2)
  1. 你必须配置 SSL 以使 HTTPS 工作。在这个例子中,我们只是使用了一个自签名的证书,但其他配置参阅用 HTTPS 保护服务器
  2. 启用 HTTP 和 HTTPS 是一项选择功能——设置 dualProtocol 标志可以启用它。在默认情况下,Micronaut 只启用一个

也可以将所有的 HTTP 请求自动重定向到 HTTPS。除了前面的配置,你需要启用这个选项。例如使用 application.yml

启用 HTTP 到 HTTPS 重定向

micronaut:
server:
ssl:
enabled: true
build-self-signed: true
dual-protocol : true
http-to-https-redirect: true (1)
  1. 启用 HTTP 到 HTTPS 的重定向

6.29.8 启用访问记录器

根据 apache mod_log_configTomcat Access Log Valve 的精神,可以为 HTTP 服务器启用一个访问记录器(这对 HTTP/1 和 HTTP/2 都有效)。

要启用和配置访问记录器,在 application.yml 中设置:

启用访问日志记录器

micronaut:
server:
netty:
access-logger:
enabled: true # Enables the access logger
logger-name: my-access-logger # A logger name, optional, default is `HTTP_ACCESS_LOGGER`
log-format: common # A log format, optional, default is Common Log Format

过滤访问日志

如果你希望不记录某些路径的访问,你可以在配置中指定正则表达式过滤器:

过滤访问日志

micronaut:
server:
netty:
access-logger:
enabled: true # Enables the access logger
logger-name: my-access-logger # A logger name, optional, default is `HTTP_ACCESS_LOGGER`
log-format: common # A log format, optional, default is Common Log Format
exclusions:
- /health
- /path/.+

日志备份配置

除了启用访问记录器,你还必须为指定或默认的记录器名称添加一个记录器。例如使用默认的日志记录器名称为 logback:

Logback 配置

<appender
name="httpAccessLogAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>true</append>
<file>log/http-access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>log/http-access-%d{yyyy-MM-dd}.log
</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>%msg%n</pattern>
</encoder>
<immediateFlush>true</immediateFlush>
</appender>

<logger name="HTTP_ACCESS_LOGGER" additivity="false" level="info">
<appender-ref ref="httpAccessLogAppender" />
</logger>

该模式应该只有消息标记,因为其他元素将被访问记录器处理。

日志格式

语法是基于 Apache httpd 日志格式

这些是支持的标记:

  • %a —— 远程 IP 地址
  • %A —— 本地 IP 地址
  • %b —— 发送的字节,不包括 HTTP 头,如果没有发送字节,则为'-'。
  • %B —— 已发送的字节,不包括 HTTP 头信息
  • %h —— 远程主机名称
  • %H —— 请求协议
  • %{<header>}i —— 请求头。如果省略参数(%i),则打印所有头文件。
  • %{<header>}o —— 响应头。如果省略该参数(%o),则打印所有头信息。
  • %{<cookie>}C —— 请求 cookie(COOKIE)。如果省略该参数(%C),则打印所有 cookie。
  • %{<cookie>}C —— 响应 cookie(SET_COOKIE)。如果省略该参数(%c),将打印所有的 cookie。
  • %l —— 来自 identd 的远程逻辑用户名(总是返回'-')。
  • %m —— 请求方法
  • %p —— 本地端口
  • %q —— 查询字符串(不包括"?"字符)。
  • %r —— 请求的第一行
  • %s —— 响应的HTTP状态代码
  • %{<format>}t —— 日期和时间。如果省略该参数,则使用通用日志格式("'['dd/MMM/yyyy:HH:mm:ss Z']"")。
    • 如果格式以 begin 开头:(默认情况下),时间是在请求处理的开始阶段进行。如果以 end 开头:则是日志条目被写入的时间,接近于请求处理的结束。
    • 该格式应遵循 DateTimeFormatter 的语法。
  • %{property}u —— 远程认证的用户。当 micronaut-session 在 classpath 上时,如果省略参数,则返回 session id,否则返回指定的属性,打印出'-'
  • %U —— 请求的 URI
  • %v —— 本地服务器名称
  • %D —— 处理请求的时间,以毫秒计
  • %T —— 处理请求的时间,以秒为单位

此外,你还可以使用以下常见模式的别名:

6.29.9 启动次要服务器

Micronaut 支持通过 NettyEmbeddedServerFactory 接口以编程方式创建额外的 Netty 服务器。

这在某些情况下是很有用的,例如,你需要通过不同的端口暴露不同的服务器,并可能有不同的配置(HTTPS,线程资源等)。

下面的例子演示了如何定义一个 Factory Bean,使用程序化创建的配置启动一个额外的服务器:

以编程方式创建次要服务器

import java.util.Collections;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Context;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.env.Environment;
import io.micronaut.core.util.StringUtils;
import io.micronaut.discovery.ServiceInstanceList;
import io.micronaut.discovery.StaticServiceInstanceList;
import io.micronaut.http.server.netty.NettyEmbeddedServer;
import io.micronaut.http.server.netty.NettyEmbeddedServerFactory;
import io.micronaut.http.server.netty.configuration.NettyHttpServerConfiguration;
import io.micronaut.http.ssl.ServerSslConfiguration;
import jakarta.inject.Named;

@Factory
public class SecondaryNettyServer {
public static final String SERVER_ID = "another"; // (1)

@Named(SERVER_ID)
@Context
@Bean(preDestroy = "close") // (2)
@Requires(beans = Environment.class)
NettyEmbeddedServer nettyEmbeddedServer(NettyEmbeddedServerFactory serverFactory) { // (3)
// configure server programmatically
final NettyHttpServerConfiguration configuration =
new NettyHttpServerConfiguration(); // (4)
final ServerSslConfiguration sslConfiguration = new ServerSslConfiguration(); // (5)
sslConfiguration.setBuildSelfSigned(true);
sslConfiguration.setEnabled(true);
sslConfiguration.setPort(-1); // random port
final NettyEmbeddedServer embeddedServer = serverFactory.build(configuration, sslConfiguration); // (6)
embeddedServer.start(); // (7)
return embeddedServer; // (8)
}

@Bean
ServiceInstanceList serviceInstanceList( // (9)
@Named(SERVER_ID) NettyEmbeddedServer nettyEmbeddedServer) {
return new StaticServiceInstanceList(
SERVER_ID,
Collections.singleton(nettyEmbeddedServer.getURI())
);
}
}
  1. 为服务器定义一个独特的名字
  2. 使用服务器名称定义一个 @Context 范围内的 Bean,并包括 preDestroy="close",以确保服务器在上下文关闭时被关闭。
  3. NettyEmbeddedServerFactory 注入到一个工厂 bean 中。
  4. 以编程方式创建 NettyHttpServerConfiguration
  5. 可以选择创建 ServerSslConfiguration
  6. 使用构建方法来构建服务器实例
  7. start 方法启动服务器
  8. 将服务器实例作为一个托管豆返回
  9. 如果你想通过服务器名称来注入 HTTP 客户端,可以选择定义 ServiceInstanceList 的一个实例

有了这个类,当 ApplicationContext 启动时,服务器也将以适当的配置启动。

由于在第8步中定义了 ServiceInstanceList,你就可以在测试中注入一个客户端来测试次要服务器:

注入服务器或客户端

@Client(path = "/", id = SecondaryNettyServer.SERVER_ID)
@Inject
HttpClient httpClient; // (1)

@Named(SecondaryNettyServer.SERVER_ID)
EmbeddedServer embeddedServer; // (2)
  1. 使用服务器名称按 ID 注入一个客户端
  2. 使用 @Named 注解作为限定词来注入嵌入式服务器实例

英文链接