SpringCloud基础学习
关于SpringCloud相关组件的基本入门学习
SpringCould学习
前言
版本选择
|
|
模块构建
新建项目
-
选择maven创建
-
选择骨架
-
修改字符编码
都改成utf-8
-
注解生效激活
-
设置java编译版本
-
File Type过滤(将项目下显示的一些杂七杂八的文件过滤掉)
-
将src删掉
-
将pom.xml中加入如下
-
统一管理jar包版本
1 2 3 4 5 6 7 8 9 10 11 12
<!--统一管理Jar包版本--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>12</maven.compiler.source> <maven.compiler.target>12</maven.compiler.target> <junit.version>4.12</junit.version> <lombok.version>1.18.10</lombok.version> <log4j.version>1.2.17</log4j.version> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties>
-
编写dependencyManagement
作用:
- 统一声明版本号
- 升级时只需该父工程的pom.xml中的版本号就可以处处升级
- 子模块不用写版本号
- 如果子模块要使用别的版本,只用自己声明version
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 54 55 56 57 58 59 60 61 62 63 64 65
<!--子模块继承之后,提供作用:锁定版本+子module不用写groupId和version--> <dependencyManagement><!--定义规范,但不导入--> <dependencies> <dependency> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </dependency> <!--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 阿里巴巴--> <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> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> <scope>runtime</scope> </dependency> <!-- druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> </dependencies> </dependencyManagement>
-
增加热启动插件
1 2 3 4 5 6 7 8 9 10 11 12 13
<!--热启动插件--> <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>
-
maven中跳过单元测试
-
父工程创建完成后执行 mvn:install将父工程发布到仓库方便子工程继承
支付模块构建
构建微服务模块统一步骤
- 建module
- 改pom
- 写yml
- 主启动
- 业务类
创建支付模块
-
创建module,继承自父亲模块(maven无骨架创建)
-
改pom文件
把需要使用的pom文件导入
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
<dependencies> <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> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!--mysql-connector-java--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--jdbc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
-
改yml
-
在模块下的resources下新建application.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
server: port: 8001 #服务端口号 spring: application: name: cloud-payment-service #服务名称 datasource: type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包 url: jdbc:mysql://${person.mysql.host}:${person.mysql.port}/springcloud?useUnicode-true&charcterEncoding=utf-8&useSSL=false username: root password: lcy021030 person: mysql: host: 49.234.111.177 port: 3306
-
-
主启动类
创建主启动类
-
业务类
数据库建表
1
CREATE TABLE `payment` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `serial` varchar(200) DEFAULT '', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
创建实体类
1 2 3 4 5 6 7 8 9
@NoArgsConstructor @AllArgsConstructor @Data public class Payment implements Serializable { private Long id; private String serial; }
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
@Data @AllArgsConstructor @NoArgsConstructor public class CommonResult<T> { public static final Integer SUCCESS = 20000; public static final Integer ERROR = 40000; private Integer code; private String message; private T data; public static CommonResult ok(){ return new CommonResult(SUCCESS,"success",null); } public static CommonResult error(){ return new CommonResult(ERROR,"error",null); } public CommonResult msg(String message){ this.message = message; return this; } public CommonResult code(int code){ this.code = code; return this; } public CommonResult<T> data(T data){ this.data = data; return this; } }
编写PaymentDao层
编写PaymentMapper
文件头
1 2 3 4 5 6
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="pro.risingsun.springcloud.dao.PaymentDao"> </mapper>
写Service
写controller
postman自测
创建消费者模块
创建模块
写实体类(Payment和CommonResult)
使用RestTemplate
RestTemplate
-
创建config.ApplicationContexConfig
-
将RestTemplate注册成bean
完善消费者的OrderConsumer
测试
工程重构
将部分相似的代码进行重构整合
创建新模块cloud-api-commons
写pom
将entites移入
放入本地库
maven:clean
maven:install
改造原本的两个模块的公共内容
-
删掉entities
-
在各自的pom文件中引入
Eureka
一种服务注册中心,使用Eureka的客户端连接到到Eureka Server并维持心跳连接。这样就可以通过Eureka Server来监控系统的各个微服务是否正常运行
EurekaServer
提供注册服务,各个微服务节点通过配置启动后,会从EurekaServer中心进行注册。这样RurekaSever中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到
EurekaClient
一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的、使用轮询负载算法的负载均衡器。在应用启动后,将会用EurekaServer发送心跳(默认30秒为一个周期)。如果EurekaServer在多个心跳周期内没有接收到某个节点的心跳,EurekaServer就会从服务注册表中把这个服务节点移除(默认90秒)
IDEA创建EurekaServer
创建模块
改pom
|
|
写yaml
|
|
写启动类
打上注解@EnbaleEurekaServer表示自己是EurekaServer
测试
输入localhost:7001
将payment注册进Eureka中
改pom
增加EurekaClient的依赖
|
|
改配置
|
|
改主启动类
加上注解@EnableEurekaClient
启动测试
这里的应用名称是我们在yaml文件中配置的,如下图:
将order注册进Eureka中
步骤如上,基本一致
集群Eureka搭建
互相注册,相互守望
创建cloud-eureka-server7002
参考7001进行创建
修改映射文件
写yaml
互相注册,相互守望
-
修改7001的配置文件
-
修改7002的配置文件
主启动类
测试访问
eureka7001:7001
测试访问
eureka7002:7002
将服务注册进集群
改配置文件
PaymentProvider集群搭建
参考8001搭建8002和8003
修改consumer的服务调用URL
但是这样消费者还是找不到是哪个生产者,因为三个生产者的服务名称都是cloud-payment-service,因此需要开启负载均衡
开启负载均衡
使用@LoadBalances注解赋予RestTemplate负载均衡能力
集群信息完善
增加实例名称
在payment8001和8002的配置文件中增加
增加ip显示
服务发现
对于注册进eureke里面的微服务,可以通过服务发现来获得该服务的信息
修改cloud-provider-payment8001的Controller
主启动类增加注解
测试
Eureka自我保护
某时刻某一个微服务不可用了,Eureka不会立刻清理,仍旧会对该微服务的消息进行保存
属于CAP中的AP分支
ZooKeeper
创建payment
服务器上启动Zookeeper
新建模块cloud-provider-payment8004
改pom文件
将Eureka的依赖改成zookeeper的
|
|
改配置文件(将Eureka的改成Zookeeper)
主启动类
写Controller
测试localhost:8004/payment/zk
创建order
…省略
Consul
…
Ribbon
Ribbon是一套实现负载均衡的工具
spring-cloud-starter-netflix-eureka-client
已经集成了Ribbon
如何替换负载均衡策略
该配置类不能放在@CompomentScan可以扫到的位置
主启动类增加注解
OpenFeign
用在消费端
创建模块cloud-consumer-feign-order9000
改pom
|
|
改配置文件
|
|
注册不注册都可以,消费端本来就不用注册
写主启动类
写服务调用接口
要点:
- 这里相当于是直接根据服务名和路径去服务端找相应的controller的方法,所以这里的服务名、路径、请求类型要一致才能在服务端找到方法
- 参数也要保证一致,而且需要打上相应的注解,没有SpringMVC帮忙封装了
负载均衡
自带负载均衡,使用Ribbon
OpenFeign超时控制
OpenFeign默认等待一秒钟
如果超过1秒种,Feign客户端直接报错
修改超时时间(OpenFeign自带Ribbon)
OpenFeign日志
日志级别:
- NONE:默认的,不显示任何日志
- BASIC:仅记录请求方法、URL、相应状态码及执行时间
- HEADERS:除了记录BASIC中定义的信息外,还有请求和相应的头信息
- FULL:除了HEADERS中定义的信息外,还有请求和相应的正文及元数据
开启日志
创建日志配置类
配置文件中配置日志
Hystix
重要概念
服务 降级
fallback
出现情况
- 程序运行异常
- 超时
- 服务熔断出发服务降级
- 线程池/常量池打满
服务熔断
当达到最大服务访问时,直接拒绝访问,然后调用服务降级
服务限流
限制服务访问
实践
创建生产者
新建模块cloud-provider-hystrix-payment8005
pom文件
|
|
写配置文件
写主启动类
写Service
写Controller
测试
创建消费者
…
service,远程调用
controller
服务降级
服务提供方降级
激活服务降级功能
在主启动类上加上注解@EnbaleCircuitBeaker
使用注解@HystrixComman
在业务方法上标记
服务消费方降级
改yml
改启动类
业务上处理
全局服务降级
现在的降级方式和业务代码耦合严重,而且重复冗余。那么需要进行全局的一个服务降级配置
@DefaultProerties(defaultFallback = “xxx”)设置默认的服务降级配置
如果某个方法有自己的配置,就用自己的,没有就用默认的
在Feign接口进行统一处理
写一个类实现PaymentFeignService接口,对方法进行重写
配置fallback
服务熔断
生产方Service代码如下
在错误了一定次数内失败率达到了设定值,那么进入熔断状态,然后在一定时间后(默认是5秒)会放出几个请求进去,如果还是错误降级了,那么就保持熔断状态,刷新休眠窗口期。如果请求正确了,那么熔断取消,正常运行。
参考链接
参数说明
- 请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断。
- 快照时间窗:断路器通过在统计一定时间内的请求和错误等数据决定是否开启熔断,而统计的时间范围就是快照时间窗,默认就是最近10秒
- 错误百分比阈值,当请求总数在快照时间窗内超过了阈值,而且错误请求的次数占的百分比超过了该错误百分比阈值,那么就会将断路器打开
监控页面
建模块
改pom
增加:
|
|
改yml
写主启动类
启动,访问localhost:9001/hystrix
修改8005
监控8005
访问监控网址,测试
GateWay
入门配置
创建模块
改pom
|
|
写yml
|
|
写主启动类
写配置
动态路由
配置文件配置
|
|
访问localhost:9527/payment/1测试
常用Predicate
也就是常用的断言配置
After Route Predicate
请求时间在该时间之后可以匹配
在配置文件中配置
可以通过如下Api进行获得该格式的日期:
在该日期之前无法访问
该日期之后可以访问
Before Route Predicate
请求的时间在这个时间之前可以匹配
Between Route Predicate
请求时间在两个时间内,可以匹配
Cookie Route Predicate
匹配Cookie断言
需要两个参数,第一个是Cookie name,第二个是政策表达式
Gateway会去检验对应的Cookie的value是否符合正则表达式
配置Predicate
Header Route Predicate
请求头中进行匹配
两个参数,一个是请求头名,一个是值
测试不匹配
测试匹配
Method Route Predicate
Path Route Predicate
Query Route Predicate
Filter
自定义全局日志过滤器
Config
服务端(连接github)
微服务统一配置
建模块cloud-config-center-3344
写pom
增加
|
|
写配置文件
主启动类
测试localhost:3344/master/config-dev.yml
如果连接失败,使用ssh方式连接,使用旧方法创建私匙,在github中添加,然后配置文件中改成ssh的地址
客户端(连接服务端)
创建模块cloud-config-client-3355
改pom
|
|
写bootstrap.yml
- application.yml是用户级的资源配置
- bootstrap.yml是系统级,优先级更高
写主启动类
客户端动态更新
服务端可以实时更新,但是客户端不能实时更新
引入监控依赖
|
|
配置文件暴露监控端点
在controller添加注解
@RefreshScope
Bus
什么是总线?
在微服务架构的系统总,通常用使用轻量级的消息代理来构建一个公用的消息主题,并让系统中所有微服务的实例都连接上来,由于该主题中产生的消息会被所有实例监听和消费,所以称它为消费总线。在总线上的各个实例,都可以方便的广播一些需要其他连接到该主题上的实例都知道的消息
基本原理
ConfigClient实例都监听MQ中同一个topic(默认是SpringCloudBus)。当一个服务刷新数据的时候,它会把这个消息放入到Topic中,这样其他监听同一Topic的服务就能得到通知,然后去更新自身的配置
实践
再创建一个cloud-config-client-3366,步骤略
设计
- 利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端配置
- 利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置
方法二更适合
服务端添加Rabbitmq
|
|
增加rabbitmq的配置
增加暴露刷新配置的端点
3344和3355添加消息总线的支持
|
|
|
|
发送请求POST: localhost:3344/actuator/bus-refresh完成自动更新
Stream
驱动,屏蔽底层消息中间件的差异
应用程序通过inputs或者outputs来与SpringCloudStream中的binder对象交互
通过配置来绑定(binding),而SpringCloudStream的binder对象负责与消息中间件交互
实践
生产者
创建模块cloud-stream-rabbitmq-provider8801
写pom
|
|
写yml
|
|
写主启动类
写业务接口
消费者
新建模块cloud-stream-rabbitmq-consumer8802
写pom
写yml
写业务
重复消费
此时两个消费者是不同组的,都监听一个交换机,所以都会收到消息,因此我们需要将两个消费者放到同一个组中
自定义分组
如果我们不自定义分组,那么默认使用的是不同的分组,因此可以自定义选择分组
在8802和8803中加入分组,那么收到的就是将两个消费者监听在同一个组中,消息则只能被消费一次
此时两个消费者就轮询接受消息。
原理
其实就是rabbitmq中的fandout和direct,默认情况两个消费者是不同的队列,然后绑定到同一个交换机,那么我们生产者生产一个消息到交换机,就会被两个队列都拿到。
当我们配置了组名时,其实就是变成了同一个队列,那么生产的消息发送到交换机时,交换机只发了一条消息给队列,那么监听同一个队列的消费者,只有一个能消费到消息,默认是轮询消费
Sleuth
SpringCloudSleuth提供了分布式追踪的解决方案
搭建zipkin
下载jar包后直接启动
访问
localhost:9411
查看面板
实践
cloud-provider8001添加依赖
|
|
改配置
在consumer做一样的依赖和配置