13. SpringCloudAlibaba 实践笔记:Sentinel 核心功能实践
Sentinel 核心功能介绍
Sentinel 的核心规则包括流控规则、熔断规则、热点规则、授权规则和系统规则,每种规则的配置方式不同。接下来,就详细介绍下 Sentinel 中的每种规则的作用与效果。
流控规则
Sentinel 的流控规则是其流量控制机制的核心部分,主要用于限制请求流量,保护后端服务的稳定性。流控规则可以通过不同的策略来控制请求的数量,避免系统在高并发的情况下崩溃。
Sentinel 流控规则的几个重要方面:
- 流量控制的维度
- QPS(每秒请求数):流控规则可以限制每秒的请求数量。如果请求数量超过设定的 QPS,Sentinel 会触发流控措施,如拒绝请求或延迟请求。
- 线程数:可以限制并发请求的线程数,避免系统资源耗尽。
- 流控规则的配置方式
- 单机规则配置:通过代码或配置文件在本地应用中设置流控规则。
- 动态规则配置:通过控制台或 API 动态更新流控规则,适用于实时调整流量控制策略。
- 流控效果
- 快速:当请求超过设定的限流阈值时,直接拒绝请求,并返回错误信息(如 503 服务不可用)。
- Warm Up(预热策略):在流量达到阈值之前,进行逐步增长,避免突发流量导致的系统崩溃。此策略通过预热时间来平滑流量增长。
- 排队等待:当请求量超过设定的阈值时,系统会将请求排队,直到有空余的线程或资源可以处理请求。排队时间超过最大等待时间后,请求会被拒绝。
- 流控模式
- 直接:默认的流控模式,当接口达到限流条件时,直接开启限流功能。
- 关联:当关联的资源达到限流条件时,开启限流功能。
- 链路:当从某个接口请求过来的资源达到限流条件时,开启限流功能。
直接流控演示
Sentinel 默认就是使用的直接流控模式。可以通过一下方式配置直接流控。
流控配置
以下是针对 http://localhost:8080/order/test_sentinel 接口进行简单的配置,在新增流控规则里阈值类型选择QPS,单机阈值输入3,表示每秒钟的请求量如果超过3,则会触发Sentinel的限流操作。

点击新增按钮后,会为 http://localhost:8080/order/test_sentinel 接口新增一条限流规则。

流控效果测试
在浏览器上快速刷新 http://localhost:8080/order/test_sentinel 接口,当每秒钟的刷新频率超过3次时,会出现如下所示的提示信息。

关联流控演示
订单微服务的 OrderController 有两个接口,/test_sentinel2 和 /test_sentinel,并配置 /test_sentinel 的流控规则关联 /test_sentinel2 ,并设置 /test_sentinel2 的限流阈值为 3,即 /test_sentinel2 的 QPS 超过 3 时,会触发 /test_sentinel 接口的限流功能。
流控配置
在订单微服务的 OrderController 类中新增 /test_sentinel2 接口
@GetMapping(value = "/test_sentinel2")
public String testSentinel2(){
log.info("测试Sentinel2");
return "sentinel2";
}
在浏览器上访问下 http://localhost:8080/order/test_sentinel2 接口,以便使Sentinel检测到 http://localhost:8080/order/test_sentinel2 接口

为 http://localhost:8080/order/test_sentinel 接口配置流控规则的页面,在流控模式中选择关联,在关联资源中输入 /test_sentinel2,并设置单机阈值为 3。

流控效果测试
使用 JMeter 对 http://localhost:8080/order/test_sentinel2 接口进行测试,使其每秒钟的访问次数大于3。

- 在线程组中配置每秒访问4次。
浏览器上访问 http://localhost:8080/order/test_sentinel 接口,发现已经触发了Sentinel的限流功能

链路流控演示
链路流控需要 Spring Cloud Aliabab 的版本号超过 2.2.7.RELEASE。Spring Cloud 的版本号为 Hoxton.SR12
链路流控演示

