SpringCloud+SpringCloudAlibaba学习笔记
服务注册中心
eureka
ap 高可用 分布式容错
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| eureka: instance: hostname: eureka7003.com instance-id: payment8001 prefer-ip-address: true client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
|
Ribbon 启用负载均衡
1 2 3 4 5 6 7 8
| @EnableEurekaServer @EnableDiscoveryClient
@LoadBalanced public RestTemplate getTemp() { return new RestTemplate(); }
|
zookepper
cp 强一致 分布式容错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.6.1</version> </dependency>
|
1 2 3 4 5 6 7
| spring: application: name: cloud-zoo-consumer-order cloud: zookeeper: connect-string: 192.168.10.58:2181
|
1 2
| @SpringBootApplication @EnableDiscoveryClient
|
consul
cp 强一致 分布式容错
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9
| spring: application: name: consul-payment-provider cloud: consul: host: 192.168.10.58 port: 8500 discovery: service-name: ${spring.application.name}
|
1 2
| @SpringBootApplication @EnableDiscoveryClient
|
服务调用负载均衡
Ribbon
Ribbon 切换 负载规则
- 在springboot 包扫描外层建立 配置
1 2 3 4 5 6 7
| @Configuration public class Myrule { @Bean public IRule initRule() { return new RandomRule(); } }
|
- 启动类给指定服务加载随机方法
1
| @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = Myrule.class)
|
OpenFeign
- 添加maven依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
|
- 启动类启用Feign
1 2 3 4
| @SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient @EnableFeignClients
|
- 新建接口 并注册Feign信息
1 2 3 4 5 6
| @Component @FeignClient(value = "CLOUD-PAYMENT-SERVICE") public interface Service { @GetMapping(value = "/payment/get/{id}") Response<Payment> getPaymentById(@PathVariable("id") Long id); }
|
- 提供方接口演示
1 2 3 4 5 6 7 8 9 10
| @GetMapping(value = "/payment/get/{id}") public Response<Payment> getPaymentById(@PathVariable("id") Long id) { Payment payment = paymentService.getPaymentById(id);
if (payment != null) { return Result.success(200, "查询成功,serverPort: " + serverPort, payment); } else { return Result.success(444, "没有对应记录,查询ID: " + id, null); } }
|
OpenFeign超时设置
1 2 3 4 5
| ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
|
OpenFeign 日志打印功能
- 配置Openfeign 日志级别
1 2 3 4 5 6 7
| @Configuration public class FeignLogConfig { @Bean public Logger.Level getLevel() { return Logger.Level.FULL; } }
|
- yml 项目配置文件中,给指定Feign interface 配置日志级别
1 2 3
| logging: level: ml.ytooo.feignservice.Service: debug
|
Hystrix 服务治理
- 服务降级 出险异常时,返回友好提示,防止程序异常或者阻塞
- 服务熔断 保险丝,当超出服务承载能力时,返回提示,拒绝请求
- 服务限流 闸门,配置服务承载能力
Hystrix
服务降级
当服务处理超时或者运行异常时,启动备选方案返回给调用者预期结果
主方法
需要降级处理的程序
其中
- paymentInfo_TimeOut 为预计超时程序
- paymentInfo_TimeOut_Handler 为超时备选方案
1 2 3 4 5 6 7 8 9 10 11 12 13
| @HystrixCommand(fallbackMethod = "paymentInfo_TimeOut_Handler", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") }) public String paymentInfo_TimeOut(Integer id) {
int time = 5; try { TimeUnit.MILLISECONDS.sleep(time * 1000); } catch (InterruptedException e) { e.printStackTrace(); } return "线程池: " + Thread.currentThread().getName() + " paymentInfo_TimeOut,id: " + id + "\t" + "O(∩_∩)O哈哈~" + " 耗时(秒): " + time; }
public String paymentInfo_TimeOut_Handler(Integer id) { return "线程池: " + Thread.currentThread().getName() + " paymentInfo_TimeOut_Handler,id: " + id + "\t" + "o(╥﹏╥)o"; }
|
全局降级处理
配置 defaultFallback 的走自己的降级方法,未配置的走 默认@DefaultProperties 指定的降级方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @RestController @Slf4j @DefaultProperties(defaultFallback = "globle",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500") }) public class Controller { @HystrixCommand @GetMapping("/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id) { String result = feignService.paymentInfo_TimeOut(id); log.info("*****result: " + result); return result; } public String globle() { return "全局"; } }
|
通过OpenFeign 配置其提供方全局降级配置
- 新增feign调用接口的实现类 FeignServiceImpl,实现全部方法并做降级处理
1 2 3 4 5 6 7 8 9 10 11
| @Service public class FeignServiceImpl implements FeignService { @Override public String paymentInfo_OK(Integer id) { return "降级 -- paymentInfo_OK"; } @Override public String paymentInfo_TimeOut(Integer id) { return "降级 -- paymentInfo_TimeOut"; } }
|
- feign调用接口添加注解
1
| @FeignClient(value = "CLOUD-PROVIDER-HYSTYRIX-PAYMENT",fallback = FeignServiceImpl.class)
|
服务熔断
- 服务过载时,拒绝连接请求直接调用降级方法,返回异常
- 请求下降时,慢慢恢复该服务访问,直达完全恢复
配置服务的熔断:
一下配置在 10s 内 ,10次请求有60% 失败,则熔断
HystrixProperty 配置位于 HystrixCommandProperties.class 类中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = { // @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 时间窗口期 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少百分百后跳闸 }) public String paymentCircuitBreaker(@PathVariable("id") Integer id) { if(id < 0) { throw new RuntimeException("******id 不能负数"); } String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber; } public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) { return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " +id; }
|
效果: 当连续使用 -100 请求时, 返回 “id 不能负数”, 使用100请求也返回 “id 不能负数” ,继续连续使用 100请求, 服务慢慢恢复
服务限流
使用 springcloud 阿里巴巴 sentinel 替代
Gateway 网关
Gateway
Gateway 项目配置
- 添加maven依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
|
- 移除以下依赖
1 2 3 4 5 6 7 8
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
|
- yml 配置 (后续移步配置中心)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: application: name: cloud-gateaway-gateaway cloud: gateway: discovery: locator: enabled: true routes: - id: payment_get uri : lb://CLOUD-PAYMENT-SERVICE /#启用 注册中心集群 predicates: - Path=/payment/get/**
|
- 注册进 Eureka 注册中心
Gateway 动态路由
- 开启 网关发现注册中心服务
1 2 3 4 5 6 7 8
| spring: application: name: cloud-gateaway-gateaway cloud: gateway: discovery: locator: enabled: true
|
Gateway 断言
断言是判断转发请求的条件
1 2
| predicates: - After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
|
- After,Before,Between 配置转发生效时间
1 2 3 4 5
| public static void main(String[] args) { ZonedDateTime now = ZonedDateTime.now(); System.out.println(now); }
|
- Cookie 请求需携带指定Cookie才可以访问
1 2
| predicates: - Cookie=name,ytooo
|
- Header ≈ Cookie
1 2
| predicates: - Header=name,ytooo
|
Gateway 过滤器
- (默认过滤器) 官方提供一系列过滤器,供我们 在请求转发之前,对请求进行加工处理
1 2
| filters: - AddRequestParamter=rowid,1024
|
- 自定义过滤器
自定义全局过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Component @Slf4j public class GatewayLogFilter implements GlobalFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("=====================进入全局过滤器====================="); String name = exchange.getRequest().getQueryParams().getFirst("name"); if (null == name) { exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); } return chain.filter(exchange); }
@Override public int getOrder() { return 0; } }
|
SpringCloud Config 分布式配置中心, BUS 消息总线
分布式配置中心 SpringCloud Config
服务端配置
建立git仓库 https://github.com/youdmeng/cloud-config
引入 maven
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
|
3.启动类使配置生效
1 2 3 4 5 6 7 8
| @SpringBootApplication @EnableEurekaClient @EnableConfigServer public class ConfigMain3344 { public static void main(String[] args) { SpringApplication.run(ConfigMain3344.class, args); } }
|
- 配置 配置文件
1 2 3 4 5 6 7 8 9 10 11
| spring: application: name: cloud-config-center cloud: config: server: git: uri: https://github.com/youdmeng/cloud-config search-paths: - cloud-config label: master
|
- 请求访问 : http://127.0.0.1:3344/master/config-dev.yml
客户端配置
- 引入 maven
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>
|
- 配置 配置文件
1 2 3 4 5 6 7 8 9
| spring: application: name: cloud-condig-client cloud: config: label: master name: config profile: dev uri: http://127.0.0.1:3344
|
手动刷新客户端配置
不建议使用
消息总线 Bus
设计逻辑
使用消息总线触发服务端的 bus/refresh 端点,刷新所有客户端config配置
初始条件
客户端,服务端都需要实现Springcloud Config功能
服务端配置
- 引入maven依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
|
- 配置文件中配置消息队列信息
1 2 3 4 5 6
| rabbitmq: host: 192.168.10.58 port: 5672 username: ytooo password: ytooo
|
- 配置文件中配置BUS总线暴露信息
1 2 3 4 5 6
| management: endpoints: web: exposure: include: "bus-refresh"
|
- 配置文件预览
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| server: port: 3344 spring: application: name: cloud-config-center cloud: config: server: git: uri: https://github.com/youdmeng/cloud-config search-paths: - cloud-config label: master rabbitmq: host: 192.168.10.58 port: 5672 username: ytooo password: ytooo eureka: instance: prefer-ip-address: true instance-id: cloud-config-center-3344 client: fetch-registry: true register-with-eureka: true service-url: defaultZone: http://eureka7001.com:7001/eureka/
management: endpoints: web: exposure: include: "bus-refresh"
|
客户端配置
- 引入maven依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
|
- 配置文件中配置消息队列信息
1 2 3 4 5 6
| rabbitmq: host: 192.168.10.58 port: 5672 username: ytooo password: ytooo
|
- 配置暴露端点
1 2 3 4 5 6
| management: endpoints: web: exposure: include: "*"
|
- 调用配置类添加 @RefreshScope
1 2 3 4 5 6 7 8 9 10 11
| @RestController @RefreshScope public class Controller { @Value("${config.info}") private String configInfo; @GetMapping(value = "/test") public String test() { return configInfo; } }
|
刷新配置
POST 请求config服务端 http://127.0.0.1:3344/actuator/bus-refresh
刷新成功
定点通知
POST 请求config服务端 http://127.0.0.1:3344/actuator/bus-refresh/{destination}
destination: 注册中心服务名称:端口号
🌰: http://127.0.0.1:3344/actuator/bus-refresh/cloud-condig-client:3366
SpringCloud Stream 消息驱动
消息驱动,统一各种消息中间件中的差异,提供统一简单的调用方式,屏蔽消息中间件具体调用实现
SpringCloud Stream 消息提供者项目配置 (简单使用)
- 添加maven依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency>
|
- 配置文件添加Stream配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring: application: name: cloud-stream-provider cloud: stream: binders: defaultRabbit: type: rabbit environment: spring: rabbitmq: host: 192.168.10.58 port: 5672 username: ytooo password: ytooo bindings: output: destination: studyExchange content-type: application/json binder: defaultRabbit
|
- 定义消息推送管道
1 2 3
| @EnableBinding(Source.class) public class MsgProviderImpl implements MsgProvider { }
|
- 定义消息发送管道
1 2
| @Autowired private MessageChannel out;
|
- build消息实体并发送
1 2
| Message<String> message = MessageBuilder.withPayload(msg).build(); out.send(message);
|
- 消息接收方 yml 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring: application: name: cloud-stream-rabbitmq-consumer cloud: stream: binders: defaultRabbit: type: rabbit environment: spring: rabbitmq: host: 192.168.10.58 port: 5672 username: ytooo password: ytooo bindings: input: destination: studyExchange content-type: application/json binder: defaultRabbit
|
- 接收方监听接口配置
1 2 3
| @EnableBinding(Sink.class) public class ReceiveMsgImpl implements ReceiveMsg { }
|
- 接收方注解
1 2 3 4
| @StreamListener(Sink.INPUT) public void receive(Message<String> message) { System.out.println("客服端8803收到消息: " + message.getPayload()); }
|
SpringCloud Stream 分组消费 & 持久化
- 对于不同的组中,消息是会被重复消费的(重复消费)
- 同一个组内,服务之间存在竞争关系,只被消费一次(集群环境,避免重复消费)
分组配置文件配置
1 2 3 4 5 6
| bindings: input: destination: studyExchange content-type: application/json binder: defaultRabbit group: ytooo
|
group分组 持久化
当不配置分组时,重启服务,不会自动获取之前未消费的服务
反之,配置了分组,启动时,自动获取之前未消费的消息
Sleuth 分布式请求链路跟踪
我跳过了
SpringCloud Alibaba
Nacos 注册中心
添加依赖
1 2 3 4
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
|
项目配置文件
1 2 3 4 5 6 7
| spring: application: name: cloudalibaba-nacos-consumer-order cloud: nacos: discovery: server-addr: 192.168.10.58:8848
|
主程序启动
结合 Openfeign 调用 服务
Nacos config配置中心
- 添加maven 依赖
1 2 3 4
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-config</artifactId> </dependency>
|
- bootstrap.yml中添加 配置中心相关配置
1 2 3 4 5 6 7 8 9 10 11 12
| spring: application: name: cloudalibaba-nacos-config-cclient cloud: nacos: discovery: server-addr: 192.168.10.58:8848 config: server-addr: 192.168.10.58:8848 file-extension: yaml profiles: active: dev
|
- 业务类 添加 @RefreshScope 来自动刷新
1 2 3
| @RestController @RefreshScope public class Controller {
|
- 配置文件中配置格式
在 Nacos Spring Cloud 中,dataId 的完整格式如下:
${prefix}-${spring.profiles.active}.${file-extension}
- prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。
- spring.profiles.active 即为当前环境对应的 profile,详情可以参考 Spring Boot文档。 注意:当 spring.profiles.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}
- file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型。
本实例中文件名称应为: cloudalibaba-nacos-config-cclient-dev.yaml
Nacos config配置中心 分类配置
Nacos 使用3层来隔离服务
- NameSpace 命名空间,默认 public
- Group 组, 默认 DEFAULT_GROUP
- Service 微服务, 一个Service 可以包含多个Cluster集群,默认Cluster为 DEFAULT 可以用了区分地域,来做地域容灾
yml 中配置分组信息group ,和命名空间 namespace
1 2 3 4 5 6 7 8 9 10 11 12
| spring: application: name: cloudalibaba-nacos-config-cclient cloud: nacos: discovery: server-addr: 192.168.10.58:8848 config: server-addr: 192.168.10.58:8848 file-extension: yaml group: dev namespace: a2438b02-01e1-4a3c-959c-600d93183d22
|
Sentinel 服务熔断与降级
Sentinel 服务被管方 配置
- maven 依赖
1 2 3 4 5 6 7 8
| <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
|
- yml 中配置 sentinel dashboard 监控
1 2 3 4 5 6 7 8 9 10
| spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: 192.168.10.58:8848 sentinel: transport: dashboard: 192.168.10.58:8888
|
配置降级方法: 此方法只针对页面中配置的指定 降级限流热点等方法
1 2 3 4 5 6 7 8 9
| @GetMapping(value = "/hot") @SentinelResource(value = "hot", blockHandler = "deal_hot") public String hot(String p1, String p2) { return "========================== 热点通过 =========================="; }
public String deal_hot(String p1, String p2, BlockException e) { return "========================== 热点降级 =========================="; }
|
seata 分布式事务
全局事务id
TC 事务协调者 维护全局和分支事务状态,驱动全局事务的提交或回滚
TM 事务管理器 定义全局事务的范围,开始全局事务.提交或回滚全局事务
RM 资源管理器 管理分支事务处理的资源,与TC交谈来注册分支事务和报告分支事务的状态,并驱动分支事务的提交或回滚