最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

gateway、webflux、reactor-netty請(qǐng)求日志輸出方式

 更新時(shí)間:2022年03月09日 14:40:59   作者:lizz666  
這篇文章主要介紹了gateway、webflux、reactor-netty請(qǐng)求日志輸出方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

gateway、webflux、reactor-netty請(qǐng)求日志輸出

場景

在使用spring cloud gateway時(shí)想要輸出請(qǐng)求日志,考慮到兩種實(shí)現(xiàn)方案

方案一

官網(wǎng)中使用Reactor Netty Access Logs方案,配置“-Dreactor.netty.http.server.accessLogEnabled=true”開啟日志記錄。

輸出如下:

reactor.netty.http.server.AccessLog      :
10.2.20.177 - - [02/Dec/2020:16:41:57 +0800] "GET /fapi/gw/hi/login HTTP/1.1" 200 319 8080 626 ms

  • 優(yōu)點(diǎn):簡單方便
  • 缺點(diǎn):格式固定,信息量少

方案二

創(chuàng)建一個(gè)logfilter,在logfilter中解析request,并輸出請(qǐng)求信息

  • 優(yōu)點(diǎn):可以自定義日志格式和內(nèi)容,可以獲取body信息
  • 缺點(diǎn):返回信息需要再寫一個(gè)filter,沒有匹配到路由時(shí)無法進(jìn)入到logfilter中

思路

對(duì)方案一進(jìn)行改造,使其滿足需求。對(duì)reactor-netty源碼分析,主要涉及

  • AccessLog:日志工具,日志結(jié)構(gòu)體
  • AccessLogHandler:http1.1協(xié)議日志控制,我們主要使用這個(gè)。
  • AccessLogHandler2:http2協(xié)議日志控制

代碼如下:

package reactor.netty.http.server;?
import reactor.util.Logger;
import reactor.util.Loggers;
?
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Objects;
?
final class AccessLog {
?? ?static final Logger log = Loggers.getLogger("reactor.netty.http.server.AccessLog");
?? ?static final DateTimeFormatter DATE_TIME_FORMATTER =
?? ??? ??? ?DateTimeFormatter.ofPattern("dd/MMM/yyyy:HH:mm:ss Z", Locale.US);
?? ?static final String COMMON_LOG_FORMAT =
?? ??? ??? ?"{} - {} [{}] \"{} {} {}\" {} {} {} {} ms";
?? ?static final String MISSING = "-";?
?? ?final String zonedDateTime;
?
?? ?String address;
?? ?CharSequence method;
?? ?CharSequence uri;
?? ?String protocol;
?? ?String user = MISSING;
?? ?CharSequence status;
?? ?long contentLength;
?? ?boolean chunked;
?? ?long startTime = System.currentTimeMillis();
?? ?int port;
?
?? ?AccessLog() {
?? ??? ?this.zonedDateTime = ZonedDateTime.now().format(DATE_TIME_FORMATTER);
?? ?}
?
?? ?AccessLog address(String address) {
?? ??? ?this.address = Objects.requireNonNull(address, "address");
?? ??? ?return this;
?? ?}
?
?? ?AccessLog port(int port) {
?? ??? ?this.port = port;
?? ??? ?return this;
?? ?}
?
?? ?AccessLog method(CharSequence method) {
?? ??? ?this.method = Objects.requireNonNull(method, "method");
?? ??? ?return this;
?? ?}
?
?? ?AccessLog uri(CharSequence uri) {
?? ??? ?this.uri = Objects.requireNonNull(uri, "uri");
?? ??? ?return this;
?? ?}
?
?? ?AccessLog protocol(String protocol) {
?? ??? ?this.protocol = Objects.requireNonNull(protocol, "protocol");
?? ??? ?return this;
?? ?}
?
?? ?AccessLog status(CharSequence status) {
?? ??? ?this.status = Objects.requireNonNull(status, "status");
?? ??? ?return this;
?? ?}
?
?? ?AccessLog contentLength(long contentLength) {
?? ??? ?this.contentLength = contentLength;
?? ??? ?return this;
?? ?}
?
?? ?AccessLog increaseContentLength(long contentLength) {
?? ??? ?if (chunked) {
?? ??? ??? ?this.contentLength += contentLength;
?? ??? ?}
?? ??? ?return this;
?? ?}
?
?? ?AccessLog chunked(boolean chunked) {
?? ??? ?this.chunked = chunked;
?? ??? ?return this;
?? ?}
?
?? ?long duration() {
?? ??? ?return System.currentTimeMillis() - startTime;
?? ?}
?
?? ?void log() {
?? ??? ?if (log.isInfoEnabled()) {
?? ??? ??? ?log.info(COMMON_LOG_FORMAT, address, user, zonedDateTime,
?? ??? ??? ??? ??? ?method, uri, protocol, status, (contentLength > -1 ? contentLength : MISSING), port, duration());
?? ??? ?}
?? ?}
}
  • AccessLogHandler:日志控制
