微服务架构
概念
一个大型复杂的业务系统由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。每个任务代表着一个小的业务能力,组合或者复用这些微服务的能力,支撑上层大型复杂的业务系统。
解决方案
目前流行的微服务架构是基于开源的 Spring Cloud 实现,它提供了一整套的解决方案——服务注册与发现,服务消费,服务保护与熔断,网关,分布式调用追踪,分布式配置管理等。
Spring Boot 是 Spring 的一套快速配置脚手架,使用默认大于配置的理念,用于快速开发单个微服务。提供 HTTP 协议的 RESTful 接口。
Spring Cloud 组件架构
流程:
- 请求统一通过 API 网关(Zuul)来访问内部服务。
- 网关接收到请求后,从注册中心(Eureka)获取可用服务。
- 由 Ribbon 进行均衡负载后,分发到后端具体实例。
- 微服务之间通过 Feign 进行通信处理业务。
- Hystrix 负责处理服务超时熔断。
- Turbine 监控服务间的调用和熔断相关指标。
微服务间通信
基于 HTTP 的 REST 方式
Spring Cloud 采用的是基于 HTTP 的 REST 方式,相比于 Dubbo RPC,更加轻量化和灵活,有利于跨语言服务的实现,以及服务的发布部署,但是 REST 服务调用性能会比 RPC 低一些。
HTTP 序列化和反序列化
上篇文章中学习到,当我们需要将内存中的对象持久化到磁盘,数据库中时,当我们需要与浏览器进行交互时,当我们需要实现 RPC 时,这个时候就需要序列化和反序列化了。
在使用 Spring Boot 开发微服务时,通过 @RestController
注解进行 Json 的序列化和反序列化操作。这里实际上已经体现了 HTTP 序列化和反序列化的过程,而这里其实就是 HttpMessageConverter
发挥着作用。在报文到达 SpringMVC / SpringBoot 和从 SpringMVC / SpringBoot 出去,都存在一个字符串和 Java 对象间转化的问题。这一过程,在 SpringMVC / SpringBoot 中,是通过 HttpMessageConverter
来解决的。
FastJson 和 Jackson 的序列化和反序列化交叉使用
发现接口访问很慢
经过排查发现服务A的接口调用另一个服务B的接口,接口调用关系如下图
分别查询方法请求时间
1、在服务A中检测到的searchNotices()方法的请求慢时间(511.2513ms)如下:
com.gemantic.semantic.datacenter.controller.CompanyProceedingController:searchNotices()
---[1452.6121ms] com.gemantic.semantic.datacenter.controller.CompanyProceedingController:searchNotices()
+---[50.4082ms] com.gemantic.semantic.datacenter.repository.TqOaStcodeRepository:findBySetypeAndEnddateAndSymbol() #93
+---[461.2263ms] com.gemantic.semantic.datacenter.repository.CompanyProceedingRepository:findAll() #148
+---[251.5718ms] com.gemantic.semantic.datacenter.repository.ComProceedingDetailRepository:findAllByIdIn() #158
+---[511.2513ms] com.gemantic.semantic.datacenter.rest.repository.GreatWisdomDocRepository:existByCaseNumbers() #226
2、在服务B中检测到被调用的方法existByCaseNumbersPost()方法的请求时间(77.0756ms)如下:
---[77.0756ms] com.gemantic.greatwisdom.controller.LegalProceedingDetailController:existByCaseNumbersPost()
+---[0.1424ms] org.apache.commons.logging.Log:info() #73
+---[71.7306ms] com.gemantic.greatwisdom.repository.LegalProceedingDetailRepository:findAllByCrIdIn() #74
`---[0.4626ms] com.gemantic.springcloud.model.Responses:ok() #76
问题的原因分析
-
网络的问题可以基本排除,因为在同一台机器上进行联调测试的
-
服务之间的额外处理(序列化和反序列化),如下图
- 在 Spring 的处理过程中,一次请求报文和一次响应报文,分别被抽象为一个请求消息
HttpInputMessage
和一个响应消息HttpOutputMessage
。 - 处理请求时,由合适的消息转换器将请求报文绑定为方法中的形参对象,在这里同一个对象就有可能出现多种不同的消息形式,如json、xml。同样响应请求也是同样道理。
- 在 Spring 中,针对不同的消息形式,有不同的
HttpMessageConverter
实现类来处理各种消息形式,至于各种消息解析实现的不同,则在不同的HttpMessageConverter
实现类中。
观察项目的配置发现两个服务配置的不一样
服务 A 配置如下:
@Bean
public
FastJsonHttpMessageConverter fastJsonpHttpMessageConverter() {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.BrowserSecure);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XML);
supportedMediaTypes.add(MediaType.IMAGE_GIF);
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
supportedMediaTypes.add(MediaType.IMAGE_PNG);
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_XML);
fastJsonHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
return fastJsonHttpMessageConverter;
}
服务 B 配置如下:
@Bean
public HttpMessageConverters jsonHttpMessageConverters() {
return new HttpMessageConverters(false, Collections
.singleton(new MappingJackson2HttpMessageConverter()));
}
实验和结论
将两个服务的 Json 序列化和反序列化进行统一配置再次测试,请求的时间大幅缩减了,所以可以将大部分的时间消耗原因确定为序列化的问题
推荐阅读:
Java的POJO类为什么要实现Serializable接口
如果觉得还有帮助的话,你的关注和转发是对我最大的支持,O(∩_∩)O: