文章目录
前言
为什么要使用分布式|微服务的架构体系
因为目前开发的应用同时使用的用户越来越多,数量上亿,而且遍布全世界,在天南地北的高并发环境下,既要迅捷无比又要安全稳定,于是诞生了分布式微服务这样的架构体系。本文在此体系下主要就Spring-Cloud的架构体系做一些详细的阐述。
一、分布式微服务的开发模式
分布式微服务的这种开发模式,是将一个完整的项目,划分成多个独立的组件,这些组件分布在不同的网络或者地域,彼此之间通过网络来通信和协调,在天南地北的用户面前,体验上如同一个系统。
与单体系统的区别:这里就不得不说著名的CAP理论。
CAP分别是:1、Consistency :指数据的强一致性。如果写入某个数据成功,之后读取,读到的都是新 写入的数据:如果写入失败,之后读取的都不是写入失败的数据。2、Availability :指服务的可用性。3、Partition-tolerance :指分区容错。
在分布式系统中 P是基本要求,而单体服务是 CA 系统, 微服务系统通常是 AP 系统,即同时满足了可用性和分区容错。
二、Spring-Cloud
1.简介
这里展示一段官方定义
机翻如下:
Spring Cloud 为开发者提供了快速构建分布式系统中一些常用模式的工具(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态)。分布式系统的协调导致样板模式,使用 Spring Cloud 开发人员可以快速建立实现这些模式的服务和应用程序。它们将在任何分布式环境中运行良好,包括开发人员自己的笔记本电脑、裸机数据中心和 Cloud Foundry 等托管平台。
主要有以下几个特征:
-
分布式/版本化配置
-
服务注册和发现
-
路由
-
服务到服务调用
-
负载均衡
-
断路器
-
全局锁
-
领导选举和集群状态
-
分布式消息传递
2.具体框架工具选择
Spring-Cloud的项目很多,主要包括:
- spring-cloud-netflix 最早开源
-
- eureka 服务发现
- ribbon 负载均衡
- hyxtrix 服务容错
- archlius 配置管理
- zuul 服务网关
- ...
- spring-cloud-alibaba 国产
-
- nacos 服务发现 配置管理
- dubbo 服务调用
- sentinel 服务容错
- seata 分布式事务
- ...
- spring-cloud 官方
-
- gateway 服务网关
- loadbalancer 负载均衡
- openfeign 服务调用
这里选择使用的是:
- nacos 服务发现 配置管理
- sentinel 服务容错
- seata 分布式事务
- gateway 服务网关
- openfeign 服务调用
- ribbon 负载均衡
3.网络通信
3.1 用到的协议
因为分布式微服务是使用网络来通信和协调,我们就需要使用到网络协议来沟通,常见的网络通信协议有以下几种:http\dubbo\grpc,通信过程中设计的参数序列画问题,又有如下序列化协议:json\xml\Protobuf
这里使用的是RestTemplate来发送网络协议请求,它内部支持各种的httpclient,遵循Restful协议,支持GET、POST、PUT、DELETE等基本http请求格式。
3.2 跨域问题
分布式的方式大概率可能存在跨域问题,可能不再同一域内,存在数据访问问题,这里提供两种方式:
1)通过在类上添加@CrossOrigin("csdn.net"),讲csdn站点添加到访问白名单中
2}通过自定义过滤器,自动添加特定相应头来实现添加白名单的功能,此处为所有请求添加一个“Access-Control-Allow-Origin”的响应头来甄别,部分代码示例如下:
@Component
@Order(-200)
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 允许指定域访问跨域资源
response.setHeader("Access-Control-Allow-Origin", "*");
// 允许所有请求方式
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
// 有效时间
response.setHeader("Access-Control-Max-Age", "3600");
// 允许的header参数
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,satoken");
// 如果是预检请求,直接返回
if ("OPTIONS".equals(request.getMethod())) {
System.out.println("=======================浏览器发来了OPTIONS预检请求==========");
response.getWriter().print("");
return;
}
}
}
4.网络协调
分布式微服务架构通过网络来协调的,通过配置nacos来进行服务发现、配置管理。因为网络的不确定行,这里通过多种手段来确保服务的稳定性,通过gateway网关来作为所有服务的唯一入口,通过openfeign来跨服务共享接口来进行服务调用,通过ribbon实现负载均衡,通过seata来保证事务过程前后数据的一致性,通过sentinel来保障某些服务故障后,确保整个系统是可用的。
4.1 nacos
Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。通过简单配置,将各服务项目的配置存放到nacos,由nacos统一注册、分发、导航的功能,工作流程如下:
4.2 gateway网关
系统中,API 接口资源通常是有服务网关Service Gateway(也称API网关)统一暴露,内部服务不直接对外提供API资源的暴露。好处在于隐藏内部服务,保护系统安全
网关层通常以集群的形式存在。并在服务网关层前通常会加上Nginx 用来负载均衡
网关意义:
网关将所有服务的API接口资源统一聚合,对外统一暴露
网关可以做一些用户身份认证,权限认证,防止非法请求操作API 接口,对内部服务起到保护作用
网关可以实现监控功能,实时日志输出、对请求进行记录
网关可以用来做流量监控,在高流量的情况下,对服务进行降级
API 接口从内部服务分离出来,方便做测试
当然,网关实现这些功能,需要做高可用,否则网关很可能成功架构的瓶颈,最常用的网关组件Zuul、Nginx
4.3 openfeign
它最核心的作用是为 HTTP 形式的 Rest API 提供了非常简洁高效的 RPC 调用方式。
openfeign的用途:服务发现,负载均衡,服务调用。
openfeign的实现原理:基于@EnableFeignClients 将所有被@FeignClient注解的类 注册到容器中。当这些被@FeignClient注解的类被调用时会创建一个动态代理的对象为我们创建被调用类的实例,然后都会被统一转发给 Feign 框架所定义的一个 InvocationHandler , 由该 Handler 完成后续的 HTTP 转换, 发送, 接收, 翻译HTTP响应的工作。
4.4 ribbon
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现,通过Spring Cloud的封装,可以让我们轻松地将面向服务的Rest模板请求自动转换成客户端负载均衡的服务调用。
Ribbon只具有负载均衡的能力,并不具有发送请求的能力。所以,需要配合服务通信组件,如:RestTemplate。主要的负载均衡算法有:BestAvailableRule最小并发数、WeightedResponseTimeRule响应时间加权、RoundRobinRule轮询、RandomRule随机,实现过程如下:
1、Ribbon实现了从注册中心中获取服务列表的能力。
2、然后通过获取到的服务列表,采用负载均衡算法(Ribbon默认采用的是轮询方式),利用通信框架(RestTemplate或Feign等)进行服务调用。
4.5 seata
为保证系统运行中,多服务调用过程的数据一致性使用统一的事务管理,Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
XA模式较为古老,通过TC(Transaction Coordinator协调者)来协调所有事务,所有事务均挂起不提交数据库,等所有事务均返回结果后才执行,成功提交,失败回滚。耗时耗资源。
TCC模式:Try-Confirm-Cancel,提交确认取消,三阶段。如支付宝转账操作A给B转账,先提交:从转账方A可用余额中划拨转账额,加入到冻结余额中,然后确定操作:执行接受转账方B加上转账额,清空转账方A冻结余额,若失败则执行回滚操作,A可用余额加回转账额,冻结减去转账额。
AT 模式基于 支持本地 ACID 事务 的 关系型数据库(需要额外的undo_log表):
-- 注意此处0.7.0+ 增加字段 context
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
- 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
- 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
- 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。
Saga模式
Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。使用环境:业务流程长、业务流程多,参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口
重试+幂等性校验
幂等性校验就是区分重复请求,通过外部传入ID进行区分,如充值时ID唯一,重复ID不处理。实现比较简单,不成功就不断重试,但是一旦长时间不成功,就会告罄,此时需要人工介入处理,项目比较小的时候,可以使用这种办法。
4.6 sentinel
Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
Sentinel工作机制:
-
对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
-
根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
-
Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。
三、项目开发
本次项目基于spring-boot集成管理和maven管理依赖的方式创建,便于创建和管理维护。
1 系统设计及服务划分
根据系统类型进行设计,根据需要进行服务划分,这里是一个商城项目,划分如下:
2 新建项目
根据系统设计,新建项目,导入相应的依赖,写配置,写业务代码。
主要配置包括
1、配置服务网关,主要为做好路由和跨域(yaml):
gateway:
routes:
- id: gateway-service
uri: https://www.baidu.com
order: 0
predicates:
- Host=**.foo.org
- Path=/headers
- Method=GET
- Header=X-Request-Id, \d+
- Query=foo, ba.
- Query=baz
- Cookie=chocolate, ch.p
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
2、配置nacos,写入各个服务的配置,提取公共配置和独有配置,分别存放,减少配置冗余,通过启动时网络拉取,实现动态配置。
主要是在各个服务本地配置booststrap.yaml,用于拉取nacos上的配置,此处因为是本地模拟开发,所以都是用到default方式。
spring:
application:
name: loan
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
shared-configs[0]:
data-id: common.yaml #服务名:标准为 服务名-环境名.文件后缀,默认环境省略环境名
3.、编写服务业务代码,通过openfeign共享服务接口,通过seate来统一管理服务间调用的事务,配置sentinel的容错规则保障系统运行的稳定性。