前置基础

springboot和springcloud版本匹配

查询网站

获取json字符串后使用工具进行格式化

版本选择

选择官网推荐的

image-20220304152136541

使用官网推荐的版本

image-20220304152152635

springcloud组件

  • 服务注册与发现
    • EUREKA

springcloud中文网

spirngcloud

Eureka

  1. 什么是服务治理?
  • eureka中application 字段与 服务的application name 相同

image-20220308112955737

eureka集群

  • eureka服务器之间相互注册

  • 服务在多个eureka上注册

微服务集群

  • 服务名称相同,服务器或者端口不同
  • 消费者使用服务名称去请求
  • restTemplate 作负载均衡

actuator 服务信息显示

  • 主机名称的修改

    • 默认显示机器名称+项目名称+端口号

    • ```yml
      instance-id: payment8001

      1
      2
      3
      4
      5

      * 访问信息有ip地址

      * ```yml
      prefer-ip-address: true
    • image-20220309115309930

获取薇服务信息

主启动类上添加 @EnableDiscoveryClient注解

获取服务信息:使用import org.springframework.cloud.client.discovery.DiscoveryClient;这个类

获取具体服务实例信息:使用DiscoveryClient.getinstances

Eureka的自我保护机制

  • 自我保护机制的原因

    • 防止微服务因为与eureka服务器暂时不连通,而导致eureka服务器将微服务删除
    • 宁可保留错误的信息,也不会盲目注销服务
  • 配置

    • eureka服务端

      1
      2
      enable-self-preservation: false # 关闭自我保护机制
      eviction-interval-timer-in-ms: 2000 #超时2s后驱逐服务
    • eureka客户端

    1
    2
    lease-renewal-interval-in-seconds: 2 # 发送心跳的间隔,每2秒发送一次
    lease-expiration-duration-in-seconds: 9 #eureka 最后一次收到心跳,之后的等待时间

Zookeeper

linux 安装zookeeper

配置文件说明

image-20220309154802184

安装zookeeper遇到的问题

  1. 服务启动成功,查看状态却是失败状态,客户端也连接不上

    原因是:没有安装jdk环境

springcloud整合zookeeper遇到的问题

zookeeper版本与jar包中引用的版本不兼容

解决方法;排除依赖,手动引入适合版本的依赖

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<!--先排除自带的zookeeper3.5.3-->
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>

zookeeper 策略

  • zookeeper节点是临时节点
  • 薇服务挂了, 一定时间内会保留

zookeeper集群

配置过程

consul

consul 中文网

consul 安装

下载地址

解压命令

1
unzip consul_1.11.4_linux_amd64.zip

启动命令

1
2
3
4
5
6
7
consul agent -data-dir=/usr/local/software/consul-1.11.4/data -bind=192.168.112.128 -server -bootstrap -client -ui -client=0.0.0.0 
##
consul agent -data-dir=/usr/local/software/consul-1.11.4/data -bind=192.168.112.128 -client=0.0.0.0
##
./consul agent -dev -ui -node=consul-dev -client=192.168.112.128 -server
##
./consul agent -dev -ui -node=consul-dev -client=0.0.0.0 -server

-data-dir consul数据目录
-bind 绑定ip地址
-server 代表以服务的方式启动
-bootstrap 指定自己为leader而不需要选举
-ui 启动一个内置管理的web界面
-client 指定客户端可以访问的IP。设置为0.0.0.0则任意访问,否则默认本机才可以访问。

整合consul遇到的问题

at lease one health check on one instance is failing

问题描述

image-20220311104216161

错误

image-20220311110448714

解决方案

在application.yaml 文件中开启心跳,默认是关闭的

1
2
3
4
5
6
7
8
9
10
11
12
spring:
application:
name: cloud-provider-consul
cloud:
consul:
host: 192.168.112.128
port: 8500
discovery:
service-name: ${spring.application.name}
#hostname: localhost
heartbeat:
enabled: true #开启心跳

consul 配置详解

