自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從0 開(kāi)始手寫一個(gè) RPC 框架,大多數(shù)都不清楚的技術(shù)

開(kāi)發(fā) 架構(gòu)
RPC 框架底層到底什么原理得知了RPC(遠(yuǎn)程過(guò)程調(diào)用)簡(jiǎn)單來(lái)說(shuō)就是調(diào)用遠(yuǎn)程的服務(wù)就像調(diào)用本地方法一樣,其中用到的知識(shí)有序列化和反序列化、動(dòng)態(tài)代理、網(wǎng)絡(luò)傳輸、動(dòng)態(tài)加載、反射這些知識(shí)點(diǎn)。

[[312360]]

RPC 框架底層到底什么原理得知了RPC(遠(yuǎn)程過(guò)程調(diào)用)簡(jiǎn)單來(lái)說(shuō)就是調(diào)用遠(yuǎn)程的服務(wù)就像調(diào)用本地方法一樣,其中用到的知識(shí)有序列化和反序列化、動(dòng)態(tài)代理、網(wǎng)絡(luò)傳輸、動(dòng)態(tài)加載、反射這些知識(shí)點(diǎn)。

 

從0 開(kāi)始手寫一個(gè) RPC 框架,大多數(shù)都不清楚的技術(shù)

 

發(fā)現(xiàn)這些知識(shí)都了解一些。所以就想著試試自己實(shí)現(xiàn)一個(gè)簡(jiǎn)單的RPC框架,即鞏固了基礎(chǔ)的知識(shí),也能更加深入的了解RPC原理。

當(dāng)然一個(gè)完整的RPC框架包含了許多的功能,例如服務(wù)的發(fā)現(xiàn)與治理,網(wǎng)關(guān)等等,本篇只是簡(jiǎn)單的實(shí)現(xiàn)了一個(gè)調(diào)用的過(guò)程。

傳參出參分析

一個(gè)簡(jiǎn)單請(qǐng)求可以抽象為兩步

 

從0 開(kāi)始手寫一個(gè) RPC 框架,大多數(shù)都不清楚的技術(shù)

 

那么就根據(jù)這兩步進(jìn)行分析,在請(qǐng)求之前我們應(yīng)該發(fā)送給服務(wù)端什么信息?而服務(wù)端處理完以后應(yīng)該返回客戶端什么信息?

在請(qǐng)求之前我們應(yīng)該發(fā)送給服務(wù)端什么信息?

由于我們?cè)诳蛻舳苏{(diào)用的是服務(wù)端提供的接口,所以我們需要將客戶端調(diào)用的信息傳輸過(guò)去,那么我們可以將要傳輸?shù)男畔⒎譃閮深?/p>

第一類是服務(wù)端可以根據(jù)這個(gè)信息找到相應(yīng)的接口實(shí)現(xiàn)類和方法

第二類是調(diào)用此方法傳輸?shù)膮?shù)信息

那么我們就根據(jù)要傳輸?shù)膬深愋畔⑦M(jìn)行分析,什么信息能夠找到相應(yīng)的實(shí)現(xiàn)類的相應(yīng)的方法?要找到方法必須要先找到類,這里我們可以簡(jiǎn)單的用Spring提供的Bean實(shí)例管理ApplicationContext進(jìn)行類的尋找。

所以要找到類的實(shí)例只需要知道此類的名字就行,找到了類的實(shí)例,那么如何找到方法呢?

