轻量级RPC设计与实现第二版

在上一个版本中利用netty实现了简单的一对一的RPC,需要手动设置服务地址,限制性较大。
在本文中,利用zookeeper作为服务注册中心,在服务端启动时将本地的服务信息注册到zookeeper中,当客户端发起远程服务调用时,先从zookeeper中获取该服务的地址,然后根据获得的这个地址来利用netty进行网络传送。
在服务端和注册中心之间需要建立监听,当服务信息发生变化或网络连接等问题时需要对注册中心的服务信息进行修改。在本文中创建了服务注册监控中心,利用心跳机制来判断与服务端是否有较稳定的连接,当出现网络不稳定时,则从注册中心中删除属于该服务端的服务信息。在本项目中设定在5分钟内3次以上没有发送心跳包为不稳定状态。

关于心跳机制,之前有一篇文章介绍过:Dubbo心跳机制

zookeeper注册中心

zookeeper是hadoop中的一个重要组件,其主要是作为分布式协调服务
zookeeper采用节点树的数据模型,类似linux文件系统。
每个节点称做一个ZNode,每个ZNode都可以通过路径唯一标识,同时每个节点还可以存储少量数据。
本项目借鉴dubbo的注册中心模型来设计本文的注册中心。
总体上设计了四级节点,在一个节点是一个持久节点/register,表示是记录注册服务的区域。二级节点是服务接口名,三级节点是远程服务ip地址,该节点是临时节点,节点存储的数据是具体的实现类名。

1.png

在客户端会根据服务接口名在注册中心进行查找,得到远程服务ip地址,并根据节点中存储的具体实现类名进行反射。

首先进行zookeeper初始化,利用了CuratorFramework有关类

private static void init() {
        RetryPolicy retryPolicy = new RetryNTimes(ZKConsts.RETRYTIME, ZKConsts.SLEEP_MS_BEWTEENR_RETRY);
        client = CuratorFrameworkFactory.builder().connectString(ZKConsts.ZK_SERVER_PATH)
                .sessionTimeoutMs(ZKConsts.SESSION_TIMEOUT_MS).retryPolicy(retryPolicy)
                .namespace(ZKConsts.WORK_SPACE).build();
        client.start();
    }

服务的注册代码

public static void register(URL url) {
        try {
            String interfaceName = url.getInterfaceName();
            String implClassName = url.getImplClassName();
            Stat stat = client.checkExists().forPath(getPath(interfaceName, url.toString()));
            if (stat != null) {
                System.out.println("该节点已存在!");
                client.delete().forPath(getPath(interfaceName, url.toString()));
            }
            client.create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.EPHEMERAL)
                    //权限控制,任何连接的客户端都可以操作该属性znode
                    .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                    .forPath(getPath(interfaceName, url.toString()), implClassName.getBytes());
            System.out.println(getPath(interfaceName, url.toString()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

根据服务接口名来获取远程服务连接地址

public static URL random(String interfaceName) {
        try {

            System.out.println("开始查找服务节点:" + getPath(interfaceName));
            List<String> urlList = client.getChildren().forPath("/" + interfaceName);
            System.out.println("结果:" + urlList);
            String serviceUrl = urlList.get(0);
            String[] urls = serviceUrl.split(":");
            String implClassName = get(interfaceName, serviceUrl);
            return new URL(urls[0], Integer.valueOf(urls[1]), interfaceName, implClassName);
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
        }
        return null;
    }

注册中心与服务端进行连接时需要判断是否维持了稳定的连接,如果服务端出现宕机等情况时需要从注册中心中删除这些服务。
以前的一些处理机制,有session机制和wacher机制。
session机制
每个zookeeper注册中心与服务端进行连接时会创建一个session,在设置的sessionTimeout内,服务端会与注册中心进行心跳包的定时发送,从而感知每个客户端是否宕机,如果创建某个临时Znode节点对应的session销毁时,相应的临时节点也会被注册中心删除。
watcher机制
针对每个节点的操作,都有要给监督者进行watcher,当监控的某个节点发生了变化,则会触发watcher事件。注册中心的watcher是一次性的,触发后会被销毁。父节点,子节点增删改都能够触发watcher。触发销毁后,下次需要监听时还需要再注册一次。
本文心跳机制
服务端定时向注册中心发送本机地址,看作心跳数据包,而注册中心监控则维持一个channelId和具体地址的map,并且通过IdleHandler监听空闲事件,到达一定的空闲次数则认为不活跃,当不活跃时,zookeeper删除对应的url节点。该版本实现了上面的内容,后续的步骤在以后的版本实现。
如果10s内没有触发读,就会执行userEventTriggered方法。如果5分钟中出现两次不活跃次数,就认定该连接不稳定,注册中心会移除属于该服务端的服务。你也可以根据实际情况设定不稳定标准。

 service.scheduleAtFixedRate(() -> {
                if (future.channel().isActive()) {
                    int time = new Random().nextInt(5);
                    log.info("本次定时任务获取的随机数:{}", time);
                    if (time > 3) {
                        log.info("发送本地地址到注册中心:{}", url);
                        future.channel().writeAndFlush(url);
                    }
                }
            }, 60, 60, TimeUnit.SECONDS);
 @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent state = (IdleStateEvent)evt;
            if (state.state().equals(IdleState.READER_IDLE)) {
                log.info("读空闲");
            } else if (state.state().equals(IdleState.WRITER_IDLE)) {
                log.info("写空闲");
            }
            //在一定时间内读写空闲才会关闭链接
            else if (state.state().equals(IdleState.ALL_IDLE)) {
                if (++inActiveCount == 1) {
                    start = System.currentTimeMillis();
                }
                int minute = (int)((System.currentTimeMillis() - start) / (60 * 1000)) + 1;
                log.info("第{}次读写都空闲,计时分钟数{}", inActiveCount, minute);
                if (inActiveCount > 2 && minute < 5) {
                    log.info("移除不活跃ip");
                    removeAndClose(ctx);
                } else {
                    if (minute >= 5) {
                        log.info("新周期开始");
                        start = 0;
                        inActiveCount = 0;
                    }
                }
            }
        }
    }

具体实现代码:RPC第二版

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354