手把手带你分析 (net.devh.boot.grpc 包下面的)服务端 Grpc 自动装配、服务注册的源码(Java版)

news2024/11/23 4:04:52

前言

昨天写过一篇关于如何使用 Grpc 的博客,出于好奇想知道 @GrpcService、@GrpcClient、@GrpcGlobalServerInterceptor、@GrpcGlobalClientInterceptor这些注解是如何生效的,以及服务注册的流程是怎样的,就简单过了一遍源码,帮助大家梳理一下流程吧

推荐阅读

grpc 系列:Grpc 整合 Nacos SpringBoot 日常使用(Java版本)包括 Jwt 认证
Spring 源码系列之自动装配:手把手debug自动装配源码、顺带弄懂了@Import等相关的源码(全文3w字、超详细)

源码切入点分析

可能很多小伙伴对于自主看源码无从下手,尤其是这种小众的 Jar 包,由于我们 Java 版本 Grpc 使用的是 net.devh.boot.grpc 包下面的代码,我们找到这种带 autoconfigure 尾缀的包点进去,里面的代码全是关于自动装配的源码,里面包括各种配置类的加载,顺藤摸瓜即可找到我们所需的源码。

在这里插入图片描述
自动装配有个特点会自动将 MATE-INF 目录下 spring.factories 中标注的类,生成一个个 Bean 注入到我们的 IOC 容器中(原理本文不做阐述)。知道了这一点就好办了点击 spring.factories 文件,看一下 net.devh.boot.grpc 这个包要加载哪些 Bean 即可。
在这里插入图片描述

由于我已经 debug 过了,直接说结论:大家去看 GrpcServerFactoryAutoConfiguration 这个类即可。可以看到里面注入了很多个 Bean ,大家关注 GrpcServerLifecycle、NettyGrpcServerFactory 这俩个类即可。提前剧透一下,GrpcServerLifecycle: 负责控制 Grpc 容器的开启与关闭。NettyGrpcServerFactory:负责提供 Grpc 服务、Grpc 拦截器等原料信息。

在这里插入图片描述

Grpc 容器的开启与关闭(GrpcServerLifecycle)

我们点进去 GrpcServerLifecycle 发现,他其实就是一个 SmartLifecycle,这个东西看着有点眼熟,原来是 org.spring 包下面的类,通过实现 SmartLifecycle 我们可以在 Spring 容器的开始、停止阶段做一些事情。那我们接着分析 GrpcServerLifecycle 中的代码即可。可以看到当容器启动的时候会进行调用 createAndStartGrpcServer 方法,进行创建并且启动 Grpc 服务。

在这里插入图片描述
这里贴一下 createAndStartGrpcServer 代码,里面会通过 ShadedNettyGrpcServerFactory 创建 Grpc 服务,然后进行启动。接下来进入 createServer 方法一探究竟。

protected void createAndStartGrpcServer() throws IOException {
    if (this.server == null) {
        //通过 Grpc 工厂创建 Grpc 服务
        Server localServer = this.factory.createServer();
        this.server = localServer;
        //启动 Grpc 服务
        localServer.start();
        String address = this.factory.getAddress();
        int port = this.factory.getPort();
        log.info("gRPC Server started, listening on address: {}, port: {}", address, port);
        //发布监听事件
        this.eventPublisher.publishEvent(new GrpcServerStartedEvent(this, localServer, address, port));
        Thread awaitThread = new Thread(() -> {
            try {
                localServer.awaitTermination();
            } catch (InterruptedException var2) {
                Thread.currentThread().interrupt();
            }

        });
        awaitThread.setName("grpc-server-container-" + serverCounter.incrementAndGet());
        awaitThread.setDaemon(false);
        awaitThread.start();
    }

}

其实我们看到这里的时候大概都已经很明了了,net.devh.boot.grpc 包无非就是对非 Spring 环境下使用 Grpc 做了代码封装。非 Spring 环境服务端启动 Grpc 代码如下,对比 createAndStartGrpcServer 与下面 main 方法中的代码,可以看到出奇的一致。

  public static void main(String[] args) throws IOException, InterruptedException {
        //1. 绑定端口
        ServerBuilder serverBuilder = ServerBuilder.forPort(8090);
        //2. 发布服务
        serverBuilder.addService(new LoginServiceRpcImpl());
        //3. 创建服务对象
        Server server = serverBuilder.build();
        server.start();
        server.awaitTermination();
    }

通过 ShadedNettyGrpcServerFactory 创建 Grpc 服务

来到 createServer 方法,可以看到调用了一些个 configure 方法,代码写的还是很不错的,简洁明了我们关注第一个 configureServices 方法即可。看他是如何配置 Services 的
在这里插入图片描述

通过观察 configureServices 代码,可以看到把 serviceList 中的类都装配到 Bulider 中了,而 serviceList 其实就是 @GrpcService 类的集合,在 ShadedNettyGrpcServerFactory 创建之初,就会获取被 @GrpcService 注解标注类的 Bean 放到 serviceList 中来。

  protected void configureServices(T builder) {
        Set<String> serviceNames = new LinkedHashSet();
        Iterator var3 = this.serviceList.iterator();

        while(var3.hasNext()) {
            GrpcServiceDefinition service = (GrpcServiceDefinition)var3.next();
            String serviceName = service.getDefinition().getServiceDescriptor().getName();
            if (!serviceNames.add(serviceName)) {
                throw new IllegalStateException("Found duplicate service implementation: " + serviceName);
            }

            log.info("Registered gRPC service: " + serviceName + ", bean: " + service.getBeanName() + ", class: " + service.getBeanClazz().getName());
            builder.addService(service.getDefinition());
        }

    }

这里贴一下 ShadedNettyGrpcServerFactory 相关代码,可以看到先是调用 findGrpcServices 方法获取所有被 @GrpcService 注解标注类的 BeanDefinition,然后调用 addService 方法把这些 BeanDefinition 放置在 ShadedNettyGrpcServerFactory.serviceList 集合中去。最后在配置 Grpc 服务(configureServices)的时候,将ShadedNettyGrpcServerFactory 里面的 serviceList 中的 Grpc 服务集成到一个 builder 中,最终配置好后,利用 bulider.start() 开启 Grpc 服务端。整个流程就是这样子的了。

在这里插入图片描述

findGrpcServices 源码如下:里面会对 @GrpcService 的类进行创建 Bean ,还会为所有的 GrpcService 进行绑定全局的拦截器。

 public Collection<GrpcServiceDefinition> findGrpcServices() {
 //通过 Spring 上下文获取被 @GrpcService 注解标注的所有类的名称
        Collection<String> beanNames = Arrays.asList(this.applicationContext.getBeanNamesForAnnotation(GrpcService.class));
        List<GrpcServiceDefinition> definitions = Lists.newArrayListWithCapacity(beanNames.size());
        //获取全局拦截器
        GlobalServerInterceptorRegistry globalServerInterceptorRegistry = (GlobalServerInterceptorRegistry)this.applicationContext.getBean(GlobalServerInterceptorRegistry.class);
        Iterator var4 = beanNames.iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            //拿到对 @GrpcService 标注的类,的 Bean (有责拿、无则进行创建 bean 然后返回)
            BindableService bindableService = (BindableService)this.applicationContext.getBean(beanName, BindableService.class);
            ServerServiceDefinition serviceDefinition = bindableService.bindService();
            GrpcService grpcServiceAnnotation = (GrpcService)this.applicationContext.findAnnotationOnBean(beanName, GrpcService.class);
            // 为所有 GrpcService 绑定全局拦截器
            serviceDefinition = this.bindInterceptors(serviceDefinition, grpcServiceAnnotation, globalServerInterceptorRegistry);
            definitions.add(new GrpcServiceDefinition(beanName, bindableService.getClass(), serviceDefinition));
            log.debug("Found gRPC service: " + serviceDefinition.getServiceDescriptor().getName() + ", bean: " + beanName + ", class: " + bindableService.getClass().getName());
        }
//返回所有被 @GrpcService 标注类的 BeanDefinition
        return definitions;
    }

addService: 将所有 GrpcServiceDefinition 放到 serviceList 中

在这里插入图片描述

小结

