欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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)求日志輸出

場(chǎ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):簡(jiǎn)單方便
  • 缺點(diǎn):格式固定,信息量少

方案二

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

  • 優(yōu)點(diǎn):可以自定義日志格式和內(nèi)容,可以獲取body信息
  • 缺點(diǎn):返回信息需要再寫一個(gè)filter,沒(méi)有匹配到路由時(shí)無(wú)法進(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問(wèn)題

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用的是長(zhǎng)連接,netty-webflux,zuul1.0用的就是同步webmvc。

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

不能配置在同一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)文章

  • 解決Java字符串JSON轉(zhuǎn)換異常:cn.hutool.json.JSONException:?Mismatched?hr?and?body

    解決Java字符串JSON轉(zhuǎn)換異常:cn.hutool.json.JSONException:?Mismatched?

    這篇文章主要給大家介紹了關(guān)于如何解決Java字符串JSON轉(zhuǎn)換異常:cn.hutool.json.JSONException:?Mismatched?hr?and?body的相關(guān)資料,文中將解決的辦法通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • 詳解Java如何優(yōu)雅地書寫if-else

    詳解Java如何優(yōu)雅地書寫if-else

    在日常開發(fā)中我們常常遇到有多個(gè)if?else的情況,之間書寫顯得代碼冗余難看,對(duì)于追求更高質(zhì)量代碼的同學(xué),就會(huì)思考如何優(yōu)雅地處理這種代碼。本文我們就來(lái)探討下幾種優(yōu)化if?else的方法
    2022-08-08
  • 基于Hutool的圖片驗(yàn)證碼功能模塊實(shí)現(xiàn)

    基于Hutool的圖片驗(yàn)證碼功能模塊實(shí)現(xiàn)

    為了提高系統(tǒng)的安全性,防止接口被暴力刷新,驗(yàn)證碼是個(gè)好的手段,圖片驗(yàn)證碼沒(méi)有短信驗(yàn)證碼的費(fèi)用,其是個(gè)人開發(fā)者學(xué)習(xí)的重點(diǎn),這篇文章主要介紹了基于Hutool的圖片驗(yàn)證碼功能模塊實(shí)現(xiàn),需要的朋友可以參考下
    2022-10-10
  • java 中RandomAccess接口源碼分析

    java 中RandomAccess接口源碼分析

    這篇文章主要介紹了java 中RandomAccess接口源碼分析的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Java實(shí)現(xiàn)樹形菜單的方法總結(jié)

    Java實(shí)現(xiàn)樹形菜單的方法總結(jié)

    當(dāng)我們想要展示層級(jí)結(jié)構(gòu),如文件目錄、組織結(jié)構(gòu)或分類目錄時(shí),樹形菜單是一個(gè)直觀且有效的解決方案,本文為大家整理了java中幾種常見(jiàn)方法,希望對(duì)大家有所幫助
    2023-08-08
  • 詳解SpringBoot如何實(shí)現(xiàn)統(tǒng)一后端返回格式

    詳解SpringBoot如何實(shí)現(xiàn)統(tǒng)一后端返回格式

    在前后端分離的項(xiàng)目中后端返回的格式一定要友好,不然會(huì)對(duì)前端的開發(fā)人員帶來(lái)很多的工作量。那么SpringBoot如何做到統(tǒng)一的后端返回格式呢?本文將為大家詳細(xì)講講
    2022-04-04
  • Java轉(zhuǎn)換流(InputStreamReader/OutputStreamWriter)的使用

    Java轉(zhuǎn)換流(InputStreamReader/OutputStreamWriter)的使用

    本文主要介紹了Java轉(zhuǎn)換流(InputStreamReader/OutputStreamWriter)的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • mybatis 如何返回list<String>類型數(shù)據(jù)

    mybatis 如何返回list<String>類型數(shù)據(jù)

    這篇文章主要介紹了mybatis 如何返回list<String>類型數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 挑戰(zhàn)4道Java試題

    挑戰(zhàn)4道Java試題

    這篇文章主要為大家分享了4道Java基礎(chǔ)題,幫助大家鞏固基礎(chǔ)知識(shí),夯實(shí)java基礎(chǔ)技能,感興趣的朋友快點(diǎn)挑戰(zhàn)
    2015-12-12
  • Springboot mybatis plus druid多數(shù)據(jù)源解決方案 dynamic-datasource的使用詳解

    Springboot mybatis plus druid多數(shù)據(jù)源解決方案 dynamic-datasource的使用詳

    這篇文章主要介紹了Springboot mybatis plus druid多數(shù)據(jù)源解決方案 dynamic-datasource的使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11

最新評(píng)論