Micronaut 配置 HTTP 服务器

2023-03-07 17:47 更新

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

以下示例显示了如何通过配置文件(例如 application.yml)调整服务器的配置选项:

配置 HTTP 服务器设置

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.maxRequestSize=1MB
micronaut.server.host=localhost
micronaut.server.netty.maxHeaderSize=500KB
micronaut.server.netty.worker.threads=8
micronaut.server.netty.childOptions.autoRead=true
micronaut:
  server:
    maxRequestSize: 1MB
    host: localhost
    netty:
      maxHeaderSize: 500KB
      worker:
        threads: 8
      childOptions:
        autoRead: true
[micronaut]
  [micronaut.server]
    maxRequestSize="1MB"
    host="localhost"
    [micronaut.server.netty]
      maxHeaderSize="500KB"
      [micronaut.server.netty.worker]
        threads=8
      [micronaut.server.netty.childOptions]
        autoRead=true
micronaut {
  server {
    maxRequestSize = "1MB"
    host = "localhost"
    netty {
      maxHeaderSize = "500KB"
      worker {
        threads = 8
      }
      childOptions {
        autoRead = true
      }
    }
  }
}
{
  micronaut {
    server {
      maxRequestSize = "1MB"
      host = "localhost"
      netty {
        maxHeaderSize = "500KB"
        worker {
          threads = 8
        }
        childOptions {
          autoRead = true
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "maxRequestSize": "1MB",
      "host": "localhost",
      "netty": {
        "maxHeaderSize": "500KB",
        "worker": {
          "threads": 8
        },
        "childOptions": {
          "autoRead": true
        }
      }
    }
  }
}
  • 默认情况下,Micronaut 绑定到所有网络接口。使用 localhost 仅绑定到环回网络接口

  • maxHeaderSize 设置标题的最大大小

  • worker.threads 指定 Netty 工作线程数

  • autoRead 启用请求正文自动读取

表 1. NettyHttpServerConfiguration 的配置属性
属性 类型 描述

micronaut.server.netty.child-options

java.util.Map

设置 Netty 子工作者选项。

micronaut.server.netty.options

java.util.Map

设置通道选项。

micronaut.server.netty.max-initial-line-length

int

设置 HTTP 请求的最大初始行长度。默认值 (4096)。

micronaut.server.netty.max-header-size

int

设置任何一个标题的最大大小。默认值 (8192)。

micronaut.server.netty.max-chunk-size

int

设置任何单个请求块的最大大小。默认值 (8192)。

micronaut.server.netty.max-h2c-upgrade-request-size

int

设置用于将连接升级到 HTTP2 明文 (h2c) 的 HTTP1.1 请求正文的最大大小。此初始请求无法流式传输,而是完全缓冲,因此默认值 (8192) 相对较小。 <i>如果此值对于您的用例而言太小,请考虑使用空的初始“升级请求”(例如 {@code OPTIONS /}),或切换到普通 HTTP2。</i> <p> <i>不影响正常的 HTTP2 (TLS)。</i>

micronaut.server.netty.chunked-supported

boolean

设置是否支持分块传输编码。默认值(真)。

micronaut.server.netty.validate-headers

boolean

设置是否验证传入的标头。默认值(真)。

micronaut.server.netty.initial-buffer-size

int

设置初始缓冲区大小。默认值 (128)。

micronaut.server.netty.log-level

io.netty.handler.logging.LogLevel

设置 Netty 日志级别。

micronaut.server.netty.compression-threshold

int

设置请求主体必须的最小大小才能被压缩。默认值 (1024)。

micronaut.server.netty.compression-level

int

设置压缩级别 (0-9)。默认值 (6)。

micronaut.server.netty.use-native-transport

boolean

如果可用,设置是否使用 netty 的本地传输(epoll 或 kqueue)。默认值(假)。

micronaut.server.netty.fallback-protocol

java.lang.String

设置通过 ALPN 协商时要使用的回退协议。

micronaut.server.netty.keep-alive-on-server-error

boolean

是否发送连接在内部服务器错误时保持活动状态。默认值({@value DEFAULT_KEEP_ALIVE_ON_SERVER_ERROR})。

micronaut.server.netty.pcap-logging-path-pattern

java.lang.String

用于记录到 pcap 的传入连接的路径模式。这是一个不受支持的选项:行为可能会改变,或者可能会完全消失,恕不另行通知!

micronaut.server.netty.listeners

java.util.List

设置显式的 netty 侦听器配置,或者 {@code null} 如果它们应该是隐式的。

使用本机传输

与基于 NIO 的传输相比,本机 Netty 传输添加特定于特定平台的功能,产生更少的垃圾,并且通常提高性能。

要启用本机传输,首先添加依赖项:

对于 x86 上的 macOS:

 Gradle Maven 
runtimeOnly("io.netty:netty-transport-native-kqueue::osx-x86_64")
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport-native-kqueue</artifactId>
    <scope>runtime</scope>
    <classifier>osx-x86_64</classifier>
</dependency>

对于 M1 上的 macOS:

 Gradle Maven 
runtimeOnly("io.netty:netty-transport-native-kqueue::osx-aarch_64")
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport-native-kqueue</artifactId>
    <scope>runtime</scope>
    <classifier>osx-aarch_64</classifier>
</dependency>

对于 x86 上的 Linux:

 Gradle Maven 
runtimeOnly("io.netty:netty-transport-native-epoll::linux-x86_64")
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport-native-epoll</artifactId>
    <scope>runtime</scope>
    <classifier>linux-x86_64</classifier>
</dependency>

对于 ARM64 上的 Linux:

 Gradle Maven 
runtimeOnly("io.netty:netty-transport-native-epoll::linux-aarch_64")
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport-native-epoll</artifactId>
    <scope>runtime</scope>
    <classifier>linux-aarch_64</classifier>
</dependency>

然后将默认事件循环组配置为更喜欢本机传输:

配置默认事件循环以优先使用本机传输

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.netty.event-loops.default.prefer-native-transport=true
micronaut:
  netty:
    event-loops:
      default:
        prefer-native-transport: true
[micronaut]
  [micronaut.netty]
    [micronaut.netty.event-loops]
      [micronaut.netty.event-loops.default]
        prefer-native-transport=true
micronaut {
  netty {
    eventLoops {
      'default' {
        preferNativeTransport = true
      }
    }
  }
}
{
  micronaut {
    netty {
      event-loops {
        default {
          prefer-native-transport = true
        }
      }
    }
  }
}
{
  "micronaut": {
    "netty": {
      "event-loops": {
        "default": {
          "prefer-native-transport": true
        }
      }
    }
  }
}

Netty 支持简单的采样资源泄漏检测,以少量开销为代价报告是否存在泄漏。您可以通过将属性 netty.resource-leak-detector-level 设置为以下之一来禁用它或启用更高级的检测:SIMPLE(默认)、DISABLED、PARANOID 或 ADVANCED。

配置服务器线程池

HTTP 服务器建立在 Netty 之上,Netty 被设计为事件循环模型中的非阻塞 I/O 工具包。

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

micronaut.server.netty.worker 下的事件循环配置仅在事件循环组设置为不对应于任何 micronaut.netty.event-loops 配置的名称时使用。此行为已弃用,将在未来版本中删除。将 micronaut.netty.event-loops.* 用于除了通过 event-loop-group 设置名称之外的任何事件循环组配置。这不适用于父事件循环配置(micronaut.server.netty.parent)。

表 1. Worker 的配置属性
属性 类型 描述

micronaut.server.netty.worker

NettyHttpServerConfiguration$Worker

设置 Worker 事件循环配置。

micronaut.server.netty.worker.event-loop-group

java.lang.String

设置要使用的名称。

micronaut.server.netty.worker.threads

int

设置事件循环组的线程数。

micronaut.server.netty.worker.io-ratio

java.lang.Integer

设置 I/O 比率。

micronaut.server.netty.worker.executor

java.lang.String

设置执行者的名字。

micronaut.server.netty.worker.prefer-native-transport

boolean

设置是否首选本地传输(如果可用)

micronaut.server.netty.worker.shutdown-quiet-period

java.time.Duration

设置关机静默期

micronaut.server.netty.worker.shutdown-timeout

java.time.Duration

设置关机超时时间(必须>= shutdownQuietPeriod)

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

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

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

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.netty.worker.event-loop-group=other
micronaut.netty.event-loops.other.num-threads=10
micronaut:
  server:
    netty:
      worker:
        event-loop-group: other
  netty:
    event-loops:
      other:
        num-threads: 10
[micronaut]
  [micronaut.server]
    [micronaut.server.netty]
      [micronaut.server.netty.worker]
        event-loop-group="other"
  [micronaut.netty]
    [micronaut.netty.event-loops]
      [micronaut.netty.event-loops.other]
        num-threads=10
micronaut {
  server {
    netty {
      worker {
        eventLoopGroup = "other"
      }
    }
  }
  netty {
    eventLoops {
      other {
        numThreads = 10
      }
    }
  }
}
{
  micronaut {
    server {
      netty {
        worker {
          event-loop-group = "other"
        }
      }
    }
    netty {
      event-loops {
        other {
          num-threads = 10
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "netty": {
        "worker": {
          "event-loop-group": "other"
        }
      }
    },
    "netty": {
      "event-loops": {
        "other": {
          "num-threads": 10
        }
      }
    }
  }
}

线程数的默认值是系统属性 io.netty.eventLoopThreads 的值,或者如果未指定,则为可用处理器 x 2。

请参阅下表以配置事件循环:

表 2. DefaultEventLoopGroupConfiguration 的配置属性
属性 类型 描述

micronaut.netty.event-loops.*.num-threads

int

micronaut.netty.event-loops.*.io-ratio

java.lang.Integer

micronaut.netty.event-loops.*.prefer-native-transport

boolean

micronaut.netty.event-loops.*.executor

java.lang.String

micronaut.netty.event-loops.*.shutdown-quiet-period

java.time.Duration

micronaut.netty.event-loops.*.shutdown-timeout

java.time.Duration

阻塞操作

在处理阻塞操作时,Micronaut 默认将阻塞操作转移到未绑定的缓存 I/O 线程池。您可以使用名为 io 的 ExecutorConfiguration 配置 I/O 线程池。例如:

配置服务器 I/O 线程池

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.executors.io.type=fixed
micronaut.executors.io.nThreads=75
micronaut:
  executors:
    io:
      type: fixed
      nThreads: 75
[micronaut]
  [micronaut.executors]
    [micronaut.executors.io]
      type="fixed"
      nThreads=75
micronaut {
  executors {
    io {
      type = "fixed"
      nThreads = 75
    }
  }
}
{
  micronaut {
    executors {
      io {
        type = "fixed"
        nThreads = 75
      }
    }
  }
}
{
  "micronaut": {
    "executors": {
      "io": {
        "type": "fixed",
        "nThreads": 75
      }
    }
  }
}

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

@Blocking

您可以使用 @Blocking 注释将方法标记为阻塞。

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

@Blocking 仅在您使用自动线程选择时才有效。自 Micronaut 2.0 以来,Micronaut 框架默认为手动线程选择。我们推荐使用@ExecuteOn 注解在不同的线程池上执行阻塞操作。 @ExecutesOn 适用于手动和自动线程选择。

Micronaut 框架在某些地方内部使用了@Blocking:

Blocking 类型 描述

BlockingHttpClient

用于测试,为 HttpClient 操作的子集提供阻塞版本。

IOUtils

以阻塞方式读取 BufferedReader 的内容,并将其作为字符串返回。

BootstrapPropertySourceLocator

解析当前环境的远程或本地 PropertySource 实例。

Micronaut Data 还在内部利用 @Blocking 进行一些事务操作、CRUD 拦截器和存储库。

配置 Netty 客户端管道

您可以通过编写侦听注册表创建的 Bean 事件侦听器来自定义 Netty 客户端管道。

ChannelPipelineCustomizer 接口为各种处理程序 Micronaut 寄存器的名称定义常量。

作为示例,以下代码示例演示了注册 Logbook 库,其中包括用于执行请求和响应日志记录的其他 Netty 处理程序:

为 Logbook 定制 Netty 服务器管道

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

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

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

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

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

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

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

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

        @Override
        public void onStreamPipelineBuilt() {
            channel.pipeline().addLast( // (5)
                "logbook",
                new LogbookClientHandler(logbook)
            );
        }
    }
}
import io.micronaut.context.event.BeanCreatedEvent
import io.micronaut.context.event.BeanCreatedEventListener
import io.micronaut.http.client.netty.NettyClientCustomizer
import io.netty.channel.Channel
import jakarta.inject.Singleton
import org.zalando.logbook.Logbook
import org.zalando.logbook.netty.LogbookClientHandler


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

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

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

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

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

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

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

        @Override
        void onStreamPipelineBuilt() {
            channel.pipeline().addLast( // (5)
                    "logbook",
                    new LogbookClientHandler(logbook)
            )
        }
    }
}
import io.micronaut.context.annotation.Requires
import io.micronaut.context.event.BeanCreatedEvent
import io.micronaut.context.event.BeanCreatedEventListener
import io.micronaut.http.client.netty.NettyClientCustomizer
import io.micronaut.http.client.netty.NettyClientCustomizer.ChannelRole
import io.netty.channel.Channel
import jakarta.inject.Singleton
import org.zalando.logbook.Logbook
import org.zalando.logbook.netty.LogbookClientHandler

