服务端
包版本管理
一个项目运行时,同一个依赖包只能使用一个版本,所以要对子项目中相同的依赖进行统一版本管理。
一般步骤如下:
1.修改根pom
在根pom.xml中引入<dependencyManagement>
标签,用于管理依赖包的版本号。
在 dependencyManagement
标签中声明所依赖的 jar 包的版本号等信息,那么所有子项目再次引入此依赖 jar 包时则无需显式的列出版本号。
<dependencyManagement>
<dependencies>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
和<dependencyManagement>
标签是并列的,<dependencies>
用于引入依赖,dependencyManagement
仅仅用于管理依赖包的版本号。
2.删除子项目pom中的依赖的版本号声明
集成Spring Cloud Alibaba
1.添加一个版本号变量
根pom.xml 中 增加 一个 spring-cloud-alibaba.version
变量定义
<properties>
<!--SpringCloud Alibaba版本-->
<spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version>
</properties>
有时候同一系列(groupId相同)的多个依赖包往往使用同一个版本号,可以用一个变量进行版本号管理。
2.根pom增加依赖管理
<dependencyManagement>
<dependencies>
<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>
<type>pom</type>
和<scope>import</scope>
组合起来,表示从spring-cloud-alibaba-dependencies
的 pom 文件中获取到配置的多个依赖包
3.子项目依赖
修改 application/pom.xml 文件, 添加 两个依赖
<dependencies>
<!-- Spring Cloud Begin -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Spring Cloud End -->
</dependencies>
4.添加注解
修改主启动类:Application
,添加一个注解 @EnableDiscoveryClient
。
@EnableDiscoveryClient
是 Spring Cloud Alibaba 的原生注解,表示开启服务注册发现功能,服务提供者和消费者都要使用此注解。
5.配置 nacos
在application.properties
中添加配置项
#application name
spring.application.name=xxxx-user-app
#Nacos Server ip and port
spring.cloud.nacos.discovery.server-addr=nacos.dev.youkeda.com:8848
Dubbo RPC-提供用户服务
内部系统之间的通讯、调用,用 Dubbo RPC 效率更高。
使用 Dubbo 的 RPC 方式调用服务
1.增加配置
修改application.properties
配置文件
#application name
spring.application.name=xxxx-user-app
#Nacos Server ip and port
spring.cloud.nacos.discovery.server-addr=nacos.dev.youkeda.com:8848
## mount to Spring Cloud
dubbo.registry.address = spring-cloud://localhost
## auto scan
dubbo.scan.base-packages = com.youkeda.comment.service.impl
dubbo.protocol.name = dubbo
dubbo.protocol.port = -1
spring-cloud://localhost
表示Dubbo 注册中心挂载到 Spring Cloud 上,也就是说,使用与 Spring Cloud 相同的配置,并且设置通信协议为 dubbo 。
dubbo.protocol.port
用于设置 Dubbo 端口号,值为 -1 表示系统自动扫描可用的端口(从20880开始递增)。
2.子项目引入依赖包
引入 Dubbo 相关的包:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
3.注册服务
在服务实现类上加上注解@DubboService
,无需指定版本号。
Dubbo RPC-调用用户服务
1.引入 Spring Cloud Alibaba
同上
2.子项目引入依赖
服务实现类引入依赖包:
<!--dubbo-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
3.加注解
CommentServiceImpl
的属性 userService
上加上注解 @DubboReference()
,系统会自动注入一个实例
4.增加配置
修改项目配置文件application.properties
#application name
spring.application.name = xxxx-comment-app
#Nacos Server ip and port
spring.cloud.nacos.discovery.server-addr = nacos.dev.youkeda.com:8848
## mount to Spring Cloud
dubbo.registry.address = spring-cloud://localhost
## auto scan
dubbo.scan.base-packages = com.youkeda.comment.service.impl
dubbo.protocol.name = dubbo
dubbo.protocol.port = -1
## 多个服务用逗号隔开
dubbo.cloud.subscribed-services = xxxx-user-app
dubbo.cloud.subscribed-services
用于设置服务提供方的应用名称,正因为有了这个配置项,所以服务提供者和消费者都不用指定版本号了
Feign调用
Feign-客户端
1.管理新依赖的版本
根pom.xml 中 增加 一个 spring-cloud.version
变量定义
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
根pom 的依赖包管理中,加入对 SpringCloud 的依赖:
<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>
2.开启 Feign 客户端
主启动类 Application 上加一个注解:
@EnableFeignClients(basePackages = "com.youkeda.comment")
@EnableFeignClients
表示开启 Feign 客户端,同时,自动扫描指定的包,系统会自动实现客户端。
3.子项目引入依赖
子项目 comment.service
的 pom.xml 文件中,加一个依赖项:
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
4.增加具体的客户端接口
在子项目 comment.service
中写一个
@FeignClient(name = "xxxx-user-app")
public interface UserFeign {
@GetMapping("/user/findByIds")
public List<User> findByIds(@RequestParam("ids") List<Long> ids);
}
UserFeign
接口相当于一个远程 API 的代理。@FeignClient
注解主要用于配置远程服务的名称,表示将使用那个远程服务的 API。
接口中定义的方法必须要与远程服务的API一致。
UserFeign.findByIds() 方法被调用时,其实是向用户系统发出 /user/findByIds
的http 请求,同时附带上方法参数值。
@Autowired
private UserFeign userFeign;
List<User> userModels = userFeign.findByIds(userIds);
UserFeign
虽然是一个接口,但系统会自动构建一个代理对象实例注入。
总结
构建分布式应用的大概步骤:
- 项目模块化划分
- 依赖包版本管理
- 引入依赖包
- 完善配置(包括各种名称、地址、端口等)
- 使用注解
- 完成具体业务逻辑
Sentinel服务治理
Sentinel 控制台
Sentinel 是 SpringCloudAlibaba 的一个重要组件,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
点此下载Sentinel的jar包,利用终端命令启动Sentinel控制台
java -Dserver.port=9000 -Dcsp.sentinel.dashboard.server=localhost:9000 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar
默认账号密码均为
sentinel
dubbo 调用-整合 Sentinel
无论是服务提供者,还是服务消费者,都需要接入到 Sentinel ,才能在 Sentinel控制台 上看到相关的数据。
1.application/pom.xml 加入依赖
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
</dependency>
2.新增配置项
application.properties 文件新增配置项:
spring.cloud.sentinel.transport.dashboard = localhost:9000
Feign 调用-整合 Sentinel
1.application/pom.xml 加入依赖
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.新增配置项
spring.cloud.sentinel.transport.dashboard = localhost:9000
feign.sentinel.enabled = true
流量控制
配置流控规则

