首页 > 其他分享 >手写RPC框架,真不是为了装13!

手写RPC框架,真不是为了装13!

时间:2023-06-11 22:31:57浏览次数:37  
标签:13 mink consumer tian RPC provider 手写 com public


如何设计一个RPC框架

你可能没有被问到过,可能是运气好,也可能是你还没到这个级别。通常月薪20k以上,基本上都会问一些设计性的题目。

站在面试官角度:问这类题目,总比一个八股文强,这里面会涉及到很多技术点。比如:设计模式、通信协议、动态代理、虚拟化、线程池等知识。

好吧,不扯远了,我们开始聊今天的话题。

RPC全程为Remote procedure call 远程过程调用,这几个字很多人可能并不是特别的理解,再简单的说就是:

像调用本地方法一样调用远程服务

比如,下面一个案例:一个用户操作服务:

public interface UserService{
    String findUserNameById(Integer userId);
}
@Service
public class UserServiceImpl implements UserService{
    String findUserNameById(Integer userId){
        //查数据或查缓存获取到用户名
        return "田哥"
    }
}

现在一个controller里想调用UserServiceImpl的findUserNameById方法获取用户名。

@RestController
public class UserController{
    @Resource
    private UserService userService;
    
    @GetMapping("/test")
    public String test(){
        return userService.findUserNameById(1);
    }
}

假设UserController、UserServiceImpl、UserService三个类都在同一个项目里,controller想调用findUserNameById方法是非常简单的。

但是,如果controller是另外一个项目,也想像上面那样调用(细微的不同,感觉还是一样),这时候我们就可以用到了RPC框架。

1、需要把接口UserService放在一个单独项目里,我们通常称之为api项目。

2、需要把UserServiceImpl放在一个单独项目里,我们通常称之为叫做provider项目。

3、controller嘛,通过是web之类的(consumer)项目里

4、把api打成jar包,然后consumer项目、provider项目都引用它。

市面上的RPC框架,比如说:Dubbo (阿里)、Thrift(FaceBook)、gRpc(Google)、brpc (百度)等都在不同侧重点去解决最初的目的,有的想极致完美,有的追求极致性能,有的偏向极致简单。

RPC原理

回到前面我们说的像调用本地一样的调用远程服务,到底需要哪些技术支撑呢?

  • 动态代理,因为我们consumer项目里只有接口UserService定义,没有实现类,想要调用一个接口的方法?那就只能搞个代理对象了。
  • 编解码,也就是consumer需要把请求参数传到provider里去,网络传输过程先把我们的参数进行编码,然后传到provider,provider再对传过来的参数进行解码。
  • 网络通信,跨进程肯定就会涉及到网络通信。
  • 网络传输协议,consumer和provider 对于来回的参数信息,肯定得有有个标准,你按照什么样的方式传给我,不然我怎么知道你想表达什么。
  • 序列化和反序列化
  • 数据压缩,在进行数据网络传输时,如果数据太大了,我们得考虑能不能对数据进行压缩。
  • 注册中心,如果provider 进行集群部署(同样服务部署多个机器上),这时候我们需要在consumer进行手工维护,如果量一旦大起来了,这工作量可想而知。
  • 动态感知服务上下线,如果provider 存在宕机或者部署了新的节点,这时候consumer就需要知道哪些服务下线了,哪些服务上线了。
  • 动态路由,如果provider 进行集群部署(同样服务部署多个机器上),我们不可能让consumer都落在同一个节点上,这样资源不能充分利用了,古代皇帝的雨露均沾。动态路由那就会涉及到各种各样的算法,比如随机,轮询,权重等。

为什么需要注册中心,我之前分享过:

造轮子

根据上面的这些原理,田哥也搞了一个RPC框架,命名为mink(一个动物的名称)。

--mink
----mink-rpc rpc基本功能
----mink-registry 服务注册与发现
----mink-spring 集成SpringBoot

然后,我们把mink-spring打成jar包,服务发布和服务引用都把这个jar给依赖进去。

手写RPC框架,真不是为了装13!_rpc

