日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

Spring Cloud gateway 網(wǎng)關(guān)如何攔截Post請求日志

瀏覽:17日期:2023-07-02 13:19:28

gateway版本是 2.0.1

1.pom結(jié)構(gòu)

(部分內(nèi)部項(xiàng)目依賴已經(jīng)隱藏)

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--監(jiān)控相關(guān)--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- redis --><!--<dependency>--> <!--<groupId>org.springframework.boot</groupId>--> <!--<artifactId>spring-boot-starter-data-redis</artifactId>--><!--</dependency>--><!-- test-scope --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency><dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.11</version></dependency><dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.11</version></dependency><dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version></dependency><!--第三方的jdbctemplatetool--><dependency> <groupId>org.crazycake</groupId> <artifactId>jdbctemplatetool</artifactId> <version>1.0.4-RELEASE</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><!-- alibaba start --><dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId></dependency>2.表結(jié)構(gòu)

CREATE TABLE `zc_log_notes` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT ’日志信息記錄表主鍵id’, `notes` varchar(255) DEFAULT NULL COMMENT ’操作記錄信息’, `amenu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT ’一級(jí)菜單’, `bmenu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT ’二級(jí)菜單’, `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT ’操作人ip地址,先用varchar存’, `params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT ’請求值’, `response` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT ’返回值’, `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ’操作時(shí)間’, `create_user` int(11) DEFAULT NULL COMMENT ’操作人id’, `end_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ’響應(yīng)時(shí)間’, `status` int(1) NOT NULL DEFAULT ’1’ COMMENT ’響應(yīng)結(jié)果1成功0失敗’, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT=’日志信息記錄表’;3.實(shí)體結(jié)構(gòu)

@Table(catalog = 'zhiche', name = 'zc_log_notes')public class LogNotes { /** * 日志信息記錄表主鍵id */ private Integer id; /** * 操作記錄信息 */ private String notes; /** * 一級(jí)菜單 */ private String amenu; /** * 二級(jí)菜單 */ private String bmenu; /** * 操作人ip地址,先用varchar存 */ private String ip; /** * 請求參數(shù)記錄 */ private String params; /** * 返回結(jié)果記錄 */ private String response; /** * 操作時(shí)間 */ private Date createTime; /** * 操作人id */ private Integer createUser; /** * 響應(yīng)時(shí)間 */ private Date endTime; /** * 響應(yīng)結(jié)果1成功0失敗 */ private Integer status; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Integer getId() {return id; } public void setId(Integer id) {this.id = id; } public String getNotes() {return notes; } public void setNotes(String notes) {this.notes = notes; } public String getAmenu() {return amenu; } public void setAmenu(String amenu) {this.amenu = amenu; } public String getBmenu() {return bmenu; } public void setBmenu(String bmenu) {this.bmenu = bmenu; } public String getIp() {return ip; } public void setIp(String ip) {this.ip = ip; } public Date getCreateTime() {return createTime; } public void setCreateTime(Date createTime) {this.createTime = createTime; } public Integer getCreateUser() {return createUser; } public void setCreateUser(Integer createUser) {this.createUser = createUser; } public Date getEndTime() {return endTime; } public void setEndTime(Date endTime) {this.endTime = endTime; } public Integer getStatus() {return status; } public void setStatus(Integer status) {this.status = status; } public String getParams() {return params; } public void setParams(String params) {this.params = params; } public String getResponse() {return response; } public void setResponse(String response) { this.response = response; } public void setAppendResponse(String response){if (StringUtils.isNoneBlank(this.response)) { this.response = this.response + response;} else { this.response = response;} }}4.dao層和Service層省略..5.filter代碼1. RequestRecorderGlobalFilter 實(shí)現(xiàn)了GlobalFilter和Order

