当前位置: 首页 > news >正文

h5case是什么网站网站备案快速

h5case是什么网站,网站备案快速,网站首页制作案例,中铁二局被降级手写实现一个简单版的Dubbo#xff0c;深刻理解RPC框架的底层实现原理 RPC框架简介了解Dubbo的实现原理服务暴露服务引入服务调用 手写实现一个简单版的Dubbo服务暴露ServiceBeanProxyFactory#getInvokerProtocol#exportRegistryProtocol#export 服务引入RegistryProto#referD… 手写实现一个简单版的Dubbo深刻理解RPC框架的底层实现原理 RPC框架简介了解Dubbo的实现原理服务暴露服务引入服务调用 手写实现一个简单版的Dubbo服务暴露ServiceBeanProxyFactory#getInvokerProtocol#exportRegistryProtocol#export 服务引入RegistryProto#referDubboProtocol#refer 服务调用服务消费者服务提供者 与Spring对接 代码 我越来越觉得学习一个开源框架或者中间件的底层原理最好的方法不是看源码而是自己尝试去写一个。之前看过许多开源框架和中间件的源码但是随着时间的推移多多少少都有点遗忘了遗忘之后又花时间去把它捡一捡于是就在这捡起又遗忘遗忘又捡起这样来来回回的折腾中浪费了许多时间。 Dubbo的源码我很早以前就看过了而且看了不止一遍现在对于Dubbo的底层原理只记得个大概至于源码中的细节已经忘得差不多了。最近想起之前学习过Dubbo源码为了加深印象于是乎自己动手写了一个。这自己动手写跟只用眼睛看效果还真不一样因此写成文章分享出来。 RPC框架简介 在以前都是单体应用的年代是不需要RPC框架的那时候还不知道RPC是什么所有的数据都是来自于本地的数据库然后全都是本地的方法调用。 但是随着业务的发展访问量的不断增加原先的单体应用已经不能满足需求于是要做应用拆分原先的单体应用被拆成了多个服务然后就出现了服务间调用的问题。 这样复杂度就一下子上来了如何解决服务间调用的问题呢我们可以自己写代码通过http的方式去访问对方或者通过Socket连上对方的系统进行通信发送调用的方法名和调用参数等信息。但是这种方式有点笨因为这种网络通信的代码一般都是重复的而且也容易出错。 那么有没有什么办法可以让远程调用变成像之前单体应用那样就调一个本地方法就能获取到返回结果呢那就是引入一个RPC框架RPC框架封装了远程调用的底层逻辑屏蔽掉了网络通信的过程让API调用工程师们简单调一下方法就可以获取到远程系统返回的方法调用结果于是这些API调用工程师们就觉得自己很牛B了。 我们当然不能当这种API调用工程师要不然35岁之后就要强势加入骑士军团了。 其实RPC框架的原理并没有多复杂。 比如现在有一个 UserService接口在以前还是单体应用的时候Spring给我们注入的是一个实现类我们调用这个接口的方法时就可以调用UserServiceImpl实现类。 而现在这个实现类在别的系统上我们拿不到于是RPC框架给我们注入的是一个代理对象这个代理对象也实现了这个UserService接口通过这个代理对象可以访问到对方系统的UserServiceImpl。然后RPC框架通过一个stub存根封装了网络通信的逻辑当我们调用UserService接口的某个方法时实际上是通过代理对象调用到了底层的存根类stub存根再把我们调用的方法和参数封装为网络协议包发送到对端系统。对端系统也有一个stub存根接收到发送过来的协议包后解析出要调用的方法和参数交给UserServiceImpl去处理然后再把处理结果通过stub存根返回给我们本地的存根我们本地的存根再把结果返回给我们。 但是A系统怎么知道B系统的ip地址和端口好呢如果是在A系统上配死那就太low了一旦B系统改了ip地址或端口A系统就要改配置重启。因此还需要一个注册中心存储B系统发布的ip地址和端口A系统从注册中心拉取B系统的ip地址和端口。此时B系统就是服务提供者把ip地址和端口注册到注册中心这个动作就是服务暴露而A系统就是服务消费者从注册中心拉取服务提供者的ip地址端口等信息这个动作就是服务引入A系统调用B系统这个动作就叫服务调用。 这样一个RPC调用框架就形成了。 了解Dubbo的实现原理 但是理解到这一层还远远不够毕竟别人可以面试前把这些背一背面试的时候忽悠面试官。于是我们要阅读源码对开源框架有更深一层的理解这样才能让面试官从一众的渣渣中把我们区分出来。 Dubbo是众多的RPC框架中用的比较多的一个我们来参考参考Dubbo的原理也是分为服务暴露、服务引入、服务调用三块。 服务暴露 首先我们写的类是这样子的 UserServiceImpl上声明了Service注解这个是Dubbo自己定义的Service注解不是Spring的那个就代表我们要把这个UserServiceImpl暴露出来供服务消费者调用。 Dubbo要扫描它然后把它封装成ServiceBeanServiceBean是Dubbo封装的一个用来做服务暴露的实现类它实现了Spring的ApplicationListenerContextRefreshedEvent接口会监听ContextRefreshedEvent容器刷新完成事件触发服务暴露而ServiceBean的export方法就是服务暴露的入口。 Dubbo在服务暴露中一共做了三件事 把我们的UserServiceImpl对象包装成Invoker再把Invoker包装成Exporter再把Exporter放入一个map中key是根据实现类计算出来的一个唯一的serviceKey。这一步相当于是本地注册方便后面接收到远程调用请求时根据请求信息计算出serviceKey找到目标实现类。开启Netty服务端用于接收远程调用请求。ip地址、端口、协议等服务信息注册到注册中心这些信息注册到注册中心后消费方就可以从注册中心中获取到服务提供者的信息了。 服务引入 至于服务引入我们写的消费者类一般是这样 属性上声明了这个Reference注解表示这个属性需要通过Dubbo的服务引入来进行属性注入。 那么Dubbo就是扫描所有被Reference注解修饰的属性进行属性注入。Dubbo这里处理属性注入的逻辑与Spring处理Autowired的逻辑大体是相同的这里先不用管后面会讲解。扫描到的属性通过ReferenceBean的get()方法触发服务引入返回一个代理对象注入到该属性中。 而服务引入里面做了些啥呢其实就是从注册中心中获取服务提供者的ip端口等信息开启NettyClient连接上服务提供者然后封装到一个Invoker中这个Invoker就等同于上面说的存根类。然后通过动态代理返回一个代理对象这个代理对象会调用Invoker进行远程方法调用。 服务调用 服务调用的逻辑自然是被隐藏在了Invoker里面。服务间通过Netty进行网络通信服务消费者把调用的方法名接口的类全限定名方法参数等封装成一个Invocation对象然后序列化成二进制通过Netty发送给服务提供者服务提供者的Netty接收到服务消费者发来的数据时会反序列化成Invocation对象根据服务消费者提供的信息找到具体的实现类进行处理处理结果再由Netty返回给服务消费者。 最终整体流程就是这样 没有看过源码的也可以记一下这张图然后面试的时候喷给面试官也能拿个60分。 按照这个思路我们就可以动手写代码了。 手写实现一个简单版的Dubbo 我们写代码的顺序还是按照服务暴露、服务引入、服务调用的顺序来但是我们实现的迷你版Dubbo是要与Spring对接的所以最后还要实现与Spring对接的逻辑。 这里要注意下面还是以一边讲解一边画图的方式进行不会贴代码。因为如果贴代码的话这文章没个几万字是结束不了的。想看代码的可以到代码仓去下载文末会附上代码仓的地址。 服务暴露 ServiceBean 服务暴露的逻辑从ServiceBean开始我们就先从ServiceBean开始实现我们的迷你版Dubbo。 我们的ServiceBean也是实现ApplicationListenerContextRefreshedEvent接口在onApplicationEvent(ContextRefreshedEvent event)方法中调用export()方法触发服务暴露。 export()方法首先要读取注册中心的配置Dubbo是通过RegistryConfig类型封装注册中心的配置的我们也定义一个RegistryConfig类封装注册中心的配置这里通过RegistryConfig获取到注册中心的ip地址和端口等信息。 Dubbo是通过URL对象去封装服务暴露的信息比如协议、ip地址、端口、访问路径调哪个类的哪个方法、方法参数等信息然后注册到注册中心的也是这个URL对象转成的url格式的字符串。我们这里也定义一个URL对象去存这些信息我们组装我们的URL对象。 URL中包含的协议、端口等信息可以通过ProtocolConfig进行配置然后从ProtocolConfig中取出放入到URL对象中。 封装好URL对象后接下来就是通过ProxyFactory代理工厂生成服务提供方的Invoker这个Invoker保存了真正的服务实现类。 最后就是通过Protocol对象的export方法进行服务暴露这里也是参考Dubbo的Dubbo真正的服务暴露逻辑是封装在Protocol的export方法里面的本地注册、开启Netty、注册相关信息到注册中心等动作都是在这里面进行。我们也定义一个Protocol接口。 这样看来ServiceBean需要RegistryConfig和ProtocolConfig这两个对象我们可以通过ApplicationContext去获取因此ServiceBean需要实现ApplicationContextAware这个接口通过这个接口获取到ApplicationContext然后ServiceBean还要实现InitializingBean接口在InitializingBean接口的afterPropertiesSet()方法里面通过ApplicationContext获取RegistryConfig和ProtocolConfig这两个对象。 这里的ApplicationContextAware、InitializingBean、ApplicationListener等接口都是Spring的接口不熟悉的可以去补一下Spring的知识。 ProxyFactory#getInvoker ProxyFactory.getInvoker(ref, this.interfaceClass, url) 这一步是封装具体实现类ref为Invoker的。这个Invoker的invoke方法会从Invocation中获取方法名、方法参数类型和方法参数然后通过反射调用具体实现类ref。 Protocol#export protocol.export(registryURL, invoker);这一步是真正进行服务暴露的但是这里的protocol其实是一个代理类Dubbo通过他自己实现的SPI机制会调用到具体的Protocol实现类。我们这里也是一个代理类使用的时JDK的动态代理但是我们就不实现自己的SPI机制了我们就使用Java的SPI机制。在InvocationHandler的invoke方法中通过Java的SPI机制加载所有的实现类循环遍历进行匹配匹配逻辑是看URL中的protocol属性也就是url中的协议然后反射调用匹配到的具体实现类。 现在的整体流程就走到这里 RegistryProtocol#export 这里的URL的protocol属性是registry因此这里匹配到的是RegistryProtocol类型的Protocol于是会调用到RegistryProtocol的export方法。 RegistryProtocol的export方法又会再次调用protocol的export方法这里的protocol依然是代理对象但是这次的URL的protocol属性是dubbo因此会调用到DubboProtocol的export方法。 DubboProtocol的export方法会把Invoker包装成Exporter用接口类全限定名作为keyExporter作为value放入到一个map这样当接收到远程调用请求时就可以通过接口名找到对应的Invoker进而调用里面的实现类。 DubboProtocol的export方法接下来会开启Netty服务端用于接收远程调用请求。 DubboProtocol的export方法结束后返回到RegistryProtocol的export方法接下来会调用注册中心的客户端把服务暴露信息注册到注册中心比如注册中心是Zookeeper则通过Zookeeper的客户端把服务暴露信息发布到Zookeeper注册中心。这里注册中心的具体类型还是通过动态代理加上Java的SPI机制来动态进行匹配的而注册到注册中心的信息也是url格式的。 到这里服务暴露的流程就结束了 服务引入 服务引入的入口是在ReferenceBean的get()方法我们ReferenceBean的get()的会返回一个代理对象注入到被Reference注解修饰的属性上。这里先不用管ReferenceBean的get()方法如何被调用到我们先完成ReferenceBean的get()方法的服务引入逻辑。 ReferenceBean的get()方法第一步也是通过RegistryConfig获取到注册中心的ip和端口等信息。 然后调用Protocol的refer方法进行服务引入这个方法会返回一个Invoker对象这个Invoker对象的invoke方法会通过Netty向远端发起远程调用。 最后通过ProxyFactory的getProxy方法把Invoker封装到一个代理对象中这里我们还是使用JDK的动态代理InvocationHandler的invoke方法会调用Invoker的invoke方法。 RegistryProto#refer Protocol的refer方法是真正进行服务引入的方法这里还是通过动态代理首先走到RegistryProto的refer方法 RegistryProto的refer方法返回的Invoker对象是比较复杂的有个几层的嵌套。 考虑到服务提供者有可能是以集群的形式部署的因此我们这里定义了一个ClusterInvoker类型的Invoker与之对应。ClusterInvoker包装了一个RegistryDirectory对象它是一个服务目录里面保存了一个ListInvoker这里的一个Invoker对应一个服务提供者。这样我们的服务就具备一定的容错能力可以通过负载均衡选出一个Invoker一个调不通可以换下一个。 那ListInvoker里面的Invoker是怎么来的呢 首先我们会监听注册中心一旦服务提供者上下线导致注册中心中的信息有变动我们会收到通知收到通知后我们重新从注册中心中查询服务提供者的url。同时在创建RegistryDirectory对象时RegistryDirectory的构造方法也会主动去注册中心查一次。 然后我们定义了一个NotifyListener接口我们的RegistryDirectory实现了NotifyListener接口NotifyListener的notify方法接收从注册中心查询回来的url循环调用protocol.refer(url, serviceType)方法。protocol.refer(url, serviceType)会调用到DubboProtocol的refer方法DubboProtocol的refer方法就会返回与服务提供者对应的一个Invoker。 DubboProtocol#refer DubboProtocol的refer方法会从参数url中解析出服务提供者的ip地址和端口号开启Netty客户端连接到服务提供者然后包装成一个Invoker返回。 那么服务引入的整体逻辑就是这样 服务调用 服务消费者 服务消费者的代理对象会通过InvocationHandler的invoke方法调用到ClusterInvoker的invoke方法。 ClusterInvoker的invoke方法调用RegistryDirectory的list方法获取ListInvoker然后通过负载均衡算法选出一个进行调用调用失败则切换下一个。 负载均衡选出一个Invoker后调用Invoker的invoke方法就会进入到DubboInvoker的invoke方法。DubboInvoker的invoke方法把接口类全限定名、方法名、方法参数类型、方法参数等信息包装成Invocation对象调用Netty客户端发送请求。 Netty客户端的编解码器会把Invocation对象序列化成二进制然后发送到网络。 因为Netty是异步的因此这里要创建一个Future对象然后绑定一个id发送请求时把这id带上服务提供者处理完后返回处理结果时同时返回这个id服务消费者就能通过id拿到Future设置返回结果到这个Future上那么当时发送请求的线程就能通过这个Future拿到返回结果。 服务提供者 服务提供者的Netty接收到请求后反序列成Invocation对象根据Invocation中的接口类全限定名从map中取出Exporter。然后从Exporter中取出Invoker调用Invoker的invoke方法Invoker的invoke方法从Invocation取出方法名方法参数类型、方法参数反射调用具体实现类具体实现类处理完成后返回的结果带上服务消费者传过来的id一起返回给服务提供者。 与Spring对接 这些都写好以后一个RPC框架大体就形成了但是我们还差最后一步就是与Spring对接。 我们参考Dubbo定义了一个EnableDubbo注解EnableDubbo注解通过Import注解Spring容器导入一个DubboComponentScanRegistrar类DubboComponentScanRegistrar实现了Spring的ImportBeanDefinitionRegistrar接口重写了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法DubboComponentScanRegistrar的registerBeanDefinitions方法会被Spring回调到。DubboComponentScanRegistrar的registerBeanDefinitions方法往Spring容器中注入两个bean一个是ServiceAnnotationBeanPostProcessor类型实现了Spring的BeanDefinitionRegistryPostProcessor接口用于扫描Service注解然后注册BeanDefinition到Spring容器中另一个是ReferenceAnnotationBeanPostProcessor类型是一个Bean后置处理器用于扫描Reference注解进行服务引入的。 ServiceAnnotationBeanPostProcessor通过Spring的ClassPathBeanDefinitionScanner类扫描出Service注解修饰的类的BeanDefinition然后创建一个ServiceBean类型的BeanDefinition配置相应的interfaceClass和ref等属性然后注册到容器中。 ClassPathBeanDefinitionScanner是Spring提供的一个用来扫描指定包路径获取BeanDefinition的工具类。 ReferenceAnnotationBeanPostProcessor参考Spring处理Autowired的逻辑ReferenceAnnotationBeanPostProcessor继承了InstantiationAwareBeanPostProcessorAdapter并重写了InstantiationAwareBeanPostProcessorAdapter的postProcessPropertyValues方法ReferenceAnnotationBeanPostProcessor的postProcessPropertyValues方法会被Spring回调到给当前bean进行属性注入然后postProcessPropertyValues方法中收集当前bean中被Reference注解修饰的属性创建ReferenceBean对ReferenceBean进行相应配置调用ReferenceBean的get()方法返回一个代理对象注入到该属性中。 InstantiationAwareBeanPostProcessorAdapter是Spring提供的一个Bean后置处理器可以通过继承InstantiationAwareBeanPostProcessorAdapter并重写postProcessPropertyValues方法定制我们的属性注入逻辑。 代码 到这里我们的RPC框架就大功告成了。基本上是参考Dubbo来写的保留了Dubbo的核心逻辑又去掉了一些细枝末节比起原来的Dubbo代码简化了许多但是确实能够帮助我们比较深刻的理解Dubbo的原理和逻辑。 本文章涉及的所有代码都上传到git代码仓库了由于篇幅有限这些代码就不贴到本篇文章中了有兴趣的可以到代码仓库下载下来看一下。 git代码仓库地址https://gitcode.net/weixin_43889578/mini-dubbo
http://www.w-s-a.com/news/581777/