手写RPC框架,真不是为了装13!_rpc_02

手写RPC框架,真不是为了装13!_java_03


框架使用

上面,我们创造了轮子,下面,我们就来看如何使用。

手写RPC框架,真不是为了装13!_java_04

mink-demo就是一个Spring Boot项目,有三个module。

手写RPC框架,真不是为了装13!_java_05


我们把mink-api打成jar包,共consumer和provider使用。

也就是我们在consumer和provider都引入:

<dependency>
    <groupId>com.tian</groupId>
    <artifactId>mink-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

下面,我们来看看provider端的代码:

<dependency>
   <groupId>com.tian</groupId>
   <artifactId>mink-spring</artifactId>
   <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
   <groupId>com.tian</groupId>
   <artifactId>mink-api</artifactId>
   <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter</artifactId>
   <version>2.5.4</version>
</dependency>

我们的mink框架,只需要引入mink-spring依赖即可。

接着就是properties的配置:

mink.rpc.servicePort=20880
mink.rpc.registryType=0
mink.rpc.registryAddress=127.0.0.1:2181

再来看看具体服务实现类:

package com.tian.service;

import com.tian.annotation.MinkService;

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @description 用户服务
 * @createTime 2022年08月23日 18:16
 */
@MinkService
public class UserServiceImpl implements UserService {
    @Override
    public String findUserNameByiD(Integer id) {
        System.out.println("服务调用");
        return "tian";
    }
}

这一步,我们只需要在实现类上加上注解@MinkService即可。

最后就是项目启动类:

@ComponentScan(basePackages = {"com.tian.service","com.tian"})
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

我们启动项目(注册中心用的是zookeeper):

手写RPC框架,真不是为了装13!_java_06


从日志中可以看出,我们的服务已经成功注册到注册中心了。

下面,我们来看看consumer端代码。

首先来看看依赖:

<dependency>
   <groupId>com.tian</groupId>
   <artifactId>mink-spring</artifactId>
   <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
   <groupId>com.tian</groupId>
   <artifactId>mink-api</artifactId>
   <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter</artifactId>
   <version>2.5.4</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <version>2.5.4</version>
</dependency>

这依赖也很简单,没什么好说的。

再来看看properties配置项:

mink.rpc.registryType=0
mink.rpc.registryAddress=127.0.0.1:2181

server.port=8090

是不是也很简单?

再来看看我们的controller代码:

package com.tian.controller;

import com.tian.annotation.MinkReference;
import com.tian.service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @description 消费端
 * @createTime 2022年08月23日 23:08
 */
@RestController
public class UserController {

    @MinkReference
    private UserService userService;

    @RequestMapping("/test")
    public String test() {
        return userService.findUserNameByiD(1);
    }
}

需要用到对应服务,只需要添加注解@MinkReference即可。

最后就是项目启动类,简单的不行。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

启动日志:

手写RPC框架,真不是为了装13!_java_07

访问:http://localhost:8090/test

手写RPC框架,真不是为了装13!_spring boot_08

成功!整个过程,非常轻松地集成mink框架并在业务代码中使用。

总结起来,其实就三步:

1、pom中添加依赖

2、properties文件中配置注册中心信息

3、使用的时候加上注解@MinkReference或 @MinkService即可

总结

mink框架用到的技术点(后期会加入线程池,其他序列化技术、监控):

工厂模式、动态代理、模板方法模式、注册式饿汉式单列模式

底层通信用的是netty框架、自定义协议、序列化与反序列化(java原生态和json)、编解码

集成SpringBoot、Spring扩展点 

集成zookeeper实现服务注册、服务上下线动态感知

自定义注解、自定义String工具类、自定义Collection工具类、自定义数组工具类等。

自己动手写完这个mink框架,面试还是可以吹吹,同时,也意味着你对上面技术点掌握到手,你觉得面试官还不喜欢吗?

我们不是为了装逼而造轮子,而是想通过造轮子这个过程,把我们学到的技术给用进去。学到的技术如果不用,那永远是个半吊子,并且很快就忘掉。

在实现的过程中,有任何问题都可以找我探讨,一方面完善mink框架,一方面技术提升!


标签:13,mink,consumer,tian,RPC,provider,手写,com,public
From: https://blog.51cto.com/u_11702014/6459173

相关文章

  • 使用外置存储设备扩展exroot(MT1300)
    环境说明:GL-INETMT1300设备一台8GU盘一个,已经格式化文件系统为EXT41.安装相关工具opkgupdateopkginstallblock-mountkmod-fs-ext4e2fsprogsfdisk 2.修改fstab配置文件,更改现有文件系统的挂载点DEVICE="$(sed-n-e"/\s\/overlay\s.*$/s///p"/etc/mtab)"uci-q......
  • 2024备考Week13
    一、本周总结:使用时间:(先目标30h,刚达到,下周目标35h)总计30h,数学10h40min,专业课8h50min,英语9h30min。整体效率比之前高,在逐步恢复学习状态中。二、存在问题:1.数学所花时间不够,英语所花时间太多,时间分配不均;2.学习时间有待进一步挖掘,还有很大提升空间,争取下周35h,逐步提升学习状态。......
  • Google 开源 RPC 框架 gRPC
    gRPC是Google开源的一款高性能RPC框架,前两天发布了1.0版本。RPC(RemoteProcedureCall)即远程过程调用,通过RPC,客户端的应用程序可以方便地调用另外一台机器上的服务端程序,因而常被应用于分布式系统中。RPC框架通常使用IDL(InterfaceDescriptionLanguage)定义客户......
  • 第13章 代理模式(Proxy Pattern)
    代理模式(ProxyPattern)——.NET设计模式系列之十四Terrylee,2006年5月摘要:在软件系统中,有些对象有时候由于跨越网络或者其他的障碍,而不能够或者不想直接访问另一个对象,如果直接访问会给系统带来不必要的复杂性,这时候可以在客户程序和目标对象之间增加一层中间层,让代理对象来代替目标......
  • 2013-7-12学习笔记
    形参改变影响实参主要应用在以下几个方面:1.用于外部函数要交换主函数中的两个值,可以用指针来实现,但通过指针来实现主函数中两个数的交换必须要知道,哪些类型属于值传递,哪些类型属于地址传递,对于值传递的那些类型(例如:整形、字符型)才有必要用指针来实现,因为数组,字符串属于地址传递类......
  • 算法学习day52动态规划part13-674、300、718
    packageLeetCode.DPpart13;/***674.最长连续递增序列*给定一个未经排序的整数数组,找到最长且连续递增的子序列,并返回该序列的长度。*连续递增的子序列可以由两个下标l和r(l<r)确定,*如果对于每个l<=i<r,都有nums[i]<nums[i+1],*那么子序列[nums[......
  • 9.13 泛型问题引出
    Object可以接受所有的数据类型,这是泛型产生的主要依据~~~classPoint{privateObjectx;privateObjecty;publicvoidsetX(Objectx){this.x=x;}publicvoidsetY(Objecty){this.y=y;}publicObjectgetX(){......
  • 1327. 列出指定时间段内所有的下单产品
    【题目】表:Products+------------------+---------+|ColumnName     |Type   |+------------------+---------+|product_id      |int    ||product_name    |varchar||product_category|varchar|+------------------+---------+produc......
  • 13.双向链表的算法实现
      单链表中每个结点除了存储自身数据之后,还存储了下一个结点的地址,因此可以轻松访问下一个结点,以及后面的后继结点,但是如果想访问前面的结点就不行了,再也回不去了。  例如删除结点p时,要先找到它的前一个结点q,然后才能删掉p结点,单向链表只能往后走,不能向前走。如果需要向前走......
  • rpc简介
    10分钟带你学会分布式网络技术RPCRPC,RemoteProcedureCall,中文称作远程过程调用”,通过网络从远程计算机上请求服务。RPC是一种技术思想,是一种普适性的方法,而非一种规范和协议。所以广义上来说,http协议也属于rpc。狭义上,我认为rpc就是让本地调用远程方法变得简单。grpc就是基于h......