分布式服务框架技术细节
什么是微服务?
传统的单机应用程序随着应用规模和复杂度的增长,多个团队在一个应用上进行开发,每当各个团队需要修改代码时,整个应用程序都需要重新构建、重新测试和重新部署。
而微服务允许将一个大型的应用分解为具有严格职责定义的便于管理的组件系统,即分解和分离应用程序的功能,使它们完全独立,形成多个小的,松耦合的分布式服务。
每个微服务都可以独立地去构建、部署和测试。
微服务的特性:
1)、灵活性:可以将解耦的服务进行组合和重新安排,以快速交付新的功能。一个正在使用的代码单元越小,更改代码越不复杂,测试部署代码所需的时间越短。
2)、有弹性:使应用程序在出现不可恢复的错误的情况下能够优雅地降级
3)、可伸缩性:解耦的服务可以轻松地跨多个服务器进行水平分布,从而可以适当地对功能/服务进行伸缩
一句话总结——小型的、简单的和解耦的服务=可伸缩的、有弹性和灵活的应用程序。
如何编写健壮的服务?
1)、大小合适:如何确保正确地划分微服务的大小,以避免微服务承担太多的职责?请记住,适当的大小允许快速更改应用程序,并降低整个应用程序中断的总体风险。
2)、位置透明:在微服务应用程序中,多个服务实例可以快速启动和关闭时,如何管理服务调用的物理细节?
3)、有弹性
4)、可重复
5)、可伸缩
微服务拆分原则:
一般首先需要从整体上梳理公司的业务,包括过去、现在及未来预见几年内的业务进展,梳理公司的发展战略。
将业务模块化,分解出各个业务模块之间的依赖及业务模块之间的边界。按照业务边界及业务之间的依赖顺序进行系统拆分。
分布式服务框架显示需求:
RPC框架要有服务治理机制,一般来说,服务治理机制包括服务依赖梳理、负载均衡、服务分组灰度发布、链路监控、服务质量统计、服务自动发现、自动下线与
服务注册中心等功能。可以简单认为RPC框架与服务治理结合起来,就构成了一套完整的分布式服务框架。SOA架构最重要就是选择一个合适的RPC框架。
分布式服务框架总体架构和所需技术:
分布式服务框架主要包括服务消费端、服务提供端、服务数据网络传输的序列化与反序列化、服务数据的通信机制、服务注册中心、服务治理这几个组成部分。
分布式服务框架序列化与反序列化实现
序列化(Serialization)是将对象的状态信息转换为可存储或传输的形式过程。简言之,把对象转换为字节数组(或者文本)的过程就是对象的序列化。
反序列化(Deserialization)是序列化的逆过程,将字节数组(或文本)反序列化为对象,把字节数组(或文本)恢复为对象的过程称为对象的反序列化。
序列化的好处:
1)、通过将对象序列化为字节数组,使得不共享内存通过网络连接的系统之间能够进行对象的传输。
2)、通过将对象序列化为字节数组,能够将对象永久存储到存储设备
3)、解决远程接口调用JVM之间内存无法共享的问题
评估一个序列化算法优劣的两个重要指标:
1)、序列化后码流的大小
2)、序列化本身的速度及系统资源开销大小(包括内存、CPU等)
Java默认序列化
Java的序列化主要通过对象输出流java.io.ObjectOutputStream与对象输入流java.io.ObjectInputStream来实现,被序列化的类需要实现Serializable接口。
使用transient声明的字段,序列化机制会忽略该字段。
但是Java默认的序列化只支持Java语言,不支持跨语言,且性能欠佳,序列化后产生的码流过大。
XML序列化框架
XML序列化的优势在于可读性好,利于调试。因为使用标签来表示数据,导致序列化后码流大,而且效率不高。常见的实现有XStream与Java自带的
XML序列化和反序列化两种方式。
JSON序列化
JSON(JavaScript Object Notation)是一种轻量级的数据交换方式。相比XML,JSON的码流更小,而且保留了XML可读性好的优势。常见的JSON
序列化开源工具有:Jackson、fastjson、GSON。
fastjson要求序列化对象的属性必须实现get/set方法才能完成对该属性的序列化。fastjson的优势在于非常易用的API操作及高性能
Hessian序列化框架
Hessian是一个支持跨语言传输的二进制序列化协议。相比于Java默认的序列化机制,Hessian具有更好的性能与易用性,而且支持多种不同的语言。
实现分布式服务框架服务的发布与引入
Spring Boot
SpringBoot基于约定优于配置的理念,极大地简化了利用Spring开发所需的各种配置工作,几乎可以做到零配置。而且Spring Boot是微服务实现中使用的
核心技术。Spring Boot通过简化构建基于REST的微服务的核心任务,大大简化了微服务开发。Spring Boot还极大地简化了将HTTP类型的动词(GET、PUT、
POST和DELETE)映射到URL、JSON协议序列化与Java对象的相互转化,以及将Java异常映射回标准HTTP错误代码的工作。
分布式服务框架能够使用FactoryBean接口实现远程服务的发布与引入。
Spring中的FactoryBean接口:
public interface FactoryBean<T> { T getObject() throws Exception; 返回JavaBean的具体实例,如果isSingleton()返回true,则该实例将会放到Spring容器中单实例缓存池中 Class<?> getObjectType(); 返回Java Bean类型 boolean isSingleton(); 返回Java Bean对象实例是否是单例对象
}
Spring中获取一个Bean实例的执行流程
AbstractBeanFactory#getBean 通过指定bean的名称获取Bean实例
AbstractBeanFactory#doGetBean 执行获取Bean的具体逻辑
AbstractBeanFactory#getObjectForBeanInstance 直接返回beanInstance或者从FactoryBean获取Bean实例
FactoryBeanRegistrySupport#getObjectFromFactoryBean 从FactoryBean获取bean的实例
FactoryBeanRegistrySupport#doGetObjectFromFactoryBean 执行具体的从FactoryBean中获取bean的逻辑
FactoryBean#getObject 调用FactoryBean接口的实现类的getObject方法,返回具体的Bean实例
我们可以自定义MyFactoryBean实现FactoryBean接口,在getObject方法中创建并返回bean的实例。在配置文件中class指定MyFactoryBean全路径即可。
Spring支持集成RPC框架介绍:
Spring对常见的RPC框架提供了相应的集成支持,统一标准化了RPC服务发布与引入编程模型,简化了RPC服务及调用的开发流程,支持如下的远程
调用技术:
1)、Remote Method Invocation(RMI):通过类RmiProxyFactory(引入服务)与类RmiServiceExporter(暴露服务)分别完成RMI服务的引入与发布。
2)、HTTP Invoker:Spring提供的一种远程调用技术,使用Java内置的序列化算法,以及通过HTTP协议进行数据的传输。相应的支持类是
HttpInvokerProxyFactoryBean和HttpInvokerServiceExporter。与RMI的相同点在于序列化算法相同,都是Java内置的序列化算法。不同点在于RMI
传输协议为TCP/IP协议,而HTTP Invoker为HTTP协议。HTTP协议传输性能低于TCP/IP协议,但是不会被防火墙拦截。
3)、Hessian:是一个轻量级的Web服务实现工具,它采用的是二进制协议,因此很适合发送二进制数据。它的一个基本原理就是把远程服务对象以二进
制的方式进行发送和接收。Spring通过HessianProxyFactoryBean和HessianServiceExporter来完成基于Hessian序列化协议的服务的引入与发布。
4)、JAX-WS:Spring通过JAX-WS提供对WS服务的支持。类似的提供了JaxWsClientFactoryBean和SimpleJaxWsServiceExporter来完成WS服务的
引入与发布
5)、JMS:响应的支持类为JmsInvokerProxyFactoryBean和JmsInvokerServiceExporter
这些xxxFactoryBean支持类都实现了FactoryBean接口。
分布式服务框架底层通信实现
通信的本质是IO
Linux下实现的IO模型:
Linux的内核将所有外部设备都看做一个文件来操作,对一个文件的读写操作会调用内核提供的系统命令,返回一个file descriptor(fd,文件描述符)。对一个socket
的读写也会有相应的描述符,称为socket描述符。描述符就是一个数字,它执行内核中的一个结构体(文件路径,数据区等一些属性)。
因为程序运行在操作系统上,编程语言实现的IO操作API最终依赖于操作系统的IO实现。先理清阻塞、非阻塞、同步、异步这几个概念:
阻塞:调用方发起调用请求,在没有返回结果之前,调用方线程被挂起,出于一直等待状态
非阻塞:与阻塞相对,调用方发起调用请求,当前线程不会等待挂起,而会立即返回。后续可以通过轮询等手段来获取调用结果状态。
同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回
异步:与同步相对,当一个异步过程调用发出后,调用者不会立刻得到结果,通过回调等措施来处理这个调用。
Linux实现了5中IO模型
1、阻塞IO模型
默认情况下,所有的文件操作都是阻塞的,在进程空间中调用recvform(recvform函数,用于从Socket套接口上接收数据),其系统调用直到数据包到达且被
复制到应用进程的缓冲区中或者发生错误才返回,在此期间会一直等待,进程在从调用recvform开始到它返回的整段时间内都是被阻塞的。
2、非阻塞IO模型
recvform从应用层到内核的时候,如果该缓冲区没有数据的话,就直接返回一个EWOULDBLOCK错误,一般都是对非阻塞IO模型进行轮询来检查这个状态,看
内核是不是有数据到来。
3、IO复用模型
Linux提供select/poll,进程通过将一个或多个fd传递给select或者poll系统调用,阻塞在select操作上,这样select/poll可以帮我们侦测多个fd是否处于就绪状态。
select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,因此它的使用受到了一些制约。Linux还提供了一个epoll系统调用,epoll使用基于事件驱动的方式
代替顺序扫描,因此性能更高。当有fd就绪时,立即回调函数rollback。
(Java核心类库Selector就是基于epoll的多路复用技术实现)
4、信号驱动IO模型
首先开启套接口信号驱动IO功能,并通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,
就为该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvform来读取数据,并通知主循环函数处理数据。(好了告诉我,我来处理,我先去忙)
5、异步IO
告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。与信号驱动模型的主要区别是:信号驱动IO是由内
核通知我们何时可以开始一个IO操作;异步IO模型由内核通知我们何时已经完成。(等处理好了再告诉我)
IO多路复用技术:在IO编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者IO多路复用技术进行处理。IO多路复用技术通过把多个IO的阻塞到
同一个select的阻塞上,从而可以使系统在单线程的情况下同时处理多个客户端请求。而且与传统的多线程模型比,最大的优势是开销小,系统不需要创建新的额
外进程或这线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。
分布式服务框架服务治理
1)、服务的注册与发现
2)、软负载
3)、服务质量监控与服务指标数据采集(统计QPS,每天调用总量,响应时间等指标)
4)、记录负责人
5)、服务分组路由(对某个服务升级之后,需要灰度发布或者AB测试(一部分用A服务,另一部分用B服务),要求服务有自动分组能力,
可以将某个消费组的请求只打到对应的服务组上)
6)、服务依赖关系分析(理清依赖关系,自动画出依赖关系图,可以正确安排系统发布顺序)
7)、服务降级:若某个非关键服务出错率很高,对业务链路造成了影响,使用一键降级将该服务从调用链路中摘除
8)、服务权重调整:若某些机器配置好,有更高的吞吐能力,能支持更高的QPS,则对该机器配置更高的服务权重。
9)、服务调用链路追踪:调用链路横跨多个服务,多个应用的话,对每一次调用分配一个唯一的标识将服务之间的调用串联起来,有助于排查线上问题