<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于redis實現(xiàn)rpc服務注冊

          共 16933字,需瀏覽 34分鐘

           ·

          2021-06-23 01:41

          前言

          昨天我們手寫了一個簡單到不能再簡單的rpc服務,對rpc服務有了一個基本的認知,但昨天的實現(xiàn)太過簡單,甚至都算不上rpc,因為rpc服務的核心是動態(tài)代理,但是今天我想先實現(xiàn)rpc的注冊,今天的服務注冊我沒有用zk,而是redis,用redis的目的就是讓各位小伙伴都能真正明白,任何組件的選用都不是必須的,而是一種更優(yōu)的選擇。

          服務注冊

          首先,我們要定義以下幾個注解,這些注解的作用就是輔助我們完成服務的注冊

          定義注解

          第一個注解和我們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 {
          }

          最后一個注解是加在屬性上的,主要是為了方便后期實現(xiàn)動態(tài)代理

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

          包掃描

          我們這里直接就把之前寫的包掃描器直接用起來了,這里會根據(jù)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();
                          }
                      }
                  });
              }

          本來打算在消費者注冊完直接給接口賦值的(注釋部分),但是在實際操作的時候,發(fā)現(xiàn)這種方式行不通,因為服務提供者和消費者是不同的應用,序列化之后的服務提供者的實例(rpcRegisterEntity.getNewInstance())是沒法強轉(zhuǎn)的,控制臺一直會報類型不匹配的錯誤:

          而且,后來我想了下,如果這種方式真的實現(xiàn)了,那就沒socket什么事了,還能叫rpc嗎?所以要想實現(xiàn),真正的動態(tài)調(diào)用,還是要通過動態(tài)代理。

          這么說來,昨天實現(xiàn)的也不能叫rpc,因為沒有實現(xiàn)動態(tài)代理。

          測試

          分別運行服務提供者和消費者,然后我們?nèi)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">redis看下,服務是否已經(jīng)注冊上,如果看到如下所示,表面服務已經(jīng)成功注冊:

          同時,我發(fā)現(xiàn)服務提供者的實例信息是空的,這又進一步表明,這種直接反射賦值方式行不通,只有動態(tài)代理才能拯救我們的rpc服務。

          總結(jié)

          不得不說,相比于zk,redis確實不適合做服務注冊,畢竟zk的樹形結(jié)構看起來就很友好,但是我暫時不考慮換成zk,等動態(tài)代理實現(xiàn)了再說。

          另外,在實際測試中,我發(fā)現(xiàn)除了classFUllName之外,其他的參數(shù)都是冗余的,但是像服務的地址、端口等比較重要的信息又沒有,所以后面要把服務注冊的entity優(yōu)化下,暫時就先這樣。

          明天,我明天打算分享動態(tài)代理的實現(xiàn)過程,這一塊實現(xiàn)了,rpc框架就成了,具體的明天再說,好了,今天就到這里吧!

          完整項目開源地址如下,感興趣的小伙伴可以去看下,后續(xù)我們會繼續(xù)實現(xiàn)相關功能,比如整合注冊中心、實現(xiàn)動態(tài)代理等待:

          https://github.com/Syske/syske-rpc-server
          - END -


          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产裸体网站 | 狼人视频网站 | 亚洲正在视频 | 亚洲日韩一区二区无码 | 无码视频app |