1. 1 不可撤销
  2. 2 小年兽 程嘉敏
  3. 3 手放开 李圣杰
  4. 4 迷人的危险3(翻自 dance flow) FAFA
  5. 5 山楂树之恋 程佳佳
  6. 6 summertime cinnamons / evening cinema
  7. 7 不谓侠(Cover 萧忆情Alex) CRITTY
  8. 8 神武醉相思(翻自 优我女团) 双笙
  9. 9 空山新雨后 音阙诗听 / 锦零
  10. 10 Wonderful U (Demo Version) AGA
  11. 11 广寒宫 丸子呦
  12. 12 陪我看日出 回音哥
  13. 13 春夏秋冬的你 王宇良
  14. 14 世界が终わるまでは… WANDS
  15. 15 多想在平庸的生活拥抱你 隔壁老樊
  16. 16 千禧 徐秉龙
  17. 17 我的一个道姑朋友 双笙
  18. 18 大鱼  (Cover 周深) 双笙
  19. 19 霜雪千年(Cover 洛天依 / 乐正绫) 双笙 / 封茗囧菌
  20. 20 云烟成雨(翻自 房东的猫) 周玥
  21. 21 情深深雨濛濛 杨胖雨
  22. 22 Five Hundred Miles Justin Timberlake / Carey Mulligan / Stark Sands
  23. 23 斑马斑马 房东的猫
  24. 24 See You Again Wiz Khalifa / Charlie Puth
  25. 25 Faded Alan Walker / Iselin Solheim
  26. 26 Natural J.Fla
  27. 27 New Soul Vox Angeli
  28. 28 ハレハレヤ(朗朗晴天)(翻自 v flower) 猫瑾
  29. 29 像鱼 王贰浪
  30. 30 Bye Bye Bye Lovestoned
  31. 31 Blame You 眠 / Lopu$
  32. 32 Believer J.Fla
  33. 33 书信 戴羽彤
  34. 34 柴 鱼 の c a l l i n g【已售】 幸子小姐拜托了
  35. 35 夜空中最亮的星(翻自 逃跑计划) 戴羽彤
  36. 36 慢慢喜欢你 LIve版(翻自 莫文蔚) 戴羽彤
  37. 37 病变(翻自 cubi) 戴羽彤
  38. 38 那女孩对我说 (完整版) Uu
  39. 39 绿色 陈雪凝
  40. 40 月牙湾 LIve版(翻自 F.I.R.) 戴羽彤
夜空中最亮的星(翻自 逃跑计划) - 戴羽彤
00:00 / 04:10

夜空中最亮的星 能否听清

那仰望的人 心底的孤独和叹息

夜空中最亮的星 能否记起

那曾与我同行 消失在风里的身影

我祈祷拥有一颗透明的心灵

和会流泪的眼睛

给我再去相信的勇气

越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请指引我靠近你

夜空中最亮的星 是否知道

那曾与我同行的身影 如今在哪里

夜空中最亮的星 是否在意

是等太阳先升起 还是意外先来临

我宁愿所有痛苦都留在心底

也不愿忘记你的眼睛

哦 给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行 哒~

我祈祷拥有一颗透明的心灵

和会流泪的眼睛 哦

给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行

基于redis实现rpc服务注册

前言

昨天我们手写了一个简单到不能再简单的rpc服务,对rpc服务有了一个基本的认知,但昨天的实现太过简单,甚至都算不上rpc,因为rpc服务的核心是动态代理,但是今天我想先实现rpc的注册,今天的服务注册我没有用zk,而是redis,用redis的目的就是让各位小伙伴都能真正明白,任何组件的选用都不是必须的,而是一种更优的选择。

服务注册

首先,我们要定义以下几个注解,这些注解的作用就是辅助我们完成服务的注册

定义注解

第一个注解和我们syske-boot中的注解作用一致,主要是为了扫描类

