gateway、webflux、reactor-netty请求日志输出的方式是什么

gateway、webflux、reactor-netty请求日志输出的方式是什么

本篇内容介绍了“gateway、webflux、reactor-netty请求日志输出的方式是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

gateway、webflux、reactor-netty请求日志输出

场景

在使用spring cloud gateway时想要输出请求日志,考虑到两种实现方案

方案一

官网中使用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

  • 优点:简单方便

  • 缺点:格式固定,信息量少

方案二

创建一个logfilter,在logfilter中解析request,并输出请求信息

  • 优点:可以自定义日志格式和内容,可以获取body信息

  • 缺点:返回信息需要再写一个filter,没有匹配到路由时无法进入到logfilter中

思路

对方案一进行改造,使其满足需求。对reactor-netty源码分析,主要涉及

  • AccessLog:日志工具,日志结构体

  • AccessLogHandler:http1.1协议日志控制,我们主要使用这个。

  • AccessLogHandler2:http2协议日志控制

代码如下:

packagereactor.netty.http.server;importreactor.util.Logger;importreactor.util.Loggers;importjava.time.ZonedDateTime;importjava.time.format.DateTimeFormatter;importjava.util.Locale;importjava.util.Objects;finalclassAccessLog{staticfinalLoggerlog=Loggers.getLogger("reactor.netty.http.server.AccessLog");staticfinalDateTimeFormatterDATE_TIME_FORMATTER=DateTimeFormatter.ofPattern("dd/MMM/yyyy:HH:mm:ssZ",Locale.US);staticfinalStringCOMMON_LOG_FORMAT="{}-{}[{}]\"{}{}{}\"{}{}{}{}ms";staticfinalStringMISSING="-";finalStringzonedDateTime;Stringaddress;CharSequencemethod;CharSequenceuri;Stringprotocol;Stringuser=MISSING;CharSequencestatus;longcontentLength;booleanchunked;longstartTime=System.currentTimeMillis();intport;AccessLog(){this.zonedDateTime=ZonedDateTime.now().format(DATE_TIME_FORMATTER);}AccessLogaddress(Stringaddress){this.address=Objects.requireNonNull(address,"address");returnthis;}AccessLogport(intport){this.port=port;returnthis;}AccessLogmethod(CharSequencemethod){this.method=Objects.requireNonNull(method,"method");returnthis;}AccessLoguri(CharSequenceuri){this.uri=Objects.requireNonNull(uri,"uri");returnthis;}AccessLogprotocol(Stringprotocol){this.protocol=Objects.requireNonNull(protocol,"protocol");returnthis;}AccessLogstatus(CharSequencestatus){this.status=Objects.requireNonNull(status,"status");returnthis;}AccessLogcontentLength(longcontentLength){this.contentLength=contentLength;returnthis;}AccessLogincreaseContentLength(longcontentLength){if(chunked){this.contentLength+=contentLength;}returnthis;}AccessLogchunked(booleanchunked){this.chunked=chunked;returnthis;}longduration(){returnSystem.currentTimeMillis()-startTime;}voidlog(){if(log.isInfoEnabled()){log.info(COMMON_LOG_FORMAT,address,user,zonedDateTime,method,uri,protocol,status,(contentLength>-1?contentLength:MISSING),port,duration());}}}

  • AccessLogHandler:日志控制

packagereactor.netty.http.server;importio.netty.buffer.ByteBuf;importio.netty.buffer.ByteBufHolder;importio.netty.channel.ChannelDuplexHandler;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelPromise;importio.netty.channel.socket.SocketChannel;importio.netty.handler.codec.http.HttpRequest;importio.netty.handler.codec.http.HttpResponse;importio.netty.handler.codec.http.HttpResponseStatus;importio.netty.handler.codec.http.HttpUtil;importio.netty.handler.codec.http.LastHttpContent;/***@authorVioletaGeorgieva*/finalclassAccessLogHandlerextendsChannelDuplexHandler{AccessLogaccessLog=newAccessLog();@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg){if(msginstanceofHttpRequest){finalHttpRequestrequest=(HttpRequest)msg;finalSocketChannelchannel=(SocketChannel)ctx.channel();accessLog=newAccessLog().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")publicvoidwrite(ChannelHandlerContextctx,Objectmsg,ChannelPromisepromise){if(msginstanceofHttpResponse){finalHttpResponseresponse=(HttpResponse)msg;finalHttpResponseStatusstatus=response.status();if(status.equals(HttpResponseStatus.CONTINUE)){//"FutureReturnValueIgnored"thisisdeliberatectx.write(msg,promise);return;}finalbooleanchunked=HttpUtil.isTransferEncodingChunked(response);accessLog.status(status.codeAsText()).chunked(chunked);if(!chunked){accessLog.contentLength(HttpUtil.getContentLength(response,-1));}}if(msginstanceofLastHttpContent){accessLog.increaseContentLength(((LastHttpContent)msg).content().readableBytes());ctx.write(msg,promise.unvoid()).addListener(future->{if(future.isSuccess()){accessLog.log();}});return;}if(msginstanceofByteBuf){accessLog.increaseContentLength(((ByteBuf)msg).readableBytes());}if(msginstanceofByteBufHolder){accessLog.increaseContentLength(((ByteBufHolder)msg).content().readableBytes());}//"FutureReturnValueIgnored"thisisdeliberatectx.write(msg,promise);}}

执行顺序

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

解决方案

对AccessLog和AccessLogHandler进行重写,输出自己想要的内容和样式。

AccessLogHandler中重写了ChannelDuplexHandler中的channelRead和write方法,还可以对ChannelInboundHandler和ChannelOutboundHandler中的方法进行重写,覆盖请求的整个生命周期。

spring-webflux、gateway、springboot-start-web问题

Spring-webflux

当两者一起时配置的并不是webflux web application, 仍然时一个spring mvc web application。

官方文档中有这么一段注解:

很多开发者添加spring-boot-start-webflux到他们的spring mvc web applicaiton去是为了使用reactive WebClient. 如果希望更改webApplication 类型需要显示的设置,如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>

结论一:

当两者一起时配置的并不是webflux web application, 仍然时一个spring mvc web application。但是启动不会报错,可以正常使用,但是webflux功能失效

Spring-gateway

因为gateway和zuul不一样,gateway用的是长连接,netty-webflux,zuul1.0用的就是同步webmvc。

所以你的非gateway子项目启动用的是webmvc,你的gateway启动用的是webflux. spring-boot-start-web和spring-boot-start-webflux相见分外眼红。

不能配置在同一pom.xml,或者不能在同一项目中出现,不然就会启动报错

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

结论二:

当spring-cloud-gateway和spring-boot-starer-web两者一起时配置的时候, 启动直接报错,依赖包冲突不兼容

“gateway、webflux、reactor-netty请求日志输出的方式是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注恰卡编程网网站,小编将为大家输出更多高质量的实用文章!

发布于 2022-03-09 22:48:43
收藏
分享
海报
0 条评论
22
上一篇:Python数据结构与算法中的栈怎么构建 下一篇:springboot怎么集成@DS注解实现数据源切换
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码