首頁  >  文章  >  手寫RPC框架,真不是為了裝13!

手寫RPC框架,真不是為了裝13!

Java后端技术全栈
Java后端技术全栈轉載
2023-08-16 17:01:18707瀏覽

面試中,很容易被面試官問到:

如何設計一個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框架,真不是為了裝13!

手寫RPC框架,真不是為了裝13!


框架使用

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

手寫RPC框架,真不是為了裝13!

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

手寫RPC框架,真不是為了裝13!


我们把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!


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

下面,我们来看看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>

   org.springframework.boot
   spring-boot-starter-web
   2.5.4

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

再来看看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!

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

手寫RPC框架,真不是為了裝13!

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

总结起来,其实就三步:

1、pom中添加依赖

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

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

以上是手寫RPC框架,真不是為了裝13!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:Java后端技术全栈。如有侵權,請聯絡admin@php.cn刪除

相關文章

看更多