net.devh.boot.grpc 这个包利用了 Srping 自动装配的特性,对 GrpcServerLifecycle、ShadedNettyGrpcServerFactory 这俩个类生成 Bean。然后 GrpcServerLifecycle 利用 Spring 扩展点中的 SmartLifecycle 接口实现在 Spring 容器启动完成后,通过 ShadedNettyGrpcServerFactory 创建自己的 Grpc 服务。最终完成 Grpc 服务的注册。而 ShadedNettyGrpcServerFactory 顾名思议即一个工厂。在创建之初就会扫描所有带 @GrpcService 注解的类,然后生成 Bean。

🌹🌹🌹🌹🌹🌹本文只是简单分析了一下流程,具体的细节读者可以自行去剖析即可🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹发现更多好文章,不妨关注我一波,日后分享更多实战有意思的文章🌹🌹🌹🌹🌹

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

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

相关文章

新人必看!手把手教你如何使用浏览器表格插件(下)

摘要&#xff1a;本文由葡萄城技术团队于博客园原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 | 问题背景 作为一名优秀的打工人&#xff0c;Excel是大家上班中必不可少的办…

理解Linux TunTap设备

入门 TUN/TAP是操作系统内核中的虚拟网络设备&#xff0c;可以完成用户空间与内核空间的数据的交互。网络协议栈中的数据通过该设备可以进入到用户空间中&#xff0c;而用户空间中的程序通过该设备空间进入到内核空间的网络协议栈。 TUN模拟的是三层设备&#xff0c;操作三层…

chatgpt赋能python:Python主程序:提升编程效率与合作性的最佳选择

Python 主程序&#xff1a;提升编程效率与合作性的最佳选择 前言 Python 作为一门简单、易于学习并具备强大功能的编程语言&#xff0c;已经成为了最受欢迎的编程语言之一。Python 主程序不仅能够编写复杂的算法和进行数据处理&#xff0c;而且还可以实现广泛的应用&#xff…

基于SpringBoot+Uniapp的球队周边微信小程序

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着微信小程序的兴起…

【jeecg-boot】jeecg-boot的一些功能扩展:

文章目录 一、Template里面将数组对象里面的值遍历>对象的key二、利用ES6的解构赋值互换数组数据&#xff1a;三、a-select实现可输入可下拉:四、a-table实现动态表头&#xff1a;五、jeecg-boot列自定义&#xff1a;六、jeecg-boot合计行&#xff1a; 一、Template里面将数…

Android 逆向工程,反编译心得

前言 apk的反编译是我们在Android开发中绕不开的一个坎&#xff0c;对于反编译这门技术&#xff0c;我们应该抱着学习的态度&#xff0c;学的越多&#xff0c;也越能防备别人反编译我们&#xff0c;这就是所谓的知己知彼吧&#xff0c;哈哈 需要准备的工具 Apktool&#xff…

centos下Harbor的安装(超详细+避坑)

前提 这篇文章讲的是在我的本地虚拟机上安装Harbor的一些过程和中途所遇到的一些问题和排除问题的情况说明&#xff1b;安装好的harbor的访问信息如下&#xff1a;http://192.168.45.146:8033/harbor&#xff08;admin/Harbor12345&#xff09;环境 本次所使用的环境和软件的各…

国外APP外包开发及上线流程

现在很多APP都做成全球通用版&#xff0c;尤其是一些小游戏类的APP&#xff0c;玩法全球基本都类似&#xff0c;在多个国家上线多个销售渠道。今天和大家分享一下Google Play上线流程及注意事项&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件…

ChatGPT:AI时代的创造力激活

《你好&#xff0c;ChatGPT》是一本深入探索人工智能&#xff08;AI&#xff09;领域的畅销书籍&#xff0c;它以ChatGPT为切入点&#xff0c;系统地介绍了AI和AIGC的基础概念、技术原理、应用领域和未来展望。这本书通俗易懂&#xff0c;由浅入深&#xff0c;层层递进&#xf…

WMS仓储管理系统解决方案能帮助电子企业解决哪些问题

WMS仓储管理系统解决方案是一种针对仓库管理的软件系统&#xff0c;它能够有效地解决电子企业在仓储管理方面的问题。在电子行业&#xff0c;由于产品的生命周期较短&#xff0c;且需求变化快速&#xff0c;WMS仓库管理系统的应用对于电子企业的管理有着重要的意义。本文将探讨…

