HTTP 服务器具有许多配置选项。它们在扩展 HttpServerConfiguration 的 NettyHttpServerConfiguration 配置类中定义。
以下示例显示了如何通过配置文件(例如 application.yml)调整服务器的配置选项:
配置 HTTP 服务器设置
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.maxRequestSize=1MB
micronaut.server.host=localhost
micronaut.server.netty.maxHeaderSize=500KB
micronaut.server.netty.worker.threads=8
micronaut.server.netty.childOptions.autoRead=true
|
micronaut:
server:
maxRequestSize: 1MB
host: localhost
netty:
maxHeaderSize: 500KB
worker:
threads: 8
childOptions:
autoRead: true
|
[micronaut]
[micronaut.server]
maxRequestSize="1MB"
host="localhost"
[micronaut.server.netty]
maxHeaderSize="500KB"
[micronaut.server.netty.worker]
threads=8
[micronaut.server.netty.childOptions]
autoRead=true
|
micronaut {
server {
maxRequestSize = "1MB"
host = "localhost"
netty {
maxHeaderSize = "500KB"
worker {
threads = 8
}
childOptions {
autoRead = true
}
}
}
}
|
{
micronaut {
server {
maxRequestSize = "1MB"
host = "localhost"
netty {
maxHeaderSize = "500KB"
worker {
threads = 8
}
childOptions {
autoRead = true
}
}
}
}
}
|
{
"micronaut": {
"server": {
"maxRequestSize": "1MB",
"host": "localhost",
"netty": {
"maxHeaderSize": "500KB",
"worker": {
"threads": 8
},
"childOptions": {
"autoRead": true
}
}
}
}
}
|
表 1. NettyHttpServerConfiguration 的配置属性
属性 |
类型 |
描述 |
micronaut.server.netty.child-options
|
java.util.Map
|
设置 Netty 子工作者选项。
|
micronaut.server.netty.options
|
java.util.Map
|
设置通道选项。
|
micronaut.server.netty.max-initial-line-length
|
int
|
设置 HTTP 请求的最大初始行长度。默认值 (4096)。
|
micronaut.server.netty.max-header-size
|
int
|
设置任何一个标题的最大大小。默认值 (8192)。
|
micronaut.server.netty.max-chunk-size
|
int
|
设置任何单个请求块的最大大小。默认值 (8192)。
|
micronaut.server.netty.max-h2c-upgrade-request-size
|
int
|
设置用于将连接升级到 HTTP2 明文 (h2c) 的 HTTP1.1 请求正文的最大大小。此初始请求无法流式传输,而是完全缓冲,因此默认值 (8192) 相对较小。 <i>如果此值对于您的用例而言太小,请考虑使用空的初始“升级请求”(例如 {@code OPTIONS /}),或切换到普通 HTTP2。</i> <p> <i>不影响正常的 HTTP2 (TLS)。</i>
|
micronaut.server.netty.chunked-supported
|
boolean
|
设置是否支持分块传输编码。默认值(真)。
|
micronaut.server.netty.validate-headers
|
boolean
|
设置是否验证传入的标头。默认值(真)。
|
micronaut.server.netty.initial-buffer-size
|
int
|
设置初始缓冲区大小。默认值 (128)。
|
micronaut.server.netty.log-level
|
io.netty.handler.logging.LogLevel
|
设置 Netty 日志级别。
|
micronaut.server.netty.compression-threshold
|
int
|
设置请求主体必须的最小大小才能被压缩。默认值 (1024)。
|
micronaut.server.netty.compression-level
|
int
|
设置压缩级别 (0-9)。默认值 (6)。
|
micronaut.server.netty.use-native-transport
|
boolean
|
如果可用,设置是否使用 netty 的本地传输(epoll 或 kqueue)。默认值(假)。
|
micronaut.server.netty.fallback-protocol
|
java.lang.String
|
设置通过 ALPN 协商时要使用的回退协议。
|
micronaut.server.netty.keep-alive-on-server-error
|
boolean
|
是否发送连接在内部服务器错误时保持活动状态。默认值({@value DEFAULT_KEEP_ALIVE_ON_SERVER_ERROR})。
|
micronaut.server.netty.pcap-logging-path-pattern
|
java.lang.String
|
用于记录到 pcap 的传入连接的路径模式。这是一个不受支持的选项:行为可能会改变,或者可能会完全消失,恕不另行通知!
|
micronaut.server.netty.listeners
|
java.util.List
|
设置显式的 netty 侦听器配置,或者 {@code null} 如果它们应该是隐式的。
|
使用本机传输
与基于 NIO 的传输相比,本机 Netty 传输添加特定于特定平台的功能,产生更少的垃圾,并且通常提高性能。
要启用本机传输,首先添加依赖项:
对于 x86 上的 macOS:
Gradle |
Maven |
runtimeOnly("io.netty:netty-transport-native-kqueue::osx-x86_64")
|
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<scope>runtime</scope>
<classifier>osx-x86_64</classifier>
</dependency>
|
对于 M1 上的 macOS:
Gradle |
Maven |
runtimeOnly("io.netty:netty-transport-native-kqueue::osx-aarch_64")
|
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<scope>runtime</scope>
<classifier>osx-aarch_64</classifier>
</dependency>
|
对于 x86 上的 Linux:
Gradle |
Maven |
runtimeOnly("io.netty:netty-transport-native-epoll::linux-x86_64")
|
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<scope>runtime</scope>
<classifier>linux-x86_64</classifier>
</dependency>
|
对于 ARM64 上的 Linux:
Gradle |
Maven |
runtimeOnly("io.netty:netty-transport-native-epoll::linux-aarch_64")
|
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<scope>runtime</scope>
<classifier>linux-aarch_64</classifier>
</dependency>
|
然后将默认事件循环组配置为更喜欢本机传输:
配置默认事件循环以优先使用本机传输
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.netty.event-loops.default.prefer-native-transport=true
|
micronaut:
netty:
event-loops:
default:
prefer-native-transport: true
|
[micronaut]
[micronaut.netty]
[micronaut.netty.event-loops]
[micronaut.netty.event-loops.default]
prefer-native-transport=true
|
micronaut {
netty {
eventLoops {
'default' {
preferNativeTransport = true
}
}
}
}
|
{
micronaut {
netty {
event-loops {
default {
prefer-native-transport = true
}
}
}
}
}
|
{
"micronaut": {
"netty": {
"event-loops": {
"default": {
"prefer-native-transport": true
}
}
}
}
}
|
Netty 支持简单的采样资源泄漏检测,以少量开销为代价报告是否存在泄漏。您可以通过将属性 netty.resource-leak-detector-level 设置为以下之一来禁用它或启用更高级的检测:SIMPLE(默认)、DISABLED、PARANOID 或 ADVANCED。
配置服务器线程池
HTTP 服务器建立在 Netty 之上,Netty 被设计为事件循环模型中的非阻塞 I/O 工具包。
Netty worker 事件循环使用“默认”命名的事件循环组。这可以通过 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.worker
|
NettyHttpServerConfiguration$Worker
|
设置 Worker 事件循环配置。
|
micronaut.server.netty.worker.event-loop-group
|
java.lang.String
|
设置要使用的名称。
|
micronaut.server.netty.worker.threads
|
int
|
设置事件循环组的线程数。
|
micronaut.server.netty.worker.io-ratio
|
java.lang.Integer
|
设置 I/O 比率。
|
micronaut.server.netty.worker.executor
|
java.lang.String
|
设置执行者的名字。
|
micronaut.server.netty.worker.prefer-native-transport
|
boolean
|
设置是否首选本地传输(如果可用)
|
micronaut.server.netty.worker.shutdown-quiet-period
|
java.time.Duration
|
设置关机静默期
|
micronaut.server.netty.worker.shutdown-timeout
|
java.time.Duration
|
设置关机超时时间(必须>= shutdownQuietPeriod)
|
可以使用具有相同配置选项的 micronaut.server.netty.parent 配置父事件循环。
服务器也可以配置为使用不同的命名工作事件循环:
为服务器使用不同的事件循环
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.netty.worker.event-loop-group=other
micronaut.netty.event-loops.other.num-threads=10
|
micronaut:
server:
netty:
worker:
event-loop-group: other
netty:
event-loops:
other:
num-threads: 10
|
[micronaut]
[micronaut.server]
[micronaut.server.netty]
[micronaut.server.netty.worker]
event-loop-group="other"
[micronaut.netty]
[micronaut.netty.event-loops]
[micronaut.netty.event-loops.other]
num-threads=10
|
micronaut {
server {
netty {
worker {
eventLoopGroup = "other"
}
}
}
netty {
eventLoops {
other {
numThreads = 10
}
}
}
}
|
{
micronaut {
server {
netty {
worker {
event-loop-group = "other"
}
}
}
netty {
event-loops {
other {
num-threads = 10
}
}
}
}
}
|
{
"micronaut": {
"server": {
"netty": {
"worker": {
"event-loop-group": "other"
}
}
},
"netty": {
"event-loops": {
"other": {
"num-threads": 10
}
}
}
}
}
|
线程数的默认值是系统属性 io.netty.eventLoopThreads 的值,或者如果未指定,则为可用处理器 x 2。
请参阅下表以配置事件循环:
表 2. DefaultEventLoopGroupConfiguration 的配置属性
属性 |
类型 |
描述 |
micronaut.netty.event-loops.*.num-threads
|
int
|
|
micronaut.netty.event-loops.*.io-ratio
|
java.lang.Integer
|
|
micronaut.netty.event-loops.*.prefer-native-transport
|
boolean
|
|
micronaut.netty.event-loops.*.executor
|
java.lang.String
|
|
micronaut.netty.event-loops.*.shutdown-quiet-period
|
java.time.Duration
|
|
micronaut.netty.event-loops.*.shutdown-timeout
|
java.time.Duration
|
|
阻塞操作
在处理阻塞操作时,Micronaut 默认将阻塞操作转移到未绑定的缓存 I/O 线程池。您可以使用名为 io 的 ExecutorConfiguration 配置 I/O 线程池。例如:
配置服务器 I/O 线程池
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.executors.io.type=fixed
micronaut.executors.io.nThreads=75
|
micronaut:
executors:
io:
type: fixed
nThreads: 75
|
[micronaut]
[micronaut.executors]
[micronaut.executors.io]
type="fixed"
nThreads=75
|
micronaut {
executors {
io {
type = "fixed"
nThreads = 75
}
}
}
|
{
micronaut {
executors {
io {
type = "fixed"
nThreads = 75
}
}
}
}
|
{
"micronaut": {
"executors": {
"io": {
"type": "fixed",
"nThreads": 75
}
}
}
}
|
上面的配置创建了一个有 75 个线程的固定线程池。
@Blocking
您可以使用 @Blocking 注释将方法标记为阻塞。
如果将 micronaut.server.thread-selection 设置为 AUTO,Micronaut 框架会将使用 @Blocking 注释的方法的执行卸载到 IO 线程池(请参阅:TaskExecutors)。
@Blocking 仅在您使用自动线程选择时才有效。自 Micronaut 2.0 以来,Micronaut 框架默认为手动线程选择。我们推荐使用@ExecuteOn 注解在不同的线程池上执行阻塞操作。 @ExecutesOn 适用于手动和自动线程选择。
Micronaut 框架在某些地方内部使用了@Blocking:
Micronaut Data 还在内部利用 @Blocking 进行一些事务操作、CRUD 拦截器和存储库。
配置 Netty 客户端管道
您可以通过编写侦听注册表创建的 Bean 事件侦听器来自定义 Netty 客户端管道。
ChannelPipelineCustomizer 接口为各种处理程序 Micronaut 寄存器的名称定义常量。
作为示例,以下代码示例演示了注册 Logbook 库,其中包括用于执行请求和响应日志记录的其他 Netty 处理程序:
为 Logbook 定制 Netty 服务器管道
Java |
Groovy |
Kotlin |
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)
);
}
}
}
|
import io.micronaut.context.event.BeanCreatedEvent
import io.micronaut.context.event.BeanCreatedEventListener
import io.micronaut.http.client.netty.NettyClientCustomizer
import io.netty.channel.Channel
import jakarta.inject.Singleton
import org.zalando.logbook.Logbook
import org.zalando.logbook.netty.LogbookClientHandler
@Requires(beans = Logbook.class)
@Singleton
class LogbookNettyClientCustomizer
implements BeanCreatedEventListener<NettyClientCustomizer.Registry> { // (1)
private final Logbook logbook;
LogbookNettyClientCustomizer(Logbook logbook) {
this.logbook = logbook
}
@Override
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
NettyClientCustomizer specializeForChannel(Channel channel, ChannelRole role) {
return new Customizer(channel) // (4)
}
@Override
void onStreamPipelineBuilt() {
channel.pipeline().addLast( // (5)
"logbook",
new LogbookClientHandler(logbook)
)
}
}
}
|
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.client.netty.NettyClientCustomizer.ChannelRole
import io.netty.channel.Channel
import jakarta.inject.Singleton
import org.zalando.logbook.Logbook
import org.zalando.logbook.netty.LogbookClientHandler
@Requires(beans = [Logbook::class])
@Singleton
class LogbookNettyClientCustomizer(private val logbook: Logbook) :
BeanCreatedEventListener<NettyClientCustomizer.Registry> { // (1)
override fun onCreated(event: BeanCreatedEvent<NettyClientCustomizer.Registry>): NettyClientCustomizer.Registry {
val registry = event.bean
registry.register(Customizer(null)) // (2)
return registry
}
private inner class Customizer constructor(private val channel: Channel?) :
NettyClientCustomizer { // (3)
override fun specializeForChannel(channel: Channel, role: ChannelRole) = Customizer(channel) // (4)
override fun onStreamPipelineBuilt() {
channel!!.pipeline().addLast( // (5)
"logbook",
LogbookClientHandler(logbook)
)
}
}
}
|
LogbookNettyClientCustomizer 监听一个 Registry 并且需要一个 Logbook bean 的定义
根定制器在没有通道的情况下初始化并注册
实际的定制器实现了 NettyClientCustomizer
创建新频道时,会为该频道创建一个新的专用定制器
当客户端发出流管道已完全构建的信号时,将注册日志处理程序
Logbook 有一个主要错误,限制了它在 netty 中的实用性。
配置 Netty 服务器管道
您可以通过编写监听 Registry 创建的 Bean 事件监听器来自定义 Netty 服务器管道。
ChannelPipelineCustomizer 接口为各种处理程序 Micronaut 寄存器的名称定义常量。
作为示例,以下代码示例演示了注册 Logbook 库,其中包括用于执行请求和响应日志记录的其他 Netty 处理程序:
为 Logbook 定制 Netty 服务器管道
Java |
Groovy |
Kotlin |
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)
);
}
}
}
|
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
class LogbookNettyServerCustomizer
implements BeanCreatedEventListener<NettyServerCustomizer.Registry> { // (1)
private final Logbook logbook;
LogbookNettyServerCustomizer(Logbook logbook) {
this.logbook = logbook
}
@Override
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
NettyServerCustomizer specializeForChannel(Channel channel, ChannelRole role) {
return new Customizer(channel) // (4)
}
@Override
void onStreamPipelineBuilt() {
channel.pipeline().addBefore( // (5)
ChannelPipelineCustomizer.HANDLER_HTTP_STREAM,
"logbook",
new LogbookServerHandler(logbook)
)
}
}
}
|
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.micronaut.http.server.netty.NettyServerCustomizer.ChannelRole
import io.netty.channel.Channel
import jakarta.inject.Singleton
import org.zalando.logbook.Logbook
import org.zalando.logbook.netty.LogbookServerHandler
@Requires(beans = [Logbook::class])
@Singleton
class LogbookNettyServerCustomizer(private val logbook: Logbook) :
BeanCreatedEventListener<NettyServerCustomizer.Registry> { // (1)
override fun onCreated(event: BeanCreatedEvent<NettyServerCustomizer.Registry>): NettyServerCustomizer.Registry {
val registry = event.bean
registry.register(Customizer(null)) // (2)
return registry
}
private inner class Customizer constructor(private val channel: Channel?) :
NettyServerCustomizer { // (3)
override fun specializeForChannel(channel: Channel, role: ChannelRole) = Customizer(channel) // (4)
override fun onStreamPipelineBuilt() {
channel!!.pipeline().addBefore( // (5)
ChannelPipelineCustomizer.HANDLER_HTTP_STREAM,
"logbook",
LogbookServerHandler(logbook)
)
}
}
}
|
LogbookNettyServerCustomizer 监听一个 Registry 并且需要一个 Logbook bean 的定义
根定制器在没有通道的情况下初始化并注册
实际的定制器实现了 NettyServerCustomizer
创建新频道时,会为该频道创建一个新的专用定制器
当服务器发出流管道已完全构建的信号时,将注册日志处理程序
Logbook 有一个主要错误,限制了它在 netty 中的实用性。
高级侦听器配置
您也可以手动指定每个侦听器,而不是配置单个端口。
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.netty.listeners.httpListener.host=127.0.0.1
micronaut.server.netty.listeners.httpListener.port=8086
micronaut.server.netty.listeners.httpListener.ssl=false
micronaut.server.netty.listeners.httpsListener.port=8087
micronaut.server.netty.listeners.httpsListener.ssl=true
|
micronaut:
server:
netty:
listeners:
httpListener:
host: 127.0.0.1
port: 8086
ssl: false
httpsListener:
port: 8087
ssl: true
|
[micronaut]
[micronaut.server]
[micronaut.server.netty]
[micronaut.server.netty.listeners]
[micronaut.server.netty.listeners.httpListener]
host="127.0.0.1"
port=8086
ssl=false
[micronaut.server.netty.listeners.httpsListener]
port=8087
ssl=true
|
micronaut {
server {
netty {
listeners {
httpListener {
host = "127.0.0.1"
port = 8086
ssl = false
}
httpsListener {
port = 8087
ssl = true
}
}
}
}
}
|
{
micronaut {
server {
netty {
listeners {
httpListener {
host = "127.0.0.1"
port = 8086
ssl = false
}
httpsListener {
port = 8087
ssl = true
}
}
}
}
}
}
|
{
"micronaut": {
"server": {
"netty": {
"listeners": {
"httpListener": {
"host": "127.0.0.1",
"port": 8086,
"ssl": false
},
"httpsListener": {
"port": 8087,
"ssl": true
}
}
}
}
}
}
|
如果您手动指定监听器,其他配置如 micronaut.server.port 将被忽略。
可以为每个侦听器单独启用或禁用 SSL。启用后,SSL 将按上述方式配置。
嵌入式服务器还支持使用 netty 绑定到 unix 域套接字。这需要以下依赖项:
Gradle |
Maven |
implementation("io.netty:netty-transport-native-unix-common")
|
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-unix-common</artifactId>
</dependency>
|
服务器还必须配置为使用本地传输(epoll 或 kqueue)。
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.netty.listeners.unixListener.family=UNIX
micronaut.server.netty.listeners.unixListener.path=/run/micronaut.socket
micronaut.server.netty.listeners.unixListener.ssl=true
|
micronaut:
server:
netty:
listeners:
unixListener:
family: UNIX
path: /run/micronaut.socket
ssl: true
|
[micronaut]
[micronaut.server]
[micronaut.server.netty]
[micronaut.server.netty.listeners]
[micronaut.server.netty.listeners.unixListener]
family="UNIX"
path="/run/micronaut.socket"
ssl=true
|
micronaut {
server {
netty {
listeners {
unixListener {
family = "UNIX"
path = "/run/micronaut.socket"
ssl = true
}
}
}
}
}
|
{
micronaut {
server {
netty {
listeners {
unixListener {
family = "UNIX"
path = "/run/micronaut.socket"
ssl = true
}
}
}
}
}
}
|
{
"micronaut": {
"server": {
"netty": {
"listeners": {
"unixListener": {
"family": "UNIX",
"path": "/run/micronaut.socket",
"ssl": true
}
}
}
}
}
}
|
要使用抽象域套接字而不是普通套接字,请在路径前加上 NUL 字符,例如“\0/run/micronaut.socket”
配置 CORS
Micronaut 开箱即用地支持 CORS(跨源资源共享)。默认情况下,拒绝 CORS 请求。
CORS 通过配置
要启用 CORS 请求处理,请修改应用程序配置文件中的配置:
CORS 配置示例
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.enabled=true
|
micronaut:
server:
cors:
enabled: true
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
enabled=true
|
micronaut {
server {
cors {
enabled = true
}
}
}
|
{
micronaut {
server {
cors {
enabled = true
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"enabled": true
}
}
}
}
|
通过仅启用 CORS 处理,采用“完全开放”策略,允许来自任何来源的请求。
要更改所有来源或特定来源的设置,请更改配置以提供一个或多个“配置”。通过提供任何配置,不配置默认的“全开”配置。
CORS 配置
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.all=...
micronaut.server.cors.configurations.web=...
micronaut.server.cors.configurations.mobile=...
|
micronaut:
server:
cors:
enabled: true
configurations:
all:
...
web:
...
mobile:
...
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
enabled=true
[micronaut.server.cors.configurations]
all="..."
web="..."
mobile="..."
|
micronaut {
server {
cors {
enabled = true
configurations {
all = "..."
web = "..."
mobile = "..."
}
}
}
}
|
{
micronaut {
server {
cors {
enabled = true
configurations {
all = "..."
web = "..."
mobile = "..."
}
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"enabled": true,
"configurations": {
"all": "...",
"web": "...",
"mobile": "..."
}
}
}
}
}
|
在上面的示例中,提供了三种配置。它们的名称(all、web、mobile)并不重要,在 Micronaut 中没有任何意义。它们的存在纯粹是为了能够轻松识别配置的预期用户。
相同的配置属性可以应用于每个配置。有关可以定义的属性,请参阅 CorsOriginConfiguration。提供的每个配置的值将默认为相应字段的默认值。
当发出 CORS 请求时,将在配置中搜索完全匹配或通过正则表达式匹配请求源的允许源。
Allowed Origins
要允许给定配置的任何来源,请不要在您的配置中包含 allowedOrigins 键。
对于多个有效来源,将配置的 allowedOrigins 键设置为字符串列表。每个值可以是静态值 (http://www.foo.com) 或正则表达式 (^http(|s)://www\.google\.com$)。
正则表达式被传递给 Pattern#compile 并与 Matcher#matches 的请求源进行比较。
示例 CORS 配置
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.allowedOrigins[0]=http://foo.com
micronaut.server.cors.configurations.web.allowedOrigins[1]=^http(|s):\/\/www\.google\.com$
|
micronaut:
server:
cors:
enabled: true
configurations:
web:
allowedOrigins:
- http://foo.com
- ^http(|s):\/\/www\.google\.com$
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
enabled=true
[micronaut.server.cors.configurations]
[micronaut.server.cors.configurations.web]
allowedOrigins=[
"http://foo.com",
"^http(|s):\\/\\/www\\.google\\.com$"
]
|
micronaut {
server {
cors {
enabled = true
configurations {
web {
allowedOrigins = ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
}
}
}
}
}
|
{
micronaut {
server {
cors {
enabled = true
configurations {
web {
allowedOrigins = ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
}
}
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"enabled": true,
"configurations": {
"web": {
"allowedOrigins": ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
}
}
}
}
}
}
|
Allowed Methods
要允许给定配置的任何请求方法,请不要在您的配置中包含 allowedMethods 键。
对于多个允许的方法,将配置的 allowedMethods 键设置为字符串列表。
示例 CORS 配置
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.allowedOrigins[0]=http://foo.com
micronaut.server.cors.configurations.web.allowedOrigins[1]=^http(|s):\/\/www\.google\.com$
|
micronaut:
server:
cors:
enabled: true
configurations:
web:
allowedOrigins:
- http://foo.com
- ^http(|s):\/\/www\.google\.com$
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
enabled=true
[micronaut.server.cors.configurations]
[micronaut.server.cors.configurations.web]
allowedOrigins=[
"http://foo.com",
"^http(|s):\\/\\/www\\.google\\.com$"
]
|
micronaut {
server {
cors {
enabled = true
configurations {
web {
allowedOrigins = ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
}
}
}
}
}
|
{
micronaut {
server {
cors {
enabled = true
configurations {
web {
allowedOrigins = ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
}
}
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"enabled": true,
"configurations": {
"web": {
"allowedOrigins": ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
}
}
}
}
}
}
|
Allowed Headers
要允许给定配置的任何请求标头,请不要在您的配置中包含 allowedHeaders 键。
对于多个允许的标头,将配置的 allowedHeaders 键设置为字符串列表。
示例 CORS 配置
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.allowedHeaders[0]=Content-Type
micronaut.server.cors.configurations.web.allowedHeaders[1]=Authorization
|
micronaut:
server:
cors:
enabled: true
configurations:
web:
allowedHeaders:
- Content-Type
- Authorization
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
enabled=true
[micronaut.server.cors.configurations]
[micronaut.server.cors.configurations.web]
allowedHeaders=[
"Content-Type",
"Authorization"
]
|
micronaut {
server {
cors {
enabled = true
configurations {
web {
allowedHeaders = ["Content-Type", "Authorization"]
}
}
}
}
}
|
{
micronaut {
server {
cors {
enabled = true
configurations {
web {
allowedHeaders = ["Content-Type", "Authorization"]
}
}
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"enabled": true,
"configurations": {
"web": {
"allowedHeaders": ["Content-Type", "Authorization"]
}
}
}
}
}
}
|
Exposed Headers
要配置在通过 Access-Control-Expose-Headers 标头响应 CORS 请求时发送的标头,请在您的配置中包含 exposedHeaders 键的字符串列表。默认情况下没有公开。
示例 CORS 配置
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.exposedHeaders[0]=Content-Type
micronaut.server.cors.configurations.web.exposedHeaders[1]=Authorization
|
micronaut:
server:
cors:
enabled: true
configurations:
web:
exposedHeaders:
- Content-Type
- Authorization
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
enabled=true
[micronaut.server.cors.configurations]
[micronaut.server.cors.configurations.web]
exposedHeaders=[
"Content-Type",
"Authorization"
]
|
micronaut {
server {
cors {
enabled = true
configurations {
web {
exposedHeaders = ["Content-Type", "Authorization"]
}
}
}
}
}
|
{
micronaut {
server {
cors {
enabled = true
configurations {
web {
exposedHeaders = ["Content-Type", "Authorization"]
}
}
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"enabled": true,
"configurations": {
"web": {
"exposedHeaders": ["Content-Type", "Authorization"]
}
}
}
}
}
}
|
Allow Credentials
CORS 请求默认允许凭据。要禁止凭据,请将 allowCredentials 选项设置为 false。
示例 CORS 配置
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.allowCredentials=false
|
micronaut:
server:
cors:
enabled: true
configurations:
web:
allowCredentials: false
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
enabled=true
[micronaut.server.cors.configurations]
[micronaut.server.cors.configurations.web]
allowCredentials=false
|
micronaut {
server {
cors {
enabled = true
configurations {
web {
allowCredentials = false
}
}
}
}
}
|
{
micronaut {
server {
cors {
enabled = true
configurations {
web {
allowCredentials = false
}
}
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"enabled": true,
"configurations": {
"web": {
"allowCredentials": false
}
}
}
}
}
}
|
Max Age
预检请求可以缓存的默认最长期限为 30 分钟。要更改该行为,请以秒为单位指定一个值。
示例 CORS 配置
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.maxAge=3600
|
micronaut:
server:
cors:
enabled: true
configurations:
web:
maxAge: 3600 # 1 hour
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
enabled=true
[micronaut.server.cors.configurations]
[micronaut.server.cors.configurations.web]
maxAge=3600
|
micronaut {
server {
cors {
enabled = true
configurations {
web {
maxAge = 3600
}
}
}
}
}
|
{
micronaut {
server {
cors {
enabled = true
configurations {
web {
maxAge = 3600
}
}
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"enabled": true,
"configurations": {
"web": {
"maxAge": 3600
}
}
}
}
}
}
|
Multiple Header Values
默认情况下,当一个标头有多个值时,将发送多个标头,每个标头都有一个值。通过设置配置选项,可以更改行为以发送带有逗号分隔值列表的单个标头。
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.cors.single-header=true
|
micronaut:
server:
cors:
single-header: true
|
[micronaut]
[micronaut.server]
[micronaut.server.cors]
single-header=true
|
micronaut {
server {
cors {
singleHeader = true
}
}
}
|
{
micronaut {
server {
cors {
single-header = true
}
}
}
}
|
{
"micronaut": {
"server": {
"cors": {
"single-header": true
}
}
}
}
|
Securing the Server with HTTPS
Micronaut 开箱即用地支持 HTTPS。默认情况下 HTTPS 被禁用,所有请求都使用 HTTP 服务。要启用 HTTPS 支持,请修改您的配置。例如:
HTTPS 配置示例
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.ssl.enabled=true
micronaut.server.ssl.buildSelfSigned=true
|
micronaut:
server:
ssl:
enabled: true
buildSelfSigned: true
|
[micronaut]
[micronaut.server]
[micronaut.server.ssl]
enabled=true
buildSelfSigned=true
|
micronaut {
server {
ssl {
enabled = true
buildSelfSigned = true
}
}
}
|
{
micronaut {
server {
ssl {
enabled = true
buildSelfSigned = true
}
}
}
}
|
{
"micronaut": {
"server": {
"ssl": {
"enabled": true,
"buildSelfSigned": true
}
}
}
}
|
默认情况下,支持 HTTPS 的 Micronaut 在端口 8443 上启动,但您可以使用属性 micronaut.server.ssl.port 更改端口。
为了生成自签名证书,Micronaut HTTP 服务器将使用 netty。 Netty 使用两种方法之一来生成证书。
如果您使用预先生成的证书(出于安全考虑,您应该这样做),则不需要执行这些步骤。
Gradle |
Maven |
implementation("org.bouncycastle:bcpkix-jdk15on")
|
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
|
此配置将在浏览器中生成警告。
使用有效的 x509 证书
也可以将 Micronaut 配置为使用现有的有效 x509 证书,例如使用 Let's Encrypt 创建的证书。您将需要 server.crt 和 server.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
原始 server.crt 文件
原始 server.key 文件
要创建的 server.p12 文件
证书的别名
在创建 server.p12 文件期间,有必要定义一个密码,稍后在 Micronaut 中使用证书时将需要该密码。
现在修改你的配置:
HTTPS 配置示例
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.ssl.enabled=true
micronaut.ssl.key-store.path=classpath:server.p12
micronaut.ssl.key-store.password=mypassword
micronaut.ssl.key-store.type=PKCS12
|
micronaut:
ssl:
enabled: true
key-store:
path: classpath:server.p12
password: mypassword
type: PKCS12
|
[micronaut]
[micronaut.ssl]
enabled=true
[micronaut.ssl.key-store]
path="classpath:server.p12"
password="mypassword"
type="PKCS12"
|
micronaut {
ssl {
enabled = true
keyStore {
path = "classpath:server.p12"
password = "mypassword"
type = "PKCS12"
}
}
}
|
{
micronaut {
ssl {
enabled = true
key-store {
path = "classpath:server.p12"
password = "mypassword"
type = "PKCS12"
}
}
}
}
|
{
"micronaut": {
"ssl": {
"enabled": true,
"key-store": {
"path": "classpath:server.p12",
"password": "mypassword",
"type": "PKCS12"
}
}
}
}
|
使用此配置,如果我们启动 Micronaut 并连接到 https://localhost:8443,我们仍然会在浏览器中看到警告,但如果我们检查证书,我们可以检查它是否是由 Let's Encrypt 生成的。
最后,我们可以通过在 /etc/hosts 文件中为域添加别名来测试证书对浏览器是否有效:
$ cat /etc/hosts
...
127.0.0.1 my-domain.org
...
现在我们可以连接到 https://my-domain.org:8443:
使用 Java 密钥库 (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)
有必要为密钥库定义密码
要创建的文件
之前创建的PKCS12文件,以及创建时定义的密码
之前使用的别名
如果 srcstorepass 或别名与 p12 文件中定义的不同,转换将失败。
现在修改你的配置:
HTTPS 配置示例
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.ssl.enabled=true
micronaut.ssl.key-store.path=classpath:server.keystore
micronaut.ssl.key-store.password=newPassword
micronaut.ssl.key-store.type=JKS
|
micronaut:
ssl:
enabled: true
key-store:
path: classpath:server.keystore
password: newPassword
type: JKS
|
[micronaut]
[micronaut.ssl]
enabled=true
[micronaut.ssl.key-store]
path="classpath:server.keystore"
password="newPassword"
type="JKS"
|
micronaut {
ssl {
enabled = true
keyStore {
path = "classpath:server.keystore"
password = "newPassword"
type = "JKS"
}
}
}
|
{
micronaut {
ssl {
enabled = true
key-store {
path = "classpath:server.keystore"
password = "newPassword"
type = "JKS"
}
}
}
}
|
{
"micronaut": {
"ssl": {
"enabled": true,
"key-store": {
"path": "classpath:server.keystore",
"password": "newPassword",
"type": "JKS"
}
}
}
}
|
启动 Micronaut,应用程序将使用密钥库中的证书在 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", "*")
));
启用 HTTP 和 HTTPS
Micronaut 支持绑定 HTTP 和 HTTPS。要启用双协议支持,请修改您的配置。例如:
双协议配置示例
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.ssl.enabled=true
micronaut.server.ssl.build-self-signed=true
micronaut.server.dual-protocol=true
|
micronaut:
server:
ssl:
enabled: true
build-self-signed: true
dual-protocol : true
|
[micronaut]
[micronaut.server]
[micronaut.server.ssl]
enabled=true
build-self-signed=true
dual-protocol=true
|
micronaut {
server {
ssl {
enabled = true
buildSelfSigned = true
}
dualProtocol = true
}
}
|
{
micronaut {
server {
ssl {
enabled = true
build-self-signed = true
}
dual-protocol = true
}
}
}
|
{
"micronaut": {
"server": {
"ssl": {
"enabled": true,
"build-self-signed": true
},
"dual-protocol": true
}
}
}
|
也可以自动将所有 HTTP 请求重定向到 HTTPS。除了前面的配置外,您还需要启用此选项。例如:
启用 HTTP 到 HTTPS 重定向
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.ssl.enabled=true
micronaut.server.ssl.build-self-signed=true
micronaut.server.dual-protocol=true
micronaut.server.http-to-https-redirect=true
|
micronaut:
server:
ssl:
enabled: true
build-self-signed: true
dual-protocol : true
http-to-https-redirect: true
|
[micronaut]
[micronaut.server]
[micronaut.server.ssl]
enabled=true
build-self-signed=true
dual-protocol=true
http-to-https-redirect=true
|
micronaut {
server {
ssl {
enabled = true
buildSelfSigned = true
}
dualProtocol = true
httpToHttpsRedirect = true
}
}
|
{
micronaut {
server {
ssl {
enabled = true
build-self-signed = true
}
dual-protocol = true
http-to-https-redirect = true
}
}
}
|
{
"micronaut": {
"server": {
"ssl": {
"enabled": true,
"build-self-signed": true
},
"dual-protocol": true,
"http-to-https-redirect": true
}
}
}
|
Enabling Access Logger
本着 apache mod_log_config 和 Tomcat 访问日志阀的精神,可以为 HTTP 服务器启用访问记录器(这适用于 HTTP/1 和 HTTP/2)。
要启用和配置访问记录器,请在您的配置文件(例如 application.yml)中设置:
Enabling the access logger
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.netty.access-logger.enabled=true
micronaut.server.netty.access-logger.logger-name=my-access-logger
micronaut.server.netty.access-logger.log-format=common
|
micronaut:
server:
netty:
access-logger:
enabled: true
logger-name: my-access-logger
log-format: common
|
[micronaut]
[micronaut.server]
[micronaut.server.netty]
[micronaut.server.netty.access-logger]
enabled=true
logger-name="my-access-logger"
log-format="common"
|
micronaut {
server {
netty {
accessLogger {
enabled = true
loggerName = "my-access-logger"
logFormat = "common"
}
}
}
}
|
{
micronaut {
server {
netty {
access-logger {
enabled = true
logger-name = "my-access-logger"
log-format = "common"
}
}
}
}
}
|
{
"micronaut": {
"server": {
"netty": {
"access-logger": {
"enabled": true,
"logger-name": "my-access-logger",
"log-format": "common"
}
}
}
}
}
|
过滤访问日志
如果你不想记录对某些路径的访问,你可以在配置中指定正则表达式过滤器:
过滤访问日志
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.server.netty.access-logger.enabled=true
micronaut.server.netty.access-logger.logger-name=my-access-logger
micronaut.server.netty.access-logger.log-format=common
micronaut.server.netty.access-logger.exclusions[0]=/health
micronaut.server.netty.access-logger.exclusions[1]=/path/.+
|
micronaut:
server:
netty:
access-logger:
enabled: true
logger-name: my-access-logger
log-format: common
exclusions:
- /health
- /path/.+
|
[micronaut]
[micronaut.server]
[micronaut.server.netty]
[micronaut.server.netty.access-logger]
enabled=true
logger-name="my-access-logger"
log-format="common"
exclusions=[
"/health",
"/path/.+"
]
|
micronaut {
server {
netty {
accessLogger {
enabled = true
loggerName = "my-access-logger"
logFormat = "common"
exclusions = ["/health", "/path/.+"]
}
}
}
}
|
{
micronaut {
server {
netty {
access-logger {
enabled = true
logger-name = "my-access-logger"
log-format = "common"
exclusions = ["/health", "/path/.+"]
}
}
}
}
}
|
{
"micronaut": {
"server": {
"netty": {
"access-logger": {
"enabled": true,
"logger-name": "my-access-logger",
"log-format": "common",
"exclusions": ["/health", "/path/.+"]
}
}
}
}
}
|
登录配置
除了启用访问记录器之外,您还必须为指定的或默认的记录器名称添加一个记录器。例如,使用默认记录器名称进行 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']'")。
-
%{property}u - 远程认证用户。当 micronaut-session 在类路径上时,如果参数被省略则返回会话 ID,否则指定的属性会打印“-”
-
%U - 请求的 URI
-
%v - 本地服务器名称
-
%D - 处理请求所花费的时间,以毫秒为单位
-
%T - 处理请求所花费的时间,以秒为单位
此外,您可以为常见模式使用以下别名:
- common - %h %l %u %t "%r" %s %b 通用日志格式 (CLF)
- combined - %h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" 组合日志格式
启动二级服务器
Micronaut支持通过NettyEmbeddedServerFactory接口以编程方式创建额外的Netty服务器。
这在你需要通过不同的端口和潜在的不同配置(HTTPS,线程资源等)暴露不同的服务器的情况下非常有用。
下面的例子演示了如何定义一个Factory Bean,使用程序化创建的配置启动一个额外的服务器。
以编程方式创建二级服务器
Java |
Groovy |
Kotlin |
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())
);
}
}
|
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
class SecondaryNettyServer {
static final String SERVER_ID = "another" // (1)
@Named(SERVER_ID)
@Context
@Bean(preDestroy = "stop") // (2)
@Requires(beans = Environment.class)
NettyEmbeddedServer nettyEmbeddedServer(NettyEmbeddedServerFactory serverFactory) { // (3)
def configuration =
new NettyHttpServerConfiguration() // (4)
def sslConfiguration = new ServerSslConfiguration() // (5)
sslConfiguration.setBuildSelfSigned(true)
sslConfiguration.enabled = true
sslConfiguration.port = -1 // random port
// configure server programmatically
final NettyEmbeddedServer embeddedServer = serverFactory.build(configuration, sslConfiguration) // (5)
embeddedServer.start() // (6)
return embeddedServer // (7)
}
@Bean
ServiceInstanceList serviceInstanceList( // (8)
@Named(SERVER_ID) NettyEmbeddedServer nettyEmbeddedServer) {
return new StaticServiceInstanceList(
SERVER_ID,
[ nettyEmbeddedServer.URI ]
)
}
}
|
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
class SecondaryNettyServer {
companion object {
const val SERVER_ID = "another" // (1)
}
@Named(SERVER_ID)
@Context
@Bean(preDestroy = "close") // (2)
@Requires(beans = [Environment::class])
fun nettyEmbeddedServer(
serverFactory: NettyEmbeddedServerFactory // (3)
) : NettyEmbeddedServer {
val configuration = NettyHttpServerConfiguration() // (4)
val sslConfiguration = ServerSslConfiguration() // (5)
sslConfiguration.setBuildSelfSigned(true)
sslConfiguration.isEnabled = true
sslConfiguration.port = -1 // random port
// configure server programmatically
val embeddedServer = serverFactory.build(configuration, sslConfiguration) // (6)
embeddedServer.start() // (7)
return embeddedServer // (8)
}
@Bean
fun serviceInstanceList( // (9)
@Named(SERVER_ID) nettyEmbeddedServer: NettyEmbeddedServer
): ServiceInstanceList {
return StaticServiceInstanceList(
SERVER_ID, setOf(nettyEmbeddedServer.uri)
)
}
}
|
- 为服务器定义一个唯一的名称
使用服务器名称定义一个 @Context 范围的 bean,并包括 preDestroy="close" 以确保在上下文关闭时关闭服务器
将 NettyEmbeddedServerFactory 注入工厂 Bean
以编程方式创建 NettyHttpServerConfiguration
可选择创建 ServerSslConfiguration
使用build方法构建服务器实例
使用start方法启动服务器
将服务器实例作为托管 bean 返回
如果您希望通过服务器名称注入 HTTP 客户端,可选择定义 ServiceInstanceList 的实例
有了这个类,当 ApplicationContext 启动时,服务器也将以适当的配置启动。
由于在步骤 8 中定义了 ServiceInstanceList,您可以将客户端注入到测试中以测试辅助服务器:
注入服务器或客户端
Java |
Groovy |
Kotlin |
@Client(path = "/", id = SecondaryNettyServer.SERVER_ID)
@Inject
HttpClient httpClient; // (1)
@Named(SecondaryNettyServer.SERVER_ID)
EmbeddedServer embeddedServer; // (2)
|
@Client(path = "/", id = SecondaryNettyServer.SERVER_ID)
@Inject
HttpClient httpClient // (1)
@Named(SecondaryNettyServer.SERVER_ID)
EmbeddedServer embeddedServer // (2)
|
@Inject
@field:Client(path = "/", id = SecondaryNettyServer.SERVER_ID)
lateinit var httpClient : HttpClient // (1)
@Inject
@field:Named(SecondaryNettyServer.SERVER_ID)
lateinit var embeddedServer : EmbeddedServer // (2)
|
使用服务器名称通过 ID 注入客户端
使用@Named 注释作为限定符来注入嵌入式服务器实例
更多建议: