基于grpc从零开始搭建一个准生产分布式应用(7) - 01 - 附:GRPC拦截器源码

开始前必读:​​基于grpc从零开始搭建一个准生产分布式应用(0) - quickStart​​ 

一、源码目录结构

二、GRPC拦截器源码

2.1、com.zd.baseframework.core.core.common.interceptor

package com.zd.baseframework.core.core.common.interceptor;import com.zd.baseframework.core.core.common.interceptor.delegate.DelegateInterceptor;
import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;/*** @Title: com.zd.baseframework.core.core.common.interceptor.GlobalServerInterceptorConfiguration* @Description  grpc服务端拦截器链设置文件,可以通过指定方法的org.springframework.core.annotation.Order注解来指定执行顺序,* 不过好像不起作用,需要按位置来写* @author liudong* @date 2022/1/13 4:40 PM*/
@Order(Ordered.LOWEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
public class GlobalServerInterceptorConfiguration {@GrpcGlobalServerInterceptor@Order(value = 10000)DelegateInterceptor delegateInterceptor(){return new DelegateInterceptor();}}
package com.zd.baseframework.core.core.common.interceptor.delegate;import io.grpc.Context;
import io.grpc.Metadata;/*** 日志常量* Created by liudong on 2017/5/26.*/
public class CONST {/**空格,用于拼接字符串*/public final static String SPLIT_BLANK = " ";/**元数据中的trackid的key值*/public final static Metadata.Key<String> TRACKID_METADATA_KEY = Metadata.Key.of("tid", Metadata.ASCII_STRING_MARSHALLER);/**保存到当前线程的上下文中*/public final static Context.Key<String> TRACK_INTIME_KEY = Context.key("universe_trackInTimeKey");public final static Context.Key<String> TRACK_LOG_KEY = Context.key("universe_trackLogKey");public final static Context.Key<String> TRACK_LOG_UID_KEY = Context.key("universe_trackLogIdKey");}
package com.zd.baseframework.core.core.common.interceptor.delegate;import io.grpc.ForwardingServerCall;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import lombok.extern.slf4j.Slf4j;@Slf4j
public class DelegateCall <ReqT, RespT> extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>  {private Metadata metadata;public DelegateCall(ServerCall<ReqT, RespT> delegate) {super(delegate);}@Overridepublic void sendMessage(RespT message) {StringBuilder delegateLog = new StringBuilder(CONST.TRACK_LOG_KEY.get());delegateLog.append(CONST.SPLIT_BLANK).append("exec=").append(System.currentTimeMillis() - Long.parseLong(CONST.TRACK_INTIME_KEY.get()));log.info(delegateLog.toString());super.sendMessage(message);}public Metadata getMetadata() {return metadata;}public void setMetadata(Metadata metadata) {this.metadata = metadata;}}
package com.zd.baseframework.core.core.common.interceptor.delegate;import io.grpc.ForwardingServerCallListener;
import io.grpc.ServerCall;public class DelegateCallListener< ReqT, RespT> extends ForwardingServerCallListener<ReqT> {private ServerCall<ReqT, RespT> serverCall;private final ServerCall.Listener<ReqT> delegate;public DelegateCallListener(ServerCall.Listener<ReqT> delegate) {this.delegate = delegate;}@Overrideprotected ServerCall.Listener<ReqT> delegate() {return delegate;}@Overridepublic void onMessage(ReqT message) {//TODO 接收消息,处理一些SQL注入等super.onMessage(message);}public ServerCall<ReqT, RespT> getServerCall() {return serverCall;}public void setServerCall(ServerCall<ReqT, RespT> serverCall) {this.serverCall = serverCall;}
}
package com.zd.baseframework.core.core.common.interceptor.delegate;import com.zd.baseframework.core.core.common.token.TokenParser;
import io.grpc.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;/*** @Title: com.zd.baseframework.core.core.common.interceptor.delegate.DelegateInterceptor* @Description 访问日志拦截器,此拦截器只打印日志并不做真正拦截,只输出原始参数。在* 在DelegateInterceptor和DelegateCall中分别输出:请求日志,格式如下:* tid=7537479305976007099 appid=Bearer ip=/127.0.0.1:64446 uri=universe.core.cases.ICaseService/GetCaseByCaseNum inTime=1642129705403* tid=7537479305976007099 appid=Bearer ip=/127.0.0.1:64446 uri=universe.core.cases.ICaseService/GetCaseByCaseNum inTime=1642129705403 exec=290** >tid:trackid,且于跟踪栈请求* >appid:接入应用的id* >ip:访问端的ip地址和端口号* >uri:客户端此次访问的uri* >param:请求的原始参数* >inTime:接收到请求的timestamp* >exec:此次请求的执行总时间** @author liudong* @date 2022/1/13 4:44 PM*/
@Slf4j
public class DelegateInterceptor implements ServerInterceptor {@Overridepublic <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata, ServerCallHandler<ReqT, RespT> serverCallHandler) {long inTime = System.currentTimeMillis();String trackId = metadata.get(CONST.TRACKID_METADATA_KEY);if (StringUtils.isEmpty(trackId)){trackId = String.valueOf(genLogId(System.nanoTime()));}StringBuilder delegateLog = new StringBuilder();delegateLog.append("tid=").append(trackId).append(CONST.SPLIT_BLANK).append("appid=").append(TokenParser.appId()).append(CONST.SPLIT_BLANK).append("ip=").append(serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR)).append(CONST.SPLIT_BLANK).append("uri=").append(serverCall.getMethodDescriptor().getFullMethodName()).append(CONST.SPLIT_BLANK).append("inTime=").append(inTime).append(CONST.SPLIT_BLANK);//保存请求时间和相关日志到请求线程中,供后面拦截器打印用Context ctx = Context.current();ctx = ctx.withValue(CONST.TRACK_INTIME_KEY, String.valueOf(inTime));ctx = ctx.withValue(CONST.TRACK_LOG_KEY, delegateLog.toString());ctx = ctx.withValue(CONST.TRACK_LOG_UID_KEY, trackId);log.info(delegateLog.toString());//下面设置的值必须为原始值,不能自定义的变量,保持参数的纯净DelegateCall<ReqT, RespT> serverCallDelegate = new DelegateCall<>(serverCall);DelegateCallListener<ReqT, RespT> delegateCallListener = new DelegateCallListener<>(serverCallHandler.startCall(serverCallDelegate, metadata));delegateCallListener.setServerCall(serverCall);return Contexts.interceptCall(ctx, serverCallDelegate, metadata, serverCallHandler);}private long genLogId(long param){long nowTime = System.currentTimeMillis();long logId = nowTime & 281474976710655L | (param >> 8 & 65535L) << 47;return logId;}
}

2.2、com.zd.baseframework.core.core.common.loggenerator

package com.zd.baseframework.core.core.common.loggenerator;import cn.hutool.core.util.StrUtil;
import com.zd.baseframework.core.core.common.interceptor.delegate.CONST;import java.io.PrintWriter;
import java.io.StringWriter;/*** 用于调用方logStr生成和埋点日志生成* 日志格式:* 埋点日志:tid=7537479305976007099 appid=Bearer ip=/127.0.0.1:64446 uri=universe.core.cases.ICaseService/GetCaseByCaseNum inTime=1642129705403 k=s act=xxx* 异常日志:tid=7537479305976007099 appid=Bearer ip=/127.0.0.1:64446 uri=universe.core.cases.ICaseService/GetCaseByCaseNum inTime=1642129705403 ep=xxx* Created by liudong on 16/5/25.*/
public final class LogGenerator {/*track日志获取*/public static String trackLog() {if(StrUtil.isEmpty(CONST.TRACK_LOG_KEY.get())){return "";}return CONST.TRACK_LOG_KEY.get();}/*track日志获取*/public static String trackUid() {return CONST.TRACK_LOG_UID_KEY.get();}/*** 生成统计日志串,用于日志埋点,一般需要和其它方法合并使用:* 日志格式 k=s act=自定义埋点标识,可自定义** @param act 埋点标识*/public static String logTracking(String act) {StringBuilder sb = new StringBuilder();sb.append(CONST.SPLIT_BLANK).append("k=s").append(CONST.SPLIT_BLANK).append("act=").append(act).append(CONST.SPLIT_BLANK);return sb.toString();}/*** 返回异常字符串,用于在control中使用:* 日志格式 ep=ExceptionMsg,前后均带空格** @param exception 异常实例*/public static String logException(Exception exception) {StringBuilder sb = new StringBuilder();sb.append(CONST.SPLIT_BLANK).append("ep=").append(exception2String(exception)).append(CONST.SPLIT_BLANK);return sb.toString();}private static String exception2String(Exception ex) {String exceptionMessage = "";if (ex != null) {StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);try {ex.printStackTrace(pw);exceptionMessage = sw.toString();} finally {try {sw.close();pw.close();} catch (Exception e) {}}}return exceptionMessage;}}
package com.zd.baseframework.core.core.common.token;
/*** @Title: com.zd.baseframework.core.core.common.token.TokenParser* @Description token解析类,用于将来存RPC时扩展用;* @author liudong* @date 2022/1/13 5:08 PM*/
public class TokenParser {/*返回用户名信息*/public static final String appId(){return "baseFrameWorkApp";}
}

三、HTTP拦截器源码

package com.zd.baseframework.core.controller.core.advice;import com.zd.baseframework.common.entity.http.BaseResponse;
import com.zd.baseframework.common.exceptions.AppException;
import io.grpc.StatusRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;@Slf4j
@ControllerAdvice(value = {"com.zd.baseframework.core.controller.core"})
public class HttpExceptionAdvice {@ResponseStatus(code = HttpStatus.OK)@ExceptionHandler(value = {AppException.class, StatusRuntimeException.class})@ResponseBodypublic BaseResponse dealKnownException(Exception e) {log.error("VMException: " + e.getMessage(), e);if (e instanceof StatusRuntimeException) {StatusRuntimeException vmException = (StatusRuntimeException) e;if (vmException.getStatus() != null && vmException.getStatus().getCode() != null) {return BaseResponse.error(vmException.getStatus().getCode().value(), e.getMessage());} else {return BaseResponse.error(e.getMessage());}}else if (e instanceof AppException) {AppException vmException = (AppException) e;if (vmException.getStatus() != null) {return BaseResponse.error(vmException.getStatus(), e.getMessage());} else {return BaseResponse.error(e.getMessage());}}  else {return BaseResponse.error(e.getMessage());}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/286746.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

清华提出ViLa,揭秘 GPT-4V 在机器人视觉规划中的潜力

人类在面对简洁的语言指令时&#xff0c;可以根据上下文进行一连串的操作。对于“拿一罐可乐”的指令&#xff0c;若可乐近在眼前&#xff0c;下意识的反应会是迅速去拿&#xff1b;而当没看到可乐时&#xff0c;人们会主动去冰箱或储物柜中寻找。这种自适应的能力源于对场景的…

算法(2)——滑动窗口

前言&#xff1a; 步骤及算法模板&#xff1a; 确定两个指针变量&#xff0c;left0,right0; 进窗口&#xff1a; 判断&#xff1a; 出窗口 更新结果 接下来我们的所用滑动窗口解决问题都需要以上几个步骤。 一、长度最小的子数组 209. 长度最小的子数组 - 力扣&#xff08;L…

【重点】【前缀树|字典树】208.实现Trie(前缀树)

题目 前缀树介绍&#xff1a;https://blog.csdn.net/DeveloperFire/article/details/128861092 什么是前缀树 在计算机科学中&#xff0c;trie&#xff0c;又称前缀树或字典树&#xff0c;是一种有序树&#xff0c;用于保存关联数组&#xff0c;其中的键通常是字符串。与二叉查…

安卓开发学习---kotlin版---笔记(三)

网络 安卓主页的网络框架&#xff1a;OkHttp 在OkHttp的基础上进行封装的&#xff1a;Retrofit框架&#xff0c;更常使用 OkHttp学习 在使用网络请求的时候&#xff0c;先添加网络访问权限&#xff1a; <uses-permission android:name"android.permission.INTERNET&…

JavaScript 内存管理的秘密武器:垃圾回收(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

SQL进阶理论篇(十四):CBO优化器是如何计算代价的?

文章目录 简介能调整的代价模型的参数有哪些&#xff1f;mysql.server_costmysql.engine_cost 如何修改这些代价参数&#xff1f;代价模型具体是如何计算的参考文献 简介 大部分RDBMS都支持基于代价的优化器CBO&#xff0c;但其实CBO仍然存在缺陷&#xff08;比如参数配置的不…

Android 原始方法实现Tablayout样式

源码&#xff1a; 【免费】Android原始方法实现Tablayout样式资源-CSDN文库 推荐&#xff1a; GitHub - hackware1993/MagicIndicator: A powerful, customizable and extensible ViewPager indicator framework. As the best alternative of ViewPagerIndicator, TabLayout …

3dsmax渲染太慢,用云渲染农场多少钱?

对于许多从事计算机图形设计的创作者来说&#xff0c;渲染速度慢是一个常见问题&#xff0c;尤其是对于那些追求极致出图效果的室内设计师和建筑可视化师&#xff0c;他们通常使用3ds Max这样的工具&#xff0c;而高质量的渲染经常意味着长时间的等待。场景复杂、细节丰富&…

Spring事务浅析

一:Spring事务简介 什么是事务&#xff1a; 数据库事务是指作为单个逻辑工作单元执行的一系列操作&#xff0c;这些操作要么一起成功&#xff0c;要么一起失败&#xff0c;是一个不可分割的工作单元。 在我们日常工作中&#xff0c;涉及到事务的场景非常多&#xff0c;一个…

一、串行FLASH文件系统FatFs简介

本节主要给大家介绍以下内容&#xff1a; 文件系统简介 FatFs文件系统简介 FatFs文件系统移植 FatFs功能使用 一、文件系统简介 当我们在使用SPI FLASH直接存储数据 当需要记录字符“STM32 SPI FLASH”时。可以把这些文字转化成ASCII码&#xff0c;存储在数组中&#xff0…

查看CPU的型号方法很多,而且步骤简单

每台计算机至少包含一个处理器&#xff0c;也称为CPU或中央处理单元。你电脑的CPU可能是由英特尔或AMD制造的。以下是如何查看你的CPU以及它的速度。 在设置应用程序中查找你的CPU 要在Windows 10或Windows 11设置应用程序中查找此信息&#xff0c;请导航到“设置”>“系统…

程序流程图的意义(合集)

程序流程图的意义 1、矩形 作用&#xff1a;一般用作要执行的处理(process)&#xff0c;在程序流程图中做执行框。 在axure中如果是画页面框架图&#xff0c;那么也可以指代一个页面。有时候我们会把页面和执行命令放在同一个流程中做说明&#xff0c;这个时候将两类不同的矩形…