配置site

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
35
36
37
38
39
spring:
cloud:
consul:
host: 192.168.9.233 # ip
port: 8500 # 端口
discovery: # consul 注册中心
service-name: zuul-service #注册在consul上面的名字,在consul的调用中,是通过此名字调用的
register-health-check: true #健康检查,保证服务处于启动状态,建议开启
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address} # 服务id
health-check-url: http://${spring.cloud.client.ip-address}:${server.port}/actuator/health # 健康检查的URI
tags: # 标签,这个属性是个list,所以用yml的list的格式注入
- gateway
- common
- other
- firstboot
enabled: true # 服务发现是否启动
management-tags: # 注册管理服务时使用的标记,默认是management
- sss
# health-check-path: /health/check # 要调用以进行运行状况检查的备用服务器路径,已经使用health-check-url覆盖了,所以这属性暂时找不到有什么用处
health-check-interval: 10s # 字符串,执行运行状况检查的频率(例如10秒),默认为10秒。
health-check-timeout: 10s # 健康检查超时(例如10秒)。
health-check-critical-timeout: 30s # 注销关键服务的时间, 要求consul的版本在1.0.7或者更高
prefer-ip-address: true # ip 偏好; 这个必须配 在注册过程中使用ip地址而不是主机名
prefer-agent-address: false # 来源,我们将如何确定使用的地址
# ip-address: 192.168.9.233 # 访问服务时要使用的IP地址(还必须设置要使用的首选IP地址)
# hostname: # Hostname to use when accessing server
# port: 8500 # Port to register the service under (defaults to listening port)
# management-port: # Port to register the management service under (defaults to management port)
# catalog-services-watch-delay: 1000 # 查看consul的服务更新间隔,默认1s
# catalog-services-watch-timeout: 2 # 查看consul服务更新间隔的超时时间, 默认两秒
query-passing: true # 这推动健康检查 pass 传递到服务器。
deregister: false # 在consul禁用自动注销登记服务。
fail-fast: true # *服务登记期间抛出异常如果这是真的,否则,日志警告(默认值为true)。
# health-check-tls-skip-verify: true # 跳过证书校验, 如果不是则进行证书校验
config: # consul 配置中心
prefix: config
enabled: true
format: YAML
data-key: settings.yml

Ribbon

自SpringCloud 2020起,已经舍弃了ribbon

使用loadbalancer 代替

2020版本之后使用ribbon,会报没有初始化服务 的错误,引入依赖就会报错

  • 依赖
  • restTemplate 的使用
  • 负载均衡策略

使用问题

  1. java.lang.IllegalStateException: No instances available for CLOUD-PAYMENT-SERVICE

    image-20220314095953525

    原因是使用的是2020版及之后的springcloud,ribbon已经被摒弃,推荐使用loadbalancer

Irule 组件

规则替换

  • 添加负载均衡规则配置类(这个类不能添加在主启动类所在包及其子包下)

  • 在主启动类上添加注释

    1
    @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)

自定义规则算法

Loadbalancer

springcloud 2020 后替换ribbon

修改规则

  1. 创建规则配置类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 这个类不添加@configuration注解
    // 这个类可以放在主启动类所在包及其自包下(ribbon不允许)
    public class CustomLoadBalancerConfiguration {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> reactorLoadBalancer(Environment environment,
    LoadBalancerClientFactory loadBalancerClientFactory) {
    String name = environment.getProperty(loadBalancerClientFactory.PROPERTY_NAME);
    return new RandomLoadBalancer(
    loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name
    );

    }
    }
  2. 将配置类设置给使用负载均衡的配置类里restTemplate

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Configuration
    @LoadBalancerClient(name = "CLOUD-PAYMENT-SERVICE", configuration = CustomLoadBalancerConfiguration.class)
    public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate () {
    return new RestTemplate();
    }
    }
  3. 这里就将负载均衡规则从默认的轮询修改为randomRule

OpenFeign

官网: https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#feign-logging

基本整合使用

超时控制

默认只等待1秒,超时会报错

2020版openfeign超时配置

1
2
3
4
5
6
7
8
9
10
11
feign:
client:
config:
default: # 这里就是指的所有被加载的默认FeignClient实现的服务配置都生效
connectTimeout: 1000
readTimeout: 2000

CLOUD-PAYMENT-SERVICE: #对单个服务FeignClient的配置
connectTimeout: 1000
readTimeout: 5000

打印日志

yaml 中的配置

1
2
3
4
5
6
7
8
9
10
11
12
#开启日志打印
logging:
level:
com.wangzhe.springcloud.service.PaymentFeign: debug
# 设置打印级别
feign:
client:
config:
CLOUD-PAYMENT-SERVICE:
connectTimeout: 1000
readTimeout: 5000
loggerLevel: BASIC #只打印请求的一些信息

日志级别信息

  • NONE, 不记录(默认)。
  • BASIC, 只记录请求方法和 URL 以及响应状态码和执行时间。
  • HEADERS, 记录基本信息以及请求和响应标头。
  • FULL, 记录请求和响应的标头、正文和元数据。

