基于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);
    }

    @Override
    public 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;
    }

    @Override
    protected ServerCall.Listener<ReqT> delegate() {
        return delegate;
    }

    @Override
    public 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 {

    @Override
    public <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})
    @ResponseBody
    public 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());
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

element plus 表格el-table行多选单选

1 行多选-点击checkbox 添加一个 el-table-column&#xff0c;设 type 属性为 selection 即可 <template><div class"box"><el-table :data"tableData" selection-change"handleSelectionChange"><el-table-column type&…

docker小白第六天

docker小白第六天 容器数据卷是什么 首先&#xff0c;容器卷有个坑&#xff1a;容器卷需要加入privilegedtrue&#xff0c;如下图所示&#xff0c;是为了解决permission denied的问题。其中“挂载”的意思是相当于一个硬盘插到主机上。使用该命令。是扩大容器的权限解决挂载目…

路由器设置代理IP教程,http代理怎么固定IP地址?

路由器设置代理IP教程 一、确定代理IP地址 首先&#xff0c;你需要确定你要使用的代理IP地址。你可以从代理服务提供商处获取代理IP地址和端口号。 二、登录路由器管理界面 在浏览器中输入路由器的IP地址&#xff0c;输入账号和密码&#xff0c;进入路由器的管理界面。 三、设置…

高级桌面编程(二)

一、前言 文章的续作前文是&#xff1a; 高级桌面编程&#xff08;一&#xff09;-CSDN博客https://blog.csdn.net/qq_71897293/article/details/135072204?spm1001.2014.3001.5502 二、自定义控件 1创建自定义控件&#xff0c;如下图所示&#xff1a; 2 在创建的页面可以…

架构设计系列之前端架构和后端架构的区别和联系

前端架构和后端架构都是软件系统中最关键的架构层&#xff0c;负责处理不同方面的任务和逻辑&#xff0c;两者之间是存在一些区别和联系的&#xff0c;我会从以下几个方面来阐述&#xff1a; 一、定位和职责 前端架构 主要关注用户界面和用户体验&#xff0c;负责处理用户与…

10000字讲解TCP协议(确认应答,超时重传,三次握手,四次挥手等等众多机制)以及UDP协议(UDP报文,校验和)

文章目录 UDP协议&#xff1f;什么是校验和&#xff1f;基于UDP的应用层协议(了解) TCP协议确认应答(可靠性机制)超时重传(可靠性机制)连接管理(可靠性机制)三次握手(重点)四次挥手(重点) 三次握手和四次挥手时客户端和服务器的状态滑动窗口(效率机制)流量控制(效率机制)窗口探…

大型语言模型:SBERT — Sentence-BERT

slavahead 一、介绍 Transformer 在 NLP 方面取得了进化进步&#xff0c;这已经不是什么秘密了。基于转换器&#xff0c;许多其他机器学习模型已经发展起来。其中之一是BERT&#xff0c;它主要由几个堆叠的变压器编码器组成。除了用于情感分析或问答等一系列不同的问题外&#…

国产ToolLLM的课代表---OpenBMB机构(清华NLP)旗下ToolBench的安装部署与运行(附各种填坑说明)

ToolBench项目可以理解为一个能直接提供训练ToolLLM的平台&#xff0c;该平台同时构建了ToolLLM的一个开源训练指令集。&#xff0c;该项目是OpenBMB机构&#xff08;面壁智能与清华NLP联合成立&#xff09;旗下的一款产品&#xff0c;OpenBMB机构名下还同时拥有另外一款明星产…

一种基于IWR6843雷达的跌倒检测系统的设计和实现(TI文档)

摘要 随着年龄增长&#xff0c;人体各项生理机能退化严重&#xff0c;老年人很容易发生跌倒&#xff0c;导致身体受到严重的伤害。近年来&#xff0c;随着国内人口老龄化越来越严重&#xff0c;主要针对老年人的跌倒检测系统正受到越来越多的关注。和常用的加速度传感器和视觉技…

vue el-date-picker中datetime类型对今天之后的日期包含时分禁用

vue el-date-picker中datetime类型对今天之后的日期包含时分禁用 目前对选择秒那一列未禁用 <template><div><el-date-pickerv-model"deactivateTime"type"datetime"format"yyyy-MM-dd HH:mm:ss"value-format"yyyy-MM-dd HH…

Unity 通过代码将一张大图切成多个小图的方法

在Unity 中要通过代码将一张贴图切割成多张小图&#xff0c;可以使用以下方法&#xff1a; /// <summary>/// 把一张图片切割成多张使用/// </summary>/// <param name"texture">原图</param>/// <param name"rows">切割的行…

SQL进阶理论篇(十三):数据库的查询优化器是什么?

文章目录 简介什么是查询优化器查询优化器的两种优化方式总结参考文献 简介 事务可以让数据库在增删改查的过程中&#xff0c;保证数据的正确性和安全性&#xff0c;而索引可以帮数据库提升数据的查找效率。查询优化器&#xff0c;则是帮助我们获取更高的SQL查询性能。 本节我…

持续集成交付CICD:K8S 自动化完成前端项目应用发布与回滚

目录 一、实验 1.环境 2.GitLab新建项目存放K8S部署文件 3.Jenkins手动测试前端项目CD 流水线代码&#xff08;下载部署文件&#xff09; 4. 将K8S master节点配置为jenkins从节点 5.K8S 手动回滚前端项目版本 6.Jenkins手动测试前端项目CD 流水线代码&#xff08;发布应…

空间转录组 多样本整合分析—stlearn

大家好&#xff0c;今天介绍一款空间转录组整合分析的python软件——stlearn。 如果你还不会安装&#xff0c;可以看下之前的推文&#xff0c;windows系统本地安装stlearn&#xff1a;本地安装jupyterlab&#xff0c;并创建conda小环境——以空间转录组细胞互作之stlearn安装为…

设计模式——0前言目录

1 设计模式介绍 应当站在产品经理的角度来学习设计模式 是软件设计中常见问题的典型解决方案&#xff0c;可用于解决代码中反复出现的设计问题 学习效果一般的原因在于自己没有站在产品经理的角度学习&#xff0c;仅仅是为了学习怎么实现&#xff0c;用什么算法实现。 分类&…

telnet的交互原理(wireshark分析)

telnet的交互原理&#xff08;wireshark篇&#xff09; telnet的协议类型是tcp&#xff0c;他的密钥用的是明文的&#xff0c;容易被捕获&#xff0c;所以后来的windows基本弃用了telnet服务端但依然保留了客户端。 下面是他的交互抓包&#xff1a; 这里面的前三条运用的是tc…

一种基于外观-运动语义表示一致性的视频异常检测框架 论文阅读

A VIDEO ANOMALY DETECTION FRAMEWORK BASED ON APPEARANCE-MOTION SEMANTICS REPRESENTATION CONSISTENCY 论文阅读 ABSTRACT1. INTRODUCTION2. PROPOSED METHOD3. EXPERIMENTAL RESULTS4. CONCLUSION阅读总结&#xff1a; 论文标题&#xff1a;A VIDEO ANOMALY DETECTION FRA…

【工作流Activiti】MyActivit的maven项目

1、Idea新建一个项目MyActivit的maven项目 2、安装插件 在 idea 里面&#xff0c;activiti 的插件叫 actiBPM&#xff0c;在插件库里面把它安装好&#xff0c;重启 idea 就行了。 3、 maven 项目中&#xff0c;并更改 pom.xml。pom 中依赖如下&#xff1a; <?xml version…

IntelliJ IDE 插件开发 | (三)消息通知与事件监听

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门IntelliJ IDE 插件开发 |&#xff08;二&#xff09;UI 界面与数据持久化IntelliJ IDE 插件开发 |&#xff08;三&#xff09;消息通知与事件监听 前言 在前两篇文章中讲解了关于插件开发的基础知识&…

【数据结构】二叉树的模拟实现

前言:前面我们学习了堆的模拟实现&#xff0c;今天我们来进一步学习二叉树&#xff0c;当然了内容肯定是越来越难的&#xff0c;各位我们一起努力&#xff01; &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:数据结构 &#x1f448; &…