- 设置 /request_sentienl1 接口和 /request_sentinel2 接口同时调用 SentinelServiceImpl 的 sendMessage() 方法。
直接对 SentinelServiceImpl.sendMessage() 进行流控,但是只对/request_sentinel1进入的流量进行流控,当/request_sentinel1流量达到阈值,只会对/request_sentinel1进入的流量进行限流,/request_sentinel2进入的流量能够正常处理。
演示步骤
订单微服务的新增 SentinelService 接口
public interface SentinelService {
/**
* 测试方法
*/
void sendMessage();
}
新增 SentinelServiceImpl 类,实现 SentinelService 接口:
@Service
public class SentinelServiceImpl implements SentinelService {
@Override
@SentinelResource("sendMessage")
public void sendMessage() {
System.out.println("测试Sentinel的链路流控模式");
}
}
新建 SentinelController 类,用于测试接口。
@Slf4j
@RestController
public class SentinelController {
@Autowired
private SentinelService sentinelService;
@GetMapping(value = "/request_sentinel1")
public String requestSentinel1(){
log.info("测试Sentinel1");
sentinelService.sendMessage();
return "sentinel1";
}
@GetMapping(value = "/request_sentinel2")
public String requestSentinel2(){
log.info("测试Sentinel2");
sentinelService.sendMessage();
return "sentinel2";
}
}
在 application.yml 文件中新增链路配置
spring:
cloud:
sentinel:
web-context-unify: false
在浏览器中分别访问 http://localhost:8080/order/request_sentinel1 和 http://localhost:8080/order/request_sentinel2,查看Sentinel中的簇点链路。

点击新增按钮保存配置,此时,如果是通过http://localhost:8080/order/request_sentinel1接口调用SentinelService#sendMessage()方法时,如果调用的频率每秒钟超过3次,就会触发Sentinel的限流操作,而通过http://localhost:8080/order/request_sentinel2接口调用SentinelService#sendMessage()方法时,则不受限制。
熔断规则
Sentinel 的熔断规则基于熔断器模式实现。熔断器在系统出现故障时,会自动进入熔断状态,阻止对故障资源的访问,一段时间后会自动进行探测,若资源恢复正常,则关闭熔断器,恢复对资源的访问。
Sentinel主要提供了三个熔断策略,分别为:慢调用比例、异常比例和异常数。

- 慢调用比例:当资源的响应时间超过特定阈值的请求比例达到设定的比例值时,触发熔断。
- 例如,如果慢调用比例阈值设置为 0.5,慢调用响应时间阈值设置为 500 毫秒,那么当资源的请求中响应时间超过 500 毫秒的请求比例达到 50% 时,就会触发熔断。
- 异常比例:当资源的异常请求比例达到设定的比例值时,触发熔断。异常情况包括业务异常和运行时异常等。
- 比如,设置异常比例阈值为 0.3,当资源的请求中出现异常的比例达到 30% 时,就会触发熔断。
- 异常数:当资源在一定时间窗口内的异常数量达到设定的阈值时,触发熔断。
- 例如,设置异常数阈值为 50,时间窗口为 10 秒,那么在 10 秒内如果资源的异常请求数量达到 50 次,就会触发熔断。
基于慢调用比例熔断演示
在Sentinel的簇点链路里找到 /request_sentinel2,点击熔断按钮,进入熔断规则配置框,按照如下方式进行配置

- 上述配置表示最大响应时长为 1ms,比例阈值达到 0.1 时,会触发熔断操作,并且熔断的时长为 2,最小请求数未 5,统计的时长为 1000 毫秒。
点击新增按钮后,不断在浏览器中刷新 http://localhost:8080/order/request_sentinel2,会发现触发了 Sentinel 的熔断操作

