一、基础概念
1、微服务架构
(1、)是服务化思想指导下的一套最佳实践架构方案。服务化,就是把单体架构中的功能模块拆分为多个独立项目。
- 粒度小
- 团队自治
- 服务自治
2、服务拆分原则
(1、)什么时候拆分
- 创业型项目:先采用单体架构,快速开发,快速试错。随着规模扩大,逐渐拆分。
- 确定的大型项目:资金充足,目标明确,可以直接选择微服务架构,避免后续拆分的麻烦。
(2、)怎么拆分
从拆分目标来说,要做到:
- 高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
- 低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖。
从拆分方式来说,一般包含两种方式:
- 纵向拆分:按照业务模块来拆分
- 横向拆分:抽取公共服务,提高复用性
工程结构有两种:
- 独立Project
- Maven聚合
二、远程调用
1、注册中心
(1、)原理
(2、)Nacos注册中心
【1、】了解
Nacos是目前国内企业中占比最多的注册中心组件。它是阿里巴巴的产品,目前已经加入SpringCloudAlibaba中。
【2、】服务注册
服务注册步骤如下:
①引入nacos discovery依赖:
②配置Nacos地址:
【3、】服务发现
2、RestTemplate工具
Spring给我们提供了一个RestTemplate工具,可以方便的实现Http请求的发送。使用步骤如下:
注入RestTemplate到Spring容器
发起远程调用
结合注册中心
3、OpenFeign
(1、)了解
OpenFeign是一个声明式的http客户端,是SpringCloud在Eureka公司开源的Feign基础上改造而来。官方地址:GitHub - OpenFeign/feign: Feign makes writing java http clients easier
其作用就是基于SpringMVC的常见注解,帮我们优雅的实现http请求的发送。
(2、)引入依赖
OpenFeign已经被SpringCloud自动装配,实现起来非常简单:
①引入依赖,包括OpenFeign和负载均衡组件SpringCloudLoadBalancer②通过@EnableFeignClients注解,启用OpenFeign功能
(3、)实践应用
③编写FeignClient
④使用FeignClient,实现远程调用
(4、)连接池
【1、】了解
OpenFeign对Http请求做了优雅的伪装,不过其底层发起http请求,依赖于其它的框架。这些框架可以自己选择,包括以下三种:
- HttpURLConnection:默认实现,不支持连接池
- Apache HttpClient :支持连接池
- OKHttp:支持连接池
具体源码可以参考FeignBlockingLoadBalancerClient类中的delegate成员变量。
【2、】实现
OpenFeign整合OKHttp的步骤如下:
①引入依赖
②开启连接池功能
(5、)注意
当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:
方式一:指定FeignClient所在包
方式二:指定FeignClient字节码
(6、)日志
【1、】了解
OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
由于Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。
【2、】解决
要自定义日志级别需要声明一个类型为Logger.Level的Bean,在其中定义日志级别:
但此时这个Bean并未生效,要想配置某个FeignClient的日志,可以在@FeignClient注解中声明:
如果想要全局配置,让所有FeignClient都按照这个日志配置,则需要在@EnableFeignClients注解中声明:
(7、)拦截器
OpenFeign中提供了一个拦截器接口,所有由OpenFeign发起的请求都会先调用拦截器处理请求:
其中的RequestTemplate类中提供了一些方法可以让我们修改请求头:
三、网关
1、了解
网关就是网络的关口,负责请求的路由、转发、身份校验。
2、网关组件
在SpringCloud中网关的实现包括两种:
3、依赖和配置
4、路由属性
网关路由对应的Java类型是RouteDefinition,其中常见的属性有:
- id:路由唯一标示
- uri:路由目标地址
- predicates:路由断言,判断请求是否符合当前路由。
- filters:路由过滤器,对请求或响应做特殊处理。
5、路由断言
名称 | 说明 | 示例 |
After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
Host | 请求必须是访问某个host(域名) | - Host=**.somehost.org,**.anotherhost.org |
Method | 请求方式必须是指定方式 | - Method=GET,POST |
Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
Weight | 权重处理 | - Weight=group1, 2 |
XForwarded Remote Addr | 基于请求的来源IP做判断 | - XForwardedRemoteAddr=192.168.1.1/24 |
6、路由过滤器
(1、)了解
网关中提供了33种路由过滤器,每种过滤器都有独特的作用。
名称 | 说明 | 示例 |
AddRequestHeader | 给当前请求添加一个请求头 | AddrequestHeader=headerName,headerValue |
RemoveRequestHeader | 移除请求中的一个请求头 | RemoveRequestHeader=headerName |
AddResponseHeader | 给响应结果中添加一个响应头 | AddResponseHeader=headerName,headerValue |
RemoveResponseHeader | 从响应结果中移除有一个响应头 | RemoveResponseHeader=headerName |
RewritePath | 请求路径重写 | RewritePath=/red/?(?<segment>.*), /$\{segment} |
StripPrefix | 去除请求路径中的N段前缀 | StripPrefix=1,则路径/a/b转发时只保留/b |
... |
(2、)自定义过滤器
【1、】了解
网关过滤器有两种,分别是:
- GatewayFilter:路由过滤器,作用于任意指定的路由;默认不生效,要配置到路由后生效。GlobalFilter:全局过滤器,作用范围是所有路由;声明后自动生效。
两种过滤器的过滤方法签名完全一致:
【2、】GlobalFilter
自定义GlobalFilter比较简单,直接实现GlobalFilter接口即可:
【3、】GatewayFilter
自定义GatewayFilter不是直接实现GatewayFilter,而是实现AbstractGatewayFilterFactory,示例如下:
7、拓展
(1、)更改请求
7、网关请求处理流程
四、配置管理
1、了解
-
微服务重复配置过多,维护成本高
-
业务配置经常变动,每次修改都要重启服务
-
网关路由配置写死,如果变更要重启网关
2、Nacos配置管理
添加一些共享配置到Nacos中,包括:Jdbc、MybatisPlus、日志、Swagger、OpenFeign等配置
3、拉取配置
基于NacosConfig拉取共享配置代替微服务的本地配置。
4、配置热更新
5、动态路由
(1、)了解
实现动态路由需要将路由配置保存到Nacos,然后在网关监听Nacos中的路由配置,并实现配置热更新。然而网关路由并不是自定义业务配置属性,本身不具备热更新功能!参考:CompositeRouteDefinitionLocator
因此我们需要自己完成两件事情:
①监听Nacos配置变更的消息
②当配置变更时,将最新的路由信息更新到网关路由表
(2、)实现
五、服务保护
1、保护方案
(1、)请求限流
【1、】了解
限制访问接口的请求的并发量,避免服务因流量激增出现故障。
(2、)线程隔离
【1、】了解
也叫做舱壁模式,模拟船舱隔板的防水原理。通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散。
(3、)失败处理
【1、】了解
给业务编写一个调用失败时的处理的逻辑,称为fallback。当调用出现故障(比如无线程可用)时,按照失败处理逻辑执行业务并返回,而不是直接抛出异常。
(4、)服务熔断
【1、】了解
由断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断该业务,则拦截该接口的请求。熔断期间,所有请求快速失败,全都走fallback逻辑。
2、保护技术
(1、)了解
3、Sentinel
(1、)了解
是阿里巴巴开源的一款微服务流量控制组件。官网地址: https://sentinelguard.io/zh-cn/index.html
(2、)簇点链路
就是单机调用链路。是一次请求进入服务后经过的每一个被Sentinel监控的资源链。默认Sentinel会监控SpringMVC的每一个Endpoint(http接口)。限流、熔断等都是针对簇点链路中的资源设置的。而资源名默认就是接口的请求路径:
(3、)请求限流
(4、)线程隔离
注意,这里勾选的是并发线程数限制,也就是说这个查询功能最多使用5个线程,而不是5QPS。如果查询商品的接口每秒处理2个请求,则5个线程的实际QPS在10左右,而超出的请求自然会被拒绝。
(5、)Fallback
(6、)服务熔断
熔断降级是解决雪崩问题的重要手段。思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
六、分布式事务
在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事务必须同时成功或失败,这样的事务就是分布式事务。其中的每个服务的事务就是一个分支事务。整个业务称为全局事务。
解决分布式事务,各个子事务之间必须能感知到彼此的事务状态,才能保证状态一致。
1、Seata组件
Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。
官网地址:Apache Seata,其中的文档、播客中提供了大量的使用说明、源码分析。
(1、)Seata架构
(2、)部署TC服务
【1、】准备数据库表
Seata支持多种存储模式,但考虑到持久化的需要,我们一般选择基于数据库存储。
【2、】准备配置文件
【3、】Docker部署
需要注意,要确保nacos、mysql都在同一网络中。如果某个容器不再同一网络,可以参考下面的命令将某容器加入指定网络:
docker network connect [网络名] [容器名]
在虚拟机的/root
目录执行下面的命令:
docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.150.101 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2
(3、)微服务集成seata
首先,要在项目中引入Seata依赖:
(4、)XA模式
【1、】了解
【2、】实现
(5、)AT模式
【1、】了解
【2、】实现
首先,添加下图中的sql到微服务对应的数据库中:
然后,修改application.yml文件,将事务模式修改为AT模式: