SpringCloudAlibaba
简介:
- 阿里巴巴提供的微服务开发一站式解决方案,是阿里巴巴开源中间件与 Spring Cloud 体系的融合;
- 依托 Spring Cloud Alibaba,只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统;
组件:
开源组件
-
-
Nacos:发现、配置和管理微服务,相当于 Eureka/Consule + Config + Admin;
-
Dubbo
-
- Java RPC,远程服务调用,相当于 Ribbon + Feign;
- 使用 @DubboTransported 注解可将底层的 Rest 协议无缝切换成 Dubbo RPC 协议,进行 RPC 调用;
-
Sentinel
-
- 限流、降级、熔断等,相当于 Hystrix + Dashboard + Turbine;
- 默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入;
- 支持查看限流降级 Metrics 监控;
-
Seata
-
- 分布式事务,使用 @GlobalTransactional 注解,在微服务中传递事务上下文,可以对业务零侵入地解决分布式事务问题;
-
RocketMQ
-
- 分布式消息系统,相当于 RabbitMQ;
- 仓库地址:https://github.com/apache/rocketmq;
- 各种插件地址:https://github.com/apache/rocketmq-externals;
-
商业组件
-
-
- Alibaba Cloud ACM:配置中心;
- Alibaba Cloud OSS: Object Storage Service,阿里云对象存储服务;
- Alibaba Cloud SchedulerX: 任务调度,相当于 Quartz;
- Alibaba Cloud SMS: 覆盖全球的短信服务;
--------------------------------------
Nacos
介绍:
-
Nacos 致力于帮助您发现、配置和管理微服务;
-
Service 是 Nacos 世界的一等公民,支持几乎所有主流类型的服务:
-
- Kubernetes Service
- gRPC & Dubbo RPC Service
- Spring Cloud RESTful Service
-
特性
-
- 服务发现和服务健康监测;
- 动态服务配置;
- 动态 DNS 服务;
- 服务及元数据管理;
-
地图
-
-
资料
-
- 源码地址:https://github.com/alibaba/nacos.git
- 历史版本:https://github.com/alibaba/nacos/releases
- 文档地址:https://nacos.io/zh-cn/index.html
-
依赖
-
- 64 位 JDK 1.8+;
- Maven 3.2.x+;
-
安装
-
-
Windows
-
- 从历史版本中下载 Zip 包,解压到本地即可使用;
-
运行:
-
Windows
-
-
startup.cmd -m standalone ---- 开启服务,默认为集群方式启动,启动报错,需改为单机版方式 standalone; shutdown.cmd ---- 关闭服务;
-
-
Linux
-
- cd /usr/nacos/bin
- sh startup.sh -m standalone ---- 开启服务,standalone 单机运行模式;
- bash startup.sh -m standalone ---- Ubuntu 开启服务;
- sh shutdown.sh ---- 关闭服务;
-
访问主页:http://127.0.0.1:8848/nacos/
-
应用
-
-
服务管理
-
-
服务注册
-
- curl -X POST “http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=springBoot&ip=127.0.0.1&port=80”
-
服务发现
-
- curl -X GET “http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=springBoot”
-
-
配置管理
-
-
发布配置
-
- curl -X POST “http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld”
-
获取配置
-
- curl -X GET “http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test”
-
-
命名空间
-
- 用于区分不同的部署环境,实现隔离,不同的命名空间逻辑上是隔离的,其主要作用是区分服务使用的范围,比如开发、测试、生产、灰度四个命名空间来互相隔离;
-
--------------------------------------
Parent Project
父工程:
-
-
- Spring Boot 版本: 2.6.3;
- Spring Cloud 版本:Spring Cloud 2021.0.1;
- Spring Cloud Alibaba 版本:2021.0.1.0;
-
搭建 Maven Java 项目
-
- 参考 Tool_IDE 创建 java_spring_cloud_alibaba;
- 删除 src,项目右键 properties ---- Java Build Path ---- Source,删除 Source 包;
-
Pom 依赖
-
-
指定打包类型:
<packaging>pom</packaging>
-
-
父工程pom.xml:
<?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.sfac</groupId>
<artifactId>spring_cloud_alibaba</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring_cloud_alibaba_entity</module>
</modules>
<name>spring_cloud_alibaba</name>
<packaging>pom</packaging>
<url>http://www.example.com</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.boot.version>2.6.3</spring.boot.version>
<spring.cloud.version>2021.0.1</spring.cloud.version>
<spring.cloud.alibaba.version>2021.0.1.0</spring.cloud.alibaba.version>
</properties>
<!-- 父工程版本管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- spring web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Apache 工具组件 -->
<!-- 字符串、对象、日期等工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 摘要运算、编码 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- IO -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- spring test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
随即clean,install打包
注意打包时的坑:
Unable to find a single main class
spring-boot-maven-plugin 这个插件,会去找程序入口,先去查找带 @SpringBootApplication 注解的类,项目中没有,就去查找 main 方法,当项目中有一个以上的 main 方法的时候,这货就懵逼了,到底和哪个妹子在一起呢?于是抛出了上面的错误;
解决方案一
在 spring-boot-maven-plugin 插件中配置 mainClass 属性,指定入口类;
<configuration>
<mainClass>***</mainClass>
</configuration>
解决方案二
如果打包的项目是继承至父类,那么方案一不适合,需要配置 start-class 属性
在父工程中:
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<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>
<start-class>com.sfac.App</start-class>
</properties>
方案三
项目里只留一个 main 方法,其余的干掉,并不推荐这么做;
在子模块中:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
在父工程中创建EntityModule的Maven项目
将微服务中使用的entity抽取出去,和SpringCloud差不多
子Module的pom.xml:
<?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.sfac</groupId>
<artifactId>spring_cloud_alibaba_entity</artifactId>
<version>1.0-SNAPSHOT</version>
<name>spring_cloud_alibaba_entity</name>
<url>http://www.example.com</url>
<packaging>jar</packaging>
<parent>
<groupId>com.sfac</groupId>
<artifactId>spring_cloud_alibaba</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<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>
<start-class>com.sfac.App</start-class>
</properties>
<dependencies>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- javax.persistence -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
</project>
随即clean,install打包
在父工程中创建TestMoudle的SpringBoot项目
pom.xml:
<?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.sfac</groupId>
<artifactId>spring_cloud_alibaba_entity</artifactId>
<version>1.0-SNAPSHOT</version>
<name>spring_cloud_alibaba_entity</name>
<url>http://www.example.com</url>
<packaging>jar</packaging>
<parent>
<groupId>com.sfac</groupId>
<artifactId>spring_cloud_alibaba</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<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>
<start-class>com.sfac.App</start-class>
</properties>
<dependencies>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- javax.persistence -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
</project>
在启动类上添加注解@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudAlibabaTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaTestApplication.class, args);
}
}
随即在父工程进行clean,install打包
在父工程中创建AccountMoudle的SpringBoot项目
pom.xml:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.sfac</groupId>
<artifactId>spring_cloud_alibaba</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.sfac</groupId>
<artifactId>spring_cloud_alibaba_account</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_cloud_alibaba_account</name>
<packaging>jar</packaging>
<description>spring_cloud_alibaba_account</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--pagehelper-spring-boot-starter -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<!-- mybatis-spring-boot-starter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- entity -->
<dependency>
<groupId>com.sfac</groupId>
<artifactId>spring_cloud_alibaba_entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在启动类上添加注解@EnableDiscoveryClient
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudAlibabaAccountApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaAccountApplication.class, args);
}
}
进行拆解后,随即在父工程进行clean,install打包
----------------------------------------
Nacos配置中心
说明:
- Nacos 除了微服务的注册与发现之外,还将配置中心整合在了一起,我们可以将整个架构体系内的所有配置都集中在 Nacos 中存储;
- 我们将改造微服务 Service Module Test 和 Service Module Account;
在test模块引入依赖:
<!-- bootstrap -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- nacos-config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
然后创建配置文件bootstrap.properties,并将之前的application.properties改名applicationBackup.properties
保留test的properties配置,抽取出去公共的配置
# for server
server.port=8762
# for bootstrap
spring.cloud.bootstrap.enabled=true
# for application
spring.application.name=spring-cloud-alibaba-test
spring.profiles.active=dev
# for nacos discovery
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# for nacos config
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.file-extension=properties
Nacos 主页创建微服务配置(详情见老师笔记)
-
http://127.0.0.1:8848/nacos
-
配置服务 ---- 配置列表 ---- 新建配置;
-
Data ID:prefix−{prefix}-prefix−{spring.profiles.active}.${file-extension}
-
- ${prefix} ---- 默认 spring.application.name,也可通过 spring.cloud.nacos.config.prefix 配置;
- spring.profiles.active−−−−环境,通过spring.profiles.active配置,若不配置,则没有−{spring.profiles.active} ---- 环境,通过 spring.profiles.active 配置,若不配置,则没有 -spring.profiles.active−−−−环境,通过spring.profiles.active配置,若不配置,则没有−{spring.profiles.active} 这部分;
- ${file-extension} ---- 文件后缀,通过 spring.cloud.nacos.config.file-extension 配置;
-
-
-
- Group:暂时使用默认;
-
-
-
-
-----------------------------------------
服务调用
说明:
-
Service Module Account 中的 User 接口调用 Service Module Test 中的 City 接口,实现微服务接口调用;
-
注册中心无论使用 Eureka Server 还是使用 Nacos,对应用层是没有影响的,这归功于 Spring Cloud Common 的封装,它在服务的注册与发现、客户端负载均衡等方面做了很好的抽象,具体实现由各厂商的中间件完成,应用层依赖于这些接口,不会关注具体的实现,就好比于 JDBC 接口,具体由什么数据库实现,应用层并不关心;
使用OpenFeign
account引入依赖
引入依赖:
<!-- loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在配置类上添加注解
@EnableFeignClients
import com.sfac.springCloudAlibabaAccount.config.CustomLoadBalancerConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients//这个
@LoadBalancerClient(name = "spring-cloud-alibaba-test",
configuration = CustomLoadBalancerConfig.class)
public class SpringCloudAlibabaAccountApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaAccountApplication.class, args);
}
}
在service包下创建OpenFeignTest接口
用于调用 Service Module Test 微服务接口
import com.sfac.test.City;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "spring-cloud-alibaba-test")//注意这个注解,调用谁写谁
public interface OpenFeignTest {
/**
* 127.0.0.1/api/city/2234 -----get
*/
@GetMapping("/api/test/city/{cityId}")
public City getCityByCityId(@PathVariable int cityId);
}
最后再serviceImpl修改相关代码即可
//首先引入这个接口
@Autowired
private OpenFeignTest openFeignTest;
@Override
public UserVo getUserVoById(int userId, int cityId) {
UserVo userVo = new UserVo();
User user = userDao.selectUserById(userId);
BeanUtils.copyProperties(user, userVo);
City city = openFeignTest.getCityByCityId(cityId);//调用即可
userVo.setCity(city);
return userVo;
}
----------------------------------
负载均衡Loadbalancer
在account模块引入依赖
<!-- loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
这样配置后,访问策略是轮询
可以添加配置类,改为随机访问
CustomLoadBalancerConfig:
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/**
* 构造一个随机策略的负载均衡器
*/
public class CustomLoadBalancerConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 在此也可返回自定义负载均衡器
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
在启动类上加入注解@LoadBalancerClient
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@LoadBalancerClient(name = "spring-cloud-alibaba-test",
configuration = CustomLoadBalancerConfig.class)//这个,如果是轮询,把后面的去掉
public class SpringCloudAlibabaAccountApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaAccountApplication.class, args);
}
}
最后打包两个不同端口号test模块,java -jar 命令运行之,然后测试即可
-----------------------------------------
GateWay网关
创建一个SpringBoot子模块
spring_cloud_alibaba_gateway
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.sfac</groupId>
<artifactId>spring_cloud_alibaba</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.hqyj</groupId>
<artifactId>spring_cloud_alibaba_gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_cloud_alibaba_gateway</name>
<description>spring_cloud_alibaba_gateway</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
</properties>
<dependencies>
<!-- gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties:
# for server
server.port=8888
# for application
spring.application.name=spring-cloud-alibaba-gateway
# for nacos discovery
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# for gateway route
spring.cloud.gateway.routes[0].id=testServer
spring.cloud.gateway.routes[0].uri=lb://spring-cloud-alibaba-test
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/test/**
spring.cloud.gateway.routes[1].id=accountServer
spring.cloud.gateway.routes[1].uri=lb://spring-cloud-alibaba-account
spring.cloud.gateway.routes[1].predicates[0]=Path=/api/account/**
spring.main.web-application-type=reactive
配置跨域的配置类:
CorsAutoConfiguration:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Configuration
public class CorsAutoConfiguration {
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = ctx.getResponse();
HttpHeaders headers = response.getHeaders();
headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, request.getHeaders().getOrigin());
// 允许的 header
//允许跨域访问
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
"X-Token,Token,Authorization,x-requested-with,Content-Type");
//允许的请求方式
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "PUT,POST, GET, OPTIONS, DELETE");
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
}
在启动类上添加注解:
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//这个
public class SpringCloudAlibabaGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaGatewayApplication.class, args);
}
}
同样clean,install打包测试:
http://127.0.0.1:8888/api/account/userVo/1/1890
和桌面上的index.html,发起ajax进行测试即可
--------------------------------------
Sentinel:监控、熔断、流控等
简介
-
主要特性
-
-
两大部分
-
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持;
- 控制台(Dashboard):基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器;
-
文档地址:https://github.com/alibaba/Sentinel/wiki/介绍;
-
仓库地址:https://github.com/alibaba/Sentinel;
安装与运行
-
历史版本:https://github.com/alibaba/Sentinel/releases;
-
下载到本地,CMD 运行
-
- 默认 8080 端口运行,可能与其它软件冲突,故选择 9090 端口;
- java -jar sentinel-dashboard-1.8.3.jar --server.port=9090
-
访问:127.0.0.1:9090 sentinel | sentinel;
-
导入依赖:
在test和account模块添加依赖
<!-- actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- alibaba-sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
在Nacos的配置中心修改配置:
test配置了配置中心,在配置中心修改
account没有,则在application.properties修改
# for sentinel
spring.cloud.sentinel.transport.dashboard=127.0.0.1:9090
spring.cloud.sentinel.transport.port=8719
management.endpoints.web.exposure.include=*
然后进行clean,install打包测试
测试各个接口,然后在Sentinel主页进行查看
http://127.0.0.1:9090/
实时监控
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r9BpqgXw-1665109300334)(C:\Users\张大帅哥\AppData\Roaming\Typora\typora-user-images\image-20220929161510412.png)]
簇点链路
添加流控、熔断规则
详情见老师笔记
流量控制
-
说明
-
- 流量控制(Flow Control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性;
-
Service Module Account 实现
-
-
对应微服务或接口上添加流控;
-
-
/api/user/{id},当每秒请求的次数超过阈值,那么会直接返回失败的信息;
-
浏览器快速刷新 http://127.0.0.1:8004/api/user/1,得到被流控的结果;
-
-
熔断降级
-
说明
-
-
服务调用中,如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用方的响应时间也会变长,线程会产生堆积,最终可能耗尽线程池,服务本身也变得不可用,Sentinel 在服务出现不稳定情况时,会对让请求快速失败,避免影响到其他资源而导致级联错误,当资源被降级后,在接下来的时间窗口内,对该资源的调用都自动熔断;
-
降级策略
-
-
慢调用比例
-
- 在 1000ms 内请求数超过 5 个,且平均响应时间超过 300ms 的请求的比例(超过 300ms 的请求占总请求的比例)大于 0.4,则该资源自动熔断 10s,10s 后根据下一个请求判断是否再次被熔断;
-
异常比例
-
- 在 1000ms 内请求数超过 5 个,且异常的比例(异常的请求占总请求的比例)大于 0.8,则该资源自动熔断 15s,15s 后根据下一个请求判断是否再次被熔断;
-
异常数
-
- 在 1000ms 内请求数超过 5 个,且异常的请求超过 6 个,那么该资源将自动熔断 20s,20s 后根据下一个请求判断是否再次被熔断;
-
-
实现:
在test的CityServiceImpl里面添加下面的代码:
@Override
@Transactional
public City getCityByCityId(int cityId) {
System.out.println("===============" + port + "===============");
if (cityId <= 0){//这里
int i = 1/0;
}
return cityDao.getCityByCityId(cityId);
}
然后在account的调用上面方法的方法上添加注解:@SentinelResource
@Override
@SentinelResource(value = "getCityByCityId",//指定一个标识
blockHandler = "blockHandler",//指定流控的方法名称
fallback = "fallback" )//指定熔断的方法名称
public UserVo getUserVoById(int userId, int cityId) {
...
}
然后写流控的熔断的方法,返回值必须和该方法一样:
@Override
@SentinelResource(value = "getCityByCityId",//指定一个标识
blockHandler = "blockHandler",//指定流控的方法名称
fallback = "fallback" )//指定熔断的方法名称
public UserVo getUserVoById(int userId, int cityId) {
UserVo userVo = new UserVo();
User user = userDao.selectUserById(userId);
BeanUtils.copyProperties(user, userVo);
City city = openFeignTest.getCityByCityId(cityId);
userVo.setCity(city);
return userVo;
}
@Override
@Transactional
public Result<UserVo> updateUserVo(UserVo userVo) {
openFeignTest.updateCity(userVo.getCity());
if (userVo.getUserId() < 0){
int i = 1/0;
}
User user = new User();
BeanUtils.copyProperties(userVo,user);
userDao.updateUser(user);
return new Result<>(Result.ResultStatus.SUCCESS.code,
"Update success",
userVo);
}
/**
* 流控或降级异常处理逻辑
*/
public UserVo blockHandler(int userId, int cityId, BlockException exception) {
if (exception instanceof FlowException) {//流控的exception
System.out.println("您被限流了.");
} else if (exception instanceof DegradeException) {//降级的exception
System.out.println("您被降级了.");
} else {
System.out.println("City 接口不可用.");
}
return new UserVo();
}
/**
* 非流控或降级异常处理逻辑(熔断)
*/
public UserVo fallback(int userId, int cityId) {
System.out.println("发生了非流控、降级异常,被熔断了.");
return new UserVo();
}
测试:
http://127.0.0.1:8888/api/account/userVo/-1/1890
------------------------------
Seata分布式事务
简介:
-
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务;
-
资料地址
-
- Seata 项目 Github 地址:https://github.com/seata/seata
- Seata 中间件下载地址:https://github.com/seata/seata/releases
- Seata 中文官方文档:http://seata.io/zh-cn/
- Seata 官方示例:https://github.com/seata/seata-samples
术语:
-
- XID(Transaction ID):全局唯一的事务 ID;
- TC (Transaction Coordinator):事务协调者,维护全局和分支事务的状态,驱动全局事务提交或回滚;
- TM (Transaction Manager):事务管理器,定义全局事务的范围,开始全局事务、提交或回滚全局事务;
- RM (Resource Manager):资源管理器,管理分支事务处理的资源,与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚;
-
处理过程
-
- TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
- XID 在微服务调用链路的上下文中传播;
- RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
- TM 向 TC 发起针 XID 的全局提交或回滚决议;
- TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求;