相关文章:

  • 网站安全建设模板深圳企业管理咨询公司
  • 做网站 还是淘宝店wordpress分类链接后加
  • wordpress腾讯云 COSseo内容优化心得
  • 特价旅游机票网站建设i营销
  • 如何成立网站深圳创业项目
  • 建设商业网站惠州网站建设推荐乐云seo
  • 如何申请免费域名做网站免费推广神器
  • 自媒体人专用网站安岳网站建设
  • 特乐网站建设做网站推广要多少钱
  • 山东省建设安全生产协会网站义乌跨境电商公司前十名
  • 做网站优化就是发文章吗起飞页自助建站平台的特点
  • 做网站还是做app好慈溪机械加工网
  • 上传下载文件网站开发的php源码腾讯企点
  • 给分管领导网站建设情况汇报怎么写网络运营的岗位职责及任职要求
  • 电线电缆技术支持中山网站建设广告设计培训学校有哪些
  • 如何禁止通过ip访问网站wordpress无法调用主题布局和图片
  • 江西建设工程信息网站重庆网站推广大全
  • 南浔区住房城乡建设局网站网页设计基础学什么
  • 萧山做网站的企业网站建设 西安
  • 江西省城乡建设厅网站百度站长资源平台
  • 本地搭建linux服务器做网站免费查企业信息查询
  • 电商网站建设与运营网上购物哪个网站最好
  • 做app做网站从何学起网站设计需要什么证
  • 设计网站最重要的是要有良好的短网址还原
  • 大连建设银行招聘网站做seo是要先有网站吗
  • 中山做网站的wordpress建站教程百科
  • 湛江专业网站制作做网站需要工具
  • 做音箱木工网站吉林平安建设网站
  • 品牌网站建设咨询灯光设计网站推荐
  • 温州网站运营打开百度一下网页版