@Requires(beans = [Logbook::class])
@Singleton
class LogbookNettyClientCustomizer(private val logbook: Logbook) :
    BeanCreatedEventListener<NettyClientCustomizer.Registry> { // (1)

    override fun onCreated(event: BeanCreatedEvent<NettyClientCustomizer.Registry>): NettyClientCustomizer.Registry {
        val registry = event.bean
        registry.register(Customizer(null)) // (2)
        return registry
    }

    private inner class Customizer constructor(private val channel: Channel?) :
        NettyClientCustomizer { // (3)

        override fun specializeForChannel(channel: Channel, role: ChannelRole) = Customizer(channel) // (4)

        override fun onStreamPipelineBuilt() {
            channel!!.pipeline().addLast( // (5)
                "logbook",
                LogbookClientHandler(logbook)
            )
        }
    }
}
  1. LogbookNettyClientCustomizer 监听一个 Registry 并且需要一个 Logbook bean 的定义

  2. 根定制器在没有通道的情况下初始化并注册

  3. 实际的定制器实现了 NettyClientCustomizer

  4. 创建新频道时,会为该频道创建一个新的专用定制器

  5. 当客户端发出流管道已完全构建的信号时,将注册日志处理程序

Logbook 有一个主要错误,限制了它在 netty 中的实用性。

