Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
主要功能
- 服务限流降级:默认支持 Servlet、Feign、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
- 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
- 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
- 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
组件
- Sentinel: 把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
- Nacos: 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
- RocketMQ: 一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
- Dubbo: Apache Dubbo™ 是一款高性能 Java RPC 框架。
- Seata: 阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
- Alibaba Cloud ACM: 一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
- Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于Cron 表达式)任务调度服务。
- Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
1、创建工程(hello-spring-cloud-alibaba)
Spring Cloud 项目都是基于 Spring Boot 进行开发,并且都是使用 Maven 做项目管理工具。在实际开发中,我们一般都会创建一个依赖管理项目作为 Maven 的 Parent 项目使用,这样做可以极大的方便我们对 Jar 包版本的统一管理。
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.ly</groupId>
<artifactId>hello-spring-cloud-alibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.ly</groupId>
<artifactId>hello-spring-cloud-alibaba-dependencies</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<spring-javaformat.version>0.0.12</spring-javaformat.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
<version>${spring-javaformat.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
<systemPropertyVariables>
<java.security.egd>file:/dev/./urandom</java.security.egd>
<java.awt.headless>true</java.awt.headless>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>enforce-rules</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>commons-logging:*:*</exclude>
</excludes>
<searchTransitive>true</searchTransitive>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
<inherited>true</inherited>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestone</id>
<name>Spring Milestone</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshot</id>
<name>Spring Snapshot</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
2、创建依赖管理项目(hello-spring-cloud-alibaba-dependencies)
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>com.ly</groupId>
<artifactId>hello-spring-cloud-alibaba-dependencies</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
<spring-cloud-alibaba.verion>2.1.0.RELEASE</spring-cloud-alibaba.verion>
</properties>
<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>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.verion}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestone</id>
<name>Spring Milestone</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshot</id>
<name>Spring Snapshot</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
3、服务注册中心(nacos)
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以 “服务” 为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
nacos 的关键特性主要包括:
- 服务发现和服务健康监测
- 动态配置服务
- 动态 DNS 服务
- 服务及其元数据管理
nacos安装
nacos在这里使用docker来进行安装,前提要先安装好docker和docker-compose。有其他安装nacos请前往官网进行查看
#clone项目
git clone https://github.com/nacos-group/nacos-docker.git
cd nacos-docker
#安装nacos
docker-compose -f example/standalone-mysql.yaml up -d
安装好之后便可以打开nacos控制台,在浏览器输入 http://192.168.141.132:8848/nacos
- 账号:nacos
- 密码:nacos
4、服务提供者(hello-spring-cloud-alibaba-provider)
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>
<parent>
<groupId>com.ly</groupId>
<artifactId>hello-spring-cloud-alibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>hello-spring-cloud-alibaba-provider</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.ly.spring.cloud.alibaba.provider.ProviderApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
spring:
application:
# 服务名
name: service-provider
cloud:
nacos:
discovery:
# 服务注册中心
server-addr: 192.168.141.132:8848
server:
# 服务端口
port: 8070
management:
# 端点检查(健康检查)
endpoints:
web:
exposure:
include: "*"
Application
@SpringBootApplication
@EnableDiscoveryClient //开启服务注册发现功能
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
Controller
@RestController
public class EchoController {
@GetMapping(value = "/echo/{string}")
public String echo(@PathVariable String string) {
return "Hello Nacos Provider " + string;
}
}
验证是否成功
通过浏览器访问 http://192.168.141.132:8848/nacos Nacos Server 网址
你会发现一个服务已经注册在服务中了,服务名为 service-provider,通过浏览器访问 http://localhost:8070/echo/hi
5、服务消费者(hello-spring-cloud-alibaba-consumer)
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>
<parent>
<groupId>com.ly</groupId>
<artifactId>hello-spring-cloud-alibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>hello-spring-cloud-alibaba-consumer</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.ly.spring.cloud.alibaba.consumer.ConsumerApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
spring:
application:
# 服务名
name: service-consumer
cloud:
nacos:
discovery:
# 服务注册中心
server-addr: 192.168.141.132:8848
server:
# 服务端口
port: 8080
management:
# 端点检查(健康检查)
endpoints:
web:
exposure:
include: "*"
Application
@SpringBootApplication
@EnableDiscoveryClient //开启服务注册发现功能
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
Configuration
在这里先用最原始的http请求,创建一个 Java 配置类,主要作用是为了注入 RestTemplate
@Configuration
public class ConsumerConfiguration {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Controller
@RestController
public class TestController {
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/echo/{str}")
public String echo(@PathVariable("str") String msg){
return restTemplate.getForObject("http://service-provider/echo/"+msg,String.class);
}
}
6、使用 Feign 客户端
Feign 是一个声明式的伪 HTTP 客户端,它使得写 HTTP 客户端变得更简单。使用 Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用 Feign 注解和 JAX-RS 注解。Feign 支持可插拔的编码器和解码器。Feign 默认集成了 Ribbon,Nacos 也很好的兼容了 Feign,默认实现了负载均衡的效果
- Feign 采用的是基于接口的注解
- Feign 整合了 Ribbon
pom文件
在 hello-spring-cloud-alibaba-consumer 项目中增加 org.springframework.cloud:spring-cloud-starter-openfeign 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Application
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //开启 Feign 功能
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
Service
创建业务结构,通过 @FeignClient(“服务名”) 注解来指定调用哪个服务
@FeignClient(value = "service-provider")
public interface EchoService {
@GetMapping(value = "/echo/{string}")
String echo(@PathVariable("string") String string);
}
Controller
@RestController
public class TestEchoController {
@Autowired
private EchoService echoService;
@GetMapping(value = "/feign/echo/{str}")
public String echo(@PathVariable String str) {
return echoService.echo(str);
}
}
7、配置负载均衡
修改 service-provider 服务的端口号如 8071,并启动多个实例,IDEA 中依次点击 Run -> Edit Configurations 并勾选 Allow parallel run 以允许 IDEA 多实例运行项目
- 在 service-provider 项目的 EchoController 中增加测试方法
@Value("${server.port}")
private String port;
@GetMapping(value = "/lb")
public String lb() {
return "Hello Nacos Provider i am from port: " + port;
}
- 在 service-consumer 项目的 EchoService 中增加测试方法
@GetMapping(value = "/lb")
String lb();
- 在 service-consumer 项目的 TestEchoController 中增加测试方法
@GetMapping(value = "/lb")
public String lb() {
return echoService.lb();
}
- 重启服务,通过浏览器访问 http://localhost:8080/lb ,反复刷新浏览器,浏览器交替显示端口号
常见负载均衡策略
- 轮循
- 加权轮循
- 最少连接数
- 最少连接数慢启动时间
- 加权最少连接
- 固定权重
- 源 IP 哈希
8、分布式配置中心
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件
pom文件
我们以 service-consumer 项目为例,修改 pom.xml ,引入 Nacos Config Starter
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
Controller
完成上述步骤后,应用会从 Nacos Config 中获取相应的配置,并添加在 Spring Environment 的 PropertySources 中。这里我们使用 @Value 注解来将对应的配置注入到 TestEchoController 的 username字段,并添加 @RefreshScope 打开动态刷新功能
@RefreshScope
@RestController
public class TestEchoController {
@Autowired
private EchoService echoService;
@Value("${user.name}")
private String username;
@GetMapping(value = "/config")
public String config() {
return echoService.echo(username);
}
}
使用控制台发布配置
spring:
application:
name: service-consumer
cloud:
nacos:
discovery:
# 服务注册中心
server-addr: 192.168.141.132:8848
server:
port: 8080
management:
# 端点检查(健康检查)
endpoints:
web:
exposure:
include: "*"
user:
name: "李四"
修改客户端配置
创建名为 bootstrap.properties 的配置文件并删除之前创建的 application.yml 配置文件
spring.application.name=service-consumer-config
spring.cloud.nacos.config.server-addr=192.168.141.132:8848
spring.cloud.nacos.config.file-extension=yaml
通过浏览器访问,可以通过控制台修改配置文件来查看变化
- 注意: Spring Boot 配置文件的加载顺序,依次为 bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml ,其中 bootstrap.properties 配置为最高优先级
9、服务熔断(Sentinel)
什么是服务雪崩
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以通过 HTTP/RPC 相互调用,在 Spring Cloud 中可以用 RestTemplate + LoadBalanceClient 和 Feign 来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证 100% 可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet 容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的 “雪崩” 效应。为了解决这个问题,业界提出了 熔断器模型。
什么是 Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 控制台
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。另外,鉴权在生产环境中也必不可少。Sentinel 控制台最少应该包含如下功能:
- 查看机器列表以及健康情况: 收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
- 监控 (单机和集群聚合): 通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
- 规则管理和推送: 统一管理推送规则。
- 鉴权: 生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制
获取
通过从 官方 GitHub Release 页面 页面下载最新版本的控制台 JAR 包。
启动
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
验证安装是否成功
通过浏览器访问 http://localhost:8080/#/login
- 账号: sentinel
- 密码: sentinel
Sentinel 客户端接入
pom文件
如果要在您的项目中引入 Sentinel,需要增加 org.springframework.cloud:spring-cloud-starter-alibaba-sentinel
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
修改配置文件
spring:
application:
# 服务名
name: service-consumer
cloud:
nacos:
discovery:
# 服务注册中心
server-addr: 192.168.141.132:8848
config:
# 服务配置中心
server-addr: 192.168.141.132:8848
# 熔断限流
sentinel:
transport:
dashboard: localhost:8888
# 开启 Feign 对 Sentinel 的支持
feign:
sentinel:
enabled: true
server:
# 服务端口
port: 8080
management:
# 端点检查(健康检查)
endpoints:
web:
exposure:
include: "*"
user:
name: "张三"
配置熔断类
编写一个 Feign 接口的实现类并增加 @Component 注解
@Component
public class EchoServiceFallback implements EchoService {
@Override
public String echo(String string) {
return "echo fallback";
}
@Override
public String lb() {
return "lb fallback";
}
}
修改 Feign 接口
@FeignClient(value = "service-provider", fallback = EchoServiceFallback.class)
public interface EchoService {
@GetMapping(value = "/echo/{string}")
String echo(@PathVariable("string") String string);
@GetMapping(value = "/lb")
String lb();
}
测试熔断
启动 service-consumer 服务并停止 service-provider 服务,通过浏览器访问 http://localhost:8080/feign/echo/hi