一、官网集合:
Springboot 官网
https://spring.io/projects/spring-boot
https://github.com/spring-projects/spring-boot
中文文档
http://www.spring-boot.org/doc/
Mybatis 官网
https://mybatis.org/mybatis-3/
SpringCloud Alibaba 官网
https://spring.io/projects/spring-cloud-alibaba#overview
github 中文官网
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
英文官网
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
https://github.com/alibaba/spring-cloud-alibaba
Nacos 官网
https://github.com/alibaba/Nacos
https://nacos.io/zh-cn/index.html
Sentinel 官网
https://github.com/alibaba/Sentinel
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
OpenFeign 官网
https://github.com/spring-cloud/spring-cloud-openfeign
Gateway 官网
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
Seata 官网
https://github.com/seata/seata
Sleuth 官网
https://github.com/spring-cloud/spring-cloud-sleuth
二、微服务架构编码构建
2.1 idea 新建 project 工作空间
步骤:
1.New Project
2.聚合总工程名字
3.Maven 选版本
4.工程名字
5.字符编码
6.注解生效激活
7.java 编译版本选 8
8.File Type 过滤
2.2 父工程 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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<name>csii_dataservice_bus</name>
<modules>
<module>csii_service_regist5001</module>
<module>csii_service_subscription6001</module>
<module>csii_service_monitoring7001</module>
<module>csii_gateway_gateway4001</module>
<module>csii_data_assets9001</module>
<module>csii_auth_management8001</module>
<module>csii_api_commons</module>
</modules>
<groupId>com.csii.dbus</groupId>
<artifactId>csii_dataservice_bus</artifactId>
<version>1.0-SNAPSHOT</version>
<!--统一管理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>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.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>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>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>
Maven 工程落地细节复习
父工程创建完成执行 mvn:install 将父工程发布到仓库方便子工程继承
2.2Rest 微服务工程构建
2.2.1 Module 模块搭建与工程样图
下边案例用以上 Module。
三、SpringBoot 集成构建
3.1 什么是 SpringBoot
Spring Boot 是由 Pivotal 团队提供的全新框架。Spring Boot 是所有基于 Spring Framework 5.0 开发的项目的起点。Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置文件。
设计目的: 用来简化新 Spring 应用的初始搭建以及开发过程。
从最根本上来讲,Spring Boot 就是一些库的集合,它能够被任意项目的构建系统所使用。它使用 “习惯优于配置” (项目中存在大量的配置,此外还内置一个习惯性的配置)的理念让你的项目快速运行起来。用大佬的话来理解,就是 spring boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 maven 整合了所有的 jar 包,spring boot 整合了所有的框架,总结一下及几点:
(1)为所有 Spring 开发提供一个更快更广泛的入门体验。
(2)零配置。无冗余代码生成和 XML 强制配置,遵循“约定大于配置” 。
(3)集成了大量常用的第三方库的配置, Spring Boot 应用为这些第三方库提供了几乎可以零配置的开箱即用的能力。
(4)提供一系列大型项目常用的非功能性特征,如嵌入式服务器、安全性、度量、运行状况检查、外部化配置等。
(5)Spring Boot 不是 Spring 的替代者,Spring 框架是通过 IOC 机制来管理 Bean 的。Spring Boot 依赖 Spring 框架来管理对 象的依赖。Spring Boot 并不是 Spring 的精简版本,而是为使用 Spring 做好各种产品级准备。
#
3.2 csii_service_regist5001 Moudle 中搭建
3.2.1 导入 POM
<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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
3.2.2 写 yml
server:
port: 5000
spring:
application:
name: nocas-csii-provider
3.2.3 加主启动类
package com.csii.regist;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
public class RegistMain5001 {
public static void main(String[] args) {
SpringApplication.run(RegistMain5001.class,args);
}
}
3.2.4 业务类(省略 service 层+mapper 层+entities 层)
@RestController
@Slf4j
public class RegistController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/getregist/nacos/{id}")
public String getRegist(@PathVariable("id") Integer id) {
return "nacos registry, serverPort: " + serverPort + "\t id" + id;
}
}
3.2.5 测试
启动 csii_service_regist5001
访问: http://localhost:5000/getregist/nacos/1
测试成功
四、Mybatis 持久层框架构建
4.1 什么是 MyBatis ?
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
MyBatis 的功能架构:
我们把 Mybatis 的功能架构分为三层:
- API 接口层:提供给外部使用的接口 API,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
- 数据处理层:负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
- 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
MyBatis 的优缺点
优点:
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个 jar 文件+配置几个 sql 映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis 不会对应用程序或者数据库的现有设计强加任何影响。 sql 写在 xml 里,便于统一管理和优化。通过 sql 基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
- 解除 sql 与程序代码的耦合:通过提供 DAL 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql 和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的 orm 字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供 xml 标签,支持编写动态 sql。
缺点:
- 编写 SQL 语句时工作量很大,尤其是字段多、关联表多时,更是如此。
- SQL 语句依赖于数据库,导致数据库移植性差,不能更换数据库。
- 框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
- 二级缓存机制不佳
4.2 Mybatis 在 csii_service_regist5001 中的使用
4.2.1 导入 POM
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
4.2.2 写 yml
server:
port: 5000
spring:
application:
name: nocas-csii-provider
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://xxx.xxx.xx.xxx:3306/csii_dbus?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
4.2.3 加 mapper 文件+entities 层+dao 层+service 层+controller 层
1.resources 下加+mapper 文件+PaymentMapper.xml
<?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="com.csii.regist.dao.PaymentDao">
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial})
</insert>
<resultMap id="BaseResultMap" type="com.csii.dbus.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="serial" property="serial" jdbcType="VARCHAR"/>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id};
</select>
</mapper>
2.entities
package com.csii.dbus.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
public class Payment {
private Long id;
private String serial;
public Payment() {
}
public Payment(Long id, String serial) {
this.id = id;
this.serial = serial;
}
}
2.dao 层
package com.csii.regist.dao;
import com.csii.dbus.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface PaymentDao {
int create(Payment payment);
Payment getPaymentById(@Param("id") Long id);
}
3.service 层
package com.csii.regist.service;
import com.csii.dbus.entities.Payment;
import org.apache.ibatis.annotations.Param;
public interface PaymentService {
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
}
package com.csii.regist.service.impl;
import com.csii.dbus.entities.Payment;
import com.csii.regist.dao.PaymentDao;
import com.csii.regist.service.PaymentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
private PaymentDao paymentDao;
public int create(Payment payment){
return paymentDao.create(payment);
}
public Payment getPaymentById(Long id){
return paymentDao.getPaymentById(id);
}
}
4.controller 层
package com.csii.regist.controller;
import com.csii.dbus.entities.CommonResult;
import com.csii.dbus.entities.Payment;
import com.csii.regist.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
@RefreshScope//实现配置自动更新
public class RegistController {
@Value("${server.port}")
private String serverPort;
@Resource
private PaymentService paymentService;
//只传给前端CommonResult,不需要前端了解其他的组件
@PostMapping(value = "/getregist/create")
public CommonResult create(Payment payment) {
int result = paymentService.create(payment);
//log.info("*****插入结果:"+result);
if (result > 0) {
return new CommonResult(200, "插入数据成功,serverPort:" + serverPort, result);
} else {
return new CommonResult(444, "插入数据失败", null);
}
}
@GetMapping(value = "/getregist/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
Payment payment = paymentService.getPaymentById(id);
//log.info("*****插入结果:"+payment);
if (payment != null) {
return new CommonResult(200, "查询成功,serverPort:" + serverPort, payment);
} else {
return new CommonResult(444, "没有对应记录,查询ID:" + id, null);
}
}
}
启动+测试成功(步骤省略)
五、 Nacos 服务注册与配置
5.1 什么是 Nacos?
Nacos 是用于构建云本机应用程序的易于使用的动态服务发现,配置和服务管理平台。
5.2 安装并运行 Nacos
5.2.1 环境
本地 Java8+Maven 环境已经 OK
5.2.2 官网下载 Nacos
https://github.com/alibaba/nacos/releases/tag/1.1.4
5.2.3 解压安装包,直接运行 bin 目录下的 startup.cmd
5.2.4 命令运行成功后直接访问 http://localhost:8848/nacos;默认账户密码都是:nacos
5.3 Nacos 作为服务注册中心演示
1.父 POM 加
<!--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>
2.本地模块 POM 加
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3.写 yml
server:
port: 5000
spring:
application:
name: nocas-csii-provider
cloud:
nacos:
discovery:
server-addr: xxx.xxx.xx.xxx:8090 #Nacos服务注册中心地址
4.主启动类加注解:@EnableDiscoveryClient 开启服务注册发现功能
package com.csii.regist;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient//开启服务注册发现功能
public class RegistMain5001 {
public static void main(String[] args) {
SpringApplication.run(RegistMain5001.class,args);
}
}
5.启动+访问测试
6.nacos 控制台证明服务注册成功
5.4Nacos 作为配置中心(基础配置+分类配置)
1.POM 依赖添加
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2.写 yml
bootstrap.yml 编写
server:
port: 5000
spring:
application:
name: nocas-csii-provider
cloud:
nacos:
discovery:
server-addr: 192.168.xxx.xxx:8090 #Nacos服务注册中心地址
config:
server-addr: 192.168.xxx.xxx:8090 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
group: DEFAULT_GROUP
#group: DEV_GROUP
namespace: bd677bce-c69e-473e-8f71-da92c6987215
application.yml 编写
server:
port: 5000
spring:
application:
name: nocas-csii-provider
cloud:
nacos:
discovery:
server-addr: xxx.xxx.xx.xxx:8090 #Nacos服务注册中心地址
config:
server-addr: xxx.xxx.xx.xxx:8090 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
group: DEFAULT_GROUP
#group: DEV_GROUP
namespace: bd677bce-c69e-473e-8f71-da92c6987215
3.controller 添加注解:@RefreshScope//实现配置自动更新
package com.csii.regist.controller;
import com.csii.dbus.entities.CommonResult;
import com.csii.dbus.entities.Payment;
import com.csii.regist.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
@RefreshScope//实现配置自动更新
public class RegistController {
@Value("${server.port}")
private String serverPort;
@Resource
private PaymentService paymentService;
//只传给前端CommonResult,不需要前端了解其他的组件
@PostMapping(value = "/getregist/create")
public CommonResult create(Payment payment) {
int result = paymentService.create(payment);
//log.info("*****插入结果:"+result);
if (result > 0) {
return new CommonResult(200, "插入数据成功,serverPort:" + serverPort, result);
} else {
return new CommonResult(444, "插入数据失败", null);
}
}
@GetMapping(value = "/getregist/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
Payment payment = paymentService.getPaymentById(id);
//log.info("*****插入结果:"+payment);
if (payment != null) {
return new CommonResult(200, "查询成功,serverPort:" + serverPort, payment);
} else {
return new CommonResult(444, "没有对应记录,查询ID:" + id, null);
}
}
}
5.4.1 Nacos 的图形化管理界面
配置管理
命名空间
Namespace+Group+Data ID 三者关系?为什么这么设计?
新建 csii_dbus/test 的 Namespace
配置规则+nacos 配置
配置内容
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://xxx.xxx.xx.xxx:3306/csii_dbus?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
# 开启所有端点允许HTTP查看
management:
endpoints:
web:
exposure:
include: '*'
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.csii.dbus.entities #所有Entity别名类所在包
启动+测试访问+访问成功+测试成功
六、OpenFeign 服务接口调用
6.1 OpenFeign 是什么?
Feign 是一个声明式的 web 服务客户端,让编写 web 服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可。
6.2 OpenFeign 能干什么?
6.3Feign 和 OpenFeign 两者区别
6.4 OpenFeign 使用步骤
接口+注解:微服务调用接口+@FeignClient
1.新建 cloud-consumer-feign-order80
2.写 POM
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- sentinel-datasource-nacos 后续持久化用 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</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.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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
3.写 yml
server:
port: 6001
spring:
application:
name: nocas-csii-subscription
cloud:
nacos:
discovery:
server-addr: 192.168.xxx.xxx:8090 #Nacos服务注册中心地址
4.主启动类加注解 :@EnableFeignClients//启动 Feign 功能
package com.csii.subscription;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient//开启服务注册发现功能
@EnableFeignClients//启动Feign功能
public class SubscriptionMain6001 {
public static void main(String[] args) {
SpringApplication.run(SubscriptionMain6001.class,args);
}
}
5.业务类
业务逻辑接口+@FeignClient 配置调用 provider 服务
package com.csii.subscription.service;
import com.csii.dbus.entities.CommonResult;
import com.csii.dbus.entities.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 = "nocas-csii-provider")
public interface PaymentFeignService {
@GetMapping(value = "/getregist/nacos/{id}")
public String getRegist(@PathVariable("id") Integer id);
}
控制层 Controller
package com.csii.subscription.controller;
import com.csii.dbus.entities.CommonResult;
import com.csii.dbus.entities.Payment;
import com.csii.subscription.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class OrderFeignController {
@Resource
private PaymentFeignService paymentFeignService;
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/sub/getregist/nacos/{id}")
public String getRegist(@PathVariable("id") Integer id){
return paymentFeignService.getRegist(id);
}
}
启动测试成功
6.5 OpenFeign 超时控制
OpenFeign 默认支持 Ribbon
1.YML 文件里需要开启 OpenFeign 客户端超时控制
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
测试:1.先去掉 yml 配置并关闭 5001 服务访问直接报错,
2.加上 yml 配置并开启 5001 服务,效果明显。
6.6 OpenFeign 日志打印功能
1.日志级别
2.配置日志 bean
package com.atguigu.springcloud.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
3.YML 文件里需要开启日志的 Feign 客户端
logging:
level:
com.atguigu.springcloud.service.PaymentFeignService: debug
idea 后台日志查看
七、Sentinel 实现熔断与限流
7.1 什么是 Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
7.2 安装与使用
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
7.2.1 启动 Nacos 成功
7.2.2 5001 服务模块编辑
1.写 POM
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.写 yml
server:
port: 5001
spring:
application:
name: nocas-csii-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
management:
endpoints:
web:
exposure:
include: '*'
- 启动 Sentinel8080
java -jar sentinel-dashboard-1.7.0
4.启动 5001 微服务后查看 sentienl 控制台
7.3 流控规则
7.3.1 基本介绍
其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName)
的时候抛出 FlowException
异常。FlowException
是 BlockException
的子类,您可以捕捉 BlockException
来自定义被限流之后的处理逻辑。
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
7.3.2 流控模式
1.系统默认直接快速失败>>>配置及说明
测试访问>>快速点:http://192.168.xxx.xxx:5001/getregist/get/1
结果:
思考:直接调用默认报错技术上 ok,但是太粗暴了,体验很不好。所以需要类似有一个 fallback 的兜底方法。
2.关联
当关联的资源达到阈值时,就限流自己;当与 A 关联的资源 B 达到阈值后,就限流自己;B 惹事,A 挂了。
配置:
postman 模拟并发密集访问/sub/getregist/nacos/1
postman 里新建多线程集合组:
大批量线程高并发访问 B,导致 A 失效了:
快速在浏览器访问:/getregist/get/1
结果:
3.链路
NodeSelectorSlot
中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root
的虚拟节点,调用链的入口都是这个虚节点的子节点。
一棵典型的调用树如下图所示:
machine-root
/ \
/ \
Entrance1 Entrance2
/ \
/ \
DefaultNode(nodeA) DefaultNode(nodeA)
上图中来自入口 Entrance1
和 Entrance2
的请求都调用到了资源 NodeA
,Sentinel 允许只根据某个入口的统计信息对资源限流。比如我们可以设置 FlowRule.strategy
为 RuleConstant.CHAIN
,同时设置 FlowRule.ref_identity
为 Entrance1
来表示只有从入口 Entrance1
的调用才会记录到 NodeA
的限流统计当中,而不关心经 Entrance2
到来的调用。
注意:为了方便理解上边这一段话大家自行与下两图匹配,不理解可以评论留言。
public static final int FLOW_GRADE_THREAD = 0;
public static final int FLOW_GRADE_QPS = 1;
public static final int DEGRADE_GRADE_RT = 0;
/**
* Degrade by biz exception ratio in the current {@link IntervalProperty#INTERVAL} second(s).
*/
public static final int DEGRADE_GRADE_EXCEPTION_RATIO = 1;
/**
* Degrade by biz exception count in the last 60 seconds.
*/
public static final int DEGRADE_GRADE_EXCEPTION_COUNT = 2;
public static final int AUTHORITY_WHITE = 0;
public static final int AUTHORITY_BLACK = 1;
public static final int STRATEGY_DIRECT = 0;
public static final int STRATEGY_RELATE = 1;
public static final int STRATEGY_CHAIN = 2;
public static final int CONTROL_BEHAVIOR_DEFAULT = 0;
public static final int CONTROL_BEHAVIOR_WARM_UP = 1;
public static final int CONTROL_BEHAVIOR_RATE_LIMITER = 2;
public static final int CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER = 3;
public static final String LIMIT_APP_DEFAULT = "default";
public static final String LIMIT_APP_OTHER = "other";
public static final int DEFAULT_SAMPLE_COUNT = 2;
public static final int DEFAULT_WINDOW_INTERVAL_MS = 1000;
配置:
测试访问:http://192.168.xxx.xxx:4001/getregist/get/1
当 1 秒流量(qps)>3 时,报错:
7.3.3 流控效果
1、快速失败:直接失败
方式是默认的流量控制方式,当 QPS 超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException
。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
直接抛错:(演示略)
Blocked by Sentinel (flow limiting)
2、Warm Up 方式:即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值(预热/冷启动方式)
即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过”冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
默认 coldFactor 为 3,即请求 QPS 从 threshold/3 开始,经预热时长逐渐升至设定的 QPS 阈值。
源码:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
public WarmUpController(double count, int warmUpPeriodInSec) {
construct(count, warmUpPeriodInSec, 3);
}
private void construct(double count, int warmUpPeriodInSec, int coldFactor) {
if (coldFactor <= 1) {
throw new IllegalArgumentException("Cold factor should be larger than 1");
}
this.count = count;
this.coldFactor = coldFactor;
// thresholdPermits = 0.5 * warmupPeriod / stableInterval.
// warningToken = 100;
warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1);
// / maxPermits = thresholdPermits + 2 * warmupPeriod /
// (stableInterval + coldInterval)
// maxToken = 200
maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor));
// slope
// slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits
// - thresholdPermits);
slope = (coldFactor - 1.0) / count / (maxToken - warningToken);
}
Warmup 配置:
测试访问:http://xxx:4001/getregist/get/1
一直狂点刚开始不行,基本 5 秒后就可以正常访问了。
3、排队等待
会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo。
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
配置:匀速排队,阈值必须设置为 QPS
测试访问 1s 通过一个,所以多线程 1000ms 访问 10 次是不行的,已受到限制只能排队。
7.4 降级规则
7.4.1 官网溜一圈
概述
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException
)。
降级策略
我们通常用以下几种方式来衡量资源是否处于稳定的状态:
- 平均响应时间 (
DEGRADE_GRADE_RT
):当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count
,以 ms 为单位),那么在接下的时间窗口(DegradeRule
中的timeWindow
,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException
)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx
来配置。
配置:
代码:
@GetMapping("/testD") public String testD() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.info("testD 测试RT"); return "------testD"; }
jmeter 压测:
- 异常比例 (
DEGRADE_GRADE_EXCEPTION_RATIO
):当资源的每秒请求量 >= N(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule
中的count
)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule
中的timeWindow
,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。
配置:
代码:
@GetMapping("/testD")
public String testD()
{
log.info("testD 测试RT");
int age = 10/0;
return "------testD";
}
jmeter 压测:
- 异常数 (
DEGRADE_GRADE_EXCEPTION_COUNT
):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若timeWindow
小于 60s,则结束熔断状态后仍可能再进入熔断状态。
配置:
代码+ jmeter 压测:同上(异常比例)
7.5 热点 key 限流
7.5.1 Overview
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
7.5.2 承上启下复习 start:@SentinelResource
代码:
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2) {
//int age = 10/0;
return "------testHotKey";
}
//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){
return "------deal_testHotKey,o(╥﹏╥)o";
}
配置 nacos:
注:方法 testHostKey 里面第一个参数只要 QPS 超过每秒 1 次,马上降级处理。如果参数索引为大于 0 的数则是连续多个参数;并不表示下标第几个。前提是不选择【高级选项】
测试成功
7.5.3 参数例外项
sentinel 配置热点:
测试成功
“p1=5”的时候可以访问 200/s
7.6 系统规则
系统自适应限流
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
背景
在开始之前,我们先了解一下系统保护的目的:
- 保证系统不被拖垮
- 在系统稳定的前提下,保持系统的吞吐量
长期以来,系统保护的思路是根据硬指标,即系统的负载 (load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当 load 开始好转,则恢复流量的进入。这个思路给我们带来了不可避免的两个问题:
- load 是一个“结果”,如果根据 load 的情况来调节流量的通过率,那么就始终有延迟性。也就意味着通过率的任何调整,都会过一段时间才能看到效果。当前通过率是使 load 恶化的一个动作,那么也至少要过 1 秒之后才能观测到;同理,如果当前通过率调整是让 load 好转的一个动作,也需要 1 秒之后才能继续调整,这样就浪费了系统的处理能力。所以我们看到的曲线,总是会有抖动。
- 恢复慢。想象一下这样的一个场景(真实),出现了这样一个问题,下游应用不可靠,导致应用 RT 很高,从而 load 到了一个很高的点。过了一段时间之后下游应用恢复了,应用 RT 也相应减少。这个时候,其实应该大幅度增大流量的通过率;但是由于这个时候 load 仍然很高,通过率的恢复仍然不高。
TCP BBR 的思想给了我们一个很大的启发。我们应该根据系统能够处理的请求,和允许进来的请求,来做平衡,而不是根据一个间接的指标(系统 load)来做限流。最终我们追求的目标是 在系统不被拖垮的情况下,提高系统的吞吐率,而不是 load 一定要到低于某个阈值。如果我们还是按照固有的思维,超过特定的 load 就禁止流量进入,系统 load 恢复就放开流量,这样做的结果是无论我们怎么调参数,调比例,都是按照果来调节因,都无法取得良好的效果。
Sentinel 在系统自适应保护的做法是,用 load1 作为启动自适应保护的因子,而允许通过的流量由处理请求的能力,即请求的响应时间以及当前系统正在处理的请求速率来决定。
系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN
),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的
maxQps * minRt
估算得出。设定参考值一般是CPU cores * 2.5
。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
自行测试+简单+不重要
7.7 注解@SentinelResource
注意:注解方式埋点不支持 private 方法。
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource
注解包含以下属性:
value
:资源名称,必需项(不能为空)entryType
:entry 类型,可选项(默认为EntryType.OUT
)blockHandler
/blockHandlerClass
:blockHandler
对应处理BlockException
的函数名称,可选项。blockHandler 函数访问范围需要是public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException
。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定blockHandlerClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
/
:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了
里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:fallback
fallbackClass
exceptionsToIgnore
- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要和原函数一致,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常。 - fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了
里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:defaultFallback
exceptionsToIgnore
- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要为空,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常。 - defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(
DegradeException
)进行处理,不能针对业务异常进行处理。
示例:好用简单不做演示,有问题评论留言
public class TestService {
// 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.
@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
public void test() {
System.out.println("Test");
}
// 原函数
@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
public String hello(long s) {
return String.format("Hello at %d", s);
}
// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
public String helloFallback(long s) {
return String.format("Halooooo %d", s);
}
// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
}
7.8 服务熔断功能
7.8.1Feign 系列
1.写 pom(略)+上边 案例已加过了
2.写 yml(略)+上边 案例已加过了
3.业务类
带@FeignClient 注解的业务接口:
package com.atguigu.springcloud.alibaba.service;
import com.atguigu.springcloud.alibaba.entities.CommonResult;
import com.atguigu.springcloud.alibaba.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "nocas-csii-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
PaymentFallbackService 实现类:
package com.atguigu.springcloud.alibaba.service;
import com.atguigu.springcloud.alibaba.entities.CommonResult;
import com.atguigu.springcloud.alibaba.entities.Payment;
import org.springframework.stereotype.Component;
@Component
public class PaymentFallbackService implements PaymentService
{
@Override
public CommonResult<Payment> paymentSQL(Long id)
{
return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
}
}
Controller:
// OpenFeign
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
return paymentService.paymentSQL(id);
}
测试 6001 调用 5001,此时故意关闭 5001 微服务提供者,看 6001 消费侧自动降级,不会被耗死。
7.8.2 熔断框架比较
7.9 规则持久化
7.9.1 是什么?
一旦我们重启应用,Sentinel 规则将消失,生产环境需要将配置规则进行持久化.
7.9.2 怎么玩?
将限流配置规则持久化进 Nacos 保存,只要刷新 5001 某个 rest 地址,sentinel 控制台的流控规则就能看到,只要 Nacos 里面 的配置不删除,针对 5001 上 Sentinel 上的流控规则持续有效。
7.9.3 步骤:
1.修改 csii_service_regist5001
2.写 POM
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
3.写 YML
server:
port: 5000
spring:
application:
name: nocas-csii-provider
cloud:
nacos:
discovery:
server-addr: 192.168.XXX.XXX:8090 #Nacos服务注册中心地址
config:
server-addr: 192.168.XXX.XXX:8090 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
group: DEFAULT_GROUP
#group: DEV_GROUP
namespace: bd677bce-c69e-473e-8f71-da92c6987215
sentinel:
transport:
dashboard: 192.168.XXX.XXX:8080
port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
datasource:
ds1:
nacos:
server-addr: 192.168.XXX.XXX:8090 #nginx代理后的地址
dataId: nocas-csii-provider
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
namespace: bd677bce-c69e-473e-8f71-da92c6987215
management:
endpoints:
web:
exposure:
include: '*'
feign:
sentinel:
enabled: true # 激活Sentinel对Feign的支持
4.添加 Nacos 业务规则配置
内容解析:
启动 5001 后刷新 sentinel 发现业务规则有了
八、Gateway 新一代网关
8.1 概述简介
2.2.1.RELEASE
要将 Spring Cloud Gateway 包含在您的项目中,请使用启动器,其组 ID 为org.springframework.cloud
,工件 ID 为spring-cloud-starter-gateway
。有关使用当前 Spring Cloud Release Train 设置构建系统的详细信息,请参见Spring Cloud Project 页面。
如果包括启动器,但不希望启用网关,请设置spring.cloud.gateway.enabled=false
。
Spring Cloud Gateway 是基于Spring Boot 2.x,Spring WebFlux和Project Reactor 构建的。结果,当您使用 Spring Cloud Gateway 时,许多您熟悉的同步库(例如,Spring Data 和 Spring Security)和模式可能不适用。如果您不熟悉这些项目,建议您在使用 Spring Cloud Gateway 之前先阅读它们的文档以熟悉一些新概念。
Spring Cloud Gateway 需要 Spring Boot 和 Spring Webflux 提供的 Netty 运行时。它不能在传统的 Servlet 容器中或作为 WAR 构建时使用。
源码架构:
能干嘛?
微服务架构中网关在哪里:
GateWay 模型 :
8.2 三大核心概念
总体:
8.3 Gateway 工作流程
官网总结:
核心逻辑:
路由转发+执行过滤器链
8.4 通过微服务名实现动态路由配置
8.4.1 新建 Module
1.csii_gateway_gateway4001
2.写 POM
<!--新增gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.csii.dbus</groupId>
<artifactId>csii_api_commons</artifactId>
<version>1.0-SNAPSHOT</version>
</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>
<!-- sentinel-datasource-nacos 后续持久化用 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>
<!-- sentinel-datasource-nacos 后续持久化用 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--spring-boot-starter-webflux:反应式Web框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>-->
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</dependency>
3.写 yml(默认情况下 Gateway 会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能)
注意:需要注意的是 uri 的协议为 lb,表示启用 Gateway 的负载均衡功能
server:
port: 4001
spring:
application:
name: csii_gateway
cloud:
nacos:
discovery:
server-addr: 192.168.xxx.xxx:8090 #Nacos服务注册中心地址
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: regist_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://192.168.xxx.xxx:5000 #匹配后提供服务的路由地址
uri: lb://nocas-csii-provider
predicates:
#- Path=/getregist/get/** #断言,路径相匹配的进行路由
- Path=/**
#- id: regist_routh2 #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: lb://cloud-payment-service
# predicates:
# - Path=/payment/lb/** #断言,路径相匹配的进行路由
# 显示详细健康信息
# management.endpoint.health.show-details=always
# 开启所有端点允许HTTP查看
management:
endpoints:
web:
exposure:
include: '*'
lb://serviceName 是 spring cloud gateway 在微服务中自动为我们创建的负载均衡 uri
4.业务类(无)
5.主启动类
package com.csii.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
//@EnableEurekaClient//Eureka
@EnableDiscoveryClient
public class GateWayMain4001 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain4001.class, args);
}
}
测试:http://localhost:4001/getregist/nacos/1 微服务名动态路由成功
8.5 Predicate 的使用
8.5.1 是什么?
8.5.1 常用的 Route Predicate
After 时间 API
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
yml 配置
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
#- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
#- Cookie=username,zhangshuai #并且Cookie是username=zhangshuai才能访问
#- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
#- Host=**.atguigu.com
#- Method=GET
#- Query=username, \d+ #要有参数名称并且是正整数才能路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
总结:
说白了,Predicate 就是为了实现一组匹配规则,让请求过来找到对应的 Route 进行处理。
8.6 Filter 的使用
8.6.1 是什么?
单一常用的 GatewayFilter 与自定义全局 GlobalFilter, 演示比较简单官网搞起来。
九、Seata 处理分布式事务
9.1 Seata 简介
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
9.2 分布式事务问题
1.分布式前
2.分布式之后
问题:一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题.
9.3 Seata-Server 安装
9.4 Seata 之原理简介
十、Sleuth 分布式请求链路追踪
10.1 概述
10.1.1 是什么?
1.Spring Cloud Sleuth 提供了一套完整的服务跟踪的解决方案。
2.在分布式系统中提供追踪解决方案并且兼容支持了 zipkin
10.1.2 为什么会出现这个技术?需要解决哪些问题?
10.1.3 解决
10.2 搭建链路监控步骤
10.2.1 zipkin
1.下载
SpringCloud 从 F 版起已不需要自己构建 Zipkin server 了,只需要调用 jar 包即可
https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
zipkin-server-2.12.9.exec.jar
2.运行控制台
访问路径:http://localhost:9411/zipkin/
完整的调用链路:
简化图:
名词解释:
Trace:类似于树结构的 Span 集合,表示一条调用链路,存在唯一标识。
span:表示调用链路来源,通俗的理解 span 就是一次请求信息。
10.2.2 csii_service_regist5001
1.写 pom
<!--包含了sleuth+zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
2.写 yml
spring:
application:
name: nocas-csii-provider
zipkin:
base-url: http://192.168.xxx.xxx:9411
sleuth:
sampler:
probability: 1 #表示全部采集
测试访问: http://localhost:4001/getregist/nacos/1
打开:http://localhost:9411/zipkin/
备注:谨记教诲:“理论+实战+小总结”。欢迎大家一起探讨,有问题请大家评论留言。
感谢语:感谢尚硅谷无私奉献
更新中…… 去除 iconfinder 上 icon 的水印
原理
利用水印像素点和原图像素点颜色合并的原理,如果拥有加过水印的图片和水印图片,就可以反向推出原图原像素点的颜色;前提是你得拥有他的水印图片