HTTP 服務(wù)器具有許多配置選項。它們在擴展 HttpServerConfiguration 的 NettyHttpServerConfiguration 配置類中定義。
以下示例顯示了如何通過配置文件(例如 application.yml)調(diào)整服務(wù)器的配置選項:
配置 HTTP 服務(wù)器設(shè)置
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
}
}
}
}
}
|
默認情況下,Micronaut 綁定到所有網(wǎng)絡(luò)接口。使用 localhost 僅綁定到環(huán)回網(wǎng)絡(luò)接口
maxHeaderSize 設(shè)置標題的最大大小
worker.threads 指定 Netty 工作線程數(shù)
autoRead 啟用請求正文自動讀取
表 1. NettyHttpServerConfiguration 的配置屬性
屬性 |
類型 |
描述 |
micronaut.server.netty.child-options
|
java.util.Map
|
設(shè)置 Netty 子工作者選項。
|
micronaut.server.netty.options
|
java.util.Map
|
設(shè)置通道選項。
|
micronaut.server.netty.max-initial-line-length
|
int
|
設(shè)置 HTTP 請求的最大初始行長度。默認值 (4096)。
|
micronaut.server.netty.max-header-size
|
int
|
設(shè)置任何一個標題的最大大小。默認值 (8192)。
|
micronaut.server.netty.max-chunk-size
|
int
|
設(shè)置任何單個請求塊的最大大小。默認值 (8192)。
|
micronaut.server.netty.max-h2c-upgrade-request-size
|
int
|
設(shè)置用于將連接升級到 HTTP2 明文 (h2c) 的 HTTP1.1 請求正文的最大大小。此初始請求無法流式傳輸,而是完全緩沖,因此默認值 (8192) 相對較小。 <i>如果此值對于您的用例而言太小,請考慮使用空的初始“升級請求”(例如 {@code OPTIONS /}),或切換到普通 HTTP2。</i> <p> <i>不影響正常的 HTTP2 (TLS)。</i>
|
micronaut.server.netty.chunked-supported
|
boolean
|
設(shè)置是否支持分塊傳輸編碼。默認值(真)。
|
micronaut.server.netty.validate-headers
|
boolean
|
設(shè)置是否驗證傳入的標頭。默認值(真)。
|
micronaut.server.netty.initial-buffer-size
|
int
|
設(shè)置初始緩沖區(qū)大小。默認值 (128)。
|
micronaut.server.netty.log-level
|
io.netty.handler.logging.LogLevel
|
設(shè)置 Netty 日志級別。
|
micronaut.server.netty.compression-threshold
|
int
|
設(shè)置請求主體必須的最小大小才能被壓縮。默認值 (1024)。
|
micronaut.server.netty.compression-level
|
int
|
設(shè)置壓縮級別 (0-9)。默認值 (6)。
|
micronaut.server.netty.use-native-transport
|
boolean
|
如果可用,設(shè)置是否使用 netty 的本地傳輸(epoll 或 kqueue)。默認值(假)。
|
micronaut.server.netty.fallback-protocol
|
java.lang.String
|
設(shè)置通過 ALPN 協(xié)商時要使用的回退協(xié)議。
|
micronaut.server.netty.keep-alive-on-server-error
|
boolean
|
是否發(fā)送連接在內(nèi)部服務(wù)器錯誤時保持活動狀態(tài)。默認值({@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
|
設(shè)置顯式的 netty 偵聽器配置,或者 {@code null} 如果它們應(yīng)該是隱式的。
|
使用本機傳輸
與基于 NIO 的傳輸相比,本機 Netty 傳輸添加特定于特定平臺的功能,產(chǎn)生更少的垃圾,并且通常提高性能。
要啟用本機傳輸,首先添加依賴項:
對于 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>
|
然后將默認事件循環(huán)組配置為更喜歡本機傳輸:
配置默認事件循環(huán)以優(yōu)先使用本機傳輸
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 設(shè)置為以下之一來禁用它或啟用更高級的檢測:SIMPLE(默認)、DISABLED、PARANOID 或 ADVANCED。
配置服務(wù)器線程池
HTTP 服務(wù)器建立在 Netty 之上,Netty 被設(shè)計為事件循環(huán)模型中的非阻塞 I/O 工具包。
Netty worker 事件循環(huán)使用“默認”命名的事件循環(huán)組。這可以通過 micronaut.netty.event-loops.default 進行配置。
micronaut.server.netty.worker 下的事件循環(huán)配置僅在事件循環(huán)組設(shè)置為不對應(yīng)于任何 micronaut.netty.event-loops 配置的名稱時使用。此行為已棄用,將在未來版本中刪除。將 micronaut.netty.event-loops.* 用于除了通過 event-loop-group 設(shè)置名稱之外的任何事件循環(huán)組配置。這不適用于父事件循環(huán)配置(micronaut.server.netty.parent)。
表 1. Worker 的配置屬性
屬性 |
類型 |
描述 |
micronaut.server.netty.worker
|
NettyHttpServerConfiguration$Worker
|
設(shè)置 Worker 事件循環(huán)配置。
|
micronaut.server.netty.worker.event-loop-group
|
java.lang.String
|
設(shè)置要使用的名稱。
|
micronaut.server.netty.worker.threads
|
int
|
設(shè)置事件循環(huán)組的線程數(shù)。
|
micronaut.server.netty.worker.io-ratio
|
java.lang.Integer
|
設(shè)置 I/O 比率。
|
micronaut.server.netty.worker.executor
|
java.lang.String
|
設(shè)置執(zhí)行者的名字。
|
micronaut.server.netty.worker.prefer-native-transport
|
boolean
|
設(shè)置是否首選本地傳輸(如果可用)
|
micronaut.server.netty.worker.shutdown-quiet-period
|
java.time.Duration
|
設(shè)置關(guān)機靜默期
|
micronaut.server.netty.worker.shutdown-timeout
|
java.time.Duration
|
設(shè)置關(guān)機超時時間(必須>= shutdownQuietPeriod)
|
可以使用具有相同配置選項的 micronaut.server.netty.parent 配置父事件循環(huán)。
服務(wù)器也可以配置為使用不同的命名工作事件循環(huán):
為服務(wù)器使用不同的事件循環(huán)
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
}
}
}
}
}
|
線程數(shù)的默認值是系統(tǒng)屬性 io.netty.eventLoopThreads 的值,或者如果未指定,則為可用處理器 x 2。
請參閱下表以配置事件循環(huán):
表 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 默認將阻塞操作轉(zhuǎn)移到未綁定的緩存 I/O 線程池。您可以使用名為 io 的 ExecutorConfiguration 配置 I/O 線程池。例如:
配置服務(wù)器 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
}
}
}
}
|
上面的配置創(chuàng)建了一個有 75 個線程的固定線程池。
@Blocking
您可以使用 @Blocking 注釋將方法標記為阻塞。
如果將 micronaut.server.thread-selection 設(shè)置為 AUTO,Micronaut 框架會將使用 @Blocking 注釋的方法的執(zhí)行卸載到 IO 線程池(請參閱:TaskExecutors)。
@Blocking 僅在您使用自動線程選擇時才有效。自 Micronaut 2.0 以來,Micronaut 框架默認為手動線程選擇。我們推薦使用@ExecuteOn 注解在不同的線程池上執(zhí)行阻塞操作。 @ExecutesOn 適用于手動和自動線程選擇。
Micronaut 框架在某些地方內(nèi)部使用了@Blocking:
Micronaut Data 還在內(nèi)部利用 @Blocking 進行一些事務(wù)操作、CRUD 攔截器和存儲庫。
配置 Netty 客戶端管道
您可以通過編寫偵聽注冊表創(chuàng)建的 Bean 事件偵聽器來自定義 Netty 客戶端管道。
ChannelPipelineCustomizer 接口為各種處理程序 Micronaut 寄存器的名稱定義常量。
作為示例,以下代碼示例演示了注冊 Logbook 庫,其中包括用于執(zhí)行請求和響應(yīng)日志記錄的其他 Netty 處理程序:
為 Logbook 定制 Netty 服務(wù)器管道
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 監(jiān)聽一個 Registry 并且需要一個 Logbook bean 的定義
根定制器在沒有通道的情況下初始化并注冊
實際的定制器實現(xiàn)了 NettyClientCustomizer
創(chuàng)建新頻道時,會為該頻道創(chuàng)建一個新的專用定制器
當客戶端發(fā)出流管道已完全構(gòu)建的信號時,將注冊日志處理程序
Logbook 有一個主要錯誤,限制了它在 netty 中的實用性。
配置 Netty 服務(wù)器管道
您可以通過編寫監(jiān)聽 Registry 創(chuàng)建的 Bean 事件監(jiān)聽器來自定義 Netty 服務(wù)器管道。
ChannelPipelineCustomizer 接口為各種處理程序 Micronaut 寄存器的名稱定義常量。
作為示例,以下代碼示例演示了注冊 Logbook 庫,其中包括用于執(zhí)行請求和響應(yīng)日志記錄的其他 Netty 處理程序:
為 Logbook 定制 Netty 服務(wù)器管道
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 監(jiān)聽一個 Registry 并且需要一個 Logbook bean 的定義
根定制器在沒有通道的情況下初始化并注冊
實際的定制器實現(xiàn)了 NettyServerCustomizer
創(chuàng)建新頻道時,會為該頻道創(chuàng)建一個新的專用定制器
當服務(wù)器發(fā)出流管道已完全構(gòu)建的信號時,將注冊日志處理程序
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
}
}
}
}
}
}
|
如果您手動指定監(jiān)聽器,其他配置如 micronaut.server.port 將被忽略。
可以為每個偵聽器單獨啟用或禁用 SSL。啟用后,SSL 將按上述方式配置。
嵌入式服務(wù)器還支持使用 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>
|
服務(wù)器還必須配置為使用本地傳輸(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 請求處理,請修改應(yīng)用程序配置文件中的配置:
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 處理,采用“完全開放”策略,允許來自任何來源的請求。
要更改所有來源或特定來源的設(shè)置,請更改配置以提供一個或多個“配置”。通過提供任何配置,不配置默認的“全開”配置。
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 中沒有任何意義。它們的存在純粹是為了能夠輕松識別配置的預(yù)期用戶。
相同的配置屬性可以應(yīng)用于每個配置。有關(guān)可以定義的屬性,請參閱 CorsOriginConfiguration。提供的每個配置的值將默認為相應(yīng)字段的默認值。
當發(fā)出 CORS 請求時,將在配置中搜索完全匹配或通過正則表達式匹配請求源的允許源。
Allowed Origins
要允許給定配置的任何來源,請不要在您的配置中包含 allowedOrigins 鍵。
對于多個有效來源,將配置的 allowedOrigins 鍵設(shè)置為字符串列表。每個值可以是靜態(tài)值 (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 鍵設(shè)置為字符串列表。
示例 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 鍵設(shè)置為字符串列表。
示例 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 標頭響應(yīng) CORS 請求時發(fā)送的標頭,請在您的配置中包含 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 請求默認允許憑據(jù)。要禁止憑據(jù),請將 allowCredentials 選項設(shè)置為 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
預(yù)檢請求可以緩存的默認最長期限為 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
默認情況下,當一個標頭有多個值時,將發(fā)送多個標頭,每個標頭都有一個值。通過設(shè)置配置選項,可以更改行為以發(fā)送帶有逗號分隔值列表的單個標頭。
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 服務(wù)。要啟用 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 服務(wù)器將使用 netty。 Netty 使用兩種方法之一來生成證書。
如果您使用預(yù)先生成的證書(出于安全考慮,您應(yīng)該這樣做),則不需要執(zhí)行這些步驟。
Gradle |
Maven |
implementation("org.bouncycastle:bcpkix-jdk15on")
|
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
|
此配置將在瀏覽器中生成警告。
使用有效的 x509 證書
也可以將 Micronaut 配置為使用現(xiàn)有的有效 x509 證書,例如使用 Let's Encrypt 創(chuàng)建的證書。您將需要 server.crt 和 server.key 文件并將它們轉(zhuǎn)換為 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 文件
要創(chuàng)建的 server.p12 文件
證書的別名
在創(chuàng)建 server.p12 文件期間,有必要定義一個密碼,稍后在 Micronaut 中使用證書時將需要該密碼。
現(xiàn)在修改你的配置:
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,我們?nèi)匀粫跒g覽器中看到警告,但如果我們檢查證書,我們可以檢查它是否是由 Let's Encrypt 生成的。
最后,我們可以通過在 /etc/hosts 文件中為域添加別名來測試證書對瀏覽器是否有效:
$ cat /etc/hosts
...
127.0.0.1 my-domain.org
...
現(xiàn)在我們可以連接到 https://my-domain.org:8443:
使用 Java 密鑰庫 (JKS)
不建議使用這種類型的證書,因為格式是專有的 - 首選 PKCS12 格式。不管怎樣,Micronaut 也支持它。
將 p12 證書轉(zhuǎn)換為 JKS 證書:
$ keytool -importkeystore \
-deststorepass newPassword -destkeypass newPassword \ (1)
-destkeystore server.keystore \ (2)
-srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass mypassword \ (3)
-alias someAlias (4)
有必要為密鑰庫定義密碼
要創(chuàng)建的文件
之前創(chuàng)建的PKCS12文件,以及創(chuàng)建時定義的密碼
之前使用的別名
如果 srcstorepass 或別名與 p12 文件中定義的不同,轉(zhuǎn)換將失敗。
現(xiàn)在修改你的配置:
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,應(yīng)用程序?qū)⑹褂妹荑€庫中的證書在 https://localhost:8443 上運行。
刷新/重新加載 HTTPS 證書
在 HTTPS 證書過期后保持最新狀態(tài)可能是一個挑戰(zhàn)。一個很好的解決方案是自動證書管理環(huán)境 (ACME) 和 Micronaut ACME 模塊,它支持從證書頒發(fā)機構(gòu)自動刷新證書。
如果無法使用證書頒發(fā)機構(gòu)并且您需要從磁盤手動更新證書,那么您應(yīng)該使用 Micronaut 對應(yīng)用程序事件的支持觸發(fā) RefreshEvent,其中包含定義 HTTPS 配置的密鑰,Micronaut 將從磁盤重新加載證書并應(yīng)用服務(wù)器的新配置。
您還可以使用刷新管理端點,但這僅適用于磁盤上證書的物理位置已更改的情況
例如,以下將從磁盤重新加載先前列出的 HTTPS 配置并將其應(yīng)用于新的傳入請求(例如,此代碼可以運行輪詢證書以獲取更改的計劃作業(yè)):
手動刷新 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。要啟用雙協(xié)議支持,請修改您的配置。例如:
雙協(xié)議配置示例
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 服務(wù)器啟用訪問記錄器(這適用于 HTTP/1 和 HTTP/2)。
要啟用和配置訪問記錄器,請在您的配置文件(例如 application.yml)中設(shè)置:
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>
該模式應(yīng)該只有消息標記,因為其他元素將由訪問記錄器處理。
日志格式
語法基于 Apache httpd 日志格式。
這些是支持的標記:
-
%a - 遠程IP地址
-
%A - 本地IP地址
-
%b - 發(fā)送的字節(jié)數(shù),不包括 HTTP 標頭,如果未發(fā)送任何字節(jié),則為“-”
-
%B - 發(fā)送的字節(jié)數(shù),不包括 HTTP 標頭
-
%h - 遠程主機名
-
%H - 請求協(xié)議
-
%{<header>}i - 請求標頭。如果參數(shù)被省略 (%i) 所有的標題都被打印出來
-
%{<header>}o - 響應(yīng)頭。如果省略參數(shù) (%o),則打印所有標題
-
%{<cookie>}C - 請求 cookie (COOKIE)。如果省略參數(shù) (%C),則打印所有 cookie
-
%{<cookie>}c - 響應(yīng) cookie (SET_COOKIE)。如果省略參數(shù) (%c),則打印所有 cookie
-
%l - 來自 identd 的遠程邏輯用戶名(始終返回“-”)
-
%m - 請求方法
-
%p - 本地端口
-
%q - 查詢字符串(不包括“?”字符)
-
%r - 請求的第一行
-
%s - 響應(yīng)的 HTTP 狀態(tài)代碼
-
%{<format>}t - 日期和時間。如果省略參數(shù),則使用通用日志格式 ("'['dd/MMM/yyyy:HH:mm:ss Z']'")。
-
%{property}u - 遠程認證用戶。當 micronaut-session 在類路徑上時,如果參數(shù)被省略則返回會話 ID,否則指定的屬性會打印“-”
-
%U - 請求的 URI
-
%v - 本地服務(wù)器名稱
-
%D - 處理請求所花費的時間,以毫秒為單位
-
%T - 處理請求所花費的時間,以秒為單位
此外,您可以為常見模式使用以下別名:
- common - %h %l %u %t "%r" %s %b 通用日志格式 (CLF)
- combined - %h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" 組合日志格式
啟動二級服務(wù)器
Micronaut支持通過NettyEmbeddedServerFactory接口以編程方式創(chuàng)建額外的Netty服務(wù)器。
這在你需要通過不同的端口和潛在的不同配置(HTTPS,線程資源等)暴露不同的服務(wù)器的情況下非常有用。
下面的例子演示了如何定義一個Factory Bean,使用程序化創(chuàng)建的配置啟動一個額外的服務(wù)器。
以編程方式創(chuàng)建二級服務(wù)器
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)
)
}
}
|
- 為服務(wù)器定義一個唯一的名稱
使用服務(wù)器名稱定義一個 @Context 范圍的 bean,并包括 preDestroy="close" 以確保在上下文關(guān)閉時關(guān)閉服務(wù)器
將 NettyEmbeddedServerFactory 注入工廠 Bean
以編程方式創(chuàng)建 NettyHttpServerConfiguration
可選擇創(chuàng)建 ServerSslConfiguration
使用build方法構(gòu)建服務(wù)器實例
使用start方法啟動服務(wù)器
將服務(wù)器實例作為托管 bean 返回
如果您希望通過服務(wù)器名稱注入 HTTP 客戶端,可選擇定義 ServiceInstanceList 的實例
有了這個類,當 ApplicationContext 啟動時,服務(wù)器也將以適當?shù)呐渲脝印?
由于在步驟 8 中定義了 ServiceInstanceList,您可以將客戶端注入到測試中以測試輔助服務(wù)器:
注入服務(wù)器或客戶端
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)
|
使用服務(wù)器名稱通過 ID 注入客戶端
使用@Named 注釋作為限定符來注入嵌入式服務(wù)器實例
更多建議: