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

news2024/12/26 0:06:37

开始前必读:​​基于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());
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenShift 4 - 管理和使用 OpenShift AI 运行环境

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.14 RHODS 2.50 的环境中验证 文章目录 启停 Notebook Server启动停止 Notebook 镜像Notebook Image 和 ImageStream使用定制的 Notebook Image 定制服务器的运行配置应用和项目用户和访问权…

嵌入式软件测试(黑盒测试)---三年嵌入式软件测试的理解

文章内容为本人这三年来在嵌入式软件测试&#xff08;黑盒&#xff09;上的一些积累吧&#xff0c;说起来也挺快的&#xff0c;毕业三年的时间就这样过去了&#xff0c;在两家公司工作过&#xff08;现在这家是第二家&#xff09;&#xff0c;这几年的测试项目基本都是围绕着嵌…

【java IO】|java NIO总结

在传统的 Java I/O 模型&#xff08;BIO&#xff09;中&#xff0c;I/O 操作是以阻塞的方式进行的。也就是说&#xff0c;当一个线程执行一个 I/O 操作时&#xff0c;它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈&#xff0c;因为需要为每个连…

解决win10下强制设置web浏览器为microsoft edge的方法

目录 问题场景实现方法禁止edge默认选项设置默认浏览器 反思 问题场景 因为一些特殊的原因&#xff0c;我需要第二个浏览器&#xff0c;我的第一个浏览器是google的chrome浏览器&#xff0c;所以我选择的是windows的默认浏览器&#xff0c;就是microsoft edge浏览器&#xff0…

MYSQL中使用IN,在xml文件中怎么写?

MYSQL&#xff1a; Spring中&#xff1a; mysql中IN后边的集合&#xff0c;在后端中使用集合代替&#xff0c;其他的没有什么注意的&#xff0c;还需要了解foreach 语法即可。

AI抠图软件哪个好用?推荐这三款抠图工具给你

AI抠图软件哪个好用&#xff1f;你是否听说过AI抠图这个操作呢&#xff1f;简单来说&#xff0c;抠图就是一种对图像进行处理的技术&#xff0c;它的目的是将图片中的某些区域去除或者替换。比如&#xff0c;如果你有一张背景很杂乱的图片&#xff0c;你想把背景去掉&#xff0…

【Python炫酷系列】一闪一闪亮星星,漫天都是小星星(完整代码)

文章目录 环境需求完整代码详细分析系列文章环境需求 python3.11.4及以上版本PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want595.…

产品需求分析师的职责内容(合集)

产品需求分析师的职责内容1 职责&#xff1a; 1、根据公司战略规划&#xff0c;负责妇产科相关平台产品的中长期规划; 2、组织需求调研、收集、分析、整理、提炼、用户的需求&#xff0c;分析形成可行性研究报告; 3、深入挖掘产品需求&#xff0c;管理用户及公司内部业务需求&a…

深度卷积神经网络的剪枝方法

深度学习领域的一种剪枝技术是“网络瘦身&#xff08;Network Slimming&#xff09;”&#xff0c;这是一种旨在深度卷积神经网络&#xff08;CNN&#xff09;中实现通道级稀疏性的简单方案。该技术利用批量归一化中的放缩层来有效识别并修剪网络中不重要的通道。本文将详细介绍…

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

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

黑盒测试中关键截图如何打点

黑盒测试中关键截图如何打点Android黑盒测试过程中如何进行有效的打点是我们经常遇到的问题&#xff0c;我们一般会在脚本内部进行数据打点&#xff0c;也可以使用其他进程录屏或截图。那我们如何选取合适的方式进行打点记录呢&#xff1f;下图是对常用打点方式的统计&#xff…

全链路压力测试:解析其主要特点

随着信息技术的飞速发展和云计算的普及&#xff0c;全链路压力测试作为一种关键的质量保障手段&#xff0c;在软件开发和系统部署中扮演着至关重要的角色。全链路压力测试以模拟真实生产环境的压力和负载&#xff0c;对整个业务流程进行全面测试&#xff0c;具有以下主要特点&a…

openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-处理错误表

文章目录 openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-处理错误表164.1 操作场景164.2 查询错误信息164.3 处理数据导入错误 openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-…

鸿蒙(HarmonyOS)应用开发—— video组件实操

video 组件 harmonyOS 系统提供了基础的video。下面就直接上代码 原始video 新建项目 customVideo项目 本地视频 网络地址&#xff0c;就不用说明&#xff0c;只需要把地址换成网络地址即可 在resource 文件夹下的rawfile文件夹下 添加视频 在index.ets Video({src:$rawf…

C++数据结构——二叉搜索树详解

目录 一&#xff0c;关于二叉搜索树 1.1 概念 1.2 基本结构 二&#xff0c;二叉搜索树接口实现 2.1 插入 2.2 查找 2.3 打印 2.4* 删除 三&#xff0c;二叉搜索树接口递归实现 3.1 查找 3.2 插入 3.3 删除 四&#xff0c;二叉搜索树的默认成员函数 五&#xff0c;…

一篇文章带你了解各个程序员接单平台,让你选择不再迷茫!!!

相信现在很多程序员都已经走上了或者准备走上网上接单这条路&#xff0c;但是目前市面上的接单平台可谓五花八门&#xff0c;对于各个平台的优缺点&#xff0c;不同的程序员该如何选择适合自己的接单平台&#xff0c;你又是否了解呢&#xff1f; 接下来就让小编用一篇文章来为…

定位咨询:企业市场竞争中的定海神针

什么是定位咨询?定位咨询能给企业带来什么帮助?在现代市场的激烈竞争中&#xff0c;定位咨询不仅是企业区分自己的重要工具&#xff0c;更是它们赢得市场份额的关键。以下是定位咨询的定义和几个核心方面&#xff0c;笔者将列举具体案例说明其重要性和实用性。 定位咨询的简单…

python接口自动化测试--requests使用和基本方法封装

之前学习了使用jmeterant做接口测试&#xff0c;并实现了接口的批量维护管理(大概500多条用例)&#xff0c;对“接口”以及“接口测试”有了一个基础了解&#xff0c;最近找了一些用python做接口测试的资料&#xff0c;一方面为了学习下如何使用python进行接口测试(如何做出一个…

券商期权手续费现在最低多少钱一张?怎么调低最方便

券商期权手续费是指您在证券公司开设期权账户并进行期权交易时&#xff0c;需要向券商支付的费用。券商期权手续费主要包括以下三个部分&#xff1a; 中国结算费用&#xff1a;这是中国证券登记结算有限责任公司向期权交易者收取的费用&#xff0c;固定为每张合约0.3元&#x…

GPT-4V with Emotion:A Zero-shot Benchmark forMultimodal Emotion Understanding

GPT-4V with Emotion:A Zero-shot Benchmark forMultimodal Emotion Understanding GPT-4V情感:多模态情感理解的zero-shot基准 1.摘要 最近&#xff0c;GPT-4视觉系统(GPT-4V)在各种多模态任务中表现出非凡的性能。然而&#xff0c;它在情感识别方面的功效仍然是个问题。本文定…