DATAV通过配置nginx代理实现https访问

DATAV通过配置nginx代理实现https访问 首先要确保你的 datav 和 datav_proxy 的界面能用http正常访问 在nginx中添加datav配置 server {listen 8181 ssl;server_name localhost;ssl_certificate server.crt;ssl_certificate_key server.key;ssl_session_cache …

“来此加密“:轻松在线申请多域名和泛域名SSL证书

启用SSL证书是网站安全的关键。它加密数据传输&#xff0c;防止黑客窃听和篡改。SSL证书提升网站可信度&#xff0c;增加用户信任。搜索引擎更青睐启用SSL证书的网站&#xff0c;提高可见性和流量。此外&#xff0c;SSL证书还防止钓鱼和恶意软件威胁&#xff0c;保护用户安全。…

A-21S吸金树脂在金矿尾水、镀金废水中回收金的应用

吸金树脂Tulsimer A-21S 一、技术介绍 传统上使用活性碳吸附金子&#xff0c;珍贵的金会被活性碳吸附于表面&#xff0c;再藉由洗涤或直接焚烧以回收金。使用离子交换树脂回收贵金属比活性碳还具有多方面的优势&#xff0c; 因为藉由特殊制造过程中&#xff0c; 我们可以在其结…

python---动态类型

动态类型&#xff1a;是指在程序运行过程中&#xff0c;变量的类型可能会发生改变。 a的类型随着程序运行过程中会发生改变。 后面写不写类型是无所谓的&#xff01; 相比之下静态类型的语言是更好的&#xff01; 代码注释 可以使用’‘’ ‘’‘ / #来对代码进行注释

03_堆+MAT工具

堆栈方法区的关系&#xff1a; HotSpot是使用指针的方式来访问对象&#xff1a; Java堆中会存放访问类元数据的地址 reference存储的就是对象的地址 三种JVM&#xff1a; Sun公司的HotSpotBEA公司的JRockitIBM公司的J9 VM 一、堆体系概述 Java7之前 Heap 堆&#xff1a;一个…

LabVIEWCompactRIO 开发指南36 确定“Clock Ticks”或模拟时间

LabVIEWCompactRIO 开发指南36 确定“Clock Ticks”或模拟时间 桌面执行节点可以控制模拟时间&#xff0c;因此开发人员可以使用模拟I/O在开发计算机上执行期间更改关键点的激励。要成功使用此功能&#xff0c;需要测量FPGA VI完成所需的时间&#xff0c;或者需要以直观地知道…

将矩阵各行顺序进行反排numpy.flipud()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将矩阵各行顺序进行反排 numpy.flipud() [太阳]选择题 请问关于以下代码的表述错误的是&#xff1f; import numpy as np a np.array([[1,2,3],[4,5,6],[7,8,9]]) print("【显示】a: \…

PLC/DCS系统中电磁干扰的来源及解决办法

自动化系统中所使用的各种类型DCS/PLC等自动化设备&#xff0c;有的是集中安装在控制室&#xff0c;有的是安装在生产现场和各种电机设备上&#xff0c;它们大多处在强电电路和强电设备所形成的恶劣电磁环境中。要提高这类控制系统可靠性&#xff0c;必须消除各种干扰才能有效保…

lwIP更新记08:TCP 回调函数中调用 tcp_abort 终于安全了

从 lwIP-1.4.0 开始&#xff0c;tcp 回调函数中调用 tcp_abort 函数终于安全了。 在此之前&#xff0c;如果从 tcp 回调函数中调用 tcp_abort&#xff0c;则会访问未分配的内存。 应用程序关闭连接&#xff0c;正常情况下是调用 tcp_close 函数&#xff0c;经过 4 次握手安全的…

XSS - 跨站脚本攻击

一、XSS简介。 XSS跨站脚本&#xff08;Cross-Site Scripting&#xff0c;XSS&#xff09;自1996年诞生以来&#xff0c;如今已经历十多年的演化。由于和另一种网页技术-层叠样式表&#xff08;Cascading Style Sheets&#xff0c;CSS&#xff09;的缩写一样&#xff0c;为了防…