不了解RPC?那我们就亲手实现一个吧,第二篇来了!

202 阅读1分钟

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

上一篇文章说了下RPC框架的实现,下面我们就来看下怎么使用我们自己写的RPC框架。

1. 使用 RPC 框架

RPC 框架的客户端和服务端的基本功能已经实现,下面我们使用刚刚实现的 RPC 框架 来调用一下 MyService.sayHello(String name) 这个接口

首先我们使用一个线程来启动服务端:

    private static void startProduct() {
        //通过一个线程启动服务端
        String host = "localhost";
        int port = 8878;

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("服务端启动........");
                    RpcProducer.produce(host, port);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

下面我们使用 刚刚实现的 RPC 框架来调用服务端的接口

    private static void client() {
        LocalAgent<MyService> serviceLocalAgent = new LocalAgent<>();
        MyService myService = serviceLocalAgent.importer(MyServiceImpl.class,
                new InetSocketAddress("localhost",8878));

        System.out.println(myService.sayHello("RPC"));
    }

在客户端,我们传了一个服务端实现类的类型、一个服务端的地址,然后就可以调用服务端的接口了。注意:我们并没有在客户端创建 接口的具体实现对象,而仅仅是把需要调用的 Class 通过 Socket 发送给了服务端。

2. 添加注册中心

考虑一下上面的代码,我们服务端的地址是写死在客户端和服务端的代码里的,如果服务比较多,而且每个服务的地址都不一样,直接写死是很难维护的,所以我们需要一个注册中心来管理每个服务的地址。

下面是一个最简单的注册中心:

public class RegistrationCenter {

    private Map<String, InetSocketAddress> serviceMap = new HashMap<>();

    public void register(String serviceName, String host, int port) {
        serviceMap.put(serviceName, new InetSocketAddress(host, port));
    }

    public InetSocketAddress getService(String serviceName) {
        return serviceMap.get(serviceName);
    }
}

注册中心维护服务名称与服务地址的映射。

我们修改一下服务端的启动代码,当服务启动之后,向注册中心注册服务。

    private static void startProduct(RegistrationCenter center) {
        //通过一个线程启动服务端
        String host = "localhost";
        int port = 8878;

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("服务端启动........");
                    RpcProducer.produce(host, port);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        //向注册中心注册服务
        center.register("MyService.sayHello", host, port);
    }

修改一下客户端的调用方式,调用服务时,根据服务名从注册中心获取服务地址。:

    private static void client(RegistrationCenter center) {
        LocalAgent<MyService> serviceLocalAgent = new LocalAgent<>();
        MyService myService = serviceLocalAgent.importer(MyServiceImpl.class,
                center.getService("MyService.sayHello"));

        System.out.println(myService.sayHello("RPC"));
    }

3. 总结

这就是 RPC 框架的基本原理,通过 Socket 建立通信,通过序列化与反序列实现数据传输,使用反射执行具体的服务。

假设你对 RPC 还是没有什么概念,也没关系,但是你需要记住下面三个概念:

  1. 生产者:或者说是服务端,更具体点就是上面的ProducerAgent类,

    负责解析消费者发送的参数,通过反射调用对应的服务,将结果序列化后发送给消费者。

  2. 消费者:就是我们的客户端,对应LocalAgent

    负责将需要调用的服务信息发送给生产者,将结果反序列化后获得实际的执行结果。

  3. 注册中心:提供服务的注册和发现的功能。对应RegistrationCenter

    责管理服务与地址的映射。