项目3:从0开始的RPC框架(简易版)

一. 基本概念

区别于传统的增删改查型的业务项目,本项目侧重于开发框架,并且涉及架构方面的技术知识点。

1. 什么是RPC?

远程过程调用(Remote Procedure Call),是一种计算机通信协议,它允许程序在不同的计算机之间交互通信,以实现本地调用的效果。

类似于如今的外卖平台,外卖平台出现之前消费者需要到线下店铺购买,耗时耗力。现在通过外卖平台,只需要下单选择商品,无需关心数据在网络中如何传输的、外卖平台是怎么转发的、骑手是如何配送的等事情,等待商品即可。

进一步说明:可以让客户端在不清楚调用细节的情况下,实现对远程计算机上某个方法或服务的调用,就像调用本地方法一样。

RPC既然是一种计算机协议,那么就需要开发者去遵循这套规范进行实现,目前市面上常见的RPC框架有:Dubbo、GRPC等。

2. 为什么需要RPC?

在现实场景中,随着企业中业务量和应用功能模块的增多,单机部署运行已经无法满足需求,可能需要将业务或功能模块拆分到不同的机器和服务器上,以减轻压力,但有些功能是通用的,因此可以将这些通用的模块划分成公共模块,组成独立的一部分。

RPC就允许一个程序(服务消费者)像调用自己的本地方法一样去调用这个公共模块的接口(服务提供者),不需要了解数据的传输处理过程、底层网络通信的细节等。RPC已经帮助做完了这些事情。

举例:

项目A提供了点餐服务,项目B需要调用点餐服务完成下单。

//伪代码
interface OrderService{
     //点餐,返回orderId
     long order(参数1,参数2,参数3);
}

如果没有RPC,并且由于项目A和项目B都是独立的系统,所以不能像SDK一样作为依赖包直接引入。那么就需要项目A提供Web服务,同时编写一个点餐服务接口提供给外界,比如访问http://xxx.com 就能调用点餐服务。之后项目B作为服务消费者,需要自己构造请求,并通过HttpClient请求上述地址。理论上来说,如果B需要多个第三方服务,那么每个服务和方法的调用都需要编写一个HTTP请求,耗时耗力。

//伪代码
url = "http://xxx.com"
req = new Req(参数1,参数2,参数3)
res = httpClient.post(url).body(req).execute()
orderId = res.data.orderId

而通过RPC框架,项目B可以像调用本地方法一样完成调用,一行代码即可解决。

//伪代码
orderId = orderService.order(参数1,参数2,参数3)

二. 简易版RPC框架

1. 设计流程

(1)现在有一个消费者和一个服务提供者。

(2)消费者想要调用服务,需要提供者启动一个web服务,然后消费者通过请求客户端发送HTTP请求或其它协议的请求来调用。比如请求xxx.com/order地址后,就会调用提供者的order方法。

(3) 但如果提供者提供了多个服务和方法,每个服务和方法都要单独写一个接口,消费者想调用的话需要对每个接口都写一段HTTP调用逻辑。效率很低。

因此,可以提供一个统一的服务调用接口,通过请求处理器根据客户端的请求参数来调用不同的方法。同时在服务提供者程序中维护一个本地服务注册器,用于记录服务和对应实现类的映射。

此外,由于Java对象无法直接在网络中传输,因此需要对传递的参数进行序列化和反序列化。

例如:消费者想要调用orderService接口的order方法,发送请求,参数为service=orderService,method=order,然后请求处理器会根据serv从服务注册器中找到对应的服务实现类,并通过Java的反射机制调用method指定的方法。

(4)再进一步,为了简化消费者发送请求的代码,实现类似本地的一行调用。可以基于代理模式,为消费者要调用的接口生成一个代理对象,由代理对象完成请求和响应的过程。

至此,简易版RPC框架完成:

 网上的一个关于RPC响应流程的图(参考):

 2. 构造初始项目

(1)项目初始化

简易版RPC框架目录:

  • common模块包含示例代码的公共依赖,比如接口、Model等。
  • consumer模块包含示例消费者代码。
  • provider模块包含例服务提供者代码。
  • khr-rpc-easy模块是简易版RPC框架。

简易版RPC框架侧重于整个调用流程的实现。

(2)common模块

公共模块需要同时被消费者和服务提供者引入,主要包含和服务相关的接口和数据模型代码。

用户实体类User:

package com.khr.example.common.model;

import java.io.Serializable;

/**
 * 用户
 */

// 继承Serializable,用于指示该类实例可以被序列化和反序列化。
// 这意味着User对象可以在程序之间进行持久化存储或在网络通信中传输。
public class User implements Serializable{

    private String name;

    public  String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }
}

用户接口服务UserService,提供一个获取用户的方法:

package com.khr.example.common.service;

import com.khr.example.common.model.User;

/**
 * 用户服务
 */
public interface UserService {
    /**
     * 获取用户
     *
     * @param user
     * @return
     */
    User getUser(User user);

}

(3)provider模块

服务提供者是真正实现了接口的模块。

先引入hutool、lombok依赖。

服务实现类UserServiceImpl:

实现公共模块中定义的用户接口服务UserService,功能是打印用户名称并返回参数中的用户对象。

package com.khr.example.provider;

import com.khr.example.common.model.User;
import com.khr.example.common.service.UserService;


/**
 * 用户服务实现类
 */
public class UserServiceImpl implements UserService {

    public User getUser(User user){
        System.out.println("用户名:"+ user.getName());
        return user;
    }
}

服务提供者启动类EasyProviderExample:

提供服务的代码之后再补充。

package com.khr.example.provider;

import com.khr.example.common.service.UserService;
import com.khr.krpc.server.HttpServer;
import com.khr.krpc.server.VertxHttpServer;
import com.khr.krpc.registry.LocalRegistry;

/**
 * 简易服务提供者示例
 */
public class EasyProviderExample {

    public static void  main(String[] args){
        //提供服务
        
    }
}

(4)consumer模块

消费者模块是需

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Phoenixxxxxxxxxxxxx

感谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值