目录
零、使用前提
创建个聚合项目,里面每个模块是一个独立的springboot项目
springcloud 版本要与 springboot版本对应,否则会报错
一、使用到的组件介绍
组件 | 作用 |
---|---|
nacos | 注册中心(服务注册与发现)、配置中心(动态配置管理) |
Ribbon | 负载均衡 |
Feign | 声明式Http客户端(调用远程服务) |
Sentinel | 服务容错(限流、降级、熔断) |
Gateway | API网关(webflux编程模式) |
Sleuth | 调用链监控 |
Seata | 原Fescar,即分布式事务解决方案 |
二、在公共模块引入依赖
1、每个微服务项目都会有个公共的模块,用于引一些公用的依赖、配置
2、引入这个依赖,spring-cloud-alibaba 其他的依赖就不用写版本号
3、注意版本号要与springboot版本对应
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
三、Nacos
1、下载nacos、并启动
链接: https://github.com/alibaba/nacos/releases
打开下载好的文件夹点击bin里startup.cmd运行
2、注册中心
公共模块引入注册中心依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
1、在每个微服务
yml指定nacos地址、服务名(才能知道注册了哪个服务)
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: mall-coupon
2、使用@EnableDiscoveryClient
注解开启服务注册与发现功能
加入后就能注册到注册中心
访问nacos,网址http://127.0.0.1:8848/nacos,账号密码都是nacos
可以看到服务已经注册进来
3、配置中心
1、导入依赖
在公共模块导入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2、新建bootstrap.yml
优先级高于application.yml
spring:
application:
name: mall-coupon
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
3、测试原有配置
1、在application.yml新增值
2、@Value获取值
3、接口输出值
@RestController
@RequestMapping("/coupon")
public class couponController {
@Value("${nacosConfigTest.name}")
private String name;
@Value("${nacosConfigTest.value}")
private String value;
@Resource
MemberFeignService memberFeignService;
@GetMapping("/test")
public String test(){
String s = memberFeignService.memberFeignTest();
String result = "这是调用者模块调用 被调用者模块返回接口:"+s;
return result;
}
@GetMapping("/configTest")
public String configTest(){
return "获取的值:"+name+":"+value;
}
}
4、此时接口获取的值
4、nacos新建配置
1、新建配置
2、将需要动态配置的输入到配置内容
3、在接口类上使用@RefreshScope
4、此时接口返回值变成动态配置了
4、命名空间(配置隔离)
默认新增的所有配置都在public空间
例如我们开发时有 1、开发环境 2、部署环境 3、测试环境
1、在nacos命名空间新增 开发、部署、测试环境
2、点击配置列表,多了几个环境
3、在dev环境下新建上方一样的配置
4、在bootstrap.yml 新增namespace
spring:
application:
name: mall-coupon
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml #指定yml文件
namespace: e9b627cb-44b1-480e-a46e-2ea019fe2656 # 命名空间id
5、此时拿的就是测试环境下的配置
5、配置分组
1、新增配置时填分组
2、在bootstrap.yml 新增指定组
spring:
application:
name: mall-coupon
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml #指定yml文件
namespace: e9b627cb-44b1-480e-a46e-2ea019fe2656 # 命名空间id
group: test #组名
3、此时拿的就是指定组的值
四、Feign声明式远程调用
1、公共模块引入openfeign
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
2、在一个模块正常写接口、另一个模块来调用这个接口
1、member模块(被调用模块)正常写接口
2、coupon模块(调用模块)新建一个feign包,建一个接口类,专门调用member模块的接口
3、在接口类上使用@FeignClient("mall-member")
指定nacos上的服务名
4、写全 member模块(被调用模块) 接口路径
3、在调用模块启动类上使用@EnableFeignClients
开启feign
coupon模块(调用者)开启feign, 指定包名
测试调用
4、feign性能优化
Feign底层的客户端实现:
1、URLConnection:默认实现,不支持连接池
2、Apache HttpClient:支持连接池
3、OKHttp:支持连接池
因此优化feign主要包括:
1、使用连接池代替默认的URLConnection
2、日志级别,最好用basic或none
引入依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置yml
feign:
client:
config:
default: # default 全局配置
loggerLevel: BASIC #日志级别,BASIC就是最基本的请求和响应信息
httpclient:
enabled: true # 支持httpClient的开关
max-connections: 200 #最大连接数
max-connections-per-route: 50 # 单个路径的最大连接数
五、Gateway网关
常用功能:路由转发、权限校验、限流控制等
1、搭建
1、新建网关模块
2、依赖
<!-- 公共模块-->
<dependency>
<groupId>com.example.mall</groupId>
<artifactId>mall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
3、@EnableDiscoveryClient
开启服务注册功能
4、新建bootstrap.yml 和 application.yml
bootstrap.yml
spring:
application:
name: mall-gateway
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: 6d405b31-5f62-4105-b8a6-339cc0b8464e
application.yml
server:
port: 88
spring:
application:
name: mall-gateway
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
5、新建gateway命名空间 和 配置
6、排除数据源相关配置
由于我们引入了公共模块,网关不需要配置数据源,不排除数据源配置会报错
2、路由转发(yml新增网关配置)
server:
port: 88
spring:
application:
name: mall-gateway
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
#网关配置
gateway:
routes:
- id: coupon
uri: http://127.0.0.1:8881/ #要去的请求地址
# uri: lb://mall-coupon #路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Query=url,coupon # url 带有/coupon就去上方uri地址 例:url=coupon
# - Path=/coupon/** # url 这个是按照路径匹配,只要以/coupon开头就符合要求
#- After=2037-01-20T17:42:47.789-07:00[Asia/Shanghai] # 这个时间之后可以访问
- id: member
uri: http://127.0.0.1:8882/ #要去的请求地址
# uri: lb://mall-member #路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Query=url,member # url 带有/coupon就去上方uri地址 url=member
# - Path=/member/** # url 这个是按照路径匹配,只要以/member开头就符合要求
测试
Spring提供了11种基本的Predicate工厂:
名称 | 说明 | 示例 |
---|---|---|
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 | 权重处理 |
3、路由过滤GatewayFilterFactory
spring提供了31种不同的路由过滤器工厂。
名称 | 说明 |
---|---|
AddRequestHeader | 给当前请求添加一个请求头 |
RemoveRequestHeader | 移除请求中的一个请求头 |
AddResponseHeader | 给响应结果中添加一个响应头 |
RemoveResponseHeader | 从响应结果中移除有一个响应头 |
RequestRateLimiter | 限制请求的流量 |
。。。 |
测试
1、添加配置
2、在接口中获取请求头
3、已添加到请求头里
4、对所有路由请求都生效
5、代码
server:
port: 88
spring:
application:
name: mall-gateway
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
#网关配置
gateway:
routes:
- id: coupon
uri: http://127.0.0.1:8881/ #要去的请求地址
# uri: lb://coupon #路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
#- Query=url,coupon # url 带有/coupon就去上方uri地址 例:url=coupon
- Path=/coupon/** # url 这个是按照路径匹配,只要以/coupon开头就符合要求
#- After=2037-01-20T17:42:47.789-07:00[Asia/Shanghai] # 这个时间之后可以访问
#filters: #过滤器
#- AddRequestHeader=Truth,niubi! #添加请求头
- id: member
uri: http://127.0.0.1:8882/ #要去的请求地址
# uri: lb://member #路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
#- Query=url,member # url 带有/coupon就去上方uri地址 url=member
- Path=/member/** # url 这个是按照路径匹配,只要以/member开头就符合要求
default-filters: #默认过滤器,会对所有的路由请求都生效
- AddRequestHeader=Truth,niubi!
4、全局过滤器(GlobalFilter)
与GetewayFilter区别在于,GetewayFilter是通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口。
案例:定义全局过滤器,拦截并判断用户身份
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面的条件:
1、 参数中是否有authorization
2、authorization参数值是否为admin
如果同时满足则放行,否则拦截
@Order(-1) // 多个过滤器,这个值越小,优先级越高
@Component
public class AuthorizationFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1、获取请求头
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
// 2、获取参数中的authorization 参数
String auth = params.getFirst("authorization");
// 3、判断参数值是否等于admin
if ("admin".equals(auth)){
// 4、是,放行
return chain.filter(exchange);
} // 5、否,拦截
// 设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
测试
正确
错误的
5、过滤器执行顺序
每一个过滤器都必须指定order值,order值越小,优先级却高,执行顺序越前
.
GlobalFilter通过实现@Order注解来指定order值,有我们自己指定
.
路由过滤器和defaultFilter的order由Spring指定,默认是按声明顺序从1递增(就是yml配置,越上面的的越高)
.
当过滤器的order值一样时,会按照defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
6、跨域问题处理
跨域:域名不一致就是跨域,主要包括
1、域名不同
:www.taobao.com 和 www.taobao.org 和 www.jd.com
2、域名相同,端口不同
:8080和8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截问题
解决方案:CORS
#网关配置
gateway:
globalcors: # 全局跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfiguration:
'[/**]':
allowedOrigins: # 允许哪些网络的跨域请求
- "http://localhost:8881"
- "http:www.leyou.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" #允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
8、配置所有代码
server:
port: 88
spring:
application:
name: mall-gateway
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
#网关配置
gateway:
globalcors: # 全局跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfiguration:
'[/**]':
allowedOrigins: # 允许哪些网络的跨域请求
- "http://localhost:8881"
- "http:www.leyou.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" #允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
routes:
- id: coupon
uri: http://127.0.0.1:8881/ #要去的请求地址
# uri: lb://coupon #路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
#- Query=url,coupon # url 带有/coupon就去上方uri地址 例:url=coupon
- Path=/coupon/** # url 这个是按照路径匹配,只要以/coupon开头就符合要求
#- After=2037-01-20T17:42:47.789-07:00[Asia/Shanghai] # 这个时间之后可以访问
#filters: #过滤器
#- AddRequestHeader=Truth,niubi! #添加请求头
- id: member
uri: http://127.0.0.1:8882/ #要去的请求地址
# uri: lb://member #路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
#- Query=url,member # url 带有/coupon就去上方uri地址 url=member
- Path=/member/** # url 这个是按照路径匹配,只要以/member开头就符合要求
default-filters: #默认过滤器,会对所有的路由请求都生效
- AddRequestHeader=Truth,niubi!
待补充。。。。。。