SpringCloudAlibaba开发文档
自己使用SpringCloudAlibaba时,组件搭配如下:注册中心+配置中心+消息总线nacos、断路器sentinel、网关gateway,还有SpringCloud中的两个组件,Web服务客户端openfeign和负载均衡ribbon。文章将介绍SpringCloudAlibaba中的这三个核心组件。
一、服务发现Nacos
nacos是一个动态命名和配置服务,它可以实现服务的注册、发现、配置和管理。nacos相当于集成了Eureka、Config和Bus的功能,可以替代Spring Cloud的这些组件。
(一)产生的业务场景
nacos的诞生是为了满足微服务架构中的配置管理、服务发现和动态负载均衡的需求,搭建起来比Eureka、Config和Bus更快,同时拥有更强大的功能。
(二)快速搭建
快速搭建注册中心
1.下载并启动Nacos软件
启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
2.登录Nacos图形界面
http://localhost:8848/nacos 默认账号密码nacos
3.客户端pom.xml文件引入依赖
注意版本和springboot统一
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
4.客户端启动类上添加@EnableDiscoveryClient注解
各个nacos客户端都需要启用注解,还是使用openfeign远程调用
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class StudentConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(StudentConsumerApplication.class,args);
}
}
5.客户端application.yml文件中配置相关参数
配置nacos服务端的地址,开放端口默认为8848
spring:
application:
name: alibaba-student-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
快速搭建配置中心
6.客户端pom.xml文件引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
7.客户端新建配置文件bootstrap.yml
namespace和group的概念在(三)中阐述。
- Nacos配置中心允许同时在本地也存在application.yml文件(eureka配置中心不允许)
- bootstap.yml(微服务连接nacos配置),application.yml(配置固定的数据)
- 经常修改的数据存放到远程,命名格式为:$ {prefix}-$ {spring.profiles.active}.$ {file-extension},如student-dev.yaml
bootstrap.yml文件
spring:
application:
name: alibaba-student-provider #eureka通过名字获取ip地址
profiles:
active: dev #优先使用xxx-dev.yaml文件
cloud:
nacos:
discovery: #发现中心
server-addr: 127.0.0.1:8848 #发现中心服务端的ip地址,端口
cluster-name: ${clustername:bj} #集群(即在一个机房,区别eureka的集群)
namespace: c9db8a77-936b-4657-aaf0-248dd7c52558 #命名空间(需要在服务端的命名空间栏创建,这里填生成的id)
group: TEST #组(配置文件自定义)
config: #配置中心
file-extension: yaml #没有yml格式
namespace: c9db8a77-936b-4657-aaf0-248dd7c52558 #命名空间(遵守环境隔离,同命名空间同组才能够调用服务)
application.yml文件
server:
port: ${port:8070} #我觉得这里可以不用写,远程配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///else
username: root
password: 123
mybatis:
type-aliases-package: com.woniu.common.entity
mapper-locations: classpath:/mapper/*Mapper.xml
logging:
level:
com.woniu.student.provider.mapper: debug
8.远程图形界面创建配置文件
- Data ID 使用 [模块名+开发环境.yaml] 来命名。注意:yaml是固定的,不支持yml后缀命名!
- 配置文件的namespace命名空间和Group,一般是和自身模块的namespace命名空间和Group配置相同。
- 编写时注意要在 : 号后面加一个空格,显示蓝色高亮,才算正确的书写格式。
补充:
- 配置中心也支持环境隔离,但是就不再引入服务分级存储的概念了。
- 配置了激活dev环境后,远程alibaba-student-provider-dev.yaml>远程alibaba-student-provider.yaml>本地
9.Nacos配置数据持久化
默认配置中心的数据,存储在Nacos中的内置数据库。重启还有,但是Nacos损坏了,数据将会丢失。支持将配置的数据存储到数据库中。数据库版本要一致,需要去官网下载sql语句建表,需要修改配置文件。
10.Nacos集群
11.seata实现分布式事务
1)下载seata服务端软件
2)按照官方文档修改seata中的配置文件,换成nacos类型作为注册中心
3)需要各模块注册到SEATA_GROUP组,启动seata服务端,会自动将该模块注册到SEATA_GROUP组,可在配置文件中修改这一默认组。其他关联模块使用seata分布式事务,也必须注册到这个相同组。
4)修改了软件的配置,需要将项目中的两个配置文件替换掉。修改之后,软件重启才能生效。
5)同样,需要加入数据源类,并在启动类上排除数据源。
补充:
-Dserver.port=9090 用于开发环境时,模拟集群
-- server.port=9090 用于软件使用环境时,切换端口
(三)技术要点
1.环境隔离
访问的Nacos的注册中心找服务,或者访问配置中心获取配置文件的值,必须在相同的Namespace和Group中,不能跨Namespace和Group调用服务和读取配置文件。
1)在nacos服务端界面创建Namespace(命名空间)
使用生成的命名空间的id作为配置文件中namespace的值。
2)Group(分组)直接在配置文件中编写即可
spring:
application:
name: alibaba-student-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
cluster-name: ${clustername:cd}
namespace: ecd5a042-4400-4bc4-a444-bd7e607455ba
group: TEST
2.服务分级存储
5051、5052、5053是同一个服务的副本。如果在eureka中,这三个都算作一个集群。但在nacos中,在一个机房的算作一个集群,这三个模块叫做服务。
为什么这样分?是为nacos自己的负载均衡算法做铺垫。
3.Nacos注册中心自带的负载均衡策略
优先调用一个集群的服务,速度会更快,如果这个集群的服务都挂了,才去调用另一个集群的服务。不用重启或更改配置,就可以在nacos界面中操作各个服务所占的权重,灵活性更高。
注意:使用Nacos的负载均衡策略需要手动编写配置,否则默认还是ribbon的轮询策略。
spring
cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: ${clustername:cd}
#ribbon负载均衡算法,默认是轮询
alibaba-student-provider: #调用的服务名
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #底层还是Riibon,默认还是轮询
二、断路器sentinel
sentinel是一个分布式系统的流量防卫组件,它可以实现服务的限流、降级、熔断和系统负载保护。sentinel相当于增强了Hystrix的功能,可以提供更丰富的规则和实时监控。
(一)产生的业务场景
sentinel的诞生是为了解决微服务架构中的流量控制、容错和系统稳定性的问题,并且搭建起来比Hytrix更快,功能也更加强大。
(二)快速搭建
1.打开软件,启动sentinel服务端
java -jar xxxx.jar --server.port=9999
2.客户端pom.xml文件引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
3.客户端application.yml文件中配置相关参数
spring:
cloud:
sentinel: #断路器
transport: #配置后台服务端端口,和前端展示页面端口
port: 8719
dashboard: localhost:9999
web-context-unify: false #配置service层路径,在各个controller中都能显示
feign: #开启sentinel熔断降级
sentinel:
enabled: true
(三)限流
限流---流控模式
1.直接
直接限制该请求路径,一秒钟只能访问两次。
2.关联
当/add的请求量超过阈值,对get限流,add正常。照顾优先级高的业务,保证优先执行。
3.链路
get和add同时调用了其他方法,限制get调用,不限制add调用。保证请求总量一定的情况,优先满足一定的资源,从调用链的下层进行控制。
- controller层有路径,默认能找到,但service的路径默认找不到。需要在service层的方法上加上@SentinelResource("xxx")注解,才能找到该路径。
- 配置文件加上以下配置,才不会合并相同路径(默认会将路径合并到第一个controller请求下)
web-context-unify: false #配置发现service层路径
限流---流控效果
4.快速失败
超过流量抛出异常,拒绝处理。
5.Warm UP(预热模式,冷加载,冷启动)
底层有一个冷启动因子默认值是3,可以自己设置预热时长,假如是10s。项目启动时,限流数为:预热时长/冷启动因子,在预热时间的时间内逐步提升单机阈值,最后达到单机阈值。
6.排队等待
单位时间超过阈值的请求,排队等待,如果在指定的时间内,及时处理,还是正常请求。超过了等待时间还是异常。单位是毫秒。
(四)Sentinel规则持久化
sentinel配置的规则,默认在内存中,重启服务就没有了。
1.客户端pom.xml文件引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.客户端application.yml文件中配置相关参数
spring:
application:
name: alibaba-student-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
# namespace: 28f9d138-1a9d-4545-9386-791bfb974b2e
# group: TEST_GROUP
# cluster-name: cd
sentinel:
transport:
port: 8719
dashboard: localhost:8080
web-context-unify: false #Sentinel默认会整合路径,使用链路模式,必须要能够分清请求来源
datasource: # Sentinel 规则持久化
ds1: #自定义名
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name} #dataId就是nacos配置中心的文件名
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow #配置的是限流相关规则
3.将Sentinel规则写到nacos配置中心
注意:这里的Data ID 不用加.yaml后缀。
配置规则百度搜索。
[
{
"resource":"/studentconsumer/get",
"limitApp":"default",
"grade":"1",
"count":"2",
"strategy":"0",
"controlBehavior":"0",
"clusterMode":false
}
]
(五)熔断降级
1.客户端pom.xml文件引入依赖
与限流依赖相同,引入了此处不需再引入
2.客户端application.yml文件中配置相关参数
配置文件中,开启sentinel断路器
feign:
sentinel:
enabled: true
3.其他的部分和hytrix相同
1)接口加上fallback属性
在之前openfeign创建的接口的@FeignClient注解上加fallback属性,指定降级方法所在类。
@FeignClient(name = "student-provider",fallback = StudentProviderFallback.class) //根据名字寻找对应模块中的方法,然后自动生成实体类
//注意这里不能直接在类上加映射,否则会报重复生成类错误
public interface StudentProviderFeignClient {
@GetMapping("/student/all")
public Object all();
@GetMapping("/student/getById/{id}")
public Object getById(@PathVariable("id") String id);
@PostMapping("/student/add")
public Object add(@RequestBody Student student);
}
2)定义降级方法
在调用模块中,新建类实现我们创建的远程调用的接口 ,配置降级方法(一般返回响应结果),返回值、参数都必须与被调用模块中的方法一致。如下实例:
@Component
public class StudentProviderFallback implements StudentProviderFeignClient{
@Override
public Object all() {
return new ResponseResault(200,"all-fallback",null);
}
@Override
public Object getById(@PathVariable("id") String id) {
return new ResponseResault(200,"getById-fallback",null);
}
@Override
public Object add(@RequestBody Student student) {
return new ResponseResault(200,"add-fallback",null);
}
}
4.慢调用比例,异常比例,异常数
- 慢调用比例:1秒内(统计时间(时间窗口)),2个请求,发生超过1000毫秒的时间调用超过50%,触发熔断,熔断时间5秒
- 异常比例:和hystrix参数完全相同
- 异常数:统计异常总数,不按比例统计
(六)技术要点
1.熔断和限流的区别
限流是对请求进行限制访问,保证系统不会崩溃。熔断是请求已经被处理,但发生了故障或者响应慢,而执行降级方法。
2.分别熔断调用、被调用模块路径的区别?
如果只对调用者的这个路径进行熔断,那么只有当调用者的请求超过阈值或异常比例升高时,才会触发熔断规则,从而快速失败,避免影响到其它的资源。如果只对被调用者的这个路径进行熔断,那么只有当被调用者的响应超时或异常比例升高时,才会触发熔断规则,从而降级返回默认值,避免影响到调用者的正常逻辑。如果对调用者和被调用者的这个路径都进行熔断,那么可以实现更细粒度的控制,根据不同的场景和需求,选择合适的熔断策略和阈值。
三、网关GateWay
GateWay可以作为一个统一的入口,将外部请求分发到内部的不同服务,并提供一些额外的功能,如认证、限流、监控等。
(一)产生的业务场景
相比zuul,Gateway可以提供更高的性能和更低的资源消耗,以及更好的异步支持。另外,Gateway还提供了更灵活的路由规则和过滤器配置,以及更好的集成Spring Boot和Spring Cloud的特性。
(二)快速搭建
注意:gateway不能引入或者引入的模块不能含有spring-boot-starter-web依赖,底层实现是靠netty,引入会发生冲突。
1.创建新的资源模块gateway
2.pom.xml文件引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
3.application.yml文件中配置相关参数
方式一:通过ip地址
server:
port: 1011
spring:
application:
name: gateway
cloud:
gateway:
routes: #配置路由
- id: student-provider #路由id,一般使用微服名
uri: http://localhost:8081/ #映射路径,转发路径 http://localhost:8081/student/1
predicates:
- Path=/student/** #请求路径 http://localhost:1011/student/1
- id: student-consumer #路由id,一般使用微服名
uri: http://localhost:7071/ #映射路径,转发路径 http://localhost:7071/studentconsumer/1
predicates: #断言,就是一个条件判断 路径断言
- Path=/studentconsumer/** #请求路径 http://localhost:1011/studentconsumer/1
方式二:网关作为注册中心的客户端,通过服务名获取调用的地址
server:
port: 1011
spring:
application:
name: gateway
cloud:
gateway:
routes: #配置路由
- id: student-provider #路由id,一般使用微服名
uri: lb://student-provider #映射路径,转发路径 http://localhost:8081/student/1
predicates:
- Path=/student/** #请求路径 http://localhost:1011/student/1
- id: student-consumer #路由id,一般使用微服名
uri: lb://student-consumer #映射路径,转发路径 http://localhost:7071/studentconsumer/1
predicates:
- Path=/studentconsumer/** #请求路径 http://localhost:1011/studentconsumer/1
eureka:
client:
service-url:
defaultZone: http://localhost:6061/eureka
3.Route Predicate Factories路由断言工厂
其实就是配置文件中的predicates,常用断言如下:
1)The After Route Predicate Factory
路径是否能进入网关。
predicates:
- Path=/studentconsumer/**
2)The Cookie Route Predicate Factory
是否有cookie。
predicates:
- Cookie=chocolate, ch.p
3)The Header Route Predicate Factory
predicates:
- Header=chocolate,ch.p
4.GatewayFilter Factories网关过滤工厂
进入网关以后,有系统内置的过滤器。框架常用的网关过滤器:
- The AddRequestHeader GatewayFilter Factory
- The AddResponseHeader GatewayFilter Factory
5.CORS Configuration网关统一跨域处理
spring:
application:
name: gateway
cloud:
gateway:
globalcors: #网关统一跨越配置
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowedHeaders: "*"
6.网关自定义GlobalFilter过滤器
可定义多个GlobalFilter过滤器,使用order排列优先级(Order值越小,越先执行),常用于认证、鉴权。大致思路:
- 第一个GlobalFilter过滤器:login请求放行,验证token
- 第二个GlobalFilter过滤器:login请求放行,验证redis中存储的用户权限
第一个拦截器:
@Component
public class LoginInterceptor implements GlobalFilter, Ordered {
@Autowired
RedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//访问/login,放行
String token = request.getHeaders().getFirst("token");
String storeToken = (String)redisTemplate.opsForValue().get("1");
if(storeToken.equals(token)){
//发行
return chain.filter(exchange);
}
ResponseResault responseResault = new ResponseResault(401,"未登录",null);
ObjectMapper objectMapper=new ObjectMapper();
String responsebody;
try {
responsebody = objectMapper.writeValueAsString(responseResault);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
//拦截
DataBuffer data = response.bufferFactory().wrap(responsebody.getBytes());;
return response.writeWith(Mono.just(data));
}
@Override
public int getOrder() {
return 0;
}
}
第二个拦截器类似
登录方法发送token:
@PostMapping("/login")
public Object login(@RequestBody Student student, HttpServletResponse response){
loginService.login(student);
String token = JwtUtil.createToken(student.getId().toString(), student.getName());
response.setHeader("token",token);
redisTemplate.opsForValue().set("1",token);
// 必须暴露响应头,前端才能拿到token,可编写配置文件
response.addHeader("Access-Control-Expose-Headers", "token");
return new ResponseResault(200,"登录成功",null);
}
补充:
- 如果过滤器太多,可以使用setHeader(order值,true),getHeader判断是否放行
- 如果采用restful风格开发,/student可能代表POST、DELETE、PUT、GET任一请求,因此 需要在数据库中权限表应该设计有如下字段:id、请求路径、请求类型。比较时,需要取出request的路径path和method方法,拼接后与数据库进行比较。
- 如果gateway需要调用其他远程模块,需要遵守restful风格,需要加入类型转换配置。比如调用方法时传入了一个对象作为参数,因为不会像@RequestBody自动转换,所以得配一个。HttpMessageConverter
- 感谢你赐予我前进的力量