package reactor.netty.http.server;?
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.LastHttpContent;
?
/**
?* @author Violeta Georgieva
?*/
final class AccessLogHandler extends ChannelDuplexHandler {
?
?? ?AccessLog accessLog = new AccessLog();
?
?? ?@Override
?? ?public void channelRead(ChannelHandlerContext ctx, Object msg) {
?? ??? ?if (msg instanceof HttpRequest) {
?? ??? ??? ?final HttpRequest request = (HttpRequest) msg;
?? ??? ??? ?final SocketChannel channel = (SocketChannel) ctx.channel();
?
?? ??? ??? ?accessLog = new AccessLog()
?? ??? ??? ? ? ? ? ?.address(channel.remoteAddress().getHostString())
?? ??? ??? ? ? ? ? ?.port(channel.localAddress().getPort())
?? ??? ??? ? ? ? ? ?.method(request.method().name())
?? ??? ??? ? ? ? ? ?.uri(request.uri())
?? ??? ??? ? ? ? ? ?.protocol(request.protocolVersion().text());
?? ??? ?}
?? ??? ?ctx.fireChannelRead(msg);
?? ?}
?
?? ?@Override
?? ?@SuppressWarnings("FutureReturnValueIgnored")
?? ?public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
?? ??? ?if (msg instanceof HttpResponse) {
?? ??? ??? ?final HttpResponse response = (HttpResponse) msg;
?? ??? ??? ?final HttpResponseStatus status = response.status();
?
?? ??? ??? ?if (status.equals(HttpResponseStatus.CONTINUE)) {
?? ??? ??? ??? ?//"FutureReturnValueIgnored" this is deliberate
?? ??? ??? ??? ?ctx.write(msg, promise);
?? ??? ??? ??? ?return;
?? ??? ??? ?}
?
?? ??? ??? ?final boolean chunked = HttpUtil.isTransferEncodingChunked(response);
?? ??? ??? ?accessLog.status(status.codeAsText())
?? ??? ??? ? ? ? ? ? .chunked(chunked);
?? ??? ??? ?if (!chunked) {
?? ??? ??? ??? ?accessLog.contentLength(HttpUtil.getContentLength(response, -1));
?? ??? ??? ?}
?? ??? ?}
?? ??? ?if (msg instanceof LastHttpContent) {
?? ??? ??? ?accessLog.increaseContentLength(((LastHttpContent) msg).content().readableBytes());
?? ??? ??? ?ctx.write(msg, promise.unvoid())
?? ??? ??? ? ? .addListener(future -> {
?? ??? ??? ? ? ? ? if (future.isSuccess()) {
?? ??? ??? ? ? ? ? ? ? accessLog.log();
?? ??? ??? ? ? ? ? }
?? ??? ??? ? ? });
?? ??? ??? ?return;
?? ??? ?}
?? ??? ?if (msg instanceof ByteBuf) {
?? ??? ??? ?accessLog.increaseContentLength(((ByteBuf) msg).readableBytes());
?? ??? ?}
?? ??? ?if (msg instanceof ByteBufHolder) {
?? ??? ??? ?accessLog.increaseContentLength(((ByteBufHolder) msg).content().readableBytes());
?? ??? ?}
?? ??? ?//"FutureReturnValueIgnored" this is deliberate
?? ??? ?ctx.write(msg, promise);
?? ?}
}

執(zhí)行順序

AccessLogHandler.channelRead > GlobalFilter.filter > AbstractLoadBalance.choose >response.writeWith >AccessLogHandler.write

解決方案

對(duì)AccessLog和AccessLogHandler進(jìn)行重寫,輸出自己想要的內(nèi)容和樣式。

AccessLogHandler中重寫了ChannelDuplexHandler中的channelRead和write方法,還可以對(duì)ChannelInboundHandler和ChannelOutboundHandler中的方法進(jìn)行重寫,覆蓋請(qǐng)求的整個(gè)生命周期。

spring-webflux、gateway、springboot-start-web問題

Spring-webflux

當(dāng)兩者一起時(shí)配置的并不是webflux web application, 仍然時(shí)一個(gè)spring mvc web application。

官方文檔中有這么一段注解:

很多開發(fā)者添加spring-boot-start-webflux到他們的spring mvc web applicaiton去是為了使用reactive WebClient. 如果希望更改webApplication 類型需要顯示的設(shè)置,如SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE).

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

結(jié)論一:

當(dāng)兩者一起時(shí)配置的并不是webflux web application, 仍然時(shí)一個(gè)spring mvc web application。但是啟動(dòng)不會(huì)報(bào)錯(cuò),可以正常使用,但是webflux功能失效

Spring-gateway

因?yàn)間ateway和zuul不一樣,gateway用的是長連接,netty-webflux,zuul1.0用的就是同步webmvc。

所以你的非gateway子項(xiàng)目啟動(dòng)用的是webmvc,你的gateway啟動(dòng)用的是webflux. spring-boot-start-web和spring-boot-start-webflux相見分外眼紅。