package com.zc.gateway.filter;import com.zc.entity.LogNotes;import com.zc.gateway.service.FilterService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.core.Ordered;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.core.io.buffer.DataBufferUtils;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpMethod;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.lang.Nullable;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;import java.net.URI;import java.nio.CharBuffer;import java.nio.charset.Charset;import java.nio.charset.StandardCharsets;/** * @author qiwenshuai * @note 目前只記錄了request方式為POST請求的方式 * @since 19-5-16 17:29 by jdk 1.8 */@Componentpublic class RequestRecorderGlobalFilter implements GlobalFilter, Ordered { @Autowired FilterService filterService; private Logger logger = LoggerFactory.getLogger(RequestRecorderGlobalFilter.class); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest originalRequest = exchange.getRequest();URI originalRequestUrl = originalRequest.getURI();//只記錄http的請求String scheme = originalRequestUrl.getScheme();if ((!'http'.equals(scheme) && !'https'.equals(scheme))) { return chain.filter(exchange);}//這是我要打印的log-StringBuilderStringBuilder logbuilder = new StringBuilder();//我自己的log實(shí)體LogNotes logNotes = new LogNotes();// 返回解碼RecorderServerHttpResponseDecorator response = new RecorderServerHttpResponseDecorator(exchange.getResponse(), logNotes, filterService);//請求解碼RecorderServerHttpRequestDecorator recorderServerHttpRequestDecorator = new RecorderServerHttpRequestDecorator(exchange.getRequest());//增加過濾攔截吧ServerWebExchange ex = exchange.mutate().request(recorderServerHttpRequestDecorator).response(response).build();// 觀察者模式 打印一下請求log// 這里可以在 配置文件中我進(jìn)行配置//if (logger.isDebugEnabled()) {response.beforeCommit(() -> Mono.defer(() -> printLog(logbuilder, response)));//}return recorderOriginalRequest(logbuilder, ex, logNotes).then(chain.filter(ex)).then(); } private Mono<Void> recorderOriginalRequest(StringBuilder logBuffer, ServerWebExchange exchange, LogNotes logNotes) {logBuffer.append(System.currentTimeMillis()).append('------------');ServerHttpRequest request = exchange.getRequest();Mono<Void> result = recorderRequest(request, logBuffer.append('n原始請求:n'), logNotes);try { filterService.addLog(logNotes);} catch (Exception e) { logger.error('保存請求參數(shù)出現(xiàn)錯(cuò)誤, e->{}', e.getMessage());}return result; } /** * 記錄原始請求邏輯 */ private Mono<Void> recorderRequest(ServerHttpRequest request, StringBuilder logBuffer, LogNotes logNotes) {URI uri = request.getURI();HttpMethod method = request.getMethod();HttpHeaders headers = request.getHeaders();logNotes.setIp(headers.getHost().getHostString());logNotes.setAmenu('一級(jí)菜單');logNotes.setBmenu('二級(jí)菜單');logNotes.setNotes('操作記錄');logBuffer.append(method.toString()).append(’ ’).append(uri.toString()).append(’n’);logBuffer.append('------------請求頭------------n');headers.forEach((name, values) -> { values.forEach(value -> {logBuffer.append(name).append(':').append(value).append(’n’); });});Charset bodyCharset = null;if (hasBody(method)) { long length = headers.getContentLength(); if (length <= 0) {logBuffer.append('------------無body------------n'); } else {logBuffer.append('------------body 長度:').append(length).append(' contentType:');MediaType contentType = headers.getContentType();if (contentType == null) { logBuffer.append('null,不記錄body------------n');} else if (!shouldRecordBody(contentType)) { logBuffer.append(contentType.toString()).append(',不記錄body------------n');} else { bodyCharset = getMediaTypeCharset(contentType); logBuffer.append(contentType.toString()).append('------------n');} }}if (bodyCharset != null) { return doRecordReqBody(logBuffer, request.getBody(), bodyCharset, logNotes) .then(Mono.defer(() -> {logBuffer.append('n------------ end ------------nn');return Mono.empty(); }));} else { logBuffer.append('------------ end ------------nn'); return Mono.empty();} } //日志輸出返回值 private Mono<Void> printLog(StringBuilder logBuilder, ServerHttpResponse response) {HttpStatus statusCode = response.getStatusCode();assert statusCode != null;logBuilder.append('響應(yīng):').append(statusCode.value()).append(' ').append(statusCode.getReasonPhrase()).append(’n’);HttpHeaders headers = response.getHeaders();logBuilder.append('------------響應(yīng)頭------------n');headers.forEach((name, values) -> { values.forEach(value -> {logBuilder.append(name).append(':').append(value).append(’n’); });});logBuilder.append('n------------ end at ').append(System.currentTimeMillis()).append('------------nn');logger.info(logBuilder.toString());return Mono.empty(); } // @Override public int getOrder() {//在GatewayFilter之前執(zhí)行return -1; } private boolean hasBody(HttpMethod method) {//只記錄這3種謂詞的body//if (method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.PATCH)return true;//return false; } //記錄簡單的常見的文本類型的request的body和response的body private boolean shouldRecordBody(MediaType contentType) {String type = contentType.getType();String subType = contentType.getSubtype();if ('application'.equals(type)) { return 'json'.equals(subType) || 'x-www-form-urlencoded'.equals(subType) || 'xml'.equals(subType) || 'atom+xml'.equals(subType) || 'rss+xml'.equals(subType);} else if ('text'.equals(type)) { return true;}//暫時(shí)不記錄formreturn false; } // 獲取請求的參數(shù) private Mono<Void> doRecordReqBody(StringBuilder logBuffer, Flux<DataBuffer> body, Charset charset, LogNotes logNotes) {return DataBufferUtils.join(body).doOnNext(buffer -> { CharBuffer charBuffer = charset.decode(buffer.asByteBuffer()); //記錄我實(shí)體的請求體 logNotes.setParams(charBuffer.toString()); logBuffer.append(charBuffer.toString()); DataBufferUtils.release(buffer);}).then(); } private Charset getMediaTypeCharset(@Nullable MediaType mediaType) {if (mediaType != null && mediaType.getCharset() != null) { return mediaType.getCharset();} else { return StandardCharsets.UTF_8;} }}2.RecorderServerHttpRequestDecorator 繼承了ServerHttpRequestDecorator

package com.zc.gateway.filter;import com.zc.entity.LogNotes;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpRequestDecorator;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;import java.util.LinkedList;import java.util.List;/** * @author qiwenshuai * @note * @since 19-5-16 17:30 by jdk 1.8 */// requestpublic class RecorderServerHttpRequestDecorator extends ServerHttpRequestDecorator { private final List<DataBuffer> dataBuffers = new LinkedList<>(); private boolean bufferCached = false; private Mono<Void> progress = null; public RecorderServerHttpRequestDecorator(ServerHttpRequest delegate) {super(delegate); }//重寫request請求體 @Override public Flux<DataBuffer> getBody() {synchronized (dataBuffers) { if (bufferCached)return copy(); if (progress == null) {progress = cache(); } return progress.thenMany(Flux.defer(this::copy));} } private Flux<DataBuffer> copy() {return Flux.fromIterable(dataBuffers).map(buf -> buf.factory().wrap(buf.asByteBuffer())); } private Mono<Void> cache() {return super.getBody().map(dataBuffers::add).then(Mono.defer(()-> { bufferCached = true; progress = null; return Mono.empty();})); }}3.RecorderServerHttpResponseDecorator 繼承了 ServerHttpResponseDecorator

package com.zc.gateway.filter;import com.zc.entity.LogNotes;import com.zc.gateway.service.FilterService;import org.reactivestreams.Publisher;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.io.buffer.DataBufferFactory;import org.springframework.core.io.buffer.DataBufferUtils;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.http.server.reactive.ServerHttpResponseDecorator;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;import org.springframework.core.io.buffer.DataBuffer;import java.nio.charset.Charset;import java.util.LinkedList;import java.util.List;/** * @author qiwenshuai * @note * @since 19-5-16 17:32 by jdk 1.8 */public class RecorderServerHttpResponseDecorator extends ServerHttpResponseDecorator { private Logger logger = LoggerFactory.getLogger(RecorderServerHttpResponseDecorator.class); private LogNotes logNotes; private FilterService filterService; RecorderServerHttpResponseDecorator(ServerHttpResponse delegate, LogNotes logNotes, FilterService filterService) {super(delegate);this.logNotes = logNotes;this.filterService = filterService; } /** * 基于netty,我這里需要顯示的釋放一次dataBuffer,但是slice出來的byte是不需要釋放的, * 與下層共享一個(gè)字符串緩沖池,gateway過濾器使用的是nettyWrite類,會(huì)發(fā)生response數(shù)據(jù)多次才能返回完全。 * 在 ServerHttpResponseDecorator 之后會(huì)釋放掉另外一個(gè)refCount. */ @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {DataBufferFactory bufferFactory = this.bufferFactory();if (body instanceof Flux) { Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body; Publisher<? extends DataBuffer> re = fluxBody.map(dataBuffer -> {// probably should reuse buffersbyte[] content = new byte[dataBuffer.readableByteCount()];// 數(shù)據(jù)讀入數(shù)組dataBuffer.read(content);// 釋放掉內(nèi)存DataBufferUtils.release(dataBuffer);// 記錄返回值String s = new String(content, Charset.forName('UTF-8'));logNotes.setAppendResponse(s);try { filterService.updateLog(logNotes);} catch (Exception e) { logger.error('Response值修改日志記錄出現(xiàn)錯(cuò)誤->{}', e);}byte[] uppedContent = new String(content, Charset.forName('UTF-8')).getBytes();return bufferFactory.wrap(uppedContent); }); return super.writeWith(re);}return super.writeWith(body); } @Override public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {return writeWith(Flux.from(body).flatMapSequential(p -> p)); }} 注意:

網(wǎng)關(guān)過濾返回值 底層用到了Netty服務(wù),在response返回的時(shí)候,有時(shí)候會(huì)寫的數(shù)據(jù)是不全的,于是我在實(shí)體類中新增了一個(gè)setAppendResponse方法進(jìn)行拼接, 再者,gateway的過濾器是鏈?zhǔn)浇Y(jié)構(gòu),需要定義order排序?yàn)樽钕?-1),然后和預(yù)置的gateway過濾器做一個(gè)combine.

代碼中用到的 dataBuffer 結(jié)構(gòu),底層其實(shí)也是類似netty的byteBuffer,用到了字節(jié)數(shù)組池,同時(shí)也用到了 引用計(jì)數(shù)器 (refInt).

為了讓jvm在gc的時(shí)候垃圾得到回收,避免內(nèi)存泄露,我們需要在轉(zhuǎn)換字節(jié)使用的地方,顯示的釋放一次

DataBufferUtils.release(dataBuffer);

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Spring
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产精品原创| 精品久久久网| 欧美性感美女一区二区| 天堂网av成人| 欧美~级网站不卡| 国产精品色网| 无码日韩精品一区二区免费| 亚洲欧洲免费| 欧美日一区二区在线观看| 久久99精品久久久久久园产越南| 久久亚洲国产精品尤物| 麻豆久久久久久久| 国内不卡的一区二区三区中文字幕| 欧美日韩午夜| 精品一区二区三区免费看| 日韩精品首页| 综合欧美亚洲| 精品国产亚洲日本| 日韩久久电影| 亚洲欧美日韩国产综合精品二区| 亚洲永久精品唐人导航网址| 国产精品mm| 99久久婷婷| 蜜臀精品一区二区三区在线观看| 日韩av一级片| av在线最新| 三级一区在线视频先锋| 国产精品任我爽爆在线播放| 日本一区二区高清不卡| 国产99精品一区| 日韩手机在线| 日韩在线观看| 中文字幕亚洲在线观看| 麻豆国产精品视频| 国内精品99| 青青草国产成人99久久| 在线亚洲人成| 在线免费观看亚洲| 捆绑调教美女网站视频一区| 91精品二区| 日本综合精品一区| 在线观看精品| 一区二区国产在线观看| 麻豆精品蜜桃视频网站| 日韩午夜黄色| 精品视频一区二区三区四区五区| 国产亚洲网站| 四虎8848精品成人免费网站| 久久亚洲视频| а√天堂8资源中文在线| 蜜桃视频一区二区三区| 日本蜜桃在线观看视频| 青草av.久久免费一区| 日韩精品水蜜桃| 国产精品mm| 亚洲丝袜美腿一区| 亚洲成人不卡| 麻豆精品在线| 午夜电影一区| 国产精品免费看| 久久九九精品| 国产一区二区三区精品在线观看| 亚洲+小说+欧美+激情+另类| 91精品二区| 亚洲风情在线资源| 麻豆精品国产91久久久久久| 亚洲精品影视| 在线视频精品| 99成人超碰| 久久男人av资源站| 国产高清日韩| 日韩精品免费视频一区二区三区 | 亚洲天堂av影院| 国产免费av国片精品草莓男男| 国产日韩综合| 九九综合在线| 在线观看精品| 国产欧洲在线| 国产一区二区三区四区| 国产精品男女| 国产日韩精品视频一区二区三区| 视频一区中文字幕精品| 免费观看久久久4p| 中文视频一区| 一区二区三区四区精品视频| 99精品美女| 秋霞国产精品| 日韩毛片视频| 精品视频免费| 久久亚州av| 国精品产品一区| 国产精品99视频| 另类小说一区二区三区| 国产精品22p| 精品国产99| 日本久久综合| 日韩在线免费| 色婷婷狠狠五月综合天色拍| 成人免费电影网址| 日本在线精品| 日韩在线观看不卡| 99精品一区| 亚洲网站视频| 国产一区成人| 亚洲精品美女| 欧美日本三区| 九九九精品视频| 日韩中文欧美| 亚洲一区激情| 日韩精选在线| 美女国产精品久久久| 精品国产麻豆| 青青久久av| 99视频一区| 亚洲精品欧美| 欧美一级二级三级视频| 国产精品66| 日韩黄色大片网站| 99热免费精品| 日韩1区2区日韩1区2区| 老鸭窝一区二区久久精品| 亚洲国产欧美日本视频| 免费观看不卡av| 免费观看久久久4p| 国产精品视频一区二区三区综合 | 欧美综合二区| 国产精品一区二区免费福利视频| 久久久91麻豆精品国产一区| 欧美亚洲日本精品| 亚洲欧美久久久| 欧美亚洲福利| 日韩黄色大片网站| 亚洲色图网站| 精品国产三区在线| 亚洲国产一区二区三区在线播放| 蜜臀久久久99精品久久久久久| 国产精品久久久久久久久免费高清| 人在线成免费视频| 蜜臀av在线播放一区二区三区| 国产日韩欧美一区| 久久国产小视频| 亚洲精品自拍| 国产成人精品一区二区三区免费| 欧美特黄a级高清免费大片a级| 欧美日韩18| 欧美日韩一二| 国产乱人伦丫前精品视频| 久久蜜桃精品| 欧美偷窥清纯综合图区| 日韩不卡免费高清视频| 综合一区二区三区| 国产精品毛片久久| 丝袜美腿一区二区三区| 国产66精品| 日韩一区精品| 亚洲成av在线| 国产精品分类| 国产精品外国| 国语对白精品一区二区| 亚洲综合福利| 欧美精品一二| 久久精品国产99| 中文一区一区三区免费在线观| 国产精品原创| 国产亚洲欧美日韩在线观看一区二区| 免费黄色成人| 91麻豆国产自产在线观看亚洲| 婷婷成人av| 婷婷综合社区| 国产一区2区| 欧美在线91| 日韩在线卡一卡二| 日韩免费一区| 国产精品久久久久77777丨| 亚洲欧美日韩精品一区二区| 高潮久久久久久久久久久久久久| 日韩精品三级| 久久福利毛片| 国产99精品| 福利精品一区| 国产精品成人3p一区二区三区| 在线看片日韩| 亚洲精品中文字幕乱码| zzzwww在线看片免费| 国产精品久久免费视频| 日韩国产在线观看一区| 亚洲资源av| 欧美午夜精彩| 亚洲成a人片| 国产色播av在线| 狠狠久久伊人| 精品欧美视频| 麻豆成人91精品二区三区| 欧美久久一区二区三区| 日本成人在线视频网站| 蜜芽一区二区三区| 中文在线不卡| 亚洲v在线看| 久久久久久久久久久妇女 | 蜜桃视频免费观看一区|