手写RPC框架(五)

v1.3

(启动器依旧使用1.2 1.3版本在启动服务版本上尚未做出大变动 主要是增加了方便学习的功能)

更新事项 以下更新均在非阻塞模块进行更新,阻塞模块可供读者自己尝试

  • 使用注解方式 改造一下启动类 不分成这么多启动类直接

    • 首先新创建了几个包 下面进行阐述下具体作用
      1.功能调用模块

    插入图片描述

    2.注解类 自定义两个注解

    • 自定义注解代码

      • 客户端启动注解

        package annotation;
        
        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;
        
        //注解 通过此注解可以判断当前是哪一个版本  选择调用哪个版本的客户端启动器
        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface RpcClientBootStrap {
            String version();
        }
        
      • 服务端启动注解

        package annotation;
        
        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;
        
        //注解 通过此注解可以判断当前是哪一个版本  选择调用哪个版本的服务端启动器
        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface RpcServerBootStrap {
            String version();
        }
        
    • 服务调用和实际进行判断的业务逻辑,下面代码就放客户端的,两者同理

      • 服务调用

        package service.call;
        
        import service.bootstrap.ClientBootStrap;
        
        import java.io.IOException;
        
        //通用启动类 将启动的逻辑藏在ClientBootStrap中
        public class ClientCall {
            public static void main(String[] args) throws IOException {
                ClientBootStrap.start();
            }
        }
        
      • 服务启动真正逻辑

        package service.bootstrap;
        
        import annotation.RpcClientBootStrap;
        
        import consumer.bootstrap.NIOConsumerBootStrap10;
        import consumer.bootstrap.NIOConsumerBootStrap11;
        import consumer.bootstrap.NIOConsumerBootStrap12;
        
        import java.io.IOException;
        
        //之后启动直接在这边启动根据 在注解中配置对应的版本号  将相应的操作封装到之后的操作中即可  这样很方便 就是每次咱加一个启动器还得改下switch
        //比如说这里的version 1.2 就是v1.2版本的启动器
        @RpcClientBootStrap(version = "1.1")
        public class ClientBootStrap {
            public static void start() throws IOException{
                //获取当前的注解上的版本然后去调用相应的远端方法  反射的方法
                //当前客户端启动器class对象
                Class<ClientBootStrap> currentClientBootStrapClass = ClientBootStrap.class;
                RpcClientBootStrap annotation = currentClientBootStrapClass.getAnnotation(RpcClientBootStrap.class);
                String currentVersion = annotation.version();
                //根据注解获得的版本进行判断是哪个版本 然后进行启动
                switch (currentVersion)
                {
                    case "1.0":
                        NIOConsumerBootStrap10.main(null);
                        break;
                    case "1.1":
                        NIOConsumerBootStrap11.main(null);
                        break;
                    case "1.2":
                        NIOConsumerBootStrap12.main(null);
                        break;
                    default:
                        System.out.println("太着急了兄弟,这个版本还没出呢!要不你给我提个PR");
                }
            }
        }
        
  • 利用ZK实现调用方法软负载均衡

    • 对zookeeper节点注册进行修改,节点名就是对应的地址,对应的数据就是被调用的次数

      • zookeeper服务注册端修改

         //因为这个地区属于一个临界区 可能会发生线程不安全问题 所以进行上🔒
                synchronized (ZkServiceRegistry.class) {
                    Stat exists = zooKeeper.exists("/service", false);
                    if (exists ==null) {
                        zooKeeper.create("/service",
                                "".getBytes(StandardCharsets.UTF_8),
                                ZooDefs.Ids.OPEN_ACL_UNSAFE,
                                CreateMode.PERSISTENT
                        );
                    }
        
                    //v1.3进行软负载均衡修改
                    exists = zooKeeper.exists("/service/"+RpcServiceName, false);
                    if (exists ==null) {
                        zooKeeper.create("/service/"+RpcServiceName,
                                "".getBytes(StandardCharsets.UTF_8),
                                ZooDefs.Ids.OPEN_ACL_UNSAFE,
                                CreateMode.PERSISTENT
                        );
                    }
        
                }
        
                String date = hostname+":"+port;
        
                //权限目前都设置为全放开   创建方式均为持久化
                //修改 v1.3  数据为访问次数 应该是可以进行加减的  然后发现服务端取的是最低的然后再进行+1
                zooKeeper.create("/service/"+RpcServiceName+"/"+date,
                        "0".getBytes(StandardCharsets.UTF_8),
                        ZooDefs.Ids.OPEN_ACL_UNSAFE,
                        CreateMode.PERSISTENT
                        );
            }
        
      • zookeeper服务发现端进行修改

        //v1.3更新 使用软负载
                //到对应节点中获取下面的子节点
                List<String> children = zooKeeper.getChildren(prePath, false, null);
                if (children.isEmpty())
                {
                    System.out.println("当前没有服务器提供该服务 请联系工作人员");
                }
        
                //进行排序 根据每个节点的访问次数 从小到大进行排序  然后选用最小的
                Collections.sort(children, new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
        
                        try {
                            return Integer.valueOf(new String(zooKeeper.getData(prePath+"/"+o1,false,null)))
                                    -
                                    Integer.valueOf(new String(zooKeeper.getData(prePath+"/"+o2,false,null)));
                        } catch (KeeperException e) {
                            e.printStackTrace();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        return 0;
                    }
                });
                //对选用的对象的访问量加1  todo 暂时不知道怎么让数据直接+1
                // 获取节点数据+1,然后修改对应节点,
                String chooseNode = children.get(0);
                byte[] data = zooKeeper.getData(prePath+"/"+chooseNode, false, null);
                int visitCount = Integer.valueOf(new String(data));
                ++visitCount;
                //version参数用于指定节点的数据版本,表名本次更新操作是针对指定的数据版本进行的。 cas
                zooKeeper.setData(prePath+"/"+chooseNode,String.valueOf(visitCount).getBytes(StandardCharsets.UTF_8),-1);
                String address = new String(children.get(0));
                return address;
            }
        
      • 效果:成功实现软负载均衡

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9ACIwCo-1653195899582)(/upload/2022/05/image-2c12437604b847aba60d45a933a2100d.png)]

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2vnFJIL7-1653195899584)(/upload/2022/05/image-e2d47e835e7e402db732e8d4684300a0.png)]

  • 问题 服务端下线的时候,希望能把zk对应的节点也进行删除,不能让用户每次都自己去到zk中删吧

    • 当前想到的办法是提取出一个方法来 在启用之前进行调用,删除节点!!有力扣刷题那味了☺

      package init;
      
      import constants.RpcConstants;
      import org.apache.zookeeper.KeeperException;
      import org.apache.zookeeper.WatchedEvent;
      import org.apache.zookeeper.Watcher;
      import org.apache.zookeeper.ZooKeeper;
      
      import java.io.IOException;
      import java.util.List;
      
      //zookeeper 进行一键初始化的方法
      public class ZK {
      
          private static ZooKeeper zooKeeper;
      
          public static void init() throws IOException, InterruptedException, KeeperException {
              zooKeeper = new ZooKeeper(RpcConstants.ZOOKEEPER_ADDRESS, RpcConstants.ZOOKEEPER_SESSION_TIMEOUT, new Watcher() {
                  @Override
                  public void process(WatchedEvent watchedEvent) {
      
                  }
              });
      
              //如果存在就删  不存在就不删
              if (zooKeeper.exists("/service",false)!=null)
              {
                  //内部得实现递归删除
                  deleteAll("/service");
              }
              zooKeeper.close();
          }
      
          //实现循环递归删除的方法
          private static void deleteAll(String prePath) throws InterruptedException, KeeperException {
              List<String> children = zooKeeper.getChildren(prePath, false);
              if (!children.isEmpty())
              {
                  for (String child : children) {
                      deleteAll(prePath+"/"+child);
                  }
              }
              zooKeeper.delete(prePath,-1);
          }
      }
      
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值