springcloud
服务注册与发现、服务调用、服务熔断、负载均衡、服务降级、服务消息队列、配置管理中心、服务网管、服务监控、全链路追踪、自动化构建部署、服务定时任务调度操作
cloud 和 boot 版本对应
版本选型
组件升级
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud2020</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>cloud-provider-payment8001</module>
<module>cloud-provider-payment8002</module>
<module>cloud-consumer-order80</module>
<module>cloud-api-commons</module>
<module>cloud-eureka-server7001</module>
<module>cloud-eureka-server7002</module>
</modules>
<!-- 统一管理jar包版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.18.0</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.2</mybatis.spring.boot.version>
</properties>
<!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version -->
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
注意:
1. 添加 <packaging>pom<packaging>
2. <dependencyManagement>为子工程规定依赖模版,并指定版本号,子工程不需要写版本
3. Maven跳过单元测试
添加微服务子模块
module、POM、YML、主启动、业务类
注意:
前后端分离
后段一般暴露通用结果类CommonResult,状态码一般是枚举类型
操作实际操作类Payment
devtools 热部署 (只允许开发时用)
1. 子工程添加devtools依赖
2. 父工程添加plugin
3.
4. ctrl+shift+alt+/ ,选register
5. 重启idea
RestTemplate ------ 多个微服务之间的远程调用
在未使用Ribbon之前 ,充当负载均衡
-----------@LoadBalanced Consumer ApplicationContextConfig类中 restTemplate 放入容器之前加上,开启restTemplate的负载均衡功能
多个服务之间的远程调用,restTemplate (对httpClient封装)
1. 创建Config,将restTemplate放入容器
f
2. 客户端的Controller中注入restTemplate,进行远程调用
注意:
1. 读get 写post (在写@GetMapping、@PostMapping时注意)
2. post请求,需要借助postman发送
Eureka 服务注册与发现
Eureka组件
module、POM、yml、启动类、测试
Server服务端
Client客户端
注意:
客户端Client的spring.application.name 就是eureka中注册名
注意:
register-with-eureka: 是否注册进eureka
fetch-registry: 是否拉取eureka中存在的服务
Eureka集群构建
eureka工作原理:
多个Eureka服务器互相注册
修改hosts文件,添加两eureka服务域名
服务注册
关键:
两个Eureka服务器相互 指向(访问每个Eureka服务器都是访问其他几个Eureka服务器)
再将provider和consumer同时注册上多个eureka服务器
Provider 负载均衡 (消费者只关心Provider微服务名称)
注意:
在Provider的Controller获取yml中的端口号 ------------- 使用@Value("${server.port}"),将yml文件中的端口号注入
注意:
各个微服务启动顺序:
1. 各个eureka服务器
2. 各个Provider
3. Consumer
关键:
1. 在Consumer的Controller中,需要跳转的PAYMENT_URL不能写死,需要写成指定的Provider微服务名称(名称下会有多个Provider),让其自己随机找Provider
2. 使用@LoadBalanced赋予RestTemplate负载均衡的能力 -------- 在ApplicationContextConfig中
访问:
localhost/consumer/payment/get/31(80端口为Consumer) --- 即可以轮询访问多个Provider
actuator微服务信息完善
1. 主机名名称修改
2. 访问信息能够有IP信息提示 服务开启安全信息
instance后面就是需要暴露的服务名
健康检查
点击eureka微服务名下的Provider修改后面info为health
服务发现Discovery
Controller层
main类
(@EnableEurekaClient后续用不多,@EnableDiscoveryClient用的多)
1. 直接通过discoveryClient.getservice()获取服务
2. discoveryClient 通过getInstances("微服务名") 得到服务实例
通过实例可获取 服务ID、主机地址、服务端口、访问服务的地址Uri
instance.getServiceId()
instance.getHost()
instance.getPort()
instance.getUri()
Eureka自我保护
高可用
默认开启自我保护
禁用自我保护
服务端,关闭自我保护,设置扫描时间
客户端,设置心跳发送时间,设置心跳等待上限时间
Zookeeper 替代 Eureka
单独一个zookeeper服务器
docker 上安装 zookeeper
docker run -d -p 2181:2181 --name zookeeper --restart always b6f6e4bc7eef
docker exec -it e7cdbd2d6d5d bash
启动客户端:./bin/zkCli.sh(./bin/zkStart.sh默认开启了 不用 ./bin/zkStart.sh start)
下载zookeeper客户端。
进入zookeeper\build
java -jar zookeeper-dev-ZooInspector.jar
Provider服务
Consumer服务
获取json串
get /services/cloud-provider-payment/4fdfce6f-59a8-4ea5-bb01-b065693c0b4b
consul
Provider
三个注册中心的异同
CP架构当网络分区出现后,为了保证一致性,就必须拒绝请求,否则无法保证一致性结论:违背了可用性A的要求,只满足一致性和分区容错,即CP
Ribbon 负载均衡
一句话:Ribbon = 负载均衡 + RestTemplate
没引入 ribbon 也能用由于:最新版 netflix-eureka-client 已经引入了 netflix-ribbon
restTemplate 的 方法 getForObject getForEntity
Ribbon核心组件:IRule
IRule:根据特定算法从服务列表中选取一个要访问的服务
默认是轮询
如何替换
所以创建MyselfRule 不在com.atguigu.springcloud(即不被@ComponentScan扫描到到包)
新增配置类中,修改为随机
http:localhost/consumer/payment/get/31 测试
Ribbon负载均衡算法
原理
轮询:
源码
CAS 比较并交换
new AtomicInteger(n).compareAndSet(expect,update)
类似方法AtomicInteger.getAndIncrement() 保证原子性
valueOffset 内存地址偏移量(地址)
CAS CPU原语 依赖于硬件的原子操作 不允许被中断
原理:CAS思想 + Unsafe类 (自旋)
主物理内存 得到一个值,拷贝到自己本地线程的工作内存里
总结:CAS 总的来说就是通过 Unsafe.class 使用当前对象内存偏移量获取指定位置的值作为,最初将主物理内存值拷贝作为期望值,但期间被修改,通过do...while条件判断时,内存偏移量(类似指针)直接获取当前改变的主物理内存值,和期望值进行比较,相同返回表示没被改,不同表示被修改,并循环增加到实际值,(在循环的期间,不同线程通过自己的期望值与主物理内存值进行时刻比较(此比较本身是cpu原语有原子性),达到多线程一致性(比synchronized轻便))
AtomicInteger.java -------- 内存偏移量 根据创建AtimicInteger对象时赋予的 value值 来获取
CAS缺点:
循环时间长,开销大
只能保证一个共享变量的原子操作
手写
原理+JUC(CAS+自旋锁的复习)
首先:先把80的@LoadBalanced注解关闭 ,再Controller 注入 自写的 LoadBalaner 接口
自写自旋锁 作负载均衡轮询算法 接口
关键:创建atomicInteger类时赋值value=0,自旋前获取期望value,比较时获取当前value
(底层CAS 内存偏移量 也是通过value反射得到的)
接着:controller中注入自写的负载均衡接口 和 服务发现接口
通过服务发现,得到包含实例的 list,通过loadbalaner 的 instance 方法返回随机的实例
疑问:
:第二次循环时,current+1只是赋值给next,但current没赋值,应该还是0,怎么第二次,循环中current增加了(?)
openFeign服务接口调用 (整合了Ribbon 创建一个接口使用注解配置,即可提供服务调用接口)
Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需 创建一个接口并在接口上添加注解即可
@EnableFeignClient 开启openFeign
@FeignClient(“指定服务名称”)
consumer端的controller 中的方法 调用的是 @FeiginClient 接口的方法
Feign 超时控制
Feign日志打印
新增config
Hystrix
Hystrix重要概念
服务降级:fallback 服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback
服务熔断:break 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
服务限流:flowlimit 秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
插曲:引入@value()包:org.springframework.beans.factory.annotation.Value
基础服务
此时service中是一个类,没有接口
2万个请求timeout,此时请求8001ok也会响应缓慢
两万个请求进入,每个请求3秒钟,一瞬间用尽了服务线程池的线程,即便是没有等待3秒钟的请求,也会出现延时的情况----系统卡顿变慢
新建80消费端,访问ok请求,2万个请求timeout的情况下,出现响应缓慢
如何解决:
服务降级
服务端8001作服务降级
在service方法,添加@HystrixCommand(fallback="兜底方法",超时时间配置)
======service======
主启动类天际@EnableCircuitBreaker
8001自测:
故意制造两个异常:
1. int age = 10/0 2. 超时异常,运行5秒,接受上限3秒
1. 服务方法5秒,超时时间3秒,请求等待3秒会自动跳转兜底方法
2. 服务方法运行报错或不可用,做服务降级,请求自动跳转兜底方法
80端,进行服务降级
=======Controller=======
服务降级:8001方在service 80方在controller
如果:
1. 8001服务降级超时上限5秒,请求方法正常执行3秒,80端服务降级超时上限1.5秒,此时80访问8001,1.5秒后就会进行服务降级
2. 80端发生服务运行报错或不可用,会直接进行服务降级
@DefaultProperties(dedaultFallback = "") 类前 定义全局fallback
全局fallback方法 参数不能有
@HystrixCommand 什么都没加,找全局falbac,加了fallback,用自定义的
PaymentHystrixService用作远程调用接口,PaymentFallbackHystrixService(实现类)用作远程调用方法的fallback-------在@FeignClient中添加fallback = 实现类.class
此时ok方法没有配置任何服务降级,只是在service层一个实现类作为fallback,当服务器关闭时,发送请求会到实现类中的方法返回
服务熔断
=====service======
=====controller=====
测试:
当多次连续10次有6次都请求id<0,服务就熔断,并服务降级,调用熔断fallback,当此时请求id>0,失败率还是高于60%,这段时间仍然会调用熔断fallback方法
hystrix工作流程
创建仪表盘9001,监控Provider微服务提供类(8001/8002/8003)
所有被监控的微服务都需要,添加actuator依赖(监控信息完善,图形化展现)
启动9001服务:监视器:http://localhost:9001/hystrix
监视端口 http://localhost:800x/hystrix.stream
2000ms 延时
被监视的hystrix8001,主配置类必须添加配置,才能在图形化界面查看
/**
* 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
* ServletRegistrationBean因为springboot的默认路径不是/hystrix.stream
* 只要在自己的项目里配置下面的Servlet就可以了
* @return
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
输入被监控的 8001服务端,可以看到熔断情况和请求情况
当请求:http://localhost:8001/payment/circuit/31 (测试熔断)http://localhost:8001/payment/circuit/-31 经过连续制造错误,产生熔断,监视界面Circuit会变成open
Gateway
一句话
微服务中当网关
注意:此时不需要web相关依赖,会发生冲突
配置两个8001服务端的方法,进行请求前网关添加
1. yml中配置
2. 代码注入RouteLocator的Bean
http://localhost:8001/payment/get/31
http://localhost:9527/payment/get/31
=====config====
启用网关gateway作负载均衡
1. 9527网关中开启从注册中心动态生成路由的功能,----开启用微服务名进行路由
2. 在9527网关中配置 uri: lb://微服务名 在此实现对一个微服务名下多个服务负载均衡
网关一侧负载均衡
此时http://localhost:9527/payment/lb,即可实现负载均衡
predicate
curl 发送get请求,带Cookie
curl 发送get请求,带Header
Filter
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#gatewayfilter-
factories
GatewayFilter 31个,GlobalFilter 10个
自定义过滤器 (常用)
Config (服务端git存config、客户端获取指定config)
创建gitee仓库 springcloud-config
其中的yml文件命名,一般为 name-profile.yml config-xxx.yml
Config 服务端(添加git仓库信息、开启@EnableConfigServer)
将配置信息以REST接口形式暴露
该hosts : 128.0.0.1 config-3344.com 安装utools option+space
访问:http://config-3344.com:3344/master/config-dev.yml
就能读取git上的文件
http://config-3344.com:3344/master/config-dev.yml
http://config-3344.com:3344/config-dev.yml (yml中配置了label,指定了分支)
http://config-3344.com:3344/config/dev/master 读出来为json字符串,需要自己获取内容
Config 客户端 (bootstrap.yml 指定yml配置中心文件、业务@Value读取yml中信息)
分布式配置的动态刷新 (actuator依赖、暴露监控端口、业务添加@RefreshScope、手动POST刷新actuator)
添加actuator依赖
暴露监控端点
客户端业务类,添加@RefreshScope 刷新类
改了还不行,只有当中心配置修改时,运维人员发送Post请求刷新3355,必须时post请求
curl -X POST "http://localhost:3355/actuator/refresh" -----避免服务重启
手动刷新、多个服务刷新、定点刷新 繁琐问题
BUS总线 (解决分布式自动刷新配置)
用来将分布式系统的节点与轻量级消息系统链连接起来的框架
方式一:通过 POST /bus/refresh 给其中一个客户端(3355),再通过cloud Bus 进行广播
能干什么
方法二:通过POST /bus/refresh 给中心服务端(3344),再通过中心服务进行广播
RabbitMQ安装
mac
brew install rabbitmq
cd /usr/local/Cellar/rabbitmq/3.8.8/
sudo sbin/rabbitmq-plugins enable rabbitmq_management
sudo vi /etc/profile
//添加环境
export RABBIT_HOME=/usr/local/Cellar/rabbitmq/3.7.4
export PATH=$PATH:$RABBIT_HOME/sbin
// 立即生效
source /etc/profile
// 后台启动
rabbitmq-server -detached
// 查看状态
rabbitmqctl status
rabbitmqctl stop 关闭
准备3355、3366服务
全局通知
设计思想 (两种)
方法二更合适:发给配置服务器server
1. 传给client 2. 传给server(3344 更合适)
为什么不要利用消息总线触发一个客户端,二是触发服务端
============3344server端 添加bus-amqp 依赖 ==========
3344server 配置rabbitmq,并暴露bus刷新配置等端点
bus-refresh
暴露监控:必须有actuator依赖
==========3355、3366===========
curl -X POST "http://localhost:3344/actuator/bus-refresh"
原理:为什么客户端配置了RabbitMQ就能收到3344d bus-refresh?
定点通知
curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
其中config-client 为 3355服务名称
通知总结
cloud stream
https://m.wang1314.com/doc/webapp/topic/20971999.html
Binder
Springcloud Stream标准流程套路
编码API和常用注解
开8801 消息生产者
生产者才有service
生产者实现类 @EnableBinding(Source.class)
http://localhost:8801/sendMessage
8802 消费者
消费者
@EnableBinding (Sink.class) 消费者没有 service
@StreamListener (Sink.INPUT)
@StreamListener(Sink.INPUT)监听输入源
clone 8802 一个 8803
分组消费持久化
持久化:
8801 消息发送:8802、8803是在同一个group 的消费者 ,此时8801发送4条消息,会分均到8802、8803中,
当在发送过程中8802停机,并删除了group,8803重启后会找回丢失当消息
sleuth 链路追踪(zipkin)
localhost:9411