引言:
由于公司商业上有实打实的需求和场景,倒逼产品开始思考架构升级,以适应这种商业环境的快速变化。架构师在进行技术选型或者架构升级前,需要做大量技术调研、竞品分析,《微服务架构综述》则是对服务化架构技术调研产生的调研报告,将会从如下三个角度分析微服务架构的“前生今世”,从而为产品团队的技术转型找到一些理论依据:
微服务有着多种框架可供挑选,在挑选合适的框架时,可以把以下几点作为参考的指标:
注:对微服务架构师来说,截止目前Spring Cloud和Dubbo体系是最完善的开源方案。dubbo现已融入spring-cloud-alibaba项目
spring |
Spring框架为开发Java应用程序提供了全面的基础架构支持。它包含一些很好的功能,如依赖注入和开箱即用的模块,如:Spring JDBC 、Spring MVC 、Spring Security、 Spring AOP、Spring ORM 、Spring Test,这些模块缩短应用程序的开发时间,提高了应用开发的效率例如 |
spring boot |
Spring Boot基本上是Spring框架的扩展,主要是简化了大量的配置工作。以约定优于配置、开箱即用策略,提供了一个可以快速搭建应用的框架,它消除了设置Spring应用程序所需的XML配置(改用注解),为更快,更高效的开发生态系统铺平了道路。 与Spring不同,Spring Boot只需要一个依赖项来启动和运行Web应用程序 |
spring cloud |
SpringCloud, 基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
|
spring作为一种抽象级别很高的框架,底层自然支持多种具体的服务发现实现技术的。
####Eureka方案
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。
SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka包含两个组件:
负责注册服务,各个节点启动后,注册,服务注册表中会有存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到
spring引入euraka server组件
注册中心服务端主要对外提供了四个个功能:
服务注册:服务提供者启动时,会通过 Eureka Client 向 Eureka Server 注册信息,Eureka Server 会存储该服务的信息,Eureka Server 内部有二层缓存机制来维护整个注册表
提供注册表:服务消费者在调用服务时,如果 Eureka Client 没有缓存注册表的话,会从 Eureka Server 获取最新的注册表
同步状态:Eureka Client 通过注册、心跳机制和 Eureka Server 同步当前客户端的状态。
服务剔除:当 Eureka Client 和 Eureka Server 不再有心跳时,Eureka Server 会将该服务实例从服务注册列表中删除,即服务剔除。
是java客户端,用于简化Eureka Server的操作,客户端同时内置使轮询负载算法的负载均衡器,在启动后,会三十秒,向服务器发送心跳,如果服务器有多个心跳周期内没有接受到某个节点的心跳,那么服务器就会在注册表中移除节点,默认周期为90秒
spring引入eureka客户端组件
eureka客户端配置,指定server端地址、端口、心跳间隔等
客户端致提供两个功能:
Register: 服务注册,服务的提供者,将自身注册到注册中心,服务提供者也是一个 Eureka Client。当 Eureka Client 向 Eureka Server 注册时,它提供自身的元数据,比如 IP 地址、端口,运行状况指示符 URL,主页等。
Renew: 服务续约,Eureka Client 会每隔 30 秒发送一次心跳来续约。 通过续约来告知 Eureka Server 该 Eureka Client 运行正常,没有出现问题。 默认情况下,如果 Eureka Server 在 90 秒内没有收到 Eureka Client 的续约,Server 端会将实例从其注册表中删除,此时间可配置,一般情况不建议更改。
Eureka 服务管理界面:
Eureka集群实现高可用:
####Consul方案
spring没有提供内嵌consul服务端的组件,因此需要使用者自行启动consul服务器
spring引入consul客户端组件
客户端地址、端口、服务注册名、心跳等配置
consul服务管理界面:
####微服务间API调用
Spring框架提供RestTemplate 类来实现rest API调用
服务端代码示例:
@GetMapping("/order") public String getOrder(){ RestTemplate restTemplate = new RestTemplate(); String res =restTemplate.getForObject("http://localhost:8888/getUser",String.class); System.out.println("res = " + res); return "getOrder" + " ==== " + res; } |
客户端代码示例:
# GET 方法调用 Map String result = restTemplate.getForObject("http://127.0.0.1:8080/xxx", String.class, vars); # POST 方法调用 JSONObject param = new JSONObject(); ResponseEntity int statusCodeValue = responseEntity.getStatusCodeValue(); HttpHeaders headers = responseEntity.getHeaders(); JSONObject body = responseEntity.getBody(); |
API调用带来的问题:
解决方案——负载均衡
为什么需要负载均衡,微服务集群场景,用于流量消峰、便于集群无感知横向扩容
####Ribbon方案
spring框架集成的ribbon组件,通过对ribbon的高度封装,实现负载均衡功能
在这里进行处理,提供三种使用方式:
// DiscoveryClient方式
List serviceInstances = discoveryClient.getInstances("Users");
serviceInstances.forEach(serviceInstance -> {
System.out.println("host = "+ serviceInstance.getHost() +", port ="+ serviceInstance.getPort() +", url ="+ serviceInstance.getUri());
});
// LoadBalancer方式
ServiceInstance serviceInstances1 = loadBalancerClient.choose("Users");
System.out.println("url = " + serviceInstances1.getUri()+", port = "+serviceInstances1.getPort() + " ,host = "+serviceInstances1.getHost());
// 负载均衡注解方式
@Configuration
public class BeanConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
ribbon实现负载均衡的原理
总结:ribbon实现的关键点是为ribbon定制的RestTemplate,ribbon利用了RestTemplate的拦截器机制,在拦截器中实现ribbon的负载均衡。负载均衡的基本实现就是利用applicationName从服务注册中心获取可用的服务地址列表,然后通过一定算法负载,决定使用哪一个服务地址来进行http调用。
####OpenFeign方案
自己百度吧,功能方面类似ribbon,但比ribbon更强大,可调试性也更佳。
####为什么需要服务熔断
微服务架构中,服务间通过网络和API进行通信。和单机架构不同的是,分布式架构中服务的生产者和消费者之间由于进程隔离、网络隔离,生产者可能挂死、可能掉线、网络可能波动,因此服务能否顺利完成始终处于一种不确定状态。特别是在微服务架构中,服务间拆分粒度更细,调用链变长,加剧了这种不确定性。
服务雪崩场景:
服务A的流量波动很大,流量经常会突然性增加!那么在这种情况下,就算服务A能扛得住请求,服务B和服务C未必能扛得住这突发的请求。此时,如果服务C因为抗不住请求,变得不可用。那么服务B的请求时也会阻塞(在等待超时、重试等机制下),慢慢耗尽服务B的线程资源,服务B就会变得不可用。紧接着,服务A也会不可用,这一过程如下图所示:
解决方案——服务熔断:
当下游的服务因为某种原因突然变得不可用或响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。
在固定时间窗口内,接口调用超时比率达到一个阈值,会开启熔断。
进入熔断状态后,后续对该服务接口的调用不再经过网络,直接执行本地的默认方法,达到服务降级的效果。
熔断不可能是永久的。当经过了规定时间之后,服务将从熔断状态回复过来,再次接受调用方的远程调用。
####Hystrix方案
在spring项目中使用Hystrix组件实现熔断,参考:[12]
熔断流程:
在服务消费者定义Hystrix熔断器。当Hystrix监控到对该服务接口调用触发两个阏值时,会在系统中自动触发熔断器,在熔断器打开期间内,任何到该接口请求均不可用,同时在断路器打开5s后断路器会处于半开状态,此时断路器允许放行一个请求到该服务接口,如果该请求执行成功,断路器彻底关闭,如果该请求执行失败断路器重新打开。
服务降级:
客户端从整体网站请求负载考虑,当某个服务熔断或者关闭之后,服务将不再被调用此时在客户端,我们可以准备一个FallbackFactory,返回一个默认的值(缺省值),整体的服务水平下降了~ 但是好歹可用
Hystrix Dashboard仪表盘:
监控每一个@HystrixCommond注解创建一组度量,构建一组信息,然后通过图形化方式展示当前方法@Hystrixcomnond的状态信息
####什么是服务网关
API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。
####服务网关的作用
####Gatway方案
Spring gatway组件实现什么功能:
引入spring gatway组件
gatway配置:
比如说有N个微服务的application.yml是相同的,我们要是想修改application.yml需要修改N个这时候我们就需要ConfigServer来帮我们解决这个问题,在配置中心修改一次众多个微服务生效。 SpringCloud Config为服务器架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
配置也分为配置服务端与配置客户端
####Config Server配置服务器
首先在configServer当中会集成Git,我们可以将对应的配置文件放到远程仓库进行管理,这是因为在修改配置的时候Git会进行版本记录,方便后期版本回退,
在获取git仓库之后会加到本地缓存,这是为了避免当远程仓库宕机之后,本地微服务还是要得到统一配置。
spring引入config server组件
配置远端仓库
之后我们直接通过地址进行拉取文件:http://localhost:8880/configclient-xxx.properties
####Config Client配置客户端
spring引入config client组件
在前面我们通过了ConfigServer获取到了远程仓库的配置文件,但是在client当中又该如何获取到呢?首先服务都注册到了注册中心,只需要获取到ConfigServer的服务id之后再去找对应的远程仓库即可,而在远程仓库当中又有很多的配置文件,在这里还需要指定获取到哪个配置文件。本地配置文件如下:
Spring cloud bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring bus的一个核心思想是通过分布式的启动器对spring boot应用进行扩展,也可以用来建立一个多个应用之间的通信频道。目前唯一实现的方式是用AMQP消息代理作为通道,同样特性的设置(有些取决于通道的设置)在更多通道的文档中。
大家可以将它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有Kafka和RabbitMQ。
MQ可以起到大流量消峰、高并发异步处理的作用
还可以在服务生产者、消费者间解耦,避免直接API调用
AMQP模型:
以注册场景举例,MQ的使用场景[4]:
由于发送邮件非必要业务,可以不同步执行,放入消息队列之后,由MQ根据订阅推送给消费者,这样可以达到分摊主程序性能、流量的目的,并且减少主程序的调用链
阿里RocketMQ[4]:
####“外部无状态,内部有状态”方案[14]
客户端传递JSESSIONID和Token给服务器端,服务器端拿到了Token确还使用session来认证授权,这是为什么呢?因为有些老的项目刚开始是用session来认证授权的,但是随着技术的更新,想要将session这种有状态方式转变为Token的无状态方式,但是重构又不是一蹴而就的,所以造成了有些子项目使用无状态方式,有些子项目又使用有状态方式;
####“网关认证授权,内部裸奔”方案[14]
用户的登陆授权全部都在网关完成,网关接收到Token之后,就将Token解密解析出对应的用户信息,比兔下图的user_id和username,然后在将解密解析后的信息传递给微服务,微服务拿到用户信息之后就开始自身的业务逻辑,这里有一个弊端,微服务只能完全的信任网关,如果网关被攻破,那就有点尴尬了;当然也有优点,实现比较简单,性能较好;
####“内部裸奔”改进方案[14]
该方案是上个方案的改进版,网关不再进行认证和授权操作,也不进行解密解析Token操作,只用作单纯的转发,用户的认证授权、解密解析Token全都交给微服务来实现,这样一来项目就更加的安全,性能和上个方案对比也不差;
####Spring Cloud Security“处处安全”方案[13]
在使用 Spring Cloud 体系来构建微服务的过程中,用户请求是通过网关(ZUUL 或 Spring APIGateway)以 HTTP 协议来传输信息,API 网关将自己注册为 Eureka 服务治理下的应用,同时也从 Eureka 服务中获取所有其他微服务的实例信息。搭建 OAuth2 认证授权服务,并不是给每个微服务调用,而是通过 API 网关进行统一调用来对网关后的微服务做前置过滤,所有的请求都必须先通过 API 网关,API 网关在进行路由转发之前对该请求进行前置校验,实现对微服务系统中的其他的服务接口的安全与权限校验。对于微服务安全认证授权机制一块,目前主流的解决方案有 OAuth2.0 与 OIDC(OpenID Connect) 等标准协议。
OAuth2.0 授权模式
注:在OAuth2.0标准中,不同角色间的请求跳转都采用标准http重定向
完整授权流程中有四个重要的角色[ RFC 6749 ]:
####四种方案对比[14]
由于微服务架构下,各个子系统拆分粒度较细,且系统架构设计复杂。大大提高了系统容错、排错、运维难度。
未解决此痛点,统一的监控中心是必不可少的方案。
常用的监控数据包括:
参考:
[1] Spring Cloud Alibaba 新一代微服务解决方案
[2] Martin Fowler个人主页微服务文章
[3] 阿里云微服务方案
[4] 《人人都是架构师》
[5] 齐商银行:行级分布式微服务系统架构
[6] 微服务架构优缺点
[7] Contino是一家英国非上市科技咨询服务商,Contino帮助客户加速采用云原生技术
[8] 知乎-微服务框架有哪些?
[9] 知乎-彻底搞清楚!SOA和微服务的区别
[10] 知乎-SOA和微服务架构的区别?
[11] CSDN-Spring Cloud 微服务解决方案 详解
[12] SpringCloud之熔断器Hystrix
[13] Spring Cloud 微服务中搭建 OAuth2.0 认证授权服务
[14] 微服务的用户认证与授权