/**
 * rpc扫描注解
 *
 * @author sysker
 * @version 1.0
 * @date 2021-06-16 23:15
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcComponentScan {
    String[] value();
}

这个注解是标记我们的服务提供者,方便我们针对服务提供者进行注册操作

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcProvider {
}

然后就是服务消费者,和服务提供者的注解类似,就是为了标记消费者

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcCustomer {
}

最后一个注解是加在属性上的,主要是为了方便后期实现动态代理

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcClient {
}

包扫描

我们这里直接就把之前写的包扫描器直接用起来了,这里会根据RpcComponentScan注解指定的包名进行扫描

public class ClassScanner {
    private static final Logger logger = LoggerFactory.getLogger(ClassScanner.class);

    private static Set<Class> classSet = Sets.newHashSet();

    private ClassScanner() {
    }

    public static Set<Class> getClassSet() {
        return classSet;
    }

    /**
     * 类加载器初始化
     *
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static void init(Class aClass) {
        try {
            // 扫描包
            componentScanInit(aClass);
        } catch (Exception e) {
            logger.error("ClassScanner init error: ", e);
        }
    }

    /**
     * 扫描指定的包路径,如果无该路径,则默认扫描服务器核心入口所在路径
     *
     * @param aClass
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static void componentScanInit(Class aClass) throws IOException, ClassNotFoundException {
        logger.info("componentScanInit start init……");
        logger.info("componentScanInit aClass: {}", aClass);
        Annotation annotation = aClass.getAnnotation(RpcComponentScan.class);
        if (Objects.isNull(annotation)) {
            Package aPackage = aClass.getPackage();
            scanPackage(aPackage.toString(), classSet);
        } else {
            String[] value = ((RpcComponentScan) annotation).value();
            for (String s : value) {
                scanPackage(s, classSet);
            }
        }
        logger.info("componentScanInit end, classSet = {}", classSet);
    }

    /**
     * 扫描指定包名下所有类,并生成classSet
     *
     * @param packageName
     * @param classSet
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static void scanPackage(String packageName, Set<Class> classSet)
            throws IOException, ClassNotFoundException {
        logger.info("start to scanPackage, packageName = {}", packageName);
        Enumeration<URL> classes = ClassLoader.getSystemResources(packageName.replace('.', '/'));
        while (classes.hasMoreElements()) {
            URL url = classes.nextElement();
            File packagePath = new File(url.getPath());
            if (packagePath.isDirectory()) {
                File[] files = packagePath.listFiles();
                for (File file : files) {
                    String fileName = file.getName();
                    if (file.isDirectory()) {
                        String newPackageName = String.format("%s.%s", packageName, fileName);
                        scanPackage(newPackageName, classSet);
                    } else {
                        String className = fileName.substring(0, fileName.lastIndexOf('.'));
                        String fullClassName = String.format("%s.%s", packageName, className);
                        classSet.add(Class.forName(fullClassName));
                    }
                }
            } else {
                String className = url.getPath().substring(0, url.getPath().lastIndexOf('.'));
                String fullClassName = String.format("%s.%s", packageName, className);
                classSet.add(Class.forName(fullClassName));
            }
        }
    }
}

目的就是扫描我们的服务提供者,方便注册服务的时候使用。

服务提供者注册

首先我们要先通过RpcProvider注解拿到我们的服务提供者,然后组装我们的的注册信息。

    private static void initServiceProvider() {
        Set<Class> classSet = ClassScanner.getClassSet();
        classSet.forEach(c -> {
            Annotation annotation = c.getAnnotation(RpcProvider.class);
            if (Objects.nonNull(annotation)) {
                Method[] methods = c.getDeclaredMethods();
                for (Method method : methods) {
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    String methodName = method.getName();
                    try {
                        Object newInstance = c.newInstance();
                        RpcRegisterEntity rpcRegisterEntity = new RpcRegisterEntity(c.getName(), methodName, parameterTypes, newInstance);
                        Class[] interfaces = c.getInterfaces();
                        String interfaceName = interfaces[0].getName();
                        RedisUtil.record2Cache(String.format(PROVIDER_KEY, interfaceName), JSON.toJSONString(rpcRegisterEntity));
                        System.out.println(JSON.toJSONString(rpcRegisterEntity));
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }            
        });
    }

注册信息注册完成后,我们将注册信息写入redis

服务消费者注册

消费者注册也是类似的方法

private static void initServiceCustomer() {
        final String CUSTOMER_KEY = "%s:customer";
        final String PROVIDER_KEY = "%s:provider";
        Set<Class> classSet = ClassScanner.getClassSet();
        classSet.forEach(c -> {
            Field[] declaredFields = c.getDeclaredFields();
            for (Field field : declaredFields) {
                try {
                    RpcClient annotation = field.getAnnotation(RpcClient.class);
                    if (Objects.nonNull(annotation)) {
                        Class<?> fieldType = field.getType();
                        String name = fieldType.getName();
                        RedisUtil.record2Cache(String.format(CUSTOMER_KEY, name), c.getName());
                        // String serviceObject = RedisUtil.getObject(String.format(PROVIDER_KEY, name));
                        // RpcRegisterEntity rpcRegisterEntity = JSON.parseObject(serviceObject, RpcRegisterEntity.class);
                        // field.set(c.newInstance(), rpcRegisterEntity.getNewInstance());
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
    }

本来打算在消费者注册完直接给接口赋值的(注释部分),但是在实际操作的时候,发现这种方式行不通,因为服务提供者和消费者是不同的应用,序列化之后的服务提供者的实例(rpcRegisterEntity.getNewInstance())是没法强转的,控制台一直会报类型不匹配的错误:

而且,后来我想了下,如果这种方式真的实现了,那就没socket什么事了,还能叫rpc吗?所以要想实现,真正的动态调用,还是要通过动态代理。

这么说来,昨天实现的也不能叫rpc,因为没有实现动态代理。

测试

分别运行服务提供者和消费者,然后我们去redis看下,服务是否已经注册上,如果看到如下所示,表面服务已经成功注册:

同时,我发现服务提供者的实例信息是空的,着有进一步表明,这种直接反射赋值方式行不通,只有动态代理才能拯救我们的rpc服务。

总结

不得不说,相比于zkredis确实不适合做服务注册,毕竟zk的树形结构看起来就很友好,但是我暂时不考虑换成zk,等动态代理实现了再说。

另外,在实际测试中,我发现除了classFUllName之外,其他的参数都是冗余的,但是像服务的地址、端口等比较重要的信息又没有,所以后面要把服务注册的entity优化下,暂时就先这样。

明天,我明天打算分享动态代理的实现过程,这一块实现了,rpc框架就成了,具体的明天再说,好了,今天就到这里吧!

完整项目开源地址如下,感兴趣的小伙伴可以去看下,后续我们会继续实现相关功能,比如整合注册中心、实现动态代理等待:

https://github.com/Syske/syske-rpc-server
posted @ 2021-06-17 13:41  云中志  阅读(438)  评论(0编辑  收藏  举报