配置 Netty 服务器管道

您可以通过编写监听 Registry 创建的 Bean 事件监听器来自定义 Netty 服务器管道。

ChannelPipelineCustomizer 接口为各种处理程序 Micronaut 寄存器的名称定义常量。

作为示例,以下代码示例演示了注册 Logbook 库,其中包括用于执行请求和响应日志记录的其他 Netty 处理程序:

为 Logbook 定制 Netty 服务器管道

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

import jakarta.inject.Singleton;

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

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

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

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

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

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

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

        @Override
        public void onStreamPipelineBuilt() {
            channel.pipeline().addBefore( // (5)
                ChannelPipelineCustomizer.HANDLER_HTTP_STREAM,
                "logbook",
                new LogbookServerHandler(logbook)
            );
        }
    }
}
import io.micronaut.context.annotation.Requires
import io.micronaut.context.event.BeanCreatedEvent
import io.micronaut.context.event.BeanCreatedEventListener
import io.micronaut.http.netty.channel.ChannelPipelineCustomizer
import io.micronaut.http.server.netty.NettyServerCustomizer
import io.netty.channel.Channel
import org.zalando.logbook.Logbook
import org.zalando.logbook.netty.LogbookServerHandler

import jakarta.inject.Singleton

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

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

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

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

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

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

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

        @Override
        void onStreamPipelineBuilt() {
            channel.pipeline().addBefore( // (5)
                    ChannelPipelineCustomizer.HANDLER_HTTP_STREAM,
                    "logbook",
                    new LogbookServerHandler(logbook)
            )
        }
    }
}
import io.micronaut.context.annotation.Requires
import io.micronaut.context.event.BeanCreatedEvent
import io.micronaut.context.event.BeanCreatedEventListener
import io.micronaut.http.netty.channel.ChannelPipelineCustomizer
import io.micronaut.http.server.netty.NettyServerCustomizer
import io.micronaut.http.server.netty.NettyServerCustomizer.ChannelRole
import io.netty.channel.Channel
import jakarta.inject.Singleton
import org.zalando.logbook.Logbook
import org.zalando.logbook.netty.LogbookServerHandler