基于异常比例熔断演示
在订单微服务的 SentinelController 类中定义一个成员变量 count,用来记录访问计数,同时,新增一个 requestSentinel4()方法用来测试基于异常比例的熔断
private int count = 0;
@GetMapping(value = "/request_sentinel4")
@SentinelResource("request_sentinel4")
public String requestSentinel4(){
log.info("测试Sentinel4");
count++;
//模拟异常,比例为50%
if (count % 2 == 0){
throw new RuntimeException("演示基于异常比例熔断");
}
return "sentinel4";
}
在浏览器中访问 http://localhost:8080/order/request_sentinel4,在 Sentinel 的簇点链路里找到/request_sentinel4。

点击熔断按钮,进入熔断规则配置框

不断在浏览器中刷新 http://localhost:8080/order/request_sentinel4,会发现触发了Sentinel的熔断操作

基于异常数熔断演示
在Sentinel的簇点链路里找到/request_sentinel4,点击熔断按钮,进入熔断规则配置框

在1秒钟内最少请求2次,当异常数大于1时,会触发熔断操作,熔断的时长为5秒。
点击保存按钮后,不断在浏览器中刷新http://localhost:8080/order/request_sentinel4,会发现触发了Sentinel的熔断操作

热点规则
Sentinel 的热点规则主要用于对资源访问中的热点参数进行限流,适用于根据不同参数进行流量控制的场景。
热点规则演示
在订单微服务的 SentinelController 类中新增 requestSentinel3()方法,其中会带有一个 String 类型的参数 header 和一个 String 类型的参数 body
@GetMapping(value = "/request_sentinel3")
@SentinelResource("request_sentinel3")
public String requestSentinel3(String header, String body){
log.info("测试Sentinel3");
return "sentinel3";
}
在浏览器中访问 http://localhost:8080/order/request_sentinel3 接口,在 Sentinel 的簇点链路中会显示 /request_sentinel3 接口。

点击热点按钮,对 requestSentinel3()方法的第一个参数 header 进行限流,如果每秒钟访问的次数超过 1 次,则触发限流。

在浏览器中不断访问http://localhost:8080/order/request_sentinel3?header=header,当每秒访问的频率超过 1 次时,会触发 Sentinel 的限流操作。
热点高级选项规则演示
在弹出的热点规则配置框中打开高级选项,在参数类型中选择 java.lang.String,因为在参数索引中输入 0,表示的是对 header 参数限流,而 header 参数是 String 类型的。在参数值里输入 header,也就是为参数名为 header 的参数赋值为字符串 header。限流阈值为 1

点击保存按钮,在浏览器不断刷新http://localhost:8080/order/request_sentinel3?header=header,会触发 Sentinel 的限流操作。
授权规则
授权规则能够根据调用来源判断是否允许执行本次请求。
在某些场景下,需要根据调用接口的来源判断是否允许执行本次请求。此时就可以使用Sentinel提供的授权规则来实现,Sentinel的授权规则能够根据请求的来源判断是否允许本次请求通过。
在Sentinel的授权规则中,提供了 白名单与黑名单 两种授权类型。
授权规则演示
创建 MyRequestOriginParser 类,实现 com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser 接口,用来处理请求的来源。
@Component
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
return httpServletRequest.getParameter("serverName");
}
}
在浏览器中访问 http://localhost:8080/order/request_sentinel4,在 Sentinel 的簇点链路里找到/request_sentinel4。

进入授权规则配置框,流控应用填写的是 test,授权类型为黑名单。这里要结合新建的 MyRequestOriginParser 类进行理解,parseOrigin()方法中直接返回了从 HttpServletRequest 中获取的 serverName 参数,而在上图中的流控应用中输出的是 test,授权类型为黑名单。

访问http://localhost:8080/order/request_sentinel4?serverName=test的话,是处于黑名单的状态,无法访问。
系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、 RT、入口 QPS 、 CPU使用率和线程数五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
- Load(仅对 Linux/Unix-like 机器生效):当系统 load 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
- RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
- CPU使用率:当单台机器上所有入口流量的 CPU使用率达到阈值即触发系统保护
系统规则演示
创建MyUrlBlockHandler类,实现 com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler 接口,用来捕获系统级Sentinel异常
@Component
public class MyUrlBlockHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
String msg = null;
if (e instanceof FlowException) {
msg = "限流了";
} else if (e instanceof DegradeException) {
msg = "降级了";
} else if (e instanceof ParamFlowException) {
msg = "热点参数限流";
} else if (e instanceof SystemBlockException) {
msg = "系统规则(负载/...不满足要求)";
} else if (e instanceof AuthorityException) {
msg = "授权规则不通过";
}
// http状态码
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Type", "application/json;charset=utf-8");
response.setContentType("application/json;charset=utf-8");
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 500);
jsonObject.put("codeMsg", msg);
response.getWriter().write(jsonObject.toJSONString());
}
}
在订单微服务的 SentinelController 类中新增 requestSentinel5()方法
@GetMapping(value = "/request_sentinel5")
@SentinelResource("request_sentinel5")
public String requestSentinel5(){
log.info("测试Sentinel5");
return "sentinel5";
}
在浏览器中访问 http://localhost:8080/order/request_sentinel5,在 Sentinel 的簇点链路里找到 /request_sentinel5。进入流控规则配置框,按照如下方式进行配置。

在浏览器中不断刷新 http://localhost:8080/order/request_sentinel5,会显示如下信息。

自定义异常处理策略
使用Sentinel时,可以使用@SentinelResource注解来指定异常处理策略。
在Sentinel中,指定发生异常时的处理策略只需要使用@SentinelResource注解即可,@SentinelResource注解提供了多个属性,可以用来配置资源的各种行为。
value:资源名称,用于唯一标识一个资源。blockHandler:指定限流时的处理方法。当资源被限流时,Sentinel 会调用这个方法来进行处理。fallback:指定出现业务异常时的降级方法。当资源执行过程中出现业务异常时,Sentinel 会调用这个方法来进行降级处理。exceptionsToTrace:指定需要进行统计的异常类型列表。只有在这个列表中的异常才会被 Sentinel 统计和处理。
@SentinelResource 注解的源码如下:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
String value() default ""; //资源名称
EntryType entryType() default EntryType.OUT; //entry类型,标记流量的方向,取值IN/OUT,默认是OUT
int resourceType() default 0;
//处理BlockException的函数名称,函数要求:
//1. 必须是 public
//2.返回类型 参数与原方法一致
//3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置
//blockHandlerClass ,并指定blockHandlerClass里面的方法。
String blockHandler() default "";
//存放blockHandler的类,对应的处理函数必须static修饰。
Class<?>[] blockHandlerClass() default {};
//用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所
//有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:
//1. 返回类型与原方法一致
//2. 参数类型需要和原方法相匹配
//3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定fallbackClass里面的方法。
String fallback() default "";
//存放fallback的类。对应的处理函数必须static修饰。
String defaultFallback() default "";
//用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常进
//行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:
//1. 返回类型与原方法一致
//2. 方法参数列表为空,或者有一个 Throwable 类型的参数。
//3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定 fallbackClass 里面的方法。
Class<?>[] fallbackClass() default {};
//指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
//需要trace的异常
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
@SentinelResouce 演示一
在订单微服务的 SentinelService 接口中新增 sendMessage2() 方法
String sendMessage2();
在 SentinelServiceImpl 中实现 sendMessage2() 方法,并且定义一个成员变量 count,用来记录请求 sendMessage2()方法的次数,同时定义 25%的异常率。在 sendMessage2() 方法上使用 @SentinelResource 指定了资源的名称、发生 BlockException 时进入的方法和发生异常时进入的方法。
private int count = 0;
@Override
@SentinelResource(
value = "sendMessage2",
blockHandler = "blockHandler",
fallback = "fallback")
public String sendMessage2() {
count ++;
//25%的异常率
if (count % 4 == 0){
throw new RuntimeException("25%的异常率");
}
return "sendMessage6";
}
public String blockHandler(BlockException e){
log.error("限流了:{}", e);
return "限流了";
}
public String fallback(Throwable e){
log.error("异常了:{}", e);
return "异常了";
}
在 SentinelController 类中新增 requestSentinel6() 方法,在方法中调用 SentinelService 接口中的 sendMessage2() 方法
@GetMapping(value = "/request_sentinel6")
public String requestSentinel6(){
log.info("测试Sentinel6");
return sentinelService.sendMessage2();
}
在浏览器中访问 http://localhost:8080/order/request_sentinel6,在 Sentinel 的簇点链路里找到/request_sentinel6。然后对 sendMessage2(一定要是 sendMessage2,选择 /request_sentinel6 将无法进入 blockHandler() 方法。)


