信阳建设企业网站公司,wordpress 默认账号,生物医药基地网站建设,seo宣传一、REST 与 RPC#xff1a;
1、什么是 REST 和 RPC 协议#xff1a;
在单体应用中#xff0c;各模块间的调用是通过编程语言级别的方法函数来实现#xff0c;但分布式系统运行在多台机器上#xff0c;一般来说#xff0c;每个服务实例都是一个进程#xff0c;服务…一、REST 与 RPC
1、什么是 REST 和 RPC 协议
在单体应用中各模块间的调用是通过编程语言级别的方法函数来实现但分布式系统运行在多台机器上一般来说每个服务实例都是一个进程服务间必须使用进程间通信机制来交互而常见的通信协议主要有 RPC 和 REST 协议。
1REST
REST 是基于 HTTP 实现使用 HTTP 协议处理数据通信更加标准化与通用因为无论哪种语言都支持 HTTP 协议。常见的 http API 都可以称为 Rest 接口。REST 是一种架构风格指一组架构约束条件和原则满足 REST 原则的应用程序或设计就是 RESTfulRESTful 把一切内容都视为资源。REST 强调组件交互的扩展性、接口的通用性、组件的独立部署、以及减少交互延迟的中间件它强化安全也能封装遗留系统。
2RPC
RPC 是一种进程间通信方式允许像调用本地服务一样调用远程服务通信协议大多采用二进制方式。
2、RPC 与 REST 的对比
类别RPCREST报文格式二进制XML、JSON网络协议TCP/HTTP/HTTP2HTTP/HTTP2序列化开销低一般网络开销低一般性能高一般访问便利性客户端比较方便但二进制消息不可读文本消息开发者可读浏览器可访问代码耦合度耦合度高松散耦合通用性低对外开发需进一步转换成REST协议高可直接对外开发使用场景内部服务外部服务
1传输协议与性能RPC 的传输协议灵活可基于 TCP 实现由于 TCP 协议处于协议栈的下层能够更灵活地对协议字段进行定制让请求报文体积更小减少网络开销提高传输性能并缩短传输耗时实现更大的吞吐量和并发数。REST 的 HTTP 协议是上层协议发送包含同等内容的信息请求中会包含很多无用的内容所占用的字节数比使用 TCP 协议传输更高因此在同等网络下HTTP 会比基于 TCP 协议的数据传输效率要低传输耗时更长不仅如此REST 的 HTTP 大部分是通过 JSON 来实现的序列化也更消耗性能但如果是基于 HTTP2.0那么经过封装也是可以作为一个 RPC 来使用的。
2灵活性、开放性与通用性REST 通过 HTTP 实现相对更加规范与通用无论哪种语言都支持 HTTP 协议所以 REST 的调用和测试都很方便但使用 RPC 则会有很多约束而如果 RPC 需要对外开放的话需要进一步处理灵活性不如 REST
3使用场景REST 主要用于对外开放的异构环境比如浏览器接口调用Api 接口调用第三方接口调用等。RPC 主要用于公司内部的服务调用性能消耗低传输效率高特别是大型的网站内部子系统较多、接口非常多的情况下适合使用 RPC
二、RPC 框架
REST 和 RPC 都常用于微服务架构中微服务的好处之一就是不限定服务的提供方使用什么技术选型能够实现大公司跨团队的技术解耦。 但是如果没有统一的通信框架各个团队的服务提供方就需要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等 “业务之外” 的重复技术劳动造成整体的低效。所以统一通信框架把上述 “业务之外” 的技术劳动统一处理是服务化首要解决的问题。
1、什么是 RPC 框架
RPC 框架的目标就是让远程服务调用更简单、透明由 RPC 框架负责屏蔽底层的序列化、传输方式和通信的细节开发者在使用时只需要了解谁在什么位置提供了什么样的远程服务接口即可并不需要关心底层通信细节和调用过程。RPC 框架作为架构微服务化的基础组件它能大大降低架构微服务化的成本提高调用方与服务提供方的研发效率。
2、RPC 框架的技术架构
如下图在典型 RPC 的使用场景中主要包含了服务发现、负载、容错、网络传输、序列化等组件其中 ”RPC协议”就指明了服务如何进行序列化和网络传输这也是RPC的核心功能。 应用级的RPC框架Dubbo、Google gRPC通信框架Netty远程通信协议RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON) 3、RPC 框架的调用流程 3.1、RPC 框架的核心组件
1客户端Client服务调用方。
2客户端存根Client Stub存放服务端地址信息将客户端的请求参数数据信息打包成网络消息再通过网络传输发送给服务端。
3服务端存根Server Stub接收客户端发送过来的请求消息并进行解包然后再调用本地服务进行处理。
4服务端Server服务的真正提供者。
3.2、RPC 的调用流程
1服务消费者Client 客户端通过本地调用的方式调用需要消费的服务
2客户端存根Client Stub接收到调用请求后负责将方法、入参等信息序列化组装成能够进行网络传输的消息体
3客户端存根Client Stub找到远程的服务地址并且将消息通过网络发送给服务端
4服务端存根Server Stub收到消息后进行解码反序列化操作
5服务端存根Server Stub根据解码结果调用本地的服务进行相关处理
6服务端Server执行具体的业务逻辑并将处理结果返回给服务端存根Server Stub
7服务端存根Server Stub将返回结果序列化并通过网络发送给消费方
8客户端存根Client Stub接收到消息并进行解码与反序列化
9服务消费方得到最终结果 而RPC框架的实现目标则是将上面的第2-10步完好地封装起来也就是把调用、编码/解码的过程给封装起来让用户感觉上像调用本地服务一样的调用远程服务 4、如何实现一个RPC框架
通过上面几点的介绍如果要我们实现一个 RPC 框架我们应该如果做呢要实现 一个RPC 框架我们只需要解决的以下几件最基本的事情
4.1、如何进行网络通讯
远程调用中客户端和服务端的通讯是基于网络连接的所以首先需要建立通信连接通过这个连接把请求信息的字节流传给服务端然后再把序列化后的响应结果传回客户端在这个通讯过程中它所使用的协议是没有限制的能完成传输就行但是在这里我们需要考虑两个问题如何选择网络协议 和 如何建立连接。
1网络协议的选择多数 RPC 框架选择 TCP 作为传输协议但其实 UDP 也可以也有部分选择HTTP比如 gRPC 使用 HTTP2但是不同的协议各有优劣势TCP 更加高效而 HTTP 在实际应用中更加的灵活具体需要根据使用场景来选择下文会介绍如何选择正确的网络传输协议
2通讯连接的建立RPC 所有交换的数据都在这个连接里传输这个连接可以是按需连接需要调用时就先建立连接调用结束后就立马断掉也可以是长连接客户端和服务器建立起连接之后保持长期持有不管此时有无数据包的发送可以配合心跳检测机制定期检测建立的连接是否存活有效多个远程过程调用共享同一个连接。
4.2、如何那行服务寻址
解决寻址的问题也就是说服务端如何确定客户端要调用的函数在本地调用中函数是直接通过函数指针来指定的但是在远程调用中函数指针是不行的因为两个进程的地址空间是完全不一样的。所以在远程调用中客户端和服务端需要分别维护一个【ID - 函数】的映射表ID在所有进程中都是唯一确定的客户端在做远程过程调用时附上这个ID服务端通过查表来确定客户端需要调用的函数然后执行相应函数的代码。
而寻址问题的具体实现方式则可以通过注册中心服务提供者完成后对外暴露相应的功能并将自己注册到注册中心上接着服务消费者从注册中心寻找服务然后调用该服务对应的方法完成远程调用
1从服务提供者的角度看
当服务提供者启动的时候需要将自己提供的服务注册到指定的注册中心以便服务消费者能够通过服务注册中心进行查找当服务提供者由于各种原因致使提供的服务停止时需要向注册中心注销停止的服务服务的提供者需要定期向服务注册中心发送心跳检测服务注册中心如果一段时间未收到来自服务提供者的心跳后认为该服务提供者已经停止服务则将该服务从注册中心上去掉。
2从调用者的角度看
服务的调用者启动的时候根据自己订阅的服务向服务注册中心查找服务提供者的地址等信息当服务调用者消费的服务上线或者下线的时候注册中心会告知该服务的调用者服务调用者下线的时候则取消订阅。
4.3、如何序列化和反序列化
在本地调用中我们只需要把参数信息压到内存栈中然后让函数自己去栈中读取但是远程过程调用时客户端跟服务端是不同的进程不能通过内存来传递参数。所以远程过程调用中客户端和服务端交互时方法的参数和结果需要通过底层的网络协议如TCP传递由于网络协议是基于二进制的只有二进制数据才能在网络中传输那么这些值需要序列化成二进制的形式通过寻址和传输将序列化的二进制发送目标服务器。目标服务器接收到数据时需要对数据进行反序列化。序列化和反序列化的速度也会影响远程调用的效率。 将对象转换成二进制流的过程叫做序列化将二进制流转换成对象的过程叫做反序列化 5、如何选择正确的PRC网络传输协议
在 RPC 中可选的网络传输方式有多种比如 TCP 协议、UDP 协议、HTTP 协议。每一种协议对整体的性能和效率都有不同的影响那如何选择一个正确的网络传输协议呢针对这个问题我们首先要搞明白各种传输协议在 RPC 中的工作方式
基于 TCP 的协议实现的 RPC 调用由于 TCP 协议处于协议栈的下层能够更加灵活地对协议字段进行定制让请求报文体积更小减少网络开销提高传输性能并缩短传输耗时实现更大的吞吐量和并发数。但是需要更多关注底层复杂的细节实现的代价更高同时对不同平台如安卓iOS 等需要重新开发出不同的工具包来进行请求发送和相应解析工作量大难以快速响应和满足用户需求。基于 HTTP 协议实现的 RPC 则可以使用 JSON 和 XML 格式的请求或响应数据而 JSON 和 XML 作为通用的格式标准使用 HTTP 协议也需要序列化和反序列化不过这不是该协议下关心的内容成熟的 Web 程序已经做好了序列化内容开源的解析工具已经相当成熟在其上进行二次开发会非常便捷和简单。但是由于 HTTP 协议是上层协议发送包含同等内容的信息请求中会包含很多无用的内容所占用的字节数比使用 TCP 协议传输更高因此在同等网络下HTTP 会比基于 TCP 协议的数据传输效率要低传输耗时更长当然压缩数据能够缩小这一差距。
三、RPC框架dubbo
1、dubbo 是什么
前面讲到RPC 常用于微服务架构中而 RPC 框架作为架构微服务化的基础组件能大大降低架构微服务化的成本提高调用方与服务提供方的研发效率。而 Dubbo 是阿里巴巴开源的基于 Java 的 RPC 分布式服务框架提供高性能和透明化的 RPC 远程服务调用方案以及 SOA 服务治理方案。另外基于 Spring Cloud Alibaba 技术栈的 Spring-cloud-alibaba-dubbo 更是对 dubbo 技术进行了封装在基于 Spring Cloud Alibaba 提供的 Nacos 注册中心下提供了 Dubbo 和 Spring Cloud 的整合方案即 Dubbo Spring Cloud使得服务内部的 RPC 协议调用几乎是零成本的改造实现了基于 RPC 的服务调用。
2、dubbo 的执行流程
2.1、dubbo 总体流程 紫色虚线启动时完成的功能蓝色虚线运行过程中执行的功能异步调用蓝色实线运行过程中执行的功能同步调用
2.1.1、dubbo 的总体执行流程说明如下
1启动容器加载运行服务提供者。
2服务提供者在启动时向注册中心注册自己提供的服务。
3服务消费者在启动时向注册中心订阅自己所需的服务。
4注册中心返回服务提供者地址列表给消费者消费者接收到之后缓存在本地中如果内容有变更注册中心将基于长连接推送变更数据给消费者。
5服务消费者从提供者地址列表中基于软负载均衡算法选择一台提供者进行调用如果调用失败再选另一台调用。
6服务消费者和提供者在内存中累计调用次数和调用时间定时每分钟发送一次统计数据到监控中心。 在consumer中使用了代理模式创建了一个Provider类的一个代理对象。通过代理对象获取Provider中的真实功能起到保护Provider真实功能的作用。 2.1.2、dubbo 的整个执行流程可以理解为生产者-消费者模型注册中心监控中心这样设计的原因在于
Consumer 与 Provider 解偶双方都可以横向增减节点数注册中心对本身可做对等集群可动态增减节点并且任意一台宕掉后将自动切换到另一台去中心化双方不直接依懒注册中心即使注册中心全部宕机短时间内也不会影响服务的调用服务提供者无状态任意一台宕掉后不影响使用
2.2、dubbo 同步调用原理
2.2.1、dubbo 同步调用流程
1客户端线程调用远程接口向服务端发送请求同时当前线程应该处于“暂停“状态即线程不能向后执行了必需要拿到服务端给自己的结果后才能向后执行2服务端接到客户端请求后处理请求将结果给客户端3客户端收到结果然后当前线程继续往后执行
dubbo 中使用了 Socket 来建立长连接、数据传输而底层结合了的 Apache mina 框架Apache mina 框架基于Reactor模型通信框架基于tcp长连接。dubbo 使用 IoSession.write() 方法进行远程调用与发送消息这个方法的远程调用过程异步的即对于当前线程来说将请求发送出来线程就可以往后执行了至于服务端的结果是服务端处理完成后再以消息的形式发送给客户端的。于是这里出现了2个问题
1当前线程怎么让它“暂停”等结果回来后再向后执行
先生成一个对象 obj在一个全局 map 里 put(ID,obj) 存放起来再用 synchronized 获取 obj 锁再调用 obj.wait() 让当前线程处于等待状态然后另一消息监听线程等到服务端处理结果到来再 map.get(ID) 找到 obj再用 synchronized 获取obj锁再调用 obj.notifyAll() 唤醒前面处于等待状态的线程。
2Socket通信是一个全双工的方式当有多个线程同时进行远程方法调用这时 client 与 server 间的 socket 连接上会有很多双方发送的消息传递前后顺序也可能是乱七八糟的server处理完结果后将结果消息发送给clientclient收到很多消息怎么知道哪个消息结果是原先哪个线程调用的
使用一个ID让其唯一然后传递给服务端再服务端又回传回来这样就知道结果是原先哪个线程的了
2.2.2、dubbo同步调用原理
1客户端使用一个线程调用远程接口生成一个唯一 IDDubbo 是使用 AtomicLong 从 0 开始累计数字的
2将打包的方法调用信息如调用的接口名称方法名称参数值列表等和处理结果的回调对象callback全部封装在一起组成一个对象object
3向专门存放调用信息的全局 ConcurrentHashMap 里面 put(ID, object)
4将 ID 和打包的方法调用信息封装成一对象 connRequest使用 IoSession.write(connRequest) 异步发送出去
5当前线程再使用 callback 的 get() 方法试图获取远程返回的结果在get()内部则先使用synchronized获取回调对象callback的锁 检测是否已经获取到结果如果没有然后调用 callback 的 wait() 方法释放 callback 上的锁让当前线程处于等待状态。
6服务端接收到请求并处理后将结果包含了唯一ID回传给客户端客户端 socket 连接上专门监听消息的线程收到消息后分析结果取到ID再从前面的 ConcurrentHashMap 里面 get(ID)从而找到 callback将方法调用结果设置到callback对象里。
7最后监听线程再获取回调对象 callback 的 synchronized 锁因为前面调用过wait() 导致释放callback的锁先使用 notifyAll() 唤醒前面处于等待状态的线程继续执行这样 callback 的 get( )方法继续执行就能拿到调用结果了至此整个过程结束 需要注意的是这里的callback对象是每次调用产生一个新的不能共享另外ID必需至少保证在一个Socket连接里面是唯一的。 3、dubbo 的负载均衡策略 dubbo 的负载均衡策略https://www.cnblogs.com/wyq178/p/9822731.html 1随机调用策略默认随机选择服务器节点该策略可以对不同服务器实例设置不同的权重权重越大分配流量越高
2轮询调用策略均匀地将请求分配到各个机器上。如果各个机器的性能不一样容易导致性能差的机器负载过高所以此时需要调整权重让性能差的机器承载权重小一些流量少一些。
3最少活跃数策略根据服务器的运行状态去选择服务如果某个机器性能越差那么接收的请求越少越不活跃此时就会给不活跃的性能差的机器分配更少的请求
4一致性哈希算法相同参数的请求一定会被分发到固定的服务器节点。当某个服务器节点挂掉的时候会基于虚拟节点均匀分配剩余的流量抖动不会太大。
4、dubbo 的容错机制
Failover默认失败自动切换当出现失败重试其它服务器默认为2次。通常用于读操作但重试会带来更长延迟。
Failfast快速失败只发起一次调用失败立即报错。通常用于非幂等性的写操作比如新增记录。
Failsafe失败安全出现异常时直接忽略。通常用于写入审计日志等操作。
Failback失败自动恢复后台记录失败请求定时重发。通常用于消息通知操作。
Forking并行调用多个服务器只要一个成功即返回。通常用于实时性要求较高的读操作但需要浪费更多服务资源。可通过 forks”2″ 来设置最大并行数。
Broadcast广播调用所有提供者逐个调用任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。
5、dubbo支持哪些协议和适用场景
1dubbo单一长连接和 NIO 异步通讯适合大并发小数据量的服务调用以及消费者远大于提供者的情况。传输协议 TCP异步 Hessian 序列化。Dubbo 官方推荐使用 dubbo 协议。但是dubbo 协议不适合传送大数据量的服务比如传文件传视频等除非请求量很低
2RMI 采用 JDK 标准的 RMI 协议实现使用 Java 标准序列化机制传输参数和返回参数对象需要实现 Serializable 接口使用阻塞式短连接传输数据包大小混合消费者和提供者个数差不多可传文件传输协议 TCP。多个短连接基于 TCP 协议传输同步传输适用常规的远程服务调用和 RMI 互操作。在依赖低版本的 Common-Collections 包Java 序列化存在安全漏洞。
3WebService基于 WebService 的远程调用协议集成 CXF 实现提供和原生 WebService 的互操作。多个短连接基于 HTTP 传输同步传输适用系统集成和跨语言调用。
4HTTP 基于 Http 表单提交的远程调用协议使用 Spring 的 HttpInvoke 实现。多个短连接传输协议 HTTP传入参数大小混合提供者个数多于消费者需要给应用程序和浏览器 JS 调用。
5Hessian集成 Hessian 服务基于 HTTP 通讯采用 Servlet 暴露服务Dubbo 内嵌 Jetty 作为服务器时默认实现提供与 Hession 服务互操作。多个短连接同步 HTTP 传输Hessian 序列化传入参数较大提供者大于消费者提供者压力较大可传文件。
6Redis基于 Redis 实现的RPC协议。
7Memcache基于 Memcache实现的 RPC 协议。
6、dubbo 的通信框架
dubbo 默认使用 Netty 作为通讯框架
7、dubbo的架构设计 7.1、图例说明
左边淡蓝背景的为服务消费方使用的接口右边淡绿色背景的为服务提供方使用的接口位于中轴线上的为双方都用到的接口。图中从下至上分为 10 层各层均为单向依赖右边的黑色箭头代表层之间的依赖关系每一层都可以剥离上层被复用其中Service 和 Config 层为 API其它各层均为 SPI。图中绿色小块的为扩展接口蓝色小块为实现类图中只显示用于关联各层的实现类。图中蓝色虚线为初始化过程即启动时组装链红色实线为方法调用过程即运行时调时链紫色三角箭头为继承可以把子类看作父类的同一个节点线上的文字为调用的方法。
7.2、各层说明
1接口服务层Service该层与实际业务逻辑相关根据 provider 和 consumer 的业务设计对应的接口和实现
2配置层Config对外配置接口以 ServiceConfig 和 ReferenceConfig 为中心
3服务代理层Proxy服务接口透明代理生成服务的客户端 Stub 和 服务端的 Skeleton以 ServiceProxy 为中心扩展接口为 ProxyFactory
4服务注册层Registry封装服务地址的注册和发现以服务 URL 为中心扩展接口为 RegistryFactory、Registry、RegistryService
5路由层Cluster封装多个提供者的路由和负载均衡并桥接注册中心以Invoker 为中心扩展接口为 Cluster、Directory、Router 和 LoadBlancce
6监控层MonitorRPC 调用次数和调用时间监控以 Statistics 为中心扩展接口为 MonitorFactory、Monitor 和 MonitorService
7远程调用层Protocal封装 RPC 调用以 Invocation 和 Result 为中心扩展接口为 Protocal、Invoker 和 Exporter
8信息交换层Exchange封装请求响应模式同步转异步。以 Request 和Response 为中心扩展接口为 Exchanger、ExchangeChannel、ExchangeClient 和 ExchangeServer
9网络 传输 层Transport抽象 mina 和 netty 为统一接口以 Message 为中心扩展接口为 Channel、Transporter、Client、Server 和 Codec
10数据序列化层Serialize可复用的一些工具扩展接口为 Serialization、ObjectInput、ObjectOutput 和 ThreadPool dubbo 的架构设计详情推荐阅读官方文档https://dubbo.apache.org/zh/docs/v2.7/dev/design/ 参考文章https://juejin.cn/post/6844904127076499463