Micronaut 配置 HTTP 服務(wù)器

2023-03-07 17:47 更新

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:

Blocking 類型 描述

BlockingHttpClient

用于測試,為 HttpClient 操作的子集提供阻塞版本。

IOUtils

以阻塞方式讀取 BufferedReader 的內(nèi)容,并將其作為字符串返回。

BootstrapPropertySourceLocator

解析當前環(huán)境的遠程或本地 PropertySource 實例。

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)
            )
        }
    }
}
  1. LogbookNettyClientCustomizer 監(jiān)聽一個 Registry 并且需要一個 Logbook bean 的定義

  2. 根定制器在沒有通道的情況下初始化并注冊

  3. 實際的定制器實現(xiàn)了 NettyClientCustomizer

  4. 創(chuàng)建新頻道時,會為該頻道創(chuàng)建一個新的專用定制器

  5. 當客戶端發(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)
            )
        }
    }
}
  1. LogbookNettyServerCustomizer 監(jiān)聽一個 Registry 并且需要一個 Logbook bean 的定義

  2. 根定制器在沒有通道的情況下初始化并注冊

  3. 實際的定制器實現(xiàn)了 NettyServerCustomizer

  4. 創(chuàng)建新頻道時,會為該頻道創(chuàng)建一個新的專用定制器

  5. 當服務(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
          }
        }
      }
    }
  }
}
  • httpListener 是監(jiān)聽器名稱,可以是任意值

  • host 是可選的,默認情況下綁定到所有接口

如果您手動指定監(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
          }
        }
      }
    }
  }
}
  • unixListener 是監(jiān)聽器名稱,可以是任意值

要使用抽象域套接字而不是普通套接字,請在路徑前加上 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
      }
    }
  }
}
  • Micronaut 將創(chuàng)建一個自簽名證書。

默認情況下,支持 HTTPS 的 Micronaut 在端口 8443 上啟動,但您可以使用屬性 micronaut.server.ssl.port 更改端口。

為了生成自簽名證書,Micronaut HTTP 服務(wù)器將使用 netty。 Netty 使用兩種方法之一來生成證書。

如果您使用預(yù)先生成的證書(出于安全考慮,您應(yīng)該這樣做),則不需要執(zhí)行這些步驟。

  • Netty 可以使用 JDK 內(nèi)部的 sun.security.x509 包。在較新的 JDK 版本上,此包受到限制,可能無法使用。您可能需要添加 --add-exports=java.base/sun.security.x509=ALL-UNNAMED 作為 VM 參數(shù)。

  • 或者,netty 將使用 Bouncy Castle BCPKIX API。這需要一個額外的依賴:

 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
  1. 原始 server.crt 文件

  2. 原始 server.key 文件

  3. 要創(chuàng)建的 server.p12 文件

  4. 證書的別名

在創(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"
      }
    }
  }
}
  • 指定 p12 文件路徑。它也可以被引用為 file:/path/to/the/file

  • 還要提供導(dǎo)出期間定義的密碼

使用此配置,如果我們啟動 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)
  1. 有必要為密鑰庫定義密碼

  2. 要創(chuàng)建的文件

  3. 之前創(chuàng)建的PKCS12文件,以及創(chuàng)建時定義的密碼

  4. 之前使用的別名

如果 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
    }
  }
}
  • 您必須配置 SSL 才能使 HTTPS 工作。在此示例中,我們僅使用帶有構(gòu)建自簽名的自簽名證書

  • 雙協(xié)議啟用 HTTP 和 HTTPS 是一個可選功能 - 設(shè)置 dualProtocol 標志啟用它。默認情況下,Micronaut 只啟用一個

也可以自動將所有 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
    }
  }
}
  • http-to-https-redirect 啟用 HTTP 到 HTTPS 重定向

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"
        }
      }
    }
  }
}
  • enabled 啟用訪問記錄器

  • 可選地指定一個記錄器名稱,默認為 HTTP_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.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/.+"]
        }
      }
    }
  }
}
  • enabled 啟用訪問記錄器

  • 可選地指定一個記錄器名稱,默認為 HTTP_ACCESS_LOGGER

  • 可選地指定日志格式,默認為通用日志格式

登錄配置

除了啟用訪問記錄器之外,您還必須為指定的或默認的記錄器名稱添加一個記錄器。例如,使用默認記錄器名稱進行 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']'")。

    • 如果格式以 begin 開頭:(默認)時間是在請求處理開始時采用的。如果它以end開頭:它是日志條目被寫入的時間,接近請求處理的結(jié)束。

    • 格式應(yīng)遵循 DateTimeFormatter 語法。

  • %{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)
        )
    }
}
  1. 為服務(wù)器定義一個唯一的名稱
  2. 使用服務(wù)器名稱定義一個 @Context 范圍的 bean,并包括 preDestroy="close" 以確保在上下文關(guān)閉時關(guān)閉服務(wù)器

  3. 將 NettyEmbeddedServerFactory 注入工廠 Bean

  4. 以編程方式創(chuàng)建 NettyHttpServerConfiguration

  5. 可選擇創(chuàng)建 ServerSslConfiguration

  6. 使用build方法構(gòu)建服務(wù)器實例

  7. 使用start方法啟動服務(wù)器

  8. 將服務(wù)器實例作為托管 bean 返回

  9. 如果您希望通過服務(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)
  1. 使用服務(wù)器名稱通過 ID 注入客戶端

  2. 使用@Named 注釋作為限定符來注入嵌入式服務(wù)器實例


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號