熔断
一个分布式架构的系统,服务的稳定性,除了受到外部流量的影响外,还可能受到下级服务的影响。
当下级服务出现问题时,熔断可以及时停止调用这个服务,防止出现问题。
Sentinel 提供以下几种熔断策略:
-
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
-
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
-
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
熔断器可以在检测到下游服务出错时,不再执行调用,而是及时返回,保护上游服务和应用。
降级
为了改善熔断后带来的影响,可以做一些优化、补偿,这个熔断后的处理,叫作服务降级。
例如,如果某电商网站的商品服务挂了,那么系统熔断后,页面上看不到任何商品,这样体验很不好。这时如果能展示一些预置好的商品,起码页面上有充实的内容,体验得到改善。
除了触发熔断规则引发服务方法调用降级以外, Sentinel 还支持服务方法调用抛异常时自动降级,回调另一个方法。
一、代码改造:
//原方法
public List<User> findByIds(@Param("ids") List<Long> ids);
//原方法的熔断 降级处理方法
public List<User> findByIdsHandler(List<Long> ids, BlockException ex);
//原方法的异常 降级处理方法
public List<User> testFallback(List<Long> ids, Throwable e);
修改服务实现类代码
原方法实现加一个注解:
@SentinelResource(value = "findUserByIds",
blockHandler = "findByIdsHandler",
blockHandlerClass = UserBlockHandler.class,
fallback = "testFallback",
fallbackClass = TestController.class)
public List<User> findByIds(@Param("ids") List<Long> ids) {
}
@SentinelResource
注解就是表示此方法需要做降级处理。
value
表示待处理的资源的名称
blockHandler
表示指定一个熔断后做降级处理的方法名。
blockHandlerClass
指定熔断降级处理方法所在的类。
fallback
指定回调方法名。
fallbackClass
指定回调方法所在的类。
二、配置熔断规则:
按照代码中 @SentinelResource
注解的 value
值配置熔断规则
Nacos动态配置
安装nacos
unzip nacos-server-2.0.3.zip
cd nacos
sh startup.sh -m standalone
浏览器输入http://IP地址:8848
访问
动态配置
修改配置后立即生效就叫做动态配置,Nacos 除了作为注册中心以外,还可以作为配置中心,提供动态配置的功能。
1.新增配置文件
在 application/src/main/resources/
目录下,新建一个 bootstrap.properties
文件。
bootstrap.properties的优先级比application.properties高,系统会先从 bootstrap.properties 文件中读取 Nacos 动态配置服务器的地址。
Bootstrap配置具有较高的优先级,不会被application.properties中相同的配置项覆盖。
Bootstrap属于引导配置,Application属于应用配置。
2.配置内容
在bootstrap.properties 文件中,写入应用名称
和Nacos动态配置服务器地址
这两个配置项:
#application name
spring.application.name = feidian-user
#Nacos Server ip and port
spring.cloud.nacos.config.server-addr = nacos.dev.youkeda.com:8848
application.properties 文件中的
spring.application.name
配置项可以删除掉。
3.代码改造
在实现类中加一个注解:
@DubboService
@RefreshScope
public class UserServiceImpl implements UserService{
@Value("${code.600.message}")
private String message600;
}
@RefreshScope
的作用就是 类中的属性对 Nacos 配置进行监听,一旦 Nacos 重新发布新的属性值则替换原来的值,实现刷新。
4.管理配置项
动态配置的配置项是写在 Nacos 中的,项目从 Nacos 读取配置项的值,注入到变量中。