在反射中通過(guò)反射能夠根據(jù)方法名和參數(shù)類型從而找到這個(gè)方法。那么此時(shí)第一類的信息我們就明了了,那么就建立相應(yīng)的是實(shí)體類存儲(chǔ)這些信息。

  1. @Data 
  2. public class Request implements Serializable { 
  3.  private static final long serialVersionUID = 3933918042687238629L; 
  4.  private String className; 
  5.  private String methodName; 
  6.  private Class<?> [] parameTypes; 
  7.  private Object [] parameters; 

服務(wù)端處理完以后應(yīng)該返回客戶端什么信息?

上面我們分析了客戶端應(yīng)該傳輸什么信息給服務(wù)端,那么服務(wù)端處理完以后應(yīng)該傳什么樣的返回值呢?

這里我們只考慮最簡(jiǎn)單的情況,客戶端請(qǐng)求的線程也會(huì)一直在等著,不會(huì)有異步處理這一說(shuō),所以這么分析的話就簡(jiǎn)單了,直接將得到的處理結(jié)果返回就行了。

  1. @Data 
  2. public class Response implements Serializable { 
  3.  private static final long serialVersionUID = -2393333111247658778L; 
  4.  private Object result; 

由于都涉及到了網(wǎng)絡(luò)傳輸,所以都要實(shí)現(xiàn)序列化的接口

如何獲得傳參信息并執(zhí)行?-客戶端

上面我們分析了客戶端向服務(wù)端發(fā)送的信息都有哪些?那么我們?nèi)绾潍@得這些信息呢?

首先我們調(diào)用的是接口,所以我們需要寫自定義注解然后在程序啟動(dòng)的時(shí)候?qū)⑦@些信息加載在Spring容器中。

有了這些信息那么我們就需要傳輸了,調(diào)用接口但是實(shí)際上執(zhí)行的確實(shí)網(wǎng)絡(luò)傳輸?shù)倪^(guò)程,所以我們需要?jiǎng)討B(tài)代理。那么就可以分為以下兩步

  • 初始化信息階段:將key為接口名,value為動(dòng)態(tài)接口類注冊(cè)進(jìn)Spring容器中
  • 執(zhí)行階段:通過(guò)動(dòng)態(tài)代理,實(shí)際執(zhí)行網(wǎng)絡(luò)傳輸

初始化信息階段

由于我們使用Spring作為Bean的管理,所以要將接口和對(duì)應(yīng)的代理類注冊(cè)進(jìn)Spring容器中。而我們?nèi)绾握业轿覀兿胍{(diào)用的接口類呢?我們可以自定義注解進(jìn)行掃描。將想要調(diào)用的接口全部注冊(cè)進(jìn)容器中。

創(chuàng)建一個(gè)注解類,用于標(biāo)注哪些接口是可以進(jìn)行Rpc的。

  1. @Target({ElementType.TYPE}) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. public @interface RpcClient { 

然后創(chuàng)建對(duì)于@RpcClient注解的掃描類RpcInitConfig,將其注冊(cè)進(jìn)Spring容器中。

  1. public class RpcInitConfig implements ImportBeanDefinitionRegistrar{ 
  2.  @Override 
  3.  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 
  4.  ClassPathScanningCandidateComponentProvider provider = getScanner(); 
  5.  //設(shè)置掃描器 
  6.  provider.addIncludeFilter(new AnnotationTypeFilter(RpcClient.class)); 
  7.  //掃描此包下的所有帶有@RpcClient的注解的類 
  8.  Set<BeanDefinition> beanDefinitionSet = provider.findCandidateComponents("com.example.rpcclient.client"); 
  9.  for (BeanDefinition beanDefinition : beanDefinitionSet){ 
  10.  if (beanDefinition instanceof AnnotatedBeanDefinition){ 
  11.  //獲得注解上的參數(shù)信息 
  12.  AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; 
  13.  String beanClassAllName = beanDefinition.getBeanClassName(); 
  14.  Map<String, Object> paraMap = annotatedBeanDefinition.getMetadata() 
  15.  .getAnnotationAttributes(RpcClient.class.getCanonicalName()); 
  16.  //將RpcClient的工廠類注冊(cè)進(jìn)去 
  17.  BeanDefinitionBuilder builder = BeanDefinitionBuilder 
  18.  .genericBeanDefinition(RpcClinetFactoryBean.class); 
  19.  //設(shè)置RpcClinetFactoryBean工廠類中的構(gòu)造函數(shù)的值 
  20.  builder.addConstructorArgValue(beanClassAllName); 
  21.  builder.getBeanDefinition().setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); 
  22.  //將其注冊(cè)進(jìn)容器中 
  23.  registry.registerBeanDefinition( 
  24.  beanClassAllName , 
  25.  builder.getBeanDefinition()); 
  26.  } 
  27.  } 
  28.  } 
  29.  //允許Spring掃描接口上的注解 
  30.  protected ClassPathScanningCandidateComponentProvider getScanner() { 
  31.  return new ClassPathScanningCandidateComponentProvider(false) { 
  32.  @Override 
  33.  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { 
  34.  return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); 
  35.  } 
  36.  }; 
  37.  } 

由于上面注冊(cè)的是工廠類,所以我們建立一個(gè)工廠類RpcClinetFactoryBean繼承Spring中的FactoryBean類,由其統(tǒng)一創(chuàng)建@RpcClient注解的代理類。推薦閱讀:Spring零配置之@Configuration注解詳解。

  1. @Data 
  2. public class RpcClinetFactoryBean implements FactoryBean { 
  3.  @Autowired 
  4.  private RpcDynamicPro rpcDynamicPro; 
  5.  private Class<?> classType; 
  6.  public RpcClinetFactoryBean(Class<?> classType) { 
  7.  this.classType = classType; 
  8.  } 
  9.  @Override 
  10.  public Object getObject(){ 
  11.  ClassLoader classLoader = classType.getClassLoader(); 
  12.  Object object = Proxy.newProxyInstance(classLoader,new Class<?>[]{classType},rpcDynamicPro); 
  13.  return object; 
  14.  } 
  15.  @Override 
  16.  public Class<?> getObjectType() { 
  17.  return this.classType; 
  18.  } 
  19.  @Override 
  20.  public boolean isSingleton() { 
  21.  return false
  22.  } 

注意此處的getObjectType方法,在將工廠類注入到容器中的時(shí)候,這個(gè)方法返回的是什么Class類型那么注冊(cè)進(jìn)容器中就是什么Class類型。

然后看一下我們創(chuàng)建的代理類RpcDynamicPro。

  1. @Component 
  2. @Slf4j 
  3. public class RpcDynamicPro implements InvocationHandler { 
  4.  @Override 
  5.  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  6.  String requestJson = objectToJson(method,args); 
  7.  Socket client = new Socket("127.0.0.1", 20006); 
  8.  client.setSoTimeout(10000); 
  9.  //獲取Socket的輸出流,用來(lái)發(fā)送數(shù)據(jù)到服務(wù)端 
  10.  PrintStream out = new PrintStream(client.getOutputStream()); 
  11.  //獲取Socket的輸入流,用來(lái)接收從服務(wù)端發(fā)送過(guò)來(lái)的數(shù)據(jù) 
  12.  BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream())); 
  13.  //發(fā)送數(shù)據(jù)到服務(wù)端 
  14.  out.println(requestJson); 
  15.  Response response = new Response(); 
  16.  Gson gson =new Gson(); 
  17.  try{ 
  18.  //從服務(wù)器端接收數(shù)據(jù)有個(gè)時(shí)間限制(系統(tǒng)自設(shè),也可以自己設(shè)置),超過(guò)了這個(gè)時(shí)間,便會(huì)拋出該異常 
  19.  String responsJson = buf.readLine(); 
  20.  response = gson.fromJson(responsJson, Response.class); 
  21.  }catch(SocketTimeoutException e){ 
  22.  log.info("Time out, No response"); 
  23.  } 
  24.  if(client != null){ 
  25.  //如果構(gòu)造函數(shù)建立起了連接,則關(guān)閉套接字,如果沒(méi)有建立起連接,自然不用關(guān)閉 
  26.  client.close(); //只關(guān)閉socket,其關(guān)聯(lián)的輸入輸出流也會(huì)被關(guān)閉 
  27.  } 
  28.  return response.getResult(); 
  29.  } 
  30.  public String objectToJson(Method method,Object [] args){ 
  31.  Request request = new Request(); 
  32.  String methodName = method.getName(); 
  33.  Class<?>[] parameterTypes = method.getParameterTypes(); 
  34.  String className = method.getDeclaringClass().getName(); 
  35.  request.setMethodName(methodName); 
  36.  request.setParameTypes(parameterTypes); 
  37.  request.setParameters(args); 
  38.  request.setClassName(getClassName(className)); 
  39.  GsonBuilder gsonBuilder = new GsonBuilder(); 
  40.  gsonBuilder.registerTypeAdapterFactory(new ClassTypeAdapterFactory()); 
  41.  Gson gson = gsonBuilder.create(); 
  42.  return gson.toJson(request); 
  43.  } 
  44.  private String getClassName(String beanClassName){ 
  45.  String className = beanClassName.substring(beanClassName.lastIndexOf(".")+1); 
  46.  className = className.substring(0,1).toLowerCase() + className.substring(1); 
  47.  return className; 
  48.  } 

我們的客戶端已經(jīng)寫完了,傳給服務(wù)端的信息我們也已經(jīng)拼裝完畢了。剩下的工作就簡(jiǎn)單了,開(kāi)始編寫服務(wù)端的代碼。

服務(wù)端處理完以后應(yīng)該返回客戶端什么信息?-服務(wù)端

服務(wù)端的代碼相比較客戶端來(lái)說(shuō)要簡(jiǎn)單一些??梢院?jiǎn)單分為下面三步

  • 拿到接口名以后,通過(guò)接口名找到實(shí)現(xiàn)類
  • 通過(guò)反射進(jìn)行對(duì)應(yīng)方法的執(zhí)行
  • 返回執(zhí)行完的信息

那么我們就根據(jù)這三步進(jìn)行編寫代碼

拿到接口名以后,通過(guò)接口名找到實(shí)現(xiàn)類

如何通過(guò)接口名拿到對(duì)應(yīng)接口的實(shí)現(xiàn)類呢?這就需要我們?cè)诜?wù)端啟動(dòng)的時(shí)候?qū)⑵鋵?duì)應(yīng)信息加載進(jìn)去。

  1. @Component 
  2. @Log4j 
  3. public class InitRpcConfig implements CommandLineRunner { 
  4.  @Autowired 
  5.  private ApplicationContext applicationContext; 
  6.  public static Map<String,Object> rpcServiceMap = new HashMap<>(); 
  7.  @Override 
  8.  public void run(String... args) throws Exception { 
  9.  Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Service.class); 
  10.  for (Object bean: beansWithAnnotation.values()){ 
  11.  Class<?> clazz = bean.getClass(); 
  12.  Class<?>[] interfaces = clazz.getInterfaces(); 
  13.  for (Class<?> inter : interfaces){ 
  14.  rpcServiceMap.put(getClassName(inter.getName()),bean); 
  15.  log.info("已經(jīng)加載的服務(wù):"+inter.getName()); 
  16.  } 
  17.  } 
  18.  } 
  19.  private String getClassName(String beanClassName){ 
  20.  String className = beanClassName.substring(beanClassName.lastIndexOf(".")+1); 
  21.  className = className.substring(0,1).toLowerCase() + className.substring(1); 
  22.  return className; 
  23.  } 

此時(shí)rpcServiceMap存儲(chǔ)的就是接口名和其對(duì)應(yīng)的實(shí)現(xiàn)類的對(duì)應(yīng)關(guān)系。

通過(guò)反射進(jìn)行對(duì)應(yīng)方法的執(zhí)行