Hystrix

服务降级

服务降级一般放在客户端,当然是可以放在服务端的

  • 当发生服务不可用时,使用兜底方案
    • 服务器超时
    • 服务器异常错误
    • 服务器宕机
    • 服务熔断

服务提供者 编码配置

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
//在主启动类上添加@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class ProviderHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(ProviderHystrixMain8001.class, args);
}
}

//在降级服务上添加 @HystrixCommand
@HystrixCommand(fallbackMethod = "timeoutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
} )
public String timeout(Integer id) {
/*try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
int a = 10/0;
return "线程: "+ Thread.currentThread().getName() + " timeout , id: " + id;
}


public String timeoutHandler(Integer id) {

return "线程: "+ Thread.currentThread().getName() + " 超时或者异常处理 timeoutHandler , id: " + id;
}

消费者客户端编码配置

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

//添加@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class ConsumerHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(ConsumerHystrixMain80.class, args);
}
}

//设置降级服务
@GetMapping("/get/timeout/{id}")
@HystrixCommand(fallbackMethod = "timeoutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
public String timeout(@PathVariable("id") Integer id) {
return paymentClientService.timeout(id);
}

public String timeoutHandler(@PathVariable("id") Integer id) {
return "消费者服务降级:服务器超时,或者异常错误, id: " + id;
}

设置默认服务降级

没有特别指定降级方法,就会使用全局降级方法

1
2
3
4
5
6
7
8
9
10
11
//在类上添加@DefaultProperties,设置全局降级方法
@DefaultProperties(defaultFallback = "globalHandler")
public class PaymentController
// 在需要降级的方法上添加 @HystrixCommand ,没有特别指定降级方法,就会使用全局降级方法

@GetMapping("/get/ok/{id}")
@HystrixCommand
public String ok(@PathVariable("id") Integer id) {
int i = 10/0;
return paymentClientService.ok(id);
}

feign 和 hystrix 结合使用

开启feign 对 hystrix的支持

1
2
3
feign:
circuitbreaker:
enabled: true

设置fallback降级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//在@FeignClient添加服务降级配置类
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX", fallback = PaymentClientImpl.class)
public interface PaymentClientService {
@GetMapping("/payment/hystrix/get/ok/{id}")
public String ok(@PathVariable("id") Integer id);

@GetMapping("/payment/hystrix/get/timeout/{id}")
public String timeout(@PathVariable("id") Integer id);
}

//服务降级类是继承了接口
//需要将它加入容器中
@Component
public class PaymentClientImpl implements PaymentClientService {
@Override
public String ok(Integer id) {
return "新版本设置的fallback,ok";
}
@Override
public String timeout(Integer id) {
return "新版本设置的fallback,timeout";
}
}

服务熔断

降级(open)—满足一定的熔断条件—》熔断(closed)—经过休眠窗口时间—》缓慢放行(half open)—如果仍然不行—》重置休眠窗口时间

熔断服务配置

hystrix熔断参数,还有其他的,去看阳哥视频中有61

1
2
3
4
5
6
@HystrixCommand(fallbackMethod = "circuitBreakerFallback", 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"), //失败百分比
})

服务限流

服务监控(目前只能在在服务中监控,不能将服务监控与服务分开)

hystrix-dashboard

http://localhost:9001/hystrix

  1. 引入依赖
1
2
3
4
5
6
<!--hystrix-dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
  1. 配置启动类
1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableHystrixDashboard //添加注解,启动支持
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class, args);
}
}

  1. 访问路径

http://localhost:9001/hystrix

  1. 被监控程序配置要求
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>

image-20220317102218065

采坑解决

问题1:

访问:http://localhost:8001/hystrix.stream

image-20220317100622682

解决

1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
public ServletRegistrationBean<HystrixMetricsStreamServlet> getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(streamServlet);

// 一启动就加载
registrationBean.setLoadOnStartup(1);
// 添加url
registrationBean.addUrlMappings("/hystrix.stream");
// 设置名称
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}

问题二:Unable to connect to Command Metric Stream.

image-20220317101602911

1
2022-03-17 09:28:56.338  WARN 23840 --- [nio-9001-exec-4] ashboardConfiguration$ProxyStreamServlet : Origin parameter: http://localhost:8001/hystrix.stream is not in the allowed list of proxy host names.  If it should be allowed add it to hystrix.dashboard.proxyStreamAllowList.

解决

1
2
3
hystrix:
dashboard:
proxy-stream-allow-list: "*"

网关服务

为什么要添加网关?

  1. 只暴露网关地址接口,隐藏保护服务的地址接口

Zuul

Gateway

基本配置

  1. 引入依赖
1
2
3
4
5
<!--springcloud gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  1. application.yaml 配置

配置路由

  1. yaml配置
1
2
3
4
5
6
7
8
9
10
11
spring:
application:
name: cloud-gateway-gateway
cloud:
gateway:
routes: # 配置路由
- id: cloud-payment-service
uri: http://localhost:8001
predicates:
- Path=/payment/hystrix/**

  1. 配置类方式

路由

静态路由

是将路由的映射写死

配置类配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class GateWayConfiguration {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {

return routeLocatorBuilder.routes()
.route("path_route",r -> r.path("/exclusive")
.uri("https://world.huanqiu.com/exclusive"))
.route("test2",r -> r.path("/article")
.uri("https://world.huanqiu.com/article"))
.route("test3",r -> r.path("/world")
.uri("https://news.cctv.com/world/"))
.build();
}
}

注意事项

  • uri 如果添加了 path,路由器导航栏还是原来的链接;
  • uri不添加path,路由器会拼接后 跳转到目标链接,导航栏链接会更新

配置文件配置

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: cloud-gateway-gateway
cloud:
gateway:
routes:
- id: cloud-payment-service
uri: http://localhost:8001
predicates:
- Path=/payment/hystrix/**

动态路由

动态路由是通过服务名称 去 注册中心去获取ip:port;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring:
application:
name: cloud-gateway-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启动态路由
routes:
- id: cloud-payment-service
uri: http://localhost:8001
predicates:
- Path=/payment/hystrix/**
- id: dynamic-route
uri: lb://cloud-payment-service #使用服务名称
predicates:
- Path=/payment/**

总结领悟

客户直接发来的请求使用网关做负载均衡,

服务之间的调用,使用ribbon,loadbalancer,openfeign 做负载均衡

断言

9中断言工厂

After

在xxxxx时间之前访问

时间格式获取

2022-03-21T10:49:53.861+08:00[Asia/Shanghai]

1
2
3
4
5
6
7
public class ApplicationTest {
public static void main(String[] args) {
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
//2022-03-21T10:49:53.861+08:00[Asia/Shanghai]
}
}

过滤器

GateWayfilter(单个)

GlobalFilter(全局)

案例

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 MyLogGateWayFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname == null) {
log.info("uname = null, 非法闯入,消灭入侵者");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}

return chain.filter(exchange);
}

@Override
public int getOrder() {
return 0;
}
}

采坑记录

问题1:

1
Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.

解决方案

gateway 网关不需要springMVC,移除spring-boot-starter-web依赖

Config

服务端

spring-cloud-config 推荐与 git整合使用

配置中心的配置文件命名方式必须是 application-profile.yaml

基本配置

  1. 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--springcloud-config-server-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
    <!--eureka-client-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
  2. 启动类配置

    1
    2
    3
    4
    5
    6
    7
    8
    @SpringBootApplication
    @EnableEurekaClient
    @EnableConfigServer
    public class ConfigCenterMain3344 {
    public static void main(String[] args) {
    SpringApplication.run(ConfigCenterMain3344.class, args);
    }
    }
  3. application.yaml配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    spring:
    application:
    name: cloud-config-center
    cloud:
    config:
    server:
    git:
    uri: https://github.com/CodeWolfs/spring-cloud-config.git #这里使用http链接,使用ssh错误没有解决
    search-paths:
    - spring-cloud-config
    # username: 2546972682@qq.com
    # password: wasdsxy5210.
    label: master #配置默认的分支

springcloud的访问规则

1
2
3
4
5
6
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

  • {application} 就是应用名称,对应到配置文件上来,就是配置文件的名称部分,例如我上面创建的配置文件。

  • {profile} 就是配置文件的版本,我们的项目有开发版本、测试环境版本、生产环境版本,对应到配置文件上来就是以 application-{profile}.yml 加以区分,例如application-dev.yml、application-sit.yml、application-prod.yml。

  • {label} 表示 git 分支,默认是 master 分支,如果项目是以分支做区分也是可以的,那就可以通过不同的 label 来控制访问不同的配置文件了。

采坑记录

问题1

This application has no explicit mapping for /error, so you are seeing this as a fallback

image-20220322103523771

解决方案:

uri使用http链接,不要使用ssh链接

客户端

bootstrap.yaml 文件了解,以及与application.yaml文件的比较

bootstrap.yaml, 是系统级的,启动的时候会根据配置加载application.yaml

application.yaml,是用户级的。

基本配置

  1. 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!--springcloud-config-client-->
    <!--这里与config服务器不同-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <!--eureka-client-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--bootstrap.yaml 需要-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
  2. 启动类配置

    1
    2
    3
    4
    5
    6
    7
    @SpringBootApplication
    @EnableEurekaClient
    public class ConfigClientMain3355 {
    public static void main(String[] args) {
    SpringApplication.run(ConfigClientMain3355.class, args);
    }
    }
  3. bootstrap.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    server:
    port: 3355

    eureka:
    client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
    defaultZone: http://eureka7001.com:7001/eureka

    spring:
    application:
    name: cloud-config-client

    cloud:
    config:
    label: master
    name: config
    profile: dev
    uri: http://config3344.com:3344

采坑记录

  1. 问题一

    org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor$ImportException: No spring.config.import set

    解决方案

    1
    2
    3
    4
    5
    <!--bootstrap.yaml 需要这个依赖-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>

配置动态刷新

按照上述的配置后,在github更新配置后,客户端获取不到最新的配置,需要服务器获取到配置,之后客户端才可以获取到配置。

手动刷新

  1. 引入依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  2. bootstrap.yaml 配置暴露

    1
    2
    3
    4
    5
    management:
    endpoints:
    web:
    exposure:
    include: "*"
  3. 在控制器上添加@RefreshScope注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @RestController
    @RequestMapping("/config/info")
    @Slf4j
    @RefreshScope
    public class ConfigInfoController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/get")
    public String getConfigInfo() {
    log.info(configInfo);
    return configInfo;
    }
    }
  4. 在git上的配置更新之后,需要以post的方式访问 “http://localhost:3355/actuator/refresh"

    1
    curl -X POST "http://localhost:3355/actuator/refresh"

自动刷新

整合bus实现自动刷新

设计思路

  1. 发送给微服务个体,有微服务个体想蠕虫病毒一样,相互发送
  2. 发送给配置中心,有配置中心通知所有微服务

这里我们选择使用第二种方法,微服务应该职责单一,不应该承担多余的不属于他的内容

基本配置

  1. 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!--监控包-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    </dependency>
    <!--bootstrap.yaml 需要-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
  2. application.yaml 配置

    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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    # 服务端配置
    spring:
    application:
    name: cloud-config-center
    #rabbitmq 配置
    rabbitmq:
    host: 192.168.112.128
    port: 5672
    username: admin
    password: admin
    cloud:
    # 配置中心配置
    config:
    server:
    git:
    uri: https://github.com/CodeWolfs/spring-cloud-config.git
    search-paths:
    - spring-cloud-config
    # username: 2546972682@qq.com
    # password: wasdsxy5210.
    label: master
    #暴露bus-refresh,刷新使用
    management:
    endpoints:
    web:
    exposure:
    include: "busrefresh"

    ##############################################
    #客户端配置

    spring:
    rabbitmq:
    username: admin
    password: admin
    host: 192.168.112.128
    port: 5672

    application:
    name: cloud-config-client

    cloud:
    config:
    uri: http://localhost:3344
    label: master
    name: config
    profile: dev
    #客户端不需要暴露端口,但是必须添加@RefreshScope注解
    management:
    endpoints:
    web:
    exposure:
    include: "*"
  3. 客户端在需要更新的类上添加@RefreshScope注解

  4. git更新后,运维人员以post方式发送http://localhost:3344/actuator/busrefresh ,这里需要注意新版本发送的是busrefresh,中间没有-。

局部通知

上述是全局通知,下面来说一下局部通知的方式

http://localhost:3344/actuator/busrefresh/{service-name}:{port}

ex:

刷新单个端口

http://localhost:3344/actuator/busrefresh/cloud-config-client:3355

刷新多个端口

http://localhost:3344/actuator/busrefresh/cloud-config-client:{3355,3366}

采坑记录

以post请求发送 http://localhost:3344/actuator/bus-refresh , 报404错误

image-20220323093820513

解决方法

新版本bus-ampq,使用busrefresh,中间不加 “ **-**”

image-20220323094039502

Bus

Bus 只支持 RabbitMQ 和 kafka

查看config中自动刷新内容

Stream

rabbitmq 整合

stream 3.1 以后推荐使用函数式编程取代注解,例如@enablebinding();

使用rabbitmq

  1. 引入依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
  2. 配置application.yaml 文件

    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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    spring:
    rabbitmq: #这里添加rabbitmq配置,是因为不加的话会报链接拒绝的问题
    host: 192.168.112.128
    port: 5672
    username: admin
    password: admin
    application:
    name: stream-rabbitmq-provider
    cloud:
    stream:
    binders:
    # 消费者环境配置
    consumerRabbit:
    type: rabbit
    environment:
    spring:
    rabbitmq:
    host: 192.168.112.128
    port: 5672
    username: admin
    password: admin

    defaultRabbit: #是声明binder属性,声明使用的是什么消息中间件,以及配置中间件环境,defaultRabbit是配置名称,在binddings中使用default-binder
    type: rabbit
    environment:
    spring:
    rabbitmq:
    host: 192.168.112.128
    port: 5672
    username: admin
    password: admin
    bindings:
    output: #生产者管道channel,发送消息需要使用,output 是对象名
    destination: studyExchange
    content-type: application/json
    default-binder: defaultRabbit

    input: #消费者channel
    destination: studyExchange
    content-type: application/json
    default-binder: consumerRabbit


    eureka:
    instance:
    prefer-ip-address: true
    hostname: rabbitmq-provider
    instance-id: rabbitmq-provider
    client:
    service-url:
    defaultZone: http://eureka7001.com:7001/eureka
    1. 编码

      1. 生产者编码

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15

        @EnableBinding(Source.class)
        @Slf4j
        public class MessageProviderImpl implements MessageProvider {

        @Resource
        private MessageChannel output; //output需要和配置文件中的一样
        @Override
        public String send() {
        String s = UUID.randomUUID().toString();
        boolean send = output.send(MessageBuilder.withPayload(s).build());
        log.info(s);
        return null;
        }
        }
      2. 消费者编码

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        @Component
        @EnableBinding(Sink.class)
        public class ReciveMessageController {

        @Value("${server.port}")
        private String serverPort;

        @StreamListener("input") //这里试了使用其他的名称,不可以,好像只能使用input
        public void reciveMessage(Message<String> message) {
        System.out.println("消费者1: " + message.getPayload() + "\tport:" + serverPort);
        }
        }

    重复消费和消息持久化

    上述配置的问题

    image-20220324110553925

    重复消费

    使用group分组解决这个问题

    这个group实际上在rabbitmq就是队列的意思,在没有指定的情况下,每个消费者都有一个队列,也就是说系统会为每个消费者创建一个队列(如果不指定的话)

    所以如果需要避免重复消费问题,就将消费者放在同一组下就可以了。

    设置的group,durable=true

    1
    2
    3
    4
    5
    6
    7
    # 这个分组绑定只能在消费者端进行,因为系统会由此确定你的消费者绑定的是哪个队列
    bindings:
    input:
    destination: studyExchange
    content-type: application/json
    default-binder: consumerRabbit
    group: wangzheB # 配置分组

    消息持久化

    假设情况

    消费者A和消费者B是同一组的消费者,使用同一group,在两台消费者停机后,删除或者修改消费者A的分组,

    此时生产者一直在往这个group里面发送消息,发送了N条消息

    此时启动消费者A,消费者A 获取不到任何消息,会错过消息

    此时启动消费者B,消费者B仍然可以获取到全部消息

rabbitmq整合 采坑

  1. 3.1后很多注解推荐使用函数式编程代替

  2. rabbitmq的链接信息需要配置两次,一次是stream的enviroment,一次是在spring下配置rabbitmq

    1. 这个是没有问题的,是environment写错了,导致配置配有读取到。

    image-20220323171602305

Sleuth And Zipkin

sleuth 是实现

zipkin 是展示

zipkin下载地址

zipkin启动命令

1
java -jar zipkin-server-2.23.16-exec.jar

服务链路跟踪

基本配置

  1. 在需要监控的服务中 引入依赖

    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-zipkin</artifactId>
    </dependency>

    <!--现在使用这两个依赖-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
    </dependency>
  2. 微服务application.yaml配置

    1
    2
    3
    4
    5
    6
    7
    8
    spring:
    application:
    name: cloud-payment-service
    zipkin:
    base-url: http://127.0.0.1:9411
    sleuth:
    sampler:
    probability: 1 #采样率,0-1之间,一般是0.5
  3. 效果图

    image-20220324152725524