点击新增按钮后在浏览器中刷新 http://localhost:8080/order/request_sentinel6,当刷新的频率超过每秒 2 次时,浏览器会显示如下信息。

当刷新的次数是 4 的倍数时,浏览器会显示如下信息。

规则持久化
为什么需要规则持久化
系统重启后恢复规则:如果没有规则持久化,当系统重启时,Sentinel 的规则会丢失,需要重新手动配置。而有了规则持久化,系统可以在启动时自动从外部存储中读取规则并加载,确保系统的流量控制和熔断降级等功能能够立即生效。
规则持久化演示
创建 SentinelPersistenceRule 类,实现 com.alibaba.csp.sentinel.init.InitFunc 接口,并在 SentinelPersistenceRule 类中获取应用的名称,覆写 init() 方法
public class SentinelPersistenceRule implements InitFunc {
//实际可以从外部配置读取
private String appcationName = "server-order";
@Override
public void init() throws Exception {
// 指定流控,降级,系统,授权,热点流控的文件地址。
String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + appcationName;
String flowRulePath = ruleDir + "/flow-rule.json";
String degradeRulePath = ruleDir + "/degrade-rule.json";
String systemRulePath = ruleDir + "/system-rule.json";
String authorityRulePath = ruleDir + "/authority-rule.json";
String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
// 创建文件
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(paramFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser
);
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
// 降级规则
ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
degradeRulePath,
degradeRuleListParser
);
DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
degradeRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
// 系统规则
ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
systemRulePath,
systemRuleListParser
);
SystemRuleManager.register2Property(systemRuleRDS.getProperty());
WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
systemRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
// 授权规则
ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
authorityRulePath,
authorityRuleListParser
);
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
authorityRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
// 热点参数规则
ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
paramFlowRulePath,
paramFlowRuleListParser
);
ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
paramFlowRulePath,
this::encodeJson
);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
}
private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<FlowRule>>() {
}
);
private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<DegradeRule>>() {
}
);
private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<SystemRule>>() {
}
);
private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<AuthorityRule>>() {
}
);
private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<ParamFlowRule>>() {
}
);
private void mkdirIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
private void createFileIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}
在订单微服务的 resources 目录下新建 META-INF 目录,并在 META-INF 目录下新建 services 目录,在 services 目录下新建名称为 com.alibaba.csp.sentinel.init.InitFunc 的文件,并在其文件中添加 SentinelPersistenceRule 类的全限定名
com.shop.order.persistence.SentinelPersistenceRule
在浏览器中访问 http://localhost:8080/order/request_sentinel6,在Sentinel的簇点链路里找到 /request_sentinel6。并配置 \request_sentinel6 对应的流量规则。新增完毕后,在 flow-rule.json 文件中便会有配置的内容。
[
{
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 2,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "/request_sentinel6",
"strategy": 0,
"warmUpPeriodSec": 10
}
]
- flow-rule.json 文件中持久化了对于/request_sentinel6 接口的配置。

浙公网安备 33010602011771号