Netty实战 -- 使用Netty实现分布式框架Dubbo RPC

简介: Netty实战 -- 使用Netty实现分布式框架Dubbo RPC
📢📢📢📣📣📣

哈喽!大家好,我是【 Bug 终结者,【CSDN新星创作者】🏆,阿里云技术博主🏆,51CTO人气博主🏆,INfoQ写作专家🏆 <br/>
一位上进心十足,拥有极强学习力的【 Java领域博主】😜😜😜 <br/>
🏅【Bug 终结者】博客的领域是【面向后端技术】的学习,未来会持续更新更多的【后端技术】以及【学习心得】。 偶尔会分享些前端基础知识,会更新实战项目,面向企业级开发应用
🏅 如果有对【后端技术】、【前端领域】感兴趣的【小可爱】,欢迎关注【Bug 终结者】💞💞💞


❤️❤️❤️ 感谢各位大可爱小可爱! ❤️❤️❤️

@[TOC]

⚡Netty系列文章

Netty实战系列文章

⚡Dubbo系列文章

Spring Boot 整合Dubbo + Zookeeper 实现分布式 消费者与服务者的业务调用

一、什么是RPC?

RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。

常见的RPC框架有: 比较知名的阿里Dubbo、Google的GRPC、Go的RPCX、Apache的Thrift,SpringCloud
在这里插入图片描述

1.1 RPC基本原理

两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样

示意图

在这里插入图片描述

RPC两个核心模块:序列化和通讯

1.2 RPC执行流程

在RPC中,Client叫做服务消费者,Server叫做服务提供者

RPC调用流程说明

  1. 服务消费方(client),以本地调用方式调用服务
  2. client stub 接收到调用后负责将方法、参数等封装成能够进行网络传输的消息体
  3. client stub 将消息进行编码并发送到服务器
  4. server stub 收到消息后进行解码
  5. server stub 根据解码结果调用本地API
  6. 本地服务 执行并返回结果给 server stub
  7. server stub 将返回结果导入进行编码并发送至消费方
  8. client stub 接收到数据进行解码
  9. 服务消费方得到结果

RPC的目标就是将2~8的步骤封装起来,用户无需关注这些细节,可以像调用本地方法一样即可完成远程服务调用

图解

在这里插入图片描述

二、什么是代理模式?

代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

Java中代理模式分为静态代理动态代理模式

2.1 案例实现

中午到了,小明很饿,于是在美团外卖上点了一份烤鸭,过了半个小时,外卖到了,小明下去拿外卖,顺利的吃上了烤鸭~

2.2 静态代理方式

由程序员手动创建代理类或工具对象,从而实现调用服务

Subject类

package com.wanshi.netty.dubborpc.netty;

public interface Subject {

    String buy(String msg);
}

SubjectImpl类

package com.wanshi.netty.dubborpc.netty;

public class SubjectImpl implements Subject{
    @Override
    public String buy(String msg) {
        return "买了" + msg;
    }
}

ProxySubject类

package com.wanshi.netty.dubborpc.netty;


public class ProxySubject {

    private Subject subject;

    {
        subject = new SubjectImpl();
    }

    public void buy(String msg) {
        System.out.println("美团外卖,使命必达,跑腿代买!");
        String buy = subject.buy(msg);
        System.out.println(buy);
    }
}

测试类

public static void main(String[] args) {
    ProxySubject subject = new ProxySubject();
    subject.buy("北京烤鸭");
}

效果

在这里插入图片描述

⛽静态代理的优缺点

缺点: 不利于扩展,调用一次就要创建一次对象,从而造成不必要的内存空间浪费

优点: 可读性好,逻辑简单,清晰明了

2.3 动态代理方式

动态代理又分为:JDK动态代理CGLIB动态代理

在程序运行时,运用反射机制动态创建而成,达到调用服务

使用以上的Subject类和实现类

SubjectInvocationHandler处理器类

package com.wanshi.netty.dubborpc.netty;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class SubjectInvocationHandler implements InvocationHandler {

    private Object obj;

    public SubjectInvocationHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = method.invoke(obj, args);
        return res;
    }
}

ProxyFactry工厂类

package com.wanshi.netty.dubborpc.netty;

import java.lang.reflect.Proxy;

public class ProxyFactry {

    public static Subject getInstance() {
        SubjectImpl subject = new SubjectImpl();
        System.out.println("美团外卖,使命必达,跑腿代买!");
        SubjectInvocationHandler subjectInvocationHandler = new SubjectInvocationHandler(subject);
        Subject proxy = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
                subject.getClass().getInterfaces(), subjectInvocationHandler);
        return proxy;
    }
}

测试类

public static void main(String[] args) {
    Subject subject = ProxyFactry.getInstance();
    String buy = subject.buy("饮料");
    System.out.println(buy);
}

效果
在这里插入图片描述

⛽动态代理的优缺点

两种动态代理对照表

JDK原生动态代理 CGLIB动态代理
核心原理 基于 ”接口实现“方式 基于类集成方式
优点 Java原生支持的,不需要任何依赖 对与代理的目标对象无限制,无需实现接口
不足之处 只能基于接口实现 无法处理final方法
实现方式 Java原生支持 需要引入Jar文件依赖

三、Netty实现DubboRPC

3.1 需求说明

  1. dubbo 底层使用了Netty作为网络通讯框架,要求用Netty实现一个简单的RPC框架
  2. 模仿dubbo,消费者和提供者约定接口和协议,消费者远程调用提供者的服务,提供者返回一个字符串,消费者打印提供者返回的数据,底层使用Netty框架

3.2 剖析需求

  1. 创建接口,定义抽象方法,用于服务消费者与服务提供者之间的约定
  2. 创建一个提供者,该类需要监听消费者的请求,并按照约定返回数据
  3. 创建一个消费者,该类需要 透明的调用自己不存在的方法,内部使用Netty 请求提供者返回数据

在这里插入图片描述

3.3 效果图

在这里插入图片描述

3.4 核心源码

♻️共用接口API

HelloService

package com.wanshi.netty.dubborpc.publicinterface;

/**
 * 公共接口,提供服务
 */
public interface HelloService {

    String hello(String msg);
}

♻️服务提供者

ServerBootstrap启动类

package com.wanshi.netty.dubborpc.provider;

import com.wanshi.netty.dubborpc.netty.NettyServer;

/**
 * 服务提供者启动类,监听消费者,并绑定端口8888
 */
public class ServerBootstrap {

    public static void main(String[] args) {
        NettyServer.startServer("127.0.0.1", 8888);
    }
}

NettyServer

package com.wanshi.netty.dubborpc.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * Netty服务器类,启动服务
 */
public class NettyServer {


    /**
     * 开启服务方法,调用内部私有启动服务方法,此类写法很常用,进一层的封装了API
     * @param hostName
     * @param port
     */
    public static void startServer(String hostName, int port) {
        startServer0(hostName, port);
    }