此時(shí)拿到了對(duì)應(yīng)關(guān)系以后就能根據(jù)客戶端傳過(guò)來(lái)的信息找到相應(yīng)的實(shí)現(xiàn)類中的方法。然后進(jìn)行執(zhí)行并返回信息就行。

  1. public Response invokeMethod(Request request){ 
  2.  String className = request.getClassName(); 
  3.  String methodName = request.getMethodName(); 
  4.  Object[] parameters = request.getParameters(); 
  5.  Class<?>[] parameTypes = request.getParameTypes(); 
  6.  Object o = InitRpcConfig.rpcServiceMap.get(className); 
  7.  Response response = new Response(); 
  8.  try { 
  9.  Method method = o.getClass().getDeclaredMethod(methodName, parameTypes); 
  10.  Object invokeMethod = method.invoke(o, parameters); 
  11.  response.setResult(invokeMethod); 
  12.  } catch (NoSuchMethodException e) { 
  13.  log.info("沒(méi)有找到"+methodName); 
  14.  } catch (IllegalAccessException e) { 
  15.  log.info("執(zhí)行錯(cuò)誤"+parameters); 
  16.  } catch (InvocationTargetException e) { 
  17.  log.info("執(zhí)行錯(cuò)誤"+parameters); 
  18.  } 
  19.  return response; 

現(xiàn)在我們兩個(gè)服務(wù)都啟動(dòng)起來(lái)并且在客戶端進(jìn)行調(diào)用就發(fā)現(xiàn)只是調(diào)用接口就能調(diào)用過(guò)來(lái)了。

總結(jié)

到現(xiàn)在一個(gè)簡(jiǎn)單的RPC就完成了,但是其中還有很多的功能需要完善,例如一個(gè)完整RPC框架肯定還需要服務(wù)注冊(cè)與發(fā)現(xiàn),而且雙方通信肯定也不能是直接開(kāi)啟一個(gè)線程一直在等著,肯定需要是異步的等等的各種功能。

后面隨著學(xué)習(xí)的深入,這個(gè)框架也會(huì)慢慢增加一些東西。不僅是對(duì)所學(xué)知識(shí)的一個(gè)應(yīng)用,更是一個(gè)總結(jié)。有時(shí)候?qū)W一個(gè)東西學(xué)起來(lái)覺(jué)得很簡(jiǎn)單,但是真正應(yīng)用的時(shí)候就會(huì)發(fā)現(xiàn)各種各樣的小問(wèn)題。

比如在寫這個(gè)例子的時(shí)候碰到一個(gè)問(wèn)題就是@Autowired的時(shí)候一直找不到SendMessage的類型,最后才發(fā)現(xiàn)是工廠類

  1. public Class<?> getObjectType() { 
  2.  return this.getClass();; 

這樣的話注冊(cè)進(jìn)容器的就是RpcClinetFactoryBean類型的而不是SendMessage的類型。

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2020-11-25 09:49:05

Hibernate

2018-08-15 10:51:01

JavaSpring MVC框架

2020-11-02 08:19:18

RPC框架Java

2020-08-18 08:22:46

歸并排序

2021-03-01 19:13:45

YAML程序員數(shù)據(jù)

2009-07-14 15:39:34

Swing大多數(shù)控件

2019-05-13 15:05:34

TomcatWeb Server協(xié)議

2021-11-09 06:01:35

前端JITAOT

2019-12-13 17:29:50

物聯(lián)網(wǎng)大數(shù)據(jù)安全

2017-05-08 14:27:49

PHP框架函數(shù)框架

2011-05-26 10:50:31

2014-01-02 10:34:54

設(shè)計(jì)設(shè)計(jì)師

2024-07-04 15:47:28

2021-03-10 08:56:37

Zookeeper

2021-02-20 09:45:02

RPC框架Java

2021-07-27 07:31:16

JavaArrayList數(shù)組

2022-05-15 21:52:04

typeTypeScriptinterface

2024-02-29 09:08:56

Encoding算法加密

2020-11-26 09:52:03

禁用43個(gè)APP

2019-10-29 09:41:54

Kafka分布式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)