大家好,欢迎来到IT知识分享网。
从公众号转载,关注微信公众号掌握更多技术动态
—————————————————————
怎么调用—优先级最高
1.服务描述
服务调用首先要解决的问题就是服务如何对外描述。比如,你对外提供了一个服务,那么这个服务的服务名叫什么?调用这个服务需要提供哪些信息?调用这个服务返回的结果是什么格式的?该如何解析?这些就是服务描述要解决的问题。常用的服务描述方式包括 RESTful API、XML 配置以及 IDL 文件三种。
推荐选择:选择是对外Restful,对内Xml,跨语言IDL
2.接口框架
微服务提倡轻量级的通信方式,一般采用 HTTP/REST 或者 RPC 方式统一接口协议。但在实践过程中,光统一接口协议还不够,还需要统一接口传递的数据格式。例如,我们需要指定接口协议为 HTTP/REST,但这还不够,还需要指定 HTTP/REST 的数据格式采用 JSON,并且 JSON 的数据都遵循如下规范。
如果我们只是简单指定了 HTTP/REST 协议,而不指定 JSON 和 JSON 的数据规范,那么就会出现这样混乱的情况:有的微服务采用 XML,有的采用 JSON,有的采用键值对;即使同样都是 JSON,JSON 数据格式也不一样。这样每个微服务都要适配几套甚至几十套接口协议,相当于把曾经由 ESB 做的事情转交给微服务自己做了,这样做的效率显然是无法接受的,因此需要统一接口框架。
接口框架不是一个可运行的系统,一般以库或者包的形式提供给所有微服务调用。例如,针对上面的 JSON 样例,可以由某个基础技术团队提供多种不同语言的解析包(Java 包、Python 包、C 库等)。
3.负载均衡的选择
- 随机算法:在请求量远超可用服务节点数量的情况下,各个服务节点被访问的概率基本相同,主要应用在各个服务节点的性能差异不大的情况下。
- 轮询算法:跟随机算法类似,各个服务节点被访问的概率也基本相同,也主要应用在各个服务节点性能差异不大的情况下。
- 加权轮询算法:在轮询算法基础上的改进,可以通过给每个节点设置不同的权重来控制访问的概率,因此主要被用在服务节点性能差异比较大的情况。比如经常会出现一种情况,因为采购时间的不同,新的服务节点的性能往往要高于旧的节点,这个时候可以给新的节点设置更高的权重,让它承担更多的请求,充分发挥新节点的性能优势。
- 最少活跃连接算法:与加权轮询算法预先定义好每个节点的访问权重不同,采用最少活跃连接算法,客户端同服务端节点的连接数是在时刻变化的,理论上连接数越少代表此时服务端节点越空闲,选择最空闲的节点发起请求,能获取更快的响应速度。尤其在服务端节点性能差异较大,而又不好做到预先定义权重时,采用最少活跃连接算法是比好的选择。
- 一致性 hash 算法:因为它能够保证同一个客户端的请求始终访问同一个服务节点,所以适合服务端节点处理不同客户端请求差异较大的场景。比如服务端缓存里保存着客户端的请求结果,如果同一客户端一直访问一个服务节点,那么就可以一直从缓存中获取数据。
- 自适应最优选择算法:对加权轮询算法的改良,一种动态加权轮询算法。在客户端本地维护一份同每一个服务节点的性能统计快照,并且每隔一段时间去更新这个快照。在发起请求时,根据“二八原则”,把服务节点分成两部分,找出 20% 的那部分响应最慢的节点,然后降低权重。这样的话,客户端就能够实时的根据自身访问每个节点性能的快慢,动态调整访问最慢的那些节点的权重,来减少访问量,从而可以优化长尾请求。
- 粘滞连接。粘滞连接用于有状态服务,尽可能让客户端总是向同一服务提供者发起服务调用,除非该提供者宕机,再连接另一台。由于服务通常被强烈建议设计成无状态的,因此该模式很少使用。
(1)Ribbon
- Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套「客户端负载均衡的工具」。
- 简单的说,Ribbon 是 Netflix 发布的开源项目,主要功能:提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。简单的说,就是在配置文件中列出 LoadBalancer (简称LB:负载均衡) 后面所有的配置,Ribbon 会自动的帮助你基于某种规则 (如简单轮询,随机连接等等) 去连接这些机器。
- Dubbo、SpringCloud 中均提供了负载均衡,「SpringCloud 的负载均衡算法可以自定义」。
- 负载均衡简单分类:
- 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
- 「Ribbon 就属于进程内LB」,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!
- 即在服务的提供方和消费方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方!
- 集中式LB
- 进程式LB
(2)Feign
Feign旨在使编写Java Http客户端变得更容易
- 前面在使用Ribbon + RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一个客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步的封装,由他来帮助我们定义和实现依赖服务接口的定义,「在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它」 (类似以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon 时,自动封装服务调用客户端的开发量。
「Feign默认集成了Ribbon」
- 利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,「通过Feign只需要定义服务绑定接口且以声明式的方法」,优雅而简单的实现了服务调用。
4.服务通信
所有的微服务都是独立的Java进程跑在独立的虚拟机上,现在基本最通用的有两种方式:
- 同步调用:
- REST(JAX-RS,Spring Boot)
- RPC(Thrift, Dubbo)
- 异步消息调用(Kafka, Notify, MetaQ)
同步调用比较简单,一致性强,但是容易出调用问题,性能体验上也会差些,特别是调用层次多的时候。
一般REST基于HTTP,更容易实现,更容易被接受,服务端实现技术也更灵活些,各个语言都能支持,同时能跨客户端,对客户端没有特殊的要求,只要封装了HTTP的SDK就能调用,所以相对使用的广一些。RPC传输协议更高效,安全更可控,特别在一个公司内部,如果有统一个的开发规范和统一的服务框架时开发效率优势更明显些。
异步消息的方式既能减低调用服务之间的耦合,又能成为调用之间的缓冲,确保消息积压不会冲垮被调用方,同时能保证调用方的服务体验,继续干自己该干的活,不至于被后台性能拖慢。不过需要付出的代价是一致性的减弱,需要接受数据最终一致性;还有就是后台服务一般要 实现幂等性,因为消息发送出于性能的考虑一般会有重复(保证消息的被收到且仅收到一次对性能是很大的考验);最后就是必须引入一个独立的broker,如果公司内部没有技术积累,对broker分布式管理也是一个很大的挑战。
REST牺牲了服务调用的性能,但也避免了原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖。
RPC 主要是基于 TCP/IP 协议的,而 HTTP 服务主要是基于 HTTP 协议的
(1)RPC服务
远程过程调用,自定义数据格式,速度快,效率高,目前流行的开源RPC框架:gRPC/Thrift/Dubbo.
①RPC 的工作机制
客户端调用一个远程的过程,将参数和附加信息序列化为消息,然后将消息发送到服务端。服务端在接受到消息后,将信息的内容反序列化,执行所请求的操作,然后将结果发送回客户端。客户端和服务端各自负责参数的序列化和反序列化。
②一个完整的RPC架构里面包含了四个核心的组件
- 客户端(Client),服务的调用方。
- 服务端(Server),真正的服务提供者。
- 客户端存根,存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
- 服务端存根,接收客户端发送过来的消息,将消息解包,并调用本地的方法。
③RPC 的优势
- 简单直接的交互。 RPC 使用 GET 来获取信息,使用 POST 来处理其他所有操作。服务端和客户端之间交互的机制归结为调用端点并获得响应。
- 易于添加新函数。 如果 API 有了新的需求,我们可以轻松地添加另一个执行这个需求的端点:1)编写一个新函数,并将其放在一个新端点之后;2)现在,客户可以访问这个端点,并获取符合其需求的信息。
- 高性能。轻量级的有效负载不会对网络产生压力,以此提供高性能,这对于共享服务器和在工作站网络上执行并行计算非常重要。RPC 还能够优化网络层,使得不同服务之间每天发送海量消息变得非常高效。
④RPC 的不足
- 和底层系统紧密耦合。 API 的抽象级别有助于其可重用性。API 与基础系统的耦合越紧密,对其他系统的可重用性就越差。RPC 与基础系统的紧密耦合不允许其在系统函数和外部 API 之间建立抽象层。这很容易引起安全问题,因为关于基础系统的细节实现很容易会泄漏到 API 中。RPC 的紧密耦合使得可伸缩性要求和松散耦合的团队难以实现。因此,客户端要么会担心调用特定端点的带来的任何可能的副作用,要么需要尝试弄清楚要调用的端点,因为客户端不了解服务器如何命名其函数。
- 可发现性低。 在 RPC 中,无法对 API 进行检验总结,或者发送请求来开始理解根据需求应该调用哪个函数。
- 函数爆炸性增长。创建新函数非常容易。因此,相较于重新编辑现有的函数,我们会倾向于创建新的功能,最终产生大量难以理解的、功能重叠的函数。
⑤什么场景用RPC
- 微服务架构。在微服务架构中,每个服务都是一个独立的进程,服务之间需要通过网络进行通信。由于微服务架构中服务数量较多,服务之间的通信量也会很大。如果使用HTTP协议,每次请求都需要建立和断开连接,这会造成很大的性能开销。而RPC协议可以使用连接池等技术来减少连接建立和断开的开销,提高性能。此外,RPC协议通常使用二进制协议进行数据传输,相对于HTTP的文本协议,具有更高的性能。
- 分布式系统。在分布式系统中,节点之间需要进行函数调用来实现分布式计算。如果使用HTTP协议,每次请求都需要建立和断开连接,这会造成很大的性能开销。而RPC协议可以使用连接池等技术来减少连接建立和断开的开销,提高性能。此外,RPC协议通常使用方法调用的语义,更符合面向对象编程的思想。
- 高并发场景。在高并发场景中,每秒钟可能有成千上万的请求需要处理。如果使用HTTP协议,每次请求都需要建立和断开连接,这会造成很大的性能开销。而RPC协议可以使用连接池等技术来提高性能,适用于高并发场景。
- 多语言环境。在多语言环境中,不同的编程语言需要进行通信。如果使用HTTP协议,需要使用通用的数据格式,如JSON、XML等,这会造成很大的性能开销。而RPC协议可以使用自定义的IDL来定义接口,实现更严格的类型检查和更好的语义。
(2)SOAP:使数据作为服务可用
①SOAP 的工作机制
XML 数据格式拖累了很多数据规范。伴随着大量的消息结构,XML 数据格式使得 SOAP 成为了最冗长的 API 架构风格。
SOAP 的消息由这些部件组成:
- 一个信封标签:用于开始和结束每条消息
- 包含请求或响应的正文
- 一个标头:用于表示消息是否由某些规范或额外要求的来确认
- 故障通知:包含了可能在请求处理过程只能够发生的任何错误
SOAP API 的逻辑由 Web 服务描述语言(WSDL)编写。该 API 描述语言定义了端点并描述了可以执行的所有过程。这使得不同的编程语言和 IDE 能够快速建立通信。SOAP 支持有状态和无状态消息传递。在有状态的情况下,服务器存储接收到的信息可能非常繁琐复杂。但这对于涉及多方和复杂交易的操作是合理的。
②SOAP 的优势
独立于语言和平台。内置创建 Web 服务的功能使得 SOAP 能够处理消息通信的同时发送独立于语言和平台响应。
绑定到各种协议。SOAP 在适用于多种场景的传输协议方面是十分灵活的。
内置错误处理。SOAP API 规范允许返回带有错误码及其说明的的 XML 重试消息。
一系列的安全拓展。SOAP 与 ES-Security 集成,因此 SOAP 可满足企业级事务要求。它在事务内部提供了隐私和完整性,同时允许在消息级别进行加密。
③SOAP 的不足
如今,由于如下几种原因,许多开发人员在听到必须集成 SOAP API 的想法后都会感到不安。
仅使用 XML。SOAP 消息包含大量的元数据,并且在请求和响应时仅支持繁冗的 XML 格式。
重量级。由于 XML 文件的大小,SOAP 服务需要很大的带宽。
非常专业化的知识。构建 SOAP API 服务器需要对所有涉及到的协议以及它们及其严格的限制都有很深的了解。
乏味的消息更新。由于需要额外的工作来添加或者删除某个消息属性,这种死板的 SOAP 模式减慢了其被采用的速度。
(3)HTTP服务REST
网络传输协议,规定数据传输格式,服务调用和提供方没有技术限定,消息封装臃肿,目前流行HttpClient、OKHttp(restTemple)
相比RPC,HTTP接口开发也就是我们常说的RESTful风格的服务接口。对于在接口不多、系统与系统交互较少的情况下,解决信息孤岛初期常使用的一种通信手段;优点就是简单、直接、开发方便。利用现成的http协议进行传输。但是对于大型企业来说,内部子系统较多、接口非常多的情况下,RPC框架的好处就显示出来了,首先就是长链接,不必每次通信都要像http一样去3次握手,减少了网络开销;其次就是RPC框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。
①REST 的工作机制
REST 的定义并不像 SOAP 那样严格。RESTful 体系结构应该遵守如下六个体系结构约束:
- 统一接口:无论设备或应用程序类型如何,都可以采用统一的方式与给定的服务端进行交互。
- 无状态:请求本身包含处理该请求所需要的状态,并且服务端不存储与会话相关的任何内容。
- 缓存
- 客户端 – 服务器体系结构:允许双方独立发展
- 应用程序的层级系统
- 服务端向客户端提供可执行代码的能力
实际上,某些服务仅在某种程度上是 RESTful 的。而它们的内核采用了 RPC 样式,将较大的服务分解为资源,并有效地使用 HTTP 基础结构。但 REST 的关键部分是超媒体(又称 HATEOAS),是超文本作为应用程序状态引擎(Hypertext As The Enginer Of Application State)的缩写。
基本来说,这意味着 REST API 在每个响应中都提供元数据,该元数据链接了有关如何使用该 API 的所有相关信息。这样便可以使客户端和服务端解耦。因此,API 提供者和 API 使用者都可以独立发展,而这并不会阻碍他们的交流。
“HATEOAS 才是 REST 的关键功能,因为它真正使得 REST 成为 REST。但由于大多数人不使用 HATEOAS,因此他们实际上是在使用 HTTP RPC。”这是 Reddit 上表达的一些激进观点。确实,HATEOAS 是 REST 的最成熟版本。
但是,这非常难以实现,因为这要求 API 客户端要比它们如今构建和使用的方式变得更先进和智能得多。因此,即便是如今非常好的 REST API 也不一定总是能做到这一点。这就是为什么 HATEOAS 主要是作为 RESTful API 设计的长期开发的愿景而存在。
当服务端实现 REST 的某些功能和 RPC 的某些功能时,在 REST 和 RPC 之间确实可能存在这样一个灰色区域。但 REST 是基于资源或名词的,而不是基于动作或动词。
②REST 的优势
客户端和服务端的解耦:由于 REST 尽可能地解耦了客户端和服务端,REST 相较于 RPC 可以提供更好的抽象性。具有抽象级别的系统能够封装其实现细节,以更好的标示和维持它的属性。这使得 REST API 足够灵活,可以随着时间的推移而发展,同时保持稳定的系统。
可发现性:客户端和服务端之间的通信描述了所有内容,因此不需要外部文档即可了解如何与 REST API 进行交互。
缓存友好:REST 重用了许多 HTTP 工具,也是唯一一种可以在 HTTP 层面上缓存数据的 API 架构风格。与其相对的是,在任何其他 API 上实现缓存都需要配置其他缓存模块。
多种格式支持:REST 拥有支持多种格式用于存储和交换数据的能力,这是它如今成为搭建公共 API 的主要选择的原因之一。
REST 的不足
没有标准的 REST 结构:在构建 REST API 方面,没有具体的正确方法。如何对资源进行建模以及哪些资源需要建模取决于不同的情况。这使得 REST 在理论上很简单,但在实践中却很困难。
庞大的负载: REST 会返回大量丰富的元数据,以便客户端可以仅从响应中了解有关应用程序状态的所有必要信息。对于具有大量带宽容量的大型网络系统来说,这种“啰嗦”的通信并不算很大的负载。但带宽容量并非总是足够的。这也是 Facebook 在 2012 年提出 GraphQL 架构风格的关键驱动因素。
响应过度和响应不足问题。REST 的响应包含的数据会过多或不足,通常会导致客户端需要发送另一个请求。
(4)GraphQL:仅请求所需要的数据
REST API 需要被多次调用才能返回所需要的资源。所以,GraphQL 被发明了,并改变了这一切游戏的规则。GraphQL 是一种语法,它描述了如何进行精确的数据请求。有些应用程序的数据模型具有许多相互引用的复杂实体,在这种情况下,实现 GraphQL 是值得的。GraphQL 的生态系统正在蓬勃发展,出现了例如 Apollo、GraphiQL 和 GraphQL Explorer 等强大的库和工具。
①GraphQL 的工作机制
GraphQL 从构建模式(Schema)开始。模式是对于用户可以在 GraphQL API 中进行的所有查询及其返回的所有类型的描述。模式构建非常困难,因为它需要使用模式定义语言(SDL)进行强类型化。
因为在客户端进行查询之前已经定义好了模式,所以客户端可以验证其查询语句,以确保服务端能够对查询语句进行响应。在查询语句到达后端应用程序时,GraphQL 操作将根据整个模式进行解释,并向前端应用程序返回解析到的数据。API 向服务端发送一个庞大的查询,该 API 返回一个仅包含我们所需数据的 JSON 响应。
②GraphQL 的优势
具有类型的模式:GraphQL 提前公开了它能做什么,从而提高了其可发现性。通过将客户端指向 GraphQL API,我们可以发现什么查询语句是可用的。
没有版本控制:版本控制的最佳实践是不要对 API 进行版本控制。
尽管 REST 提供了不同的 API 版本,GraphQL 使用的是不断更新的单一版本,这使用户可以持续访问新功能,并有助于提供更整洁、更可维护的服务器代码。
详细的错误消息:GraphQL 以类似于 SOAP 的方式提供所发生错误的详细信息。它的错误消息包括所有解析器,并指向确切的发生故障时的查询部分。
灵活的权限:GraphQL 允许选择性地公开某些功能,同时保留私人信息。而相对应的是,REST 体系架构不能仅显示部分数据,要么是全部数据,要么是没有数据。
③GraphQL 的不足
性能问题。GraphQL 权衡了复杂性,来实现其强大功能。一个请求中的嵌套字段太多会导致系统过载。因此,对于复杂的查询,REST 仍然是更好的选择。
缓存复杂度。由于 GraphQL 不再使用 HTTP 缓存语义,因此使用者需要额外自定义缓存。
大量的预开发教育。由于没有足够的时间来了解 GraphQL 的某个操作和 SDL,因此许多项目决定采用众所周知的 REST 方法。
(5)HTTP还是RPC
- 对于效率要求比较高,开发时使用统一的技术方法栈,则选择RPC。常用的为dubbo(耦合Java语言)
- 如果需要更加灵活的,跨语言,跨平台,用HTTP
每个 API 项目都有不同的限制和需求。通常,API 架构的选择取决于:
- 所使用的编程语言,
- 开发环境,以及资源预算,包括人力资源和财务资源。
在了解了每种设计风格的利与弊之后,API 设计人员可以选择最适合项目的那一种。具有强耦合性的 RPC 很适用于内部微服务,但它对外部 API 或者 API 服务而言不是一个好的选择。SOAP 的使用有些麻烦,但它强大的安全拓展使它在计费操作、预订系统和支付方面是无可替代的。
REST 是针对 API 的最高级别的抽象和最佳模型。但它往往会有些“啰嗦”而增加系统的负担 —— 如果你使用的是移动设备,这是个问题。
GraphQL 在数据获取方面向前迈出了一大步,但并不是每个人都有足够的时间后精力来掌握它。归根结底,去针对一些小型的用例来尝试某种特定 API 架构,并去了解它是否适合你的用例以及是否解决了你的问题,这样做是比较合适的。如果它适用于你的用例,就可以尝试扩展并查看它是否适用于更多的用例。
5.通信框架原理
(1)技术点
- 长连接:长连接更加省资源,长连接只有在首次创建或者链路重连才会创建链路,实现多消息复用同一个链路、
- NIO:采用nio,因为nio的多路复用技术,Selector可以管理多个通道Channel,nio的非阻塞更加高效、
- 开源nio框架:netty已经经过诸多项目的考验,并且API层对底层进行了细节隐藏,更加便捷;
(2)功能设计
①服务端的设计
- 服务端只提供上层的API,不与任何协议绑定。
- 服务端提供给用户的API尽量的屏蔽底层的通信细节,防止底层变更引起级联变 更。
- 功能不在于全面,而在于可扩展。
②客户端设计
- 第一步创建Bootstrap实例。
- 初始化TCP链接参数,设置编解码handler和其他业务handler,
- connect方法发起异步TCP连接操作;connect为异步链接,程序不会等待。TCP是否连接可以通过返回的ChannelFuture对象来通知连接结果。(同步等待:wait后notify,会有InterruptionException;注册监听器:等待操作完成后异步通知)
- 采用连接监听器的方式,异步通知结果;
- 服务端返回TCP握手应答,矽统回调监听器操作完成接口。
- 操作完成接口中实现的逻辑,通知客户端连接操作完成。
(3)可靠性设计
链路有效性检测:当前流行的做法是:心跳检测
- TCP层面:TCP的Keep-Alive;它的作用域是整个TCP协议栈;
- 协议层:主要是长连接连接协议中,例如SMPP协议。
- 应用层:通过各个业务与产品通过约定方式定时给对方发送信条消息。
心跳检测的目的:确认当前的链路是可用的。
①不同的协议,心跳机制:
- ping-pong型心跳:请求-响应型,一方发出ping,收到信息后立即回复pong;
- ping-ping型心跳:双方按照约定定时想对方发送ping;属于双向心跳。
②心跳检测策略
- 心跳超时:连续N次未检测到对方发送来的ping或者pong信息,则认为链路失效;
- 心跳失败:读取或者发送消息的时候发生了I/o异常;
无论是超时还是失败,都要关闭链路,并且有客户端发起重连,保证恢复正常。
(4)序列化
6.服务调用模式
(1)OneWay模式
只有请求,没有应答。例如:通知消息;很容易设计成异步的。消费者发起服务调用之后,直接返回,不需要同步阻塞,等待应答。
(2)请求-应答模式:一请求,一应答模式。最常用。
①同步获取结果
②同步调用后,异步获取处理结果
7.RestTemplate
(1)ClientHttpRequestFactory
RestTemplate 集成了 HttpAccessor 基础抽象类,在 HttpAccessor 中,定义了requestFactory 默认实现(默认SimpleClientHttpRequestFactory)。也可以通过 RestTemplate (ClientHttpRequestFactory requestFactory) 的构造函数,传入自定义的 ClientHttpRestFactory。
SimpleClientHttpRequestFactory 的实现,底层为 JDK 自带的 HttpURLConnection,每执行一次 exchange 方法,都会新开一个 tcp 链接句柄。请求 <=> tcp 链接一一对应。linux 默认的 tcp 链接句柄限制是 1024 (虽然可以通过配置调大),这会极大的限制并发,不能够复用 tcp 通道,浪费大量的 tcp 链接。
除了 JDK 自带 HttpURLConnection 实现的 SimpleClientHttpRequestFactory,其它常用的还有 HttpComponentsClientHttpRequestFactory、OkHttp3ClientHttpRequestFactory 等。OkHttp和HttpClient在性能和使用上不分伯仲,根据实际业务选择即可。
注意:RestTemplate会通过类加载器查找项目中是否包含HttpComponentsClientHttpRequestFactory和OkHttp3ClientHttpRequestFactory,如果包含的话,会调用setRequestFactory进行修改。所以如果pom文件中引入了httpClient或okHttpClient,则即使没有指定requestFactory,则也不会使用默认的SimpleClientHttpRequestFactory。
① HttpComponentsClientHttpRequestFactory
HttpComponentsClientHttpRequestFactory 有几种构造方法,其中最常用的有无参构造方法、以及有参 (HttpClient httpClient) 构造方法。在生产环境,一定不可使用无参构方法,因为不配置会使用配置配置,由 httpClient 构建的默认连接池会非常小 (默认最大连接是 20,每个路由最大连接是 2)。
使用默认参数高并发场景,从jstack的日志中可以很容易分析出来,有大量的线程在等待获取连接池里的连接而进行排队,因此导致了线程堆积,因此平响上升。由于线程堆积越多,系统资源占用越厉害,接口平响也会因此升高,更加剧了线程的堆积,因此很容易出现恶性循环而导致线程数超限。
②OkHttp3ClientHttpRequestFactory
在OKHttp3内部使用了双端队列管理连接池,也就是说 连接池没有数量的限制。那OKHttp3是怎么保证队列内存不溢出呢?连接池通过最大闲置连接数(maxIdleConnections)和保持存活时间(keepAliveDuration)来控制连接池中连接的数量。在连接池的内部,会维护一个守护线程,当每次往线程池中添加新的连接时,将会触发异步清理闲置连接任务。
默认情况下:最大闲置连接数:5、保持存活时间:5(mins)
工作原理:
当某一个Http请求结束后,对应的Connection实例将会标识成idle状态,然后连接池会立马判断当前连接池中的处于idle状态的Connection实例是否已经超过 maxIdleConnections 阈值,如果超过,则此Connection实例 将会被释放,即对应的TCP/ IP Socket通信也会被关闭。连接池内部有一个异步线程,会检查连接池中处于idle实例的时长,如果Connection实例时长超过了keepAliveDuration,则此Connection实例将会被剔除,即对应的TCP/ IP Socket通信也会被关闭。
对于瞬时并发很高的情况下,okhttp连接池中的TCP/IP连接将会冲的很高,可能和并发数量基本一致。但是,当http请求处理完成之后,连接池会根据maxIdleConnections来保留Connection实例数量。maxIdleConnections的设置,应当根据实际场景请求频次来定,才能发挥最大的性能。
假设连接池配置是默认配置,即:最大闲置连接数(maxIdleConnections):5,保持存活时间(keepAliveDuration):5(mins);当前瞬时并发有100个线程同时请求,那么,在okhttp内创建100个 tcp/ip连接,假设这100个线程在1s内全部完成,那么连接池内只有5个tcp/ip连接,其余的都将释放;在下一波50个并发请求过来时,连接池只有5个可以复用,剩下的95个将会重新创建tcp/ip连接,对于这种并发能力较高的场景下,最大闲置连接数(maxIdleConnections)的设置就不太合适,这样连接池的利用率只有5 /50 *100% = 10%,所以这种模式下,okhttp的性能并不高。
所以,综上所述,可以简单地衡量连接池的指标:连接池的利用率 = maxIdleConnections / 系统平均并发数
根据上述公式可以看出,利用率越高, maxIdleConnections 和 系统平均并发数 这两个值就越接近,即:maxIdleConnections 应当尽可能和系统平均并发数相等。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/114129.html