Data ID 值的格式是
应用名.properties
定制Springboot starter
什么是starter
Springboot 之所以简化了应用的开发和部署,起到重要作用的就是 starter 。
starter 也叫启动器,只需要在 Maven 中引入 starter 依赖,SpringBoot 就能自动扫描到要加载的信息并启动相应的默认配置。
独立于业务代码之外的功能配置模块可以封装成starter,复用的时候只需要将其在 pom 中引用依赖即可,SpringBoot 会为我们自动完成装配。
starter 项目相当于一个模块,跟应用项目的明显区别是,不需要Application
启动类,starter 应该是被其它应用依赖的。
新建 starter 项目
1.创建配置项类
新建一个 config
包,用于放配置相关的类
@ConfigurationProperties(prefix = "user.code")
public class UserProperties {
private String message600;
private String message601;
private String message602;
}
@ConfigurationProperties(prefix = "user.code")
用于为配置项指定一个前缀
在nacos中增加配置(动态配置)or 在 application.properties 中增加配置(静态配置)
在 Nacos 上进行动态配置,优点是修改方便,但缺点是所有集成了 starter 的应用,都需要配置一次
user.code.message600=用户名不能为空
user.code.message601=密码不能为空
user.code.message602=用户名已经存在,无法注册
user.code.enabled=true
user.code.message600
配置项会自动映射到属性 message600
2.配置项控制类
配置项控制类用于控制配置项是否生效
//标记为配置类
@Configuration
//启动配置属性
@EnableConfigurationProperties(UserProperties.class)
// 如果设置了 user.code.enabled=false 则配置不生效,默认只有值为 false 的时候才不生效。其它任意字符,都会让配置生效。
// 如果没有设置 user.code.enabled 则默认配置生效(matchIfMissing = true)
@ConditionalOnProperty(prefix = "user.code", value = "enabled", matchIfMissing = true)
public class UserConfig {
@Autowired
private UserProperties userProperties;
@Autowired
private UserService userService;
@PostConstruct
public void init() {
System.out.println("UserConfig init successful.");
if (userProperties != null) {
System.out.println("user.code.message600" + userProperties.getMessage600());
userService.setUserProperties(userProperties);
}
}
}
配置生效的时候,系统会自动实例化 userProperties 并读取配置映射到属性中;init()
方法也会执行。反之,不生效,则 userProperties 不实例化,值为 null;init()
方法也不会执行。
3.服务接口和实现类提供配置方法
接口 增加 一个 setter 方法:
public interface UserService {
void setUserProperties(UserProperties userProperties);
}
实现类 增加 配置项类依赖:
@Service
public class UserServiceImpl implements UserService {
private UserProperties userProperties;
public void setUserProperties(UserProperties userProperties) {
this.userProperties = userProperties;
}
}
UserProperties userProperties
属性没有加@Autowired
注解让系统自动注入,就是为了在UserConfig
中控制是否注入实例。
4.系统配置文件
在resources
目录下, 新建一个 META-INF
(全大写字母)目录,然后在此新目录下再建一个 spring.factories
文件,文件内容为:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.youkeda.comment.config.UserConfig
只需要指定配置项控制类即可。
5.starter 项目发布
进入项目目录,输入命令mvn clean install
,这个命令会把 starter 项目安装到本地电脑的 maven 仓库中。
本地电脑的 maven 仓库一般在用户根目录下 ~/.m2/repository