如果使用 github 克隆仓库缓慢,可以移步到笔者在 gitee 平台实时同步的仓库:https://gitee.com/woodwhales/spring-cloud-2020-study
Spring Cloud 第二季:
对应视频:尚硅谷2020最新版SpringCloud(H版&alibaba)框架开发教程全套完整版从入门到精通(大牛讲授spring cloud)
周阳老师也在自己的 GitHub 仓库上发布了当堂源码:https://github.com/zzyybs/atguigu_spirngcloud2020
笔者也 fork 了一份该源码:https://github.com/woodwhales/atguigu_spirngcloud2020
对应资料:尚硅谷SpringCloud第2季,建议使用百度脑图打开(只能看文字,不能看图片)。
网友整理的学习笔记或者资料:
https://github.com/liusCoding/springcloud-2020
所需软件:
apache-jmeter-5.3:https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.3.zip
consul_1.7.2_windows_amd64:https://releases.hashicorp.com/consul/1.7.2/consul_1.7.2_windows_amd64.zip
nacos-server-1.1.4:https://github.com/alibaba/nacos/releases/download/1.1.4/nacos-server-1.1.4.zip
seata-server-0.9.0:https://github.com/seata/seata/releases/download/v0.9.0/seata-server-0.9.0.zip
sentinel-dashboard-1.6.3:https://github.com/alibaba/Sentinel/releases/download/1.6.3/sentinel-dashboard-1.6.3.jar
zipkin-server-2.12.9-exec:https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/2.12.9/zipkin-server-2.12.9-exec.jar
zookeeper-3.4.11:https://archive.apache.org/dist/zookeeper/zookeeper-3.4.11/zookeeper-3.4.11.tar.gz
想完全打开思维导图,则需要安装 MindManager:https://www.mindmanager.com/en/support/download-library/
课程大纲的思维导图如下:
因为上述思维导图文件太大,不利于快速打开阅读学习,所以笔者在此思维导图基础上结合视频教程,自己整理一套属于自己的学习笔记。
技术要求:java8 + maven + git、github + Nginx + RabbotMQ + SpringBoot2.0
Git 源码地址:https:github.com/spring-projects/spring-boot/releases/
Spring Cloud 视频教程第一季大纲:
对应视频:尚硅谷经典SpringCloud框架开发教程全套完整版从入门到精通(大牛讲授spring cloud)
对应资料:尚硅谷SpringCloud第1季
尚硅谷Java面试题第一季:
对应视频:尚硅谷经典Java面试题第一季(java面试精讲)
对应资料:尚硅谷Java面试题第1季
尚硅谷Java面试题第二季:
对应视频:尚硅谷Java大厂面试题第二季(java面试必学,周阳主讲)
对应资料:尚硅谷Java面试题第2季
网友整理的学习笔记或者资料:
https://github.com/MaJesTySA/JVM-JUC-Core
https://github.com/yangjinwh/Mindmap
https://gitee.com/moxi159753/LearningNotes/tree/master/%E6%A0%A1%E6%8B%9B%E9%9D%A2%E8%AF%95/JUC
尚硅谷Java面试题第三季:
对应视频:尚硅谷2021逆袭版Java面试题第三季(java大厂面试题,周阳主讲)
对应资料:尚硅谷Java面试题第3季
尚硅谷Java数据结构与java算法(Java数据结构与算法):
对应视频:尚硅谷Java数据结构与java算法(Java数据结构与算法)
对应资料:尚硅谷图解Java数据结构和算法
尚硅谷JUC:
对应视频:https://www.bilibili.com/video/BV1c4411w7c7
对应资料:尚硅谷JUC
juc 与 jvm _并发编程
对应视频:https://www.bilibili.com/video/BV1vE411D7KE
对应资料:尚硅谷JUC-JVM
Spring Boot 2.0 wiki:https:github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes
Spring Cloud 版本选择需要注意对应 Spring Boot 版本,在 Spring Cloud 官网中 https://spring.io/projects/spring-cloud/#overview 提到:
本视频教程时,2020 年 02 月 15 日 之前,Spring Cloud 最新版本为:Hoxton.SR1,支持的 Spring Boot 版本为:2.2.2.RELEASE。
Spring Cloud 版本可支持的 Spring Boot 版本,在其官网:中已经写明:
Spring 系列版本对应关系,官方参考地址:https://start.spring.io/actuator/info
本笔记统一使用如下版本:
cloud:Hoxton.SR1
boot:2.2.RELEASE
cloud:alibaba 2.1.0.RELEASE
java:Java8
Maven:3.5及以上
mysql:5.7及以上
截止 2020 年,Spring Cloud 各种组件的停更/升级/替换:
-
服务注册中心 Eureka:停更 Zookeeper:替代品 Consul:替代品 Spring Cloud Alibaba Nacos:替代品(主推)
-
服务调用 Ribbon:更新变慢,有停更趋势 Loadbalancer:Spring 新主推产品
-
服务调用 Feign:停更 OpenFeign:Spring 新主推产品
-
服务降级 Hystrix:停更 resilience4j:替代品,国外使用多 alibaba Sentinel:替代品,国内使用多
-
服务网关 Zuul:停更 Zuul2:推出时间未知,慎用 gateway:Spring 新主推产品
-
服务配置 Config:停更 Spring Cloud Alibaba Nacos:替代品(主推) apollo:替代品
-
服务主线 Bus:停更 Spring Cloud Alibaba Nacos:替代品(主推)
笔者使用的 IDEA 版本为:2020.2
点击创建好的工程根目录,File -> setttings 设置字符集编码
参见笔者的博文:IDEA 常用设置:忽略显示与工程无关的文件
将上述模板工程改造成空的父工程:
设置版本依赖管理:
<?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>cn.woodwhales</groupId>
<artifactId>spring-cloud-demo1</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>spring-cloud-demo1</name>
<url>http://woodwhales.cn/</url>
<!-- 统一管理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.16.18</lombok.version>
<mysql.version>8.0.18</mysql.version>
<druid.version>1.1.16</druid.version>
<druid.spring.boot.starter.version>1.1.10</druid.spring.boot.starter.version>
<spring.boot.version>2.2.2.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.1.0.RELEASE</spring.cloud.alibaba.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
<mybatis-spring-boot-starter.version>2.1.1</mybatis-spring-boot-starter.version>
<hutool-all.version>5.1.0</hutool-all.version>
</properties>
<!-- 子模块继承之后,提供作用:锁定版本 + 子module不用谢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>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.spring.boot.starter.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</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>
<!--第三方maven私服-->
<repositories>
<repository>
<id>nexus-aliyun</id>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>
使用 IDEA 创建 project,pom 配置如下:
<?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>cn.woodwhales</groupId>
<artifactId>spring-cloud-project</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>spring-cloud-project</name>
<url>http://woodwhales.cn/</url>
<!-- 统一管理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.16.18</lombok.version>
<mysql.version>8.0.18</mysql.version>
<druid.version>1.1.16</druid.version>
<druid.spring.boot.starter.version>1.1.10</druid.spring.boot.starter.version>
<spring.boot.version>2.2.2.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.1.0.RELEASE</spring.cloud.alibaba.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
<mybatis-spring-boot-starter.version>2.1.1</mybatis-spring-boot-starter.version>
<hutool-all.version>5.1.0</hutool-all.version>
</properties>
<!-- 子模块继承之后,提供作用:锁定版本 + 子module不用写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>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.spring.boot.starter.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</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>
<!--第三方maven私服-->
<repositories>
<repository>
<id>nexus-aliyun</id>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>
选中父工程文件目录,右击选择创建 “Module”,创建 cloud-api-commons 子工程。
<?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">
<parent>
<artifactId>spring-cloud-project</artifactId>
<groupId>cn.woodwhales</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-api-commons</artifactId>
<dependencies>
<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>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
</project>
选中父工程文件目录,右击选择创建 “Module”,创建 cloud-provider-payment8001 子工程。
<?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">
<parent>
<artifactId>spring-cloud-project</artifactId>
<groupId>cn.woodwhales</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8001</artifactId>
<dependencies>
<!--引入其他工程-->
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>cn.woodwhales</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</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>
</project>
编写系统配置文件:
server:
port: 8001
spring:
application:
name: cloud-provider-payment
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloud-2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root1234
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: cn.woodwhales.springcloud.entity
各个子工程引用:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
父工程引用:
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
按键:ctrl+shift+alt+/,搜索 Registry
<?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">
<parent>
<artifactId>spring-cloud-project</artifactId>
<groupId>cn.woodwhales</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-order80</artifactId>
<dependencies>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>cn.woodwhales</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<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.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>
</project>
截止 v1.0.0 版本,本系统工程包含:cloud-api-commons、cloud-provider-payment8001、cloud-consumer-order80
1.x 引入 eureka 依赖为:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
2.x 引入 eureka 依赖为:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<?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">
<parent>
<artifactId>spring-cloud-project</artifactId>
<groupId>cn.woodwhales</groupId>
<version>2.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>cn.woodwhales</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--boot web actuator-->
<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.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
给主启动类添加 @EnableEurekaServer 注解,启动成功之后,浏览器访问:http://127.0.0.1:7001/,可以看到一个空的 eureka 服务控制台界面。
在 cloud-provider-payment8001 工程中引入:
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在启动类添加 @EnableEurekaClient 注解
配置 eureka 配置:
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。
#单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
启动服务,可在 eureka server 控制台看到已经注册的 payment 服务:
在 cloud-consumer-order80 工程中引入:
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在启动类添加 @EnableEurekaClient 注解
配置 eureka 配置:
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。
#单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
启动服务,可在 eureka server 控制台看到已经注册的 order 服务:
截止 2.0.0 版本,增加了一个 eureka 注册中心,并将 order 和 payment 服务注册到该注册中心:
搭建 eureka 服务集群是为了:提升服务注册中心的高可用性,实现负载均衡和故障同错功能。
按照搭建 cloud-eureka-server7001 工程步骤,搭建 cloud-eureka-server7001 工程。
在单机版本的 eureka 服务注册中心配置中:
eureka:
instance:
hostname: localhost
eureka.instance.hostname 在 eureka 集群中不能名称重名,因此需要修改主机的 hosts 配置文件:
127.0.0.1 eureka7001.cn
127.0.0.1 eureka7002.cn
分别修改 7001 和 7002 的系统配置文件:
server:
port: 7001
eureka:
instance:
hostname: eureka7001.cn #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
defaultZone: http://eureka7002.cn:7002/eureka/
server:
port: 7002
eureka:
instance:
hostname: eureka7002.cn #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
defaultZone: http://eureka7001.cn:7001/eureka/
启动 7001 和 7002 服务,访问:http://eureka7001.cn:7001/ 或者 http://eureka7002.cn:7002/,可以看见服务中心控制台界面。
注册 order 和 payment 到集群的 eureka 注册中心,分别修改俩者注册到 eureka 服务注册中心的配置:
eureka:
service-url:
# 集群版
defaultZone: http://eureka7001.cn:7001/eureka,http://eureka7002.cn:7002/eureka
在 payment 和 order 工程配置中,将 配置修改为如下:
eureka:
client:
service-url:
# 单机配置
# defaultZone: http://eureka7001.cn:7001/eureka/、
# 集群配置
defaultZone: http://eureka7001.cn:7001/eureka/,http://eureka7002.cn:7002/eureka/
启动 eureka 服务,启动 order 和 payment 服务,访问任一 eureka server 集群节点(http://eureka7001.cn:7001/ 或者 http://eureka7002.cn:7002/),均可见俩个服务已成功注册到集群上:
按照 cloud-provider-payment8001 工程搭建步骤,搭建相同服务,以实现服务提供者为集群服务。
值得注意的是,服务消费者 order 服务中调用 payment 的服务 http 请求地址需要修改为:
http://CLOUD-PROVIDER-PAYMENT
并在注册 RestTemplate 实例 bean 的时候添加 @LoadBalanced 注解,否则无法享受调用服务负载均衡。
@LoadBalanced 为 RestTemplate 提供了负载均衡能力。
健康信息配置的必要性:
当工程中引入了 actuator starter 之后,可以增加一些配置使得 eureka 更易运维。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在系统配置文件中配置 eureka 实例 id:
eureka:
instance:
instance-id: payment8002
达到的效果为:
设置 eureka 的访问路径显示 ip:
eureka:
instance:
prefer-ip-address: true
使用 @EnableDiscoveryClient 注解,开启 eureka 服务注册发现功能。
在业务代码中引入 DiscoveryClient 实例即可获取 eureka 注册中心中已经成功注册的所有服务信息,demo 用法参见:cn.woodwhales.springcloud.controller.OrderController#discovery
@Autowired
private DiscoveryClient discoveryClient;
public Object discovery() {
// 获取所有serviceId
List<String> serviceIds = discoveryClient.getServices();
Map<String, Map<String, Object>> eurekaMap = new HashMap<>(serviceIds.size());
for (String serviceId : serviceIds) {
Map<String, Object> instanceInfoMap = new HashMap<>(5);
// 通过serviceId获取相应的服务详情信息
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
for (ServiceInstance instance : instances) {
instanceInfoMap.put("serviceId", instance.getServiceId());
instanceInfoMap.put("instanceId", instance.getInstanceId());
instanceInfoMap.put("host", instance.getHost());
instanceInfoMap.put("port", instance.getPort());
instanceInfoMap.put("uri", instance.getUri());
}
eurekaMap.put(serviceId, instanceInfoMap);
}
return eurekaMap;
}
eureka 属于了 CAP 理论中的 AP。
在某个时刻某个服务发生故障了,eureka 注册中心不会立即将该服务清理下线,而是依旧会对该服务节点信息进行保存。
eureka server 端
eureka:
server:
# 关闭自我保护机制
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
eureka client 端
eureka:
instance:
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2
工程示例:cloud-provider-payment8004
搭建 cloud-provider-payment8004 工程,使用 zk 进行服务注册。搭建 zk 服务参见:zookeeper window环境安装、集成为windows服务、单机伪集群搭建
核心的依赖为:spring-cloud-starter-zookeeper-discovery,需要注意的是该启动器自身已经包含了一个 zk jar包,如果和要链接的 zk 服务器版本不一致,需要排除掉这个自身带的 zk,依赖和 zk 服务器版本一致的 zk jar 包。
<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--先排除自带的zookeeper3.5.3-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.11版本-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.11</version>
</dependency>
设置 zk 链接源
server:
address: 0.0.0.0
port: 8004
# 服务别名 --- 注册zookeeper到注册中心名称
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 127.0.0.1:2181
工程示例:cloud-provider-payment8004
在主启动类上添加:@EnableDiscoveryClient 注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author woodwhales
* @date 2021-01-02 24:03
*/
@EnableDiscoveryClient // 该注解用于向使用consul或者zookeeper作为注册中心时注册服务
@SpringBootApplication
public class OrderMain8004 {
public static void main(String[] args) {
SpringApplication.run(OrderMain8004.class, args);
}
}
先启动 zk 服务器,再启动 cloud-provider-payment8004 服务,可以在 zk 中看到已经成功注册的节点信息:
注意:zk 是 CAP 中的 CP,当某个节点在一定时间内无心跳回应,zk 服务器会将该节点删除。当这个节点重新恢复回来,则 zk 服务器会生成新的节点 id。因此 zk 中存储的服务信息为临时节点。
在 consul 官网(https://www.consul.io/downloads)下载 consul,并解压压缩包得到 consul.exe 文件,使用 cmd 命令查看 consul 版本:
consul -v
官方文档地址:https://www.consul.io/docs/intro,官网建议配置到 PATH 系统环境变量里,方便使用。
启动 consul 执行命令,默认启动 8500 http 端口。
consul agent -dev
更多参数含义,参见:https://www.consul.io/docs/agent/options
在工程中引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
在系统配置文件中,配置服务注册链接地址为:127.0.0.1:8500 即可。
spring:
application:
name: cloud-consumer-consul-order
cloud:
zookeeper:
connect-string: 127.0.0.1:8500
启动工程之后,浏览器访问:http://127.0.0.1:8500/ 即可看到已成功注册的服务节点信息:
Ribbon 是一个软负载均衡的客户端软件。
spring-cloud-starter-netflix-eureka-client 自身包含了 Ribbon 依赖,因此只需要引入 spring-cloud-starter-netflix-eureka-client 就已经集成了 Ribbon:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
使用 IDEA 的 maven helper 插件查看依赖关系也可以看出:
@Override
@Nullable
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
}
@Override
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
}
getForObject() 方法返回的是响应体中对象
getForEntity() 方法返回的是 ResponseEntity 对象,ResponseEntity 对象包含了请求响应的更新详细信息(状态码、响应体、响应头等)。
Ribbon 负载均衡策略的核心接口为:com.netflix.loadbalancer.IRule:
/**
* Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of
* as a Strategy for loadbalacing. Well known loadbalancing strategies include
* Round Robin, Response Time based etc.
*
* @author stonse
*
*/
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
该接口有 7 种实现:
分别为:
策略类 | 命名 | 描述 |
---|---|---|
RandomRule | 随机策略 | 随机选择server |
RoundRobinRule | 轮询策略 | 按照顺序选择server(ribbon默认策略) |
RetryRule | 重试策略 | 在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server |
BestAvailableRule | 最低并发策略 | 逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值) |
ResponseTimeWeightedRule | 响应时间加权重策略 | 根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间 |
ZoneAvoidanceRule | 区域权重策略 | 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server |
步骤1:定义自定义负载均衡接口
import java.util.List;
/**
* @author woodwhales
* @date 2021-01-13 23:03
*/
@Component
public interface MyLoadBalancer {
/**
* 从可用节点列表中获取要执行的节点
* @param serviceInstances
* @return
*/
ServiceInstance getInstance(List<ServiceInstance> serviceInstances);
}
步骤2:模仿 com.netflix.loadbalancer.RoundRobinRule 策略编写自定义 ribbon 策略
自定义 ribbon 策略的核心在于,使用自旋锁统计请求的次数。使用请求的第几次取模可用节点数得到可用节点的索引。
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author woodwhales
* @date 2021-01-13 23:22
*/
@Slf4j
@Component
public class MyLoadBalancerImpl implements MyLoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
/**
* 统计请求次数,采用自旋锁方式获取请求次数
* @return
*/
private final int compareAndGet() {
int current;
int next;
do {
current = atomicInteger.get();
next = current >= Integer.MAX_VALUE ? 0 : current + 1;
} while (!atomicInteger.compareAndSet(current, next));
log.info("*********** next => {} ***********", next);
return next;
}
@Override
public ServiceInstance getInstance(List<ServiceInstance> serviceInstances) {
// 请求的第几次 % 可用节点总数 = 当前要执行的节点
int index = compareAndGet() % serviceInstances.size();
return serviceInstances.get(index);
}
}
步骤3:使用自定义策略
注意在注册 RestTemplate 为 bean 的配置类中,将 @LoadBalanced 注解去除。
import cn.woodwhales.springcloud.entity.CommonResult;
import cn.woodwhales.springcloud.entity.Payment;
import cn.woodwhales.springcloud.ribbon.MyLoadBalancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.List;
import static java.util.Objects.isNull;
/**
* @author woodwhales
* @date 2020-12-19 22:25
*/
@Slf4j
@Controller
@RequestMapping("/consumer")
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private MyLoadBalancer myLoadBalancer;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-PAYMENT");
if(isNull(instances) || instances.isEmpty()) {
return null;
}
ServiceInstance serviceInstance = myLoadBalancer.getInstance(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri + "/payment/get/" + id, CommonResult.class);
}
}
github:https://github.com/OpenFeign/feign
官网地址:https://docs.spring.io/spring-cloud-openfeign/docs/
引入依赖
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在启动类开启注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author woodwhales
* @date 2021-01-14 00:17
*/
@EnableFeignClients
@SpringBootApplication
public class OpenFeignOrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OpenFeignOrderMain80.class, args);
}
}
编写接口,并添加 @FeignClient 注解
import cn.woodwhales.springcloud.entity.CommonResult;
import cn.woodwhales.springcloud.entity.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Component
@FeignClient(value = "CLOUD-PROVIDER-PAYMENT")
public interface PaymentService {
@GetMapping(value = "/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}
使用 openFeign
import cn.woodwhales.springcloud.entity.CommonResult;
import cn.woodwhales.springcloud.entity.Payment;
import cn.woodwhales.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author woodwhales
* @date 2020-12-19 22:25
*/
@Slf4j
@Controller
@RequestMapping("/consumer")
@RestController
public class OrderController {
@Autowired
private PaymentService paymentService;
@GetMapping("/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
return paymentService.getPaymentById(id);
}
}
OpenFeign starter 组件默认集成了 Ribbon,客户端调用服务默认容忍等待时长为 1 秒,即在不进行任何配置的情况下,使用 OpenFeign 调用服务的提供者响应时长必须在 1 秒之内,否则会 OpenFeign 会强行报异常。
在某些业务场景中,提供者必须响应超过 1 秒,则需要 OpenFeign 客户端配置超长响应时长:
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
上述配置表示,容忍 OpenFeign 调用服务提供者的响应时长 5 秒。
服务不可用时,不要让客户端等待,而是立即返回一个友好的提示。
触发场景:
- 线程池/信号量已满
- 程序响应超时
- 程序运行异常
- 满足服务熔断条件
达到最大服务访问后,直接拒绝访问,调用服务降级的方法并返回友好提示
秒杀高并发等操作,严谨服务调用拥挤,对并发调用进行排队,限制一秒种可调用数量。