一个简单RPC的实现

784 阅读3分钟

RPC框架Pigeon

Pigeon的由来

之前就有使用过相关RPC的框架,如dubbo,就想象着它是如何来实现的,能让直接调用类的方法就能实现远程调用然后获取结果啥的,看着dubbo的架构图:

image.png

看着挺简单的,就一个注册中心,然后提供者注册上去,消费者去寻找相关的提供者然后进行通信,提供者再调用实现类的方法来执行获取结果返回给消费者。

然后之前也简略的看过netty的源码,但是自己没有实际的应用场景,就想着来写一个简单的rpc就实现,也能够实践一下netty.

rpc通信方式之前就有想法怎么来实现,但是没场景就没写的,然后看到了seata通信的时候,和我之前的想法是差不多一致的,但是实现的话肯定我就弱爆了,这个rpc也借鉴了seata的相关实现。

写Pigeon遇到的一些问题

因为该rpc就使用了一天的时间来完成,然后混了太久的crud,好多知识点也不太熟练了,遇到了大大小小的问题。

刚开始遇到的就是提供者应该注册什么样的数据到注册上去,是以接口为粒度,还是以服务为粒度,以服务为粒度的话,还要注册接口与服务的映射关系,就没这么选择了,最后选的是以接口为粒度。

然后这个服务的元数据是什么呢,比如ip port什么的,因为只是简单的写一个,就没弄那么麻烦,直接就是ip:port了;

然后就是注册中心,选型有很多,比如zookeeper,nacos.. ,该怎么抽象出来,能够更好的扩展,目前只写了zookeeper的实现。

然后就是通信的一些问题了,协议怎么搞,怎么编码节码,是使用json序列话呢,还是java序列话呢。

最后整合spring的时候也遇见了一些小插曲,不过以很笨的方式解决了.

具体代码实现

image.png

Pigeon类就是屏蔽相关实现,只需要提供一个config就能启动。

public class Pigeon {
    private PigeonConfig pigeonConfig;

    private ServiceRegistry serviceRegistry;

    private RemotingClinet remotingClinet;

    private RemotingServer remotingServer;

    protected PigeonConfig getPigeonConfig() {
        return pigeonConfig;
    }

    protected ServiceRegistry getServiceRegistry() {
        return serviceRegistry;
    }

    protected RemotingClinet getRemotingClinet() {
        return remotingClinet;
    }

    protected RemotingServer getRemotingServer() {
        return remotingServer;
    }

    public Pigeon(PigeonConfig pigeonConfig) {
        this.pigeonConfig = pigeonConfig;
    }

    public void start() {
        Map<Service, Object> serviceObjectMap = searchPigeon();
        startRegistry(serviceObjectMap.keySet());
        startRemoting(serviceObjectMap);
    }

    protected void startRemoting(Map<Service, Object> serviceObjectMap) {
        remotingClinet = new RemotingClinet();
        remotingServer = new RemotingServer(pigeonConfig.getPort(), serviceObjectMap);
        remotingServer.start();
    }

    protected void startRegistry(Set<Service> serviceList) {
        serviceRegistry = new ZookeeperServiceRegistry(pigeonConfig.getPort(), pigeonConfig.getRegistry());
        serviceRegistry.addAllService(serviceList);
        serviceRegistry.start();
    }


    protected Map<Service, Object> searchPigeon() {
        Map<Service, Object> serviceHandlerMap = new HashMap<>();
        String[] scanPackages = pigeonConfig.getPackages();
        Set<Class<?>> classes = ClassUtil.getClasses(scanPackages);
        classes
                .stream()
                .filter(cl -> !cl.isInterface() && cl.isAnnotationPresent(PigeonService.class))
                .forEach(cl -> {
                    PigeonService annotation = cl.getAnnotation(PigeonService.class);
                    String group = annotation.group();
                    String version = annotation.version();
                    Object handler;
                    try {
                        handler = serviceHandler(cl);
                    } catch (Exception e) {
                        e.printStackTrace();
                        return;
                    }
                    if (handler == null) return;
                    Class<?>[] interfaces = cl.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        if (!(anInterface.equals(Object.class) || anInterface.equals(Serializable.class))) {
                            Service service = new Service(anInterface.getName(), group, version);
                            ServiceHandler serviceHandler = new ServiceHandler();
                            serviceHandler.setService(service);
                            serviceHandler.setHandler(handler);
                            serviceHandlerMap.put(service, handler);
                        }
                    }
                });
        return serviceHandlerMap;
    }


    protected Object serviceHandler(Class<?> cl) throws InstantiationException, IllegalAccessException {
        return cl.newInstance();
    }

    public <T> T getProxy(Class<T> clazz) {
        return getProxy(clazz, "default", "default");
    }


    public <T> T getProxy(Class<T> clazz, String group, String version) {
        Service service = new Service(clazz.getName(), group, version);
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, new ServiceProxy(service, serviceRegistry, remotingClinet));
    }

}

代码也有好几十个类,就不贴了, 点击跳转github仓库

具体演示

先启动一个zookeeper,目前只支持这个。。

在example模块中有个helloworld模块,如果zk是在本地且端口是默认的话那么久可以直接进行demo演示,不是的话就修改一下配置即可.

提供了基础demo和spring boot整合的demo。

QQ录屏20210508163543.gif

结束语

小伙伴有什么建议可添加下方的联系方式。

QQ: 1448983340

微信: 18390010937

B站直播地址