不能配置在同一pom.xml,或者不能在同一項(xiàng)目中出現(xiàn),不然就會(huì)啟動(dòng)報(bào)錯(cuò)

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

結(jié)論二:

當(dāng)spring-cloud-gateway和spring-boot-starer-web兩者一起時(shí)配置的時(shí)候, 啟動(dòng)直接報(bào)錯(cuò),依賴包沖突不兼容

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Springboot上傳excel并將表格數(shù)據(jù)導(dǎo)入或更新mySql數(shù)據(jù)庫的過程

    Springboot上傳excel并將表格數(shù)據(jù)導(dǎo)入或更新mySql數(shù)據(jù)庫的過程

    這篇文章主要介紹了Springboot上傳excel并將表格數(shù)據(jù)導(dǎo)入或更新mySql數(shù)據(jù)庫的過程 ,本文以Controller開始,從導(dǎo)入過程開始講述,其中包括字典表的轉(zhuǎn)換,需要的朋友可以參考下
    2018-04-04
  • java轉(zhuǎn)換字符串編碼格式的方法

    java轉(zhuǎn)換字符串編碼格式的方法

    這篇文章主要介紹了java轉(zhuǎn)換字符串編碼格式的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • Spring的編程式事務(wù)TransactionTemplate的用法詳解

    Spring的編程式事務(wù)TransactionTemplate的用法詳解

    TransactionTemplate提供了一種在代碼中進(jìn)行編程式事務(wù)管理的方式,使開發(fā)人員能夠在方法級(jí)別定義事務(wù)的開始和結(jié)束點(diǎn),本文介紹了Spring框架中TransactionTemplate的用法,感興趣的朋友跟隨小編一起看看吧
    2023-07-07
  • JavaEE微框架Spring Boot深入解讀

    JavaEE微框架Spring Boot深入解讀

    spring框架作為JavaEE框架領(lǐng)域的一款重要的開源框架,在企業(yè)應(yīng)用開發(fā)中有著很重要的作用。接下來通過本文給大家介紹JavaEE微框架Spring Boot深入解讀,需要的朋友可以參考下
    2017-04-04
  • Spring?Boot中的@EnableAutoConfiguration注解詳解

    Spring?Boot中的@EnableAutoConfiguration注解詳解

    這篇文章主要介紹了Spring?Boot中的@EnableAutoConfiguration注解詳解,Spring?Boot是一個(gè)非常流行的Java框架,它可以快速創(chuàng)建基于Spring的應(yīng)用程序。Spring?Boot提供了許多自動(dòng)配置功能,使得開發(fā)者可以非常容易地創(chuàng)建一個(gè)可運(yùn)行的應(yīng)用程序,需要的朋友可以參考下
    2023-08-08
  • JAVA多線程間通訊常用實(shí)現(xiàn)方法解析

    JAVA多線程間通訊常用實(shí)現(xiàn)方法解析

    這篇文章主要介紹了JAVA多線程間通訊常用實(shí)現(xiàn)方法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • rabbitmq basicReject/basicNack/basicRecover的區(qū)別及說明

    rabbitmq basicReject/basicNack/basicRecover的區(qū)別及說明

    這篇文章主要介紹了rabbitmq basicReject/basicNack/basicRecover的區(qū)別及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 詳解Java中的final關(guān)鍵字

    詳解Java中的final關(guān)鍵字

    子類可以在父類的基礎(chǔ)上改寫父類內(nèi)容,為了避免這種隨意改寫的情況,Java提供了final 關(guān)鍵字,用于修飾不可改變內(nèi)容。本文就來詳細(xì)說說final關(guān)鍵字的使用,需要的可以參考一下
    2022-10-10
  • Springboot項(xiàng)目啟動(dòng)不加載resources目錄下的文件問題

    Springboot項(xiàng)目啟動(dòng)不加載resources目錄下的文件問題

    這篇文章主要介紹了Springboot項(xiàng)目啟動(dòng)不加載resources目錄下的文件問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • SpringBoot如何正確配置并運(yùn)行Kafka

    SpringBoot如何正確配置并運(yùn)行Kafka

    這篇文章主要介紹了SpringBoot如何正確配置并運(yùn)行Kafka問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07

最新評(píng)論

永新县| 新和县| 香港| 南岸区| 桦甸市| 双柏县| 定远县| 城固县| 延边| 东乡族自治县| 西畴县| 泰来县| 东至县| 义马市| 宝清县| 临江市| 康平县| 黔西| 石渠县| 巫溪县| 泉州市| 交城县| 台东县| 深水埗区| 克什克腾旗| 茌平县| 通化市| 大竹县| 怀宁县| 教育| 湖北省| 左权县| 遂宁市| 许昌市| 策勒县| 新余市| 武山县| 三台县| 中阳县| 闽侯县| 阿鲁科尔沁旗|