Java使用Netty实现简单的RPC

Wesley13
• 阅读 661

造一个轮子,实现RPC调用

在写了一个Netty实现通信的简单例子后,萌发了自己实现RPC调用的想法,于是就开始进行了Netty-Rpc的工作,实现了一个简单的RPC调用工程。

如果也有兴趣动手造轮子的同学,可以先看看之前写的 使用Java实现Netty通信 这篇博客。

本文源地址:造一个RPC的轮子


准备

首先,你需要明白下列知识。

Netty

处理服务之间的通信。

Zookeeper

服务注册与发现。

SpringBoot

目前单单只是作为启动项目。

Cglib Proxy & Jdk Reflect

使用代理实例化暴露的接口,通过反射调用接口方法的实现。


RPC处理流程

Java使用Netty实现简单的RPC

  1. 服务提供者通过Netty进行对应的端口暴露。
  1. 同时提供者将需要暴露的服务信息注册到Zookeeper,Zookeeper注册的节点信息为接口的类路径,注册的Data为暴露的端口,并且需要将对应的接口实现类在ApplicationContext上下文中进行保存。
  1. 此时消费者启动后,会根据对应接口的类路径在Zookeeper进行Discover。
  1. 根据对应接口的类路径在Zookeeper中通过ReadData获取到对应暴露的端口信息。
  1. 拿到了端口信息,通过Netty发起请求。
  1. Netty发起请求接收后,通过之前的ApplicationContext上下文调用对应的接口方法。
  1. ApplicationContext调用成功后,Netty将响应的结果返回。
  1. 服务消费者得到响应结果,RPC流程结束。

动手

使用Java实现Netty通信 这篇文章的基础上,我们新建下列工程信息。

Java使用Netty实现简单的RPC

rpc-sample-api

依赖信息

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>netty-rpc-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

代码

只需要定义一个HiService接口。

public interface HiService {

    public String hi(String msg);
}

rpc-sample-server

依赖信息

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>rpc-sample-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>netty-rpc-server</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

application.yaml

register.address: 127.0.0.1:3000

代码

  • 首先实现HiService接口。

    @RpcServer(cls = HiService.class) public class HiServiceImpl implements HiService {

    public String hi(String msg) {
        return "hello, I'm Rpc, I want say : " + msg;
    }
    

    }

  • Server类。

    @Component public class Server implements ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(Server.class);
    
    @Value("${register.address}")
    private String registerAddress;
    
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> serviceBean = new HashMap<String, Object>();
    
        Map<String, Object> objectMap = applicationContext.getBeansWithAnnotation(RpcServer.class);
    
        for (Object object : objectMap.values()) {
            try {
                RpcServer annotation = object.getClass().getAnnotation(RpcServer.class);
    
                serviceBean.put("/yanzhenyidai/" + annotation.cls().getName(), object);
    
                String[] split = registerAddress.split(":");
    
                new NettyServer(split[0], Integer.valueOf(split[1])).server(serviceBean);
            } catch (Exception e) {
                logger.error("[server-start] fail ", e);
            }
        }
    }
    

    }

实现 ApplicationContextAware 以获取到Spring上下文,通过扫描RpcServer注解,得到本次需要暴露的服务信息,并且开启NettyServer的端口服务暴露及Zookeeper注册。

  • Application启动类

    @SpringBootApplication public class RpcServerApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(RpcServerApplication.class)
                .web(WebApplicationType.NONE)
                .run(args);
    }
    

    }

开启SpringBoot无Web启动。

rpc-sample-client

依赖

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>netty-rpc-client</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>rpc-sample-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.2</version>
</dependency>

<dependency>
    <groupId>asm</groupId>
    <artifactId>asm</artifactId>
    <version>3.3.1</version>
</dependency>

代码

  • Client

    public class Client {

    public <T> T create(final Class<?> cls) {
    
        return (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, new InvocationHandler() {
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
    
                Request request = new Request();
                request.setInterfaceName("/yanzhenyidai/" + cls.getName());
                request.setRequestId(UUID.randomUUID().toString());
                request.setParameter(objects);
                request.setMethodName(method.getName());
                request.setParameterTypes(method.getParameterTypes());
    
                Response response = new NettyClient().client(request);
                return response.getResult();
            }
        });
    }
    

    }

使用Cglib动态代理先实例化接口信息,在调用的时候,通过NettyClient发送请求到NettyServer,由NettyServer处理发现Zookeeper节点以及反射调用接口实现方法。

  • context.xml

注入Client的bean对象信息。

  • Application

    public class RpcClientApplication {

    public static void main(String[] args) {
    
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
    
        Client client = context.getBean(Client.class);
    
        HiService hiService = client.create(HiService.class);
        String msg = hiService.hi("msg");
        System.out.println(msg);
    
    }
    

    }

运行结果

Java使用Netty实现简单的RPC


总结

以上只是一个简单的RPC过程,相对于Dubbo RPC会更加直观的看明白,希望能对大家有所帮助作用,也欢迎大家和我一起造轮子。😝

本项目Github地址:Netty-RPC

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Netty 实现简单的RPC远程调用
RPC又称远程过程调用,我们所知的远程调用分为两种,现在在服务间通信的方式也太多已这两种为主1.是基于HTTP的restful形式的广义远程调用,以springboot的feign和restTemplate为代表,由于采用的协议是HTTP的7层调用协议,并且协议的参数和响应序列化基本以JSON格式和XML格式为主。2.是基于TCP的狭义的RPC远程调
Stella981 Stella981
2年前
Netty 入门与实战:仿写微信 IM 即时通讯系统
作为一个学Java的,如果没有研究过Netty,那么你对Java语言的使用和理解仅仅停留在表面水平,如果你要进阶,想了解Java服务器的深层高阶知识,Netty绝对是一个必须要过的门槛。有了Netty,你可以实现自己的HTTP服务器,FTP服务器,UDP服务器,RPC服务器,WebSocket服务器,Redis的Prox
Stella981 Stella981
2年前
Netty RPC的简易DEMO
这个是rpc远程调用的简单demo:Consumer通过rpc远程调用Provider的服务方法sayHelloWorld(Stringmsg),然后Provider返回""HelloWorld"给Consumer。这里采用netty来实现远程通信实现rpc调用,消费者通过代理来进行远程调用远程服务。本文涉及的知识点有代理模式,jd
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
2个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这