    /**
     * 真正启动服务的方法
     * @param hostName
     * @param port
     */
    private static void startServer0(String hostName, int port) {
        //创建2个线程,一个为主线程仅创建1个,另外创建工作线程CPU核数*2个
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            //服务器启动类
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            //初始化参数
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });

            System.out.println("服务端提供服务准备就绪...");

            //绑定端口,启动服务,异步执行方法
            ChannelFuture channelFuture = serverBootstrap.bind(hostName, port).sync();
            channelFuture.channel().closeFuture().sync();
        }  catch (Exception e) {
            e.printStackTrace();
        } finally {
            //优雅的关闭线程
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

♻️服务消费者

ClientBootstrap

package com.wanshi.netty.dubborpc.consumer;

import com.wanshi.netty.dubborpc.netty.NettyClient;
import com.wanshi.netty.dubborpc.publicinterface.HelloService;

public class ClientBootstrap {

    public static final String providerName = "hello#";

    public static void main(String[] args) throws InterruptedException {
        NettyClient client = new NettyClient();

        HelloService service = (HelloService) client.getBean(HelloService.class, providerName);

        for (;;) {
            Thread.sleep(2000);
            String hello = service.hello("你好鸭 dubbo~");
            System.out.println("服务端返回的结果:" + hello + "\n\n");
        }
    }
}

NettyClient

package com.wanshi.netty.dubborpc.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.lang.reflect.Proxy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NettyClient {

    //创建线程池,大小为CPU核数*2
    private ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    //将客户端处理器提升至全局变量
    private NettyClientHandler clientHandler;

    //记录调用服务的次数
    private int count;

    /**
     * 代理对象,执行方法,这里用到了代理模式
     * @param serviceClass
     * @param providerName
     * @return
     */
    public Object getBean(final Class<?> serviceClass, String providerName) {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{serviceClass}, (proxy, method, args) -> {

                    System.out.println("(proxy, method, args) 进入,第" + (++count) + "次调用远程服务");
                    if (clientHandler == null) {
                        initClient();
                    }
                    clientHandler.setParam(providerName + args[0]);
                    return executorService.submit(clientHandler).get();
                });
    }

    /**
     * 初始化客户端
     */
    public void initClient() {
        clientHandler = new NettyClientHandler();
        EventLoopGroup group = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();

        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        pipeline.addLast(clientHandler);
                    }
                });

        try {
            bootstrap.connect("127.0.0.1", 8888).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

四、源码下载

本教程已上传至GitHub代码托管平台,希望点个Star鸭~

NettyDubboRPC

使用Git爬取GitHub文件

♨️往期精彩热文回顾

✈️ **[Netty进阶 -- WebSocket长连接开发
](https://blog.csdn.net/weixin_45526437/article/details/123351605?spm=1001.2014.3001.5502)**
✈️ **[Netty进阶 -- 非阻塞网络编程 实现群聊+私聊+心跳检测系统
](https://blog.csdn.net/weixin_45526437/article/details/123197281?spm=1001.2014.3001.5502)**

✈️ **[一分钟教你快速 搭建Vue脚手架(Vue-Cli)项目并整合ElementUI
](https://blog.csdn.net/weixin_45526437/article/details/123247055?spm=1001.2014.3001.5502)**

⛵小结

以上就是【Bug 终结者】对基于Netty实现Dubbo RPC 简单的概述,手写Dubbo RPC,代码有点难度,但坚持的啃下来,多敲上几遍,熟能生巧,加油,相信你会对RPC有一个新的理解,加油,编程路上,你我都是追梦人~

如果这篇【文章】有帮助到你,希望可以给【 Bug 终结者】点个赞👍,创作不易,如果有对【 后端技术】、【 前端领域】感兴趣的小可爱,也欢迎关注❤️❤️❤️ 【 Bug 终结者】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】💝💝💝!
相关文章
|
11月前
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
1205 0
分布式爬虫框架Scrapy-Redis实战指南
|
9月前
|
监控 Java 调度
SpringBoot中@Scheduled和Quartz的区别是什么?分布式定时任务框架选型实战
本文对比分析了SpringBoot中的`@Scheduled`与Quartz定时任务框架。`@Scheduled`轻量易用,适合单机简单场景,但存在多实例重复执行、无持久化等缺陷;Quartz功能强大,支持分布式调度、任务持久化、动态调整和失败重试,适用于复杂企业级需求。文章通过特性对比、代码示例及常见问题解答,帮助开发者理解两者差异,合理选择方案。记住口诀:单机简单用注解,多节点上Quartz;若是任务要可靠,持久化配置不能少。
852 4
|
消息中间件 运维 数据库
Seata框架和其他分布式事务框架有什么区别
Seata框架和其他分布式事务框架有什么区别
509 153
|
存储 监控 数据可视化
常见的分布式定时任务调度框架
分布式定时任务调度框架用于在分布式系统中管理和调度定时任务,确保任务按预定时间和频率执行。其核心概念包括Job(任务)、Trigger(触发器)、Executor(执行器)和Scheduler(调度器)。这类框架应具备任务管理、任务监控、良好的可扩展性和高可用性等功能。常用的Java生态中的分布式任务调度框架有Quartz Scheduler、ElasticJob和XXL-JOB。
4978 66
|
数据采集 人工智能 分布式计算
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
阿里云推出的MaxFrame是链接大数据与AI的分布式Python计算框架,提供类似Pandas的操作接口和分布式处理能力。本文从部署、功能验证到实际场景全面评测MaxFrame,涵盖分布式Pandas操作、大语言模型数据预处理及企业级应用。结果显示,MaxFrame在处理大规模数据时性能显著提升,代码兼容性强,适合从数据清洗到训练数据生成的全链路场景...
720 5
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
|
人工智能 分布式计算 大数据
MaxFrame 产品评测:大数据与AI融合的Python分布式计算框架
MaxFrame是阿里云MaxCompute推出的自研Python分布式计算框架,支持大规模数据处理与AI应用。它提供类似Pandas的API,简化开发流程,并兼容多种机器学习库,加速模型训练前的数据准备。MaxFrame融合大数据和AI,提升效率、促进协作、增强创新能力。尽管初次配置稍显复杂,但其强大的功能集、性能优化及开放性使其成为现代企业与研究机构的理想选择。未来有望进一步简化使用门槛并加强社区建设。
583 8
|
分布式计算 大数据 数据处理
技术评测:MaxCompute MaxFrame——阿里云自研分布式计算框架的Python编程接口
随着大数据和人工智能技术的发展,数据处理的需求日益增长。阿里云推出的MaxCompute MaxFrame(简称“MaxFrame”)是一个专为Python开发者设计的分布式计算框架,它不仅支持Python编程接口,还能直接利用MaxCompute的云原生大数据计算资源和服务。本文将通过一系列最佳实践测评,探讨MaxFrame在分布式Pandas处理以及大语言模型数据处理场景中的表现,并分析其在实际工作中的应用潜力。
522 2
|
网络协议 前端开发 Java
谈谈如何使用Netty开发实现高性能的RPC服务器
RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议。说的再直白一点,就是客户端在不必知道调用细节的前提之下,调用远程计算机上运行的某个对象,使用起来就像调用本地的对象一样。
1477 0
|
存储 缓存 NoSQL
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
13940 1
|
9月前
|
算法 Java 容器
Netty源码—4.客户端接入流程
本文主要介绍了关于Netty客户端连接接入问题整理、Reactor线程模型和服务端启动流程、Netty新连接接入的整体处理逻辑、新连接接入之检测新连接、新连接接入之创建NioSocketChannel、新连接接入之绑定NioEventLoop线程、新连接接入之注册Selector和注册读事件、注册Reactor线程总结、新连接接入总结