@Requires(beans = [Logbook::class])
@Singleton
class LogbookNettyServerCustomizer(private val logbook: Logbook) :
    BeanCreatedEventListener<NettyServerCustomizer.Registry> { // (1)

    override fun onCreated(event: BeanCreatedEvent<NettyServerCustomizer.Registry>): NettyServerCustomizer.Registry {
        val registry = event.bean
        registry.register(Customizer(null)) // (2)
        return registry
    }

    private inner class Customizer constructor(private val channel: Channel?) :
        NettyServerCustomizer { // (3)

        override fun specializeForChannel(channel: Channel, role: ChannelRole) = Customizer(channel) // (4)

        override fun onStreamPipelineBuilt() {
            channel!!.pipeline().addBefore( // (5)
                ChannelPipelineCustomizer.HANDLER_HTTP_STREAM,
                "logbook",
                LogbookServerHandler(logbook)
            )
        }
    }
}
  1. LogbookNettyServerCustomizer 监听一个 Registry 并且需要一个 Logbook bean 的定义

  2. 根定制器在没有通道的情况下初始化并注册

  3. 实际的定制器实现了 NettyServerCustomizer

  4. 创建新频道时,会为该频道创建一个新的专用定制器

  5. 当服务器发出流管道已完全构建的信号时,将注册日志处理程序

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 是监听器名称,可以是任意值

  • host 是可选的,默认情况下绑定到所有接口

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

可以为每个侦听器单独启用或禁用 SSL。启用后,SSL 将按上述方式配置。

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

 Gradle Maven 
implementation("io.netty:netty-transport-native-unix-common")
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport-native-unix-common</artifactId>
</dependency>

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

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.netty.listeners.unixListener.family=UNIX
micronaut.server.netty.listeners.unixListener.path=/run/micronaut.socket
micronaut.server.netty.listeners.unixListener.ssl=true
micronaut:
  server:
    netty:
      listeners:
        unixListener:
          family: UNIX
          path: /run/micronaut.socket
          ssl: true
[micronaut]
  [micronaut.server]
    [micronaut.server.netty]
      [micronaut.server.netty.listeners]
        [micronaut.server.netty.listeners.unixListener]
          family="UNIX"
          path="/run/micronaut.socket"
          ssl=true
micronaut {
  server {
    netty {
      listeners {
        unixListener {
          family = "UNIX"
          path = "/run/micronaut.socket"
          ssl = true
        }
      }
    }
  }
}
{
  micronaut {
    server {
      netty {
        listeners {
          unixListener {
            family = "UNIX"
            path = "/run/micronaut.socket"
            ssl = true
          }
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "netty": {
        "listeners": {
          "unixListener": {
            "family": "UNIX",
            "path": "/run/micronaut.socket",
            "ssl": true
          }
        }
      }
    }
  }
}
  • unixListener 是监听器名称,可以是任意值

要使用抽象域套接字而不是普通套接字,请在路径前加上 NUL 字符,例如“\0/run/micronaut.socket”

配置 CORS

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

CORS 通过配置

要启用 CORS 请求处理,请修改应用程序配置文件中的配置:

CORS 配置示例

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.cors.enabled=true
micronaut:
  server:
    cors:
      enabled: true
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      enabled=true
micronaut {
  server {
    cors {
      enabled = true
    }
  }
}
{
  micronaut {
    server {
      cors {
        enabled = true
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "enabled": true
      }
    }
  }
}

通过仅启用 CORS 处理,采用“完全开放”策略,允许来自任何来源的请求。

要更改所有来源或特定来源的设置,请更改配置以提供一个或多个“配置”。通过提供任何配置,不配置默认的“全开”配置。

CORS 配置

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.all=...
micronaut.server.cors.configurations.web=...
micronaut.server.cors.configurations.mobile=...
micronaut:
  server:
    cors:
      enabled: true
      configurations:
        all:
          ...
        web:
          ...
        mobile:
          ...
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      enabled=true
      [micronaut.server.cors.configurations]
        all="..."
        web="..."
        mobile="..."
micronaut {
  server {
    cors {
      enabled = true
      configurations {
        all = "..."
        web = "..."
        mobile = "..."
      }
    }
  }
}
{
  micronaut {
    server {
      cors {
        enabled = true
        configurations {
          all = "..."
          web = "..."
          mobile = "..."
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "enabled": true,
        "configurations": {
          "all": "...",
          "web": "...",
          "mobile": "..."
        }
      }
    }
  }
}

在上面的示例中,提供了三种配置。它们的名称(all、web、mobile)并不重要,在 Micronaut 中没有任何意义。它们的存在纯粹是为了能够轻松识别配置的预期用户。

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

当发出 CORS 请求时,将在配置中搜索完全匹配或通过正则表达式匹配请求源的允许源。

Allowed Origins

要允许给定配置的任何来源,请不要在您的配置中包含 allowedOrigins 键。

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

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

示例 CORS 配置

 Properties Yaml Toml Groovy  Hocon  JSON 
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.allowedOrigins[0]=http://foo.com
micronaut.server.cors.configurations.web.allowedOrigins[1]=^http(|s):\/\/www\.google\.com$
micronaut:
  server:
    cors:
      enabled: true
      configurations:
        web:
          allowedOrigins:
            - http://foo.com
            - ^http(|s):\/\/www\.google\.com$
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      enabled=true
      [micronaut.server.cors.configurations]
        [micronaut.server.cors.configurations.web]
          allowedOrigins=[
            "http://foo.com",
            "^http(|s):\\/\\/www\\.google\\.com$"
          ]
micronaut {
  server {
    cors {
      enabled = true
      configurations {
        web {
          allowedOrigins = ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
        }
      }
    }
  }
}
{
  micronaut {
    server {
      cors {
        enabled = true
        configurations {
          web {
            allowedOrigins = ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
          }
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "enabled": true,
        "configurations": {
          "web": {
            "allowedOrigins": ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
          }
        }
      }
    }
  }
}

Allowed Methods

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

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

示例 CORS 配置

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.allowedOrigins[0]=http://foo.com
micronaut.server.cors.configurations.web.allowedOrigins[1]=^http(|s):\/\/www\.google\.com$
micronaut:
  server:
    cors:
      enabled: true
      configurations:
        web:
          allowedOrigins:
            - http://foo.com
            - ^http(|s):\/\/www\.google\.com$
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      enabled=true
      [micronaut.server.cors.configurations]
        [micronaut.server.cors.configurations.web]
          allowedOrigins=[
            "http://foo.com",
            "^http(|s):\\/\\/www\\.google\\.com$"
          ]
micronaut {
  server {
    cors {
      enabled = true
      configurations {
        web {
          allowedOrigins = ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
        }
      }
    }
  }
}
{
  micronaut {
    server {
      cors {
        enabled = true
        configurations {
          web {
            allowedOrigins = ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
          }
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "enabled": true,
        "configurations": {
          "web": {
            "allowedOrigins": ["http://foo.com", "^http(|s):\\/\\/www\\.google\\.com$"]
          }
        }
      }
    }
  }
}

Allowed Headers

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

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

示例 CORS 配置

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.allowedHeaders[0]=Content-Type
micronaut.server.cors.configurations.web.allowedHeaders[1]=Authorization
micronaut:
  server:
    cors:
      enabled: true
      configurations:
        web:
          allowedHeaders:
            - Content-Type
            - Authorization
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      enabled=true
      [micronaut.server.cors.configurations]
        [micronaut.server.cors.configurations.web]
          allowedHeaders=[
            "Content-Type",
            "Authorization"
          ]
micronaut {
  server {
    cors {
      enabled = true
      configurations {
        web {
          allowedHeaders = ["Content-Type", "Authorization"]
        }
      }
    }
  }
}
{
  micronaut {
    server {
      cors {
        enabled = true
        configurations {
          web {
            allowedHeaders = ["Content-Type", "Authorization"]
          }
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "enabled": true,
        "configurations": {
          "web": {
            "allowedHeaders": ["Content-Type", "Authorization"]
          }
        }
      }
    }
  }
}

Exposed Headers

要配置在通过 Access-Control-Expose-Headers 标头响应 CORS 请求时发送的标头,请在您的配置中包含 exposedHeaders 键的字符串列表。默认情况下没有公开。

示例 CORS 配置

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.exposedHeaders[0]=Content-Type
micronaut.server.cors.configurations.web.exposedHeaders[1]=Authorization
micronaut:
  server:
    cors:
      enabled: true
      configurations:
        web:
          exposedHeaders:
            - Content-Type
            - Authorization
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      enabled=true
      [micronaut.server.cors.configurations]
        [micronaut.server.cors.configurations.web]
          exposedHeaders=[
            "Content-Type",
            "Authorization"
          ]
micronaut {
  server {
    cors {
      enabled = true
      configurations {
        web {
          exposedHeaders = ["Content-Type", "Authorization"]
        }
      }
    }
  }
}
{
  micronaut {
    server {
      cors {
        enabled = true
        configurations {
          web {
            exposedHeaders = ["Content-Type", "Authorization"]
          }
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "enabled": true,
        "configurations": {
          "web": {
            "exposedHeaders": ["Content-Type", "Authorization"]
          }
        }
      }
    }
  }
}

Allow Credentials

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

示例 CORS 配置

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.allowCredentials=false
micronaut:
  server:
    cors:
      enabled: true
      configurations:
        web:
          allowCredentials: false
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      enabled=true
      [micronaut.server.cors.configurations]
        [micronaut.server.cors.configurations.web]
          allowCredentials=false
micronaut {
  server {
    cors {
      enabled = true
      configurations {
        web {
          allowCredentials = false
        }
      }
    }
  }
}
{
  micronaut {
    server {
      cors {
        enabled = true
        configurations {
          web {
            allowCredentials = false
          }
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "enabled": true,
        "configurations": {
          "web": {
            "allowCredentials": false
          }
        }
      }
    }
  }
}

Max Age

预检请求可以缓存的默认最长期限为 30 分钟。要更改该行为,请以秒为单位指定一个值。

示例 CORS 配置

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.cors.enabled=true
micronaut.server.cors.configurations.web.maxAge=3600
micronaut:
  server:
    cors:
      enabled: true
      configurations:
        web:
          maxAge: 3600 # 1 hour
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      enabled=true
      [micronaut.server.cors.configurations]
        [micronaut.server.cors.configurations.web]
          maxAge=3600
micronaut {
  server {
    cors {
      enabled = true
      configurations {
        web {
          maxAge = 3600
        }
      }
    }
  }
}
{
  micronaut {
    server {
      cors {
        enabled = true
        configurations {
          web {
            maxAge = 3600
          }
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "enabled": true,
        "configurations": {
          "web": {
            "maxAge": 3600
          }
        }
      }
    }
  }
}

Multiple Header Values

默认情况下,当一个标头有多个值时,将发送多个标头,每个标头都有一个值。通过设置配置选项,可以更改行为以发送带有逗号分隔值列表的单个标头。

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.cors.single-header=true
micronaut:
  server:
    cors:
      single-header: true
[micronaut]
  [micronaut.server]
    [micronaut.server.cors]
      single-header=true
micronaut {
  server {
    cors {
      singleHeader = true
    }
  }
}
{
  micronaut {
    server {
      cors {
        single-header = true
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "cors": {
        "single-header": true
      }
    }
  }
}

Securing the Server with HTTPS

Micronaut 开箱即用地支持 HTTPS。默认情况下 HTTPS 被禁用,所有请求都使用 HTTP 服务。要启用 HTTPS 支持,请修改您的配置。例如:

HTTPS 配置示例

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.ssl.enabled=true
micronaut.server.ssl.buildSelfSigned=true
micronaut:
  server:
    ssl:
      enabled: true
      buildSelfSigned: true
[micronaut]
  [micronaut.server]
    [micronaut.server.ssl]
      enabled=true
      buildSelfSigned=true
micronaut {
  server {
    ssl {
      enabled = true
      buildSelfSigned = true
    }
  }
}
{
  micronaut {
    server {
      ssl {
        enabled = true
        buildSelfSigned = true
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "ssl": {
        "enabled": true,
        "buildSelfSigned": true
      }
    }
  }
}
  • Micronaut 将创建一个自签名证书。

默认情况下,支持 HTTPS 的 Micronaut 在端口 8443 上启动,但您可以使用属性 micronaut.server.ssl.port 更改端口。

为了生成自签名证书,Micronaut HTTP 服务器将使用 netty。 Netty 使用两种方法之一来生成证书。

如果您使用预先生成的证书(出于安全考虑,您应该这样做),则不需要执行这些步骤。

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

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

 Gradle Maven 
implementation("org.bouncycastle:bcpkix-jdk15on")
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
</dependency>

此配置将在浏览器中生成警告。


使用有效的 x509 证书

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

$ openssl pkcs12 -export \
                 -in server.crt \ (1)
                 -inkey server.key \ (2)
                 -out server.p12 \ (3)
                 -name someAlias \ (4)
                 -chain -CAfile ca.crt -caname root
  1. 原始 server.crt 文件

  2. 原始 server.key 文件

  3. 要创建的 server.p12 文件

  4. 证书的别名

在创建 server.p12 文件期间,有必要定义一个密码,稍后在 Micronaut 中使用证书时将需要该密码。

现在修改你的配置:

HTTPS 配置示例

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.ssl.enabled=true
micronaut.ssl.key-store.path=classpath:server.p12
micronaut.ssl.key-store.password=mypassword
micronaut.ssl.key-store.type=PKCS12
micronaut:
  ssl:
    enabled: true
    key-store:
      path: classpath:server.p12
      password: mypassword
      type: PKCS12
[micronaut]
  [micronaut.ssl]
    enabled=true
    [micronaut.ssl.key-store]
      path="classpath:server.p12"
      password="mypassword"
      type="PKCS12"
micronaut {
  ssl {
    enabled = true
    keyStore {
      path = "classpath:server.p12"
      password = "mypassword"
      type = "PKCS12"
    }
  }
}
{
  micronaut {
    ssl {
      enabled = true
      key-store {
        path = "classpath:server.p12"
        password = "mypassword"
        type = "PKCS12"
      }
    }
  }
}
{
  "micronaut": {
    "ssl": {
      "enabled": true,
      "key-store": {
        "path": "classpath:server.p12",
        "password": "mypassword",
        "type": "PKCS12"
      }
    }
  }
}
  • 指定 p12 文件路径。它也可以被引用为 file:/path/to/the/file

  • 还要提供导出期间定义的密码

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


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

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

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


使用 Java 密钥库 (JKS)

不建议使用这种类型的证书,因为格式是专有的 - 首选 PKCS12 格式。不管怎样,Micronaut 也支持它。

将 p12 证书转换为 JKS 证书:

$ keytool -importkeystore \
          -deststorepass newPassword -destkeypass newPassword \ (1)
          -destkeystore server.keystore \ (2)
          -srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass mypassword \ (3)
          -alias someAlias (4)
  1. 有必要为密钥库定义密码

  2. 要创建的文件

  3. 之前创建的PKCS12文件,以及创建时定义的密码

  4. 之前使用的别名

如果 srcstorepass 或别名与 p12 文件中定义的不同,转换将失败。

现在修改你的配置:

HTTPS 配置示例

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.ssl.enabled=true
micronaut.ssl.key-store.path=classpath:server.keystore
micronaut.ssl.key-store.password=newPassword
micronaut.ssl.key-store.type=JKS
micronaut:
  ssl:
    enabled: true
    key-store:
      path: classpath:server.keystore
      password: newPassword
      type: JKS
[micronaut]
  [micronaut.ssl]
    enabled=true
    [micronaut.ssl.key-store]
      path="classpath:server.keystore"
      password="newPassword"
      type="JKS"
micronaut {
  ssl {
    enabled = true
    keyStore {
      path = "classpath:server.keystore"
      password = "newPassword"
      type = "JKS"
    }
  }
}
{
  micronaut {
    ssl {
      enabled = true
      key-store {
        path = "classpath:server.keystore"
        password = "newPassword"
        type = "JKS"
      }
    }
  }
}
{
  "micronaut": {
    "ssl": {
      "enabled": true,
      "key-store": {
        "path": "classpath:server.keystore",
        "password": "newPassword",
        "type": "JKS"
      }
    }
  }
}

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

刷新/重新加载 HTTPS 证书

在 HTTPS 证书过期后保持最新状态可能是一个挑战。一个很好的解决方案是自动证书管理环境 (ACME) 和 Micronaut ACME 模块,它支持从证书颁发机构自动刷新证书。

如果无法使用证书颁发机构并且您需要从磁盘手动更新证书,那么您应该使用 Micronaut 对应用程序事件的支持触发 RefreshEvent,其中包含定义 HTTPS 配置的密钥,Micronaut 将从磁盘重新加载证书并应用服务器的新配置。

您还可以使用刷新管理端点,但这仅适用于磁盘上证书的物理位置已更改的情况

例如,以下将从磁盘重新加载先前列出的 HTTPS 配置并将其应用于新的传入请求(例如,此代码可以运行轮询证书以获取更改的计划作业):

手动刷新 HTTPS 配置

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

@Inject ApplicationEventPublisher<RefreshEvent> eventPublisher;

...

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

启用 HTTP 和 HTTPS

Micronaut 支持绑定 HTTP 和 HTTPS。要启用双协议支持,请修改您的配置。例如:

双协议配置示例

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.ssl.enabled=true
micronaut.server.ssl.build-self-signed=true
micronaut.server.dual-protocol=true
micronaut:
  server:
    ssl:
      enabled: true
      build-self-signed: true
    dual-protocol : true
[micronaut]
  [micronaut.server]
    [micronaut.server.ssl]
      enabled=true
      build-self-signed=true
    dual-protocol=true
micronaut {
  server {
    ssl {
      enabled = true
      buildSelfSigned = true
    }
    dualProtocol = true
  }
}
{
  micronaut {
    server {
      ssl {
        enabled = true
        build-self-signed = true
      }
      dual-protocol = true
    }
  }
}
{
  "micronaut": {
    "server": {
      "ssl": {
        "enabled": true,
        "build-self-signed": true
      },
      "dual-protocol": true
    }
  }
}
  • 您必须配置 SSL 才能使 HTTPS 工作。在此示例中,我们仅使用带有构建自签名的自签名证书

  • 双协议启用 HTTP 和 HTTPS 是一个可选功能 - 设置 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 服务器启用访问记录器(这适用于 HTTP/1 和 HTTP/2)。

要启用和配置访问记录器,请在您的配置文件(例如 application.yml)中设置:

Enabling the access logger

 Properties Yaml  Toml  Groovy  Hocon  JSON 
micronaut.server.netty.access-logger.enabled=true
micronaut.server.netty.access-logger.logger-name=my-access-logger
micronaut.server.netty.access-logger.log-format=common
micronaut:
  server:
    netty:
      access-logger:
        enabled: true
        logger-name: my-access-logger
        log-format: common
[micronaut]
  [micronaut.server]
    [micronaut.server.netty]
      [micronaut.server.netty.access-logger]
        enabled=true
        logger-name="my-access-logger"
        log-format="common"
micronaut {
  server {
    netty {
      accessLogger {
        enabled = true
        loggerName = "my-access-logger"
        logFormat = "common"
      }
    }
  }
}
{
  micronaut {
    server {
      netty {
        access-logger {
          enabled = true
          logger-name = "my-access-logger"
          log-format = "common"
        }
      }
    }
  }
}
{
  "micronaut": {
    "server": {
      "netty": {
        "access-logger": {
          "enabled": true,
          "logger-name": "my-access-logger",
          "log-format": "common"
        }
      }
    }
  }
}
  • 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>

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

日志格式

语法基于 Apache httpd 日志格式。

这些是支持的标记:

  • %a - 远程IP地址

  • %A - 本地IP地址

  • %b - 发送的字节数,不包括 HTTP 标头,如果未发送任何字节,则为“-”

  • %B - 发送的字节数,不包括 HTTP 标头

  • %h - 远程主机名

  • %H - 请求协议

  • %{<header>}i - 请求标头。如果参数被省略 (%i) 所有的标题都被打印出来

  • %{<header>}o - 响应头。如果省略参数 (%o),则打印所有标题

  • %{<cookie>}C - 请求 cookie (COOKIE)。如果省略参数 (%C),则打印所有 cookie

  • %{<cookie>}c - 响应 cookie (SET_COOKIE)。如果省略参数 (%c),则打印所有 cookie

  • %l - 来自 identd 的远程逻辑用户名(始终返回“-”)

  • %m - 请求方法

  • %p - 本地端口

  • %q - 查询字符串(不包括“?”字符)

  • %r - 请求的第一行

  • %s - 响应的 HTTP 状态代码

  • %{<format>}t - 日期和时间。如果省略参数,则使用通用日志格式 ("'['dd/MMM/yyyy:HH:mm:ss Z']'")。

    • 如果格式以 begin 开头:(默认)时间是在请求处理开始时采用的。如果它以end开头:它是日志条目被写入的时间,接近请求处理的结束。

    • 格式应遵循 DateTimeFormatter 语法。

  • %{property}u - 远程认证用户。当 micronaut-session 在类路径上时,如果参数被省略则返回会话 ID,否则指定的属性会打印“-”

  • %U - 请求的 URI

  • %v - 本地服务器名称

  • %D - 处理请求所花费的时间,以毫秒为单位

  • %T - 处理请求所花费的时间,以秒为单位

此外,您可以为常见模式使用以下别名:

  • common - %h %l %u %t "%r" %s %b 通用日志格式 (CLF)
  • combined - %h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" 组合日志格式

启动二级服务器

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

这在你需要通过不同的端口和潜在的不同配置(HTTPS,线程资源等)暴露不同的服务器的情况下非常有用。

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

以编程方式创建二级服务器

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

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

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

    @Bean
    ServiceInstanceList serviceInstanceList( // (9)
            @Named(SERVER_ID) NettyEmbeddedServer nettyEmbeddedServer) {
        return new StaticServiceInstanceList(
                SERVER_ID,
                Collections.singleton(nettyEmbeddedServer.getURI())
        );
    }
}
import io.micronaut.context.annotation.Bean
import io.micronaut.context.annotation.Context
import io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Requires
import io.micronaut.context.env.Environment
import io.micronaut.core.util.StringUtils
import io.micronaut.discovery.ServiceInstanceList
import io.micronaut.discovery.StaticServiceInstanceList
import io.micronaut.http.server.netty.NettyEmbeddedServer
import io.micronaut.http.server.netty.NettyEmbeddedServerFactory
import io.micronaut.http.server.netty.configuration.NettyHttpServerConfiguration
import io.micronaut.http.ssl.ServerSslConfiguration
import jakarta.inject.Named

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

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

    @Bean
    ServiceInstanceList serviceInstanceList( // (8)
                                             @Named(SERVER_ID) NettyEmbeddedServer nettyEmbeddedServer) {
        return new StaticServiceInstanceList(
                SERVER_ID,
                [ nettyEmbeddedServer.URI ]
        )
    }
}
import io.micronaut.context.annotation.Bean
import io.micronaut.context.annotation.Context
import io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Requires
import io.micronaut.context.env.Environment
import io.micronaut.core.util.StringUtils
import io.micronaut.discovery.ServiceInstanceList
import io.micronaut.discovery.StaticServiceInstanceList
import io.micronaut.http.server.netty.NettyEmbeddedServer
import io.micronaut.http.server.netty.NettyEmbeddedServerFactory
import io.micronaut.http.server.netty.configuration.NettyHttpServerConfiguration
import io.micronaut.http.ssl.ServerSslConfiguration
import jakarta.inject.Named

@Factory
class SecondaryNettyServer {
    companion object {
        const val SERVER_ID = "another" // (1)
    }

    @Named(SERVER_ID)
    @Context
    @Bean(preDestroy = "close") // (2)
    @Requires(beans = [Environment::class])
    fun nettyEmbeddedServer(
        serverFactory: NettyEmbeddedServerFactory // (3)
    ) : NettyEmbeddedServer {
        val configuration = NettyHttpServerConfiguration() // (4)
        val sslConfiguration = ServerSslConfiguration() // (5)

        sslConfiguration.setBuildSelfSigned(true)
        sslConfiguration.isEnabled = true
        sslConfiguration.port = -1 // random port

        // configure server programmatically
        val embeddedServer = serverFactory.build(configuration, sslConfiguration) // (6)
        embeddedServer.start() // (7)
        return embeddedServer // (8)
    }

    @Bean
    fun serviceInstanceList( // (9)
        @Named(SERVER_ID) nettyEmbeddedServer: NettyEmbeddedServer
    ): ServiceInstanceList {
        return StaticServiceInstanceList(
            SERVER_ID, setOf(nettyEmbeddedServer.uri)
        )
    }
}
  1. 为服务器定义一个唯一的名称
  2. 使用服务器名称定义一个 @Context 范围的 bean,并包括 preDestroy="close" 以确保在上下文关闭时关闭服务器

  3. 将 NettyEmbeddedServerFactory 注入工厂 Bean

  4. 以编程方式创建 NettyHttpServerConfiguration

  5. 可选择创建 ServerSslConfiguration

  6. 使用build方法构建服务器实例

  7. 使用start方法启动服务器

  8. 将服务器实例作为托管 bean 返回

  9. 如果您希望通过服务器名称注入 HTTP 客户端,可选择定义 ServiceInstanceList 的实例

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

由于在步骤 8 中定义了 ServiceInstanceList,您可以将客户端注入到测试中以测试辅助服务器:

注入服务器或客户端

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

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

@Named(SecondaryNettyServer.SERVER_ID)
EmbeddedServer embeddedServer // (2)
@Inject
@field:Client(path = "/", id = SecondaryNettyServer.SERVER_ID)
lateinit var httpClient : HttpClient // (1)

@Inject
@field:Named(SecondaryNettyServer.SERVER_ID)
lateinit var embeddedServer : EmbeddedServer // (2)
  1. 使用服务器名称通过 ID 注入客户端

  2. 使用@Named 注释作为限定符来注入嵌入式服务器实例


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号