【应用】SpringCloud -- Sentinel

news2024/11/17 20:33:22

SpringCloud -- Sentinel

  • Sentinel 概述
  • Sentinel 的安装与启动
  • Sentinel 微服务创建
  • Sentinel 规则
    • 流控规则
    • 热点规则
    • 熔断规则
    • 授权规则
  • Sentinel 其他常用功能
    • 规则持久化
    • 自定义异常

Sentinel 概述

sentinel 官方文档

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。

Sentinel 的安装与启动

下载地址: 官方Github地址

下载解压完成后,进入对应文件夹执行下列指令启动服务

java -jar <对应版本jar包名称>

浏览器访问localhost:8080,即可查看 sentinel 控制面板,登录用户名密码均为 sentinel

Sentinel 微服务创建

在 SpringCloudAlibabaDemo 父项目中(详见Nacos)创建子项目

注:旧版 spring-cloud-alibaba 很多 sentinel 功能无法使用,此处使用的版本如下,需要修改父项目 pom 依赖版本:

  • spring boot 2.3.2.RELEASE

  • spring cloud Hoxton.SR9

  • spring cloud alibaba 2.2.5.RELEASE

子项目中引入依赖

    <dependencies>
        <!-- SpringCloud alibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- SpringCloud alibaba sentinel-datasource-nacos:用于持久化 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

        <!-- SpringCloud alibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!-- SpringBoot整合web组件 -->
        <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>
    </dependencies>

编写配置文件进行设置,将微服务注册进 nacos,配置 Sentinel 服务的端口

server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #Nacos服务注册中心地址
        server-addr: <Your_Nacos_IP>:8848
    sentinel:
      transport:
        #配置Sentinel Dashboard地址
        dashboard: localhost:8080
        #默认8719端口,假如被占用会自动从8719开始一依次+1扫描,知道找到未占用的端口
        port: 8719
      #关闭链路合并
      web-context-unify: false

management:
  endpoints:
    web:
      exposure:
        include: '*'

编写启动类

@SpringBootApplication
public class SentinelStart8401 {

    public static void main(String[] args) {
        SpringApplication.run(SentinelStart8401.class);
    }

}

编写业务层代码

@Service
public class SentinelService {

    @SentinelResource("getMessage")
    public String getMessage(String msg) {
        return "This is " + msg;
    }

}

编写控制类代码用于测试,@SentinelResource注解用于标注接口在 sentinel dashboard 中的名称

@RestController
@RequestMapping("/sentinel")
public class SentinelController {

    @Autowired
    SentinelService sentinelService;

    @GetMapping("/test01")
    public String testSentinel01() throws InterruptedException {
//        Thread.sleep(3000); // 测试限流并发数时阻塞请求的线程
        return sentinelService.getMessage("test01");
    }

    @GetMapping("/test02")
    public String testSentinel02() {
        return sentinelService.getMessage("test02");
    }

    @GetMapping("/test03/{id}")
    public String testSentinel03(@PathVariable("id") String id) {
        // 模拟异常,测试熔断
//        if ("3".equals(id)) {
//            throw new RuntimeException("构造异常产生...");
//        }
        return sentinelService.getMessage("test03>>>id=" + id);
    }

}

启动项目,访问相应的接口,即可在 sentinel 控制面板中查看到相应的服务

注:sentinel 默认是懒加载模式,故理想状态下需要请求一次@SentinelResource(value=“xxx”)注解的接口才能在控制台看到服务

Sentinel 规则

在 sentinel 控制台左侧点击“簇点链路”,即可看到被监控的每一个接口(在本文案例中为 test01 和 test02),每条链路后面都有流控、熔断、热点、授权四个操作按钮,点击对应按钮即可为该接口链路设置对应的规则。

在这里插入图片描述

注:下述所有的规则验证可以通过 Apifox 进行测试(官方网站),亲测好用!

流控规则

点击左侧流控规则菜单,然后点击右上角+新增流控规则即可为链路配置流控规则

在这里插入图片描述

流控规则中各个属性配置的功能如下:

  • 资源名:对应的规则适用的链路名称(即一条访问链路通道);

  • 针对来源:可以配置对调用者进行限流,默认为 default,即不区分来源;

  • 阈值类型

    • QPS:即限制资源的每秒的访问次数;

    • 并发线程数:即限制资源的并发访问量;

  • 单机阈值:为阈值类型设置限流规则生效的上限;

  • 是否集群:是否为集群模式;

  • 流控模式

    • 直接:当前资源达到限流条件时,对自身资源进行限流;

    • 关联:当关联的资源达到限流条件时,对自身资源进行限流;

    • 链路:当前资源达到限流条件时,对配置的入口资源链路进行限流;

  • 流控效果

    • 快速失败:达到设置的阈值之后,新到来的请求将会被立即拒绝并抛出异常(默认);

    • Warm up:预热模式,拒绝超出设置阈值的请求,但阈值是一个动态变化的值。当大量请求接入时该阈值将会存在一个预热的过程,逐步增加到我们所设置的阈值,阈值的初始值根据冷却因子 codeFactor(默认为 3,即初始值为设定阈值/3)确定,可以对预热时长进行设置;

    • 排队等待:当请求数超过阈值时,将会让请求排入队列,并按照我们所设置的阈值进行请求的处理,未被处理的请求将在队列中持续等待,若等待时间超过所设置的超时时间,该请求将被拒绝;

热点规则

上述的流控规则是对某个资源的请求量进行监视并依据设置的阈值进行流量控制,而热点监控则是对资源的某些热点参数进行流量控制。

点击左侧热点规则菜单,然后点击右上角+新增热点限流规则即可为链路配置热点规则

在这里插入图片描述

热点规则中各个属性配置的功能如下:

  • 资源名:进行热点规则控制的资源名称;

  • 参数索引:热点参数的索引,从 0 开始;

  • 单机阈值:对该资源进行整体的流量控制,同流控规则;

  • 统计窗口时长:设置请求数量统计的时间窗口;

  • 参数例外项

    • 参数类型:指定要进行流控的热点参数的参数类型;

    • 参数值/限流阈值:设置热点参数值,并设置该参数的限流阈值,可针对不同的参数值设置多组;

熔断规则

熔断降级主要用于解决服务雪崩问题,通过统计服务器调用的异常比例、慢请求比例,若超出设置的阈值则拦截一切访问请求,直到服务恢复时重新放行。

点击左侧熔断规则菜单,然后点击右上角+新增熔断规则即可为链路配置热点规则

在这里插入图片描述

熔断规则中各个属性配置的功能如下:

  • 资源名:进行熔断规则控制的资源名称;

  • 熔断策略

    • 慢调用比例:当慢调用请求在统计时长内的比例大于设置的比例阈值时,熔断当前资源。业务的响应时间大于设置的最大 RT 则认为是慢调用;

    • 异常比例:当异常请求的比例大于所设置的异常比例时,熔断当前资源;

    • 异常数:当异常请求的数量大于所设置的异常数时,熔断当前资源;

  • 熔断时长:服务熔断的时间;

  • 最小请求数:统计时长内触发熔断的最小请求数量;

  • 统计时长:统计判断熔断参数的时间窗口;

授权规则

授权规则可以根据请求的来源进行限制,通过白名单和黑名单构建授权规则。

点击左侧授权规则菜单,然后点击右上角+新增授权规则即可为链路配置授权规则

在这里插入图片描述

授权规则中各个属性配置的功能如下:

  • 资源名:进行授权规则控制的资源名称;
  • 流控应用:限制调用方的名称,多个调用方用英文逗号隔开;
  • 授权类型
    • 白名单:只允许白名单应用的请求访问相应资源;
    • 黑名单:只拒绝黑名单应用的请求访问相应资源;

对于授权规则,需要开发者自己手写一个类实现com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser类并实现parseOrigin方法,该方法返回请求的来源字符串(即我们设置的流控应用名称)。获取来源的逻辑由开发者自己定义,可以从请求参数或请求头等获得。下述代码获取请求头中的 zzz 属性作为来源。

@Component
public class HeadOriginParser implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        String zzz = httpServletRequest.getHeader("zzz");
        if (StringUtils.isBlank(zzz)){
            zzz = "null";
        }
        return zzz;
    }
}

若 zzz 属性对应的值为我们设置的流控应用名,则根据黑白名单进行相应的处理。

Sentinel 其他常用功能

规则持久化

sentinel 对规则的管理由三种模式:

  • 原始模式:sentinel 的默认模式,将规则保存在内存当中,重启服务会丢失;

  • pull 模式:sentinel dashboard 将规则推送给 sentinel 客户端,客户端将规则配置保存在本地;

  • push 模式:sentinel dashboard 将规则推送到远程控制中心如 nacos,sentinel 客户端监听 nacos 并获取配置变更的推送消息,完成本地配置更新;

下边配置规则持久化到 Nacos:

  1. 首先下载 sentinel 的源码,下载完成后使用编译器打开;

  2. 修改 sentinel-dashboard 模块的 pom 文件,注释掉对应行代码 在这里插入图片描述

  3. 将 test 文件夹下的com.alibaba.csp.sentinel.dashboard.rule.nacos文件夹复制到 main 文件夹下的com.alibaba.csp.sentinel.dashboard.rule文件夹

    • FlowRuleNacosProvider.java:从 nacos 配置中心动态获取流控规则;

    • FlowRuleNacosPublisher.java:上传动态获取流控规则到 nacos 配置中心;

    • NacosConfig.java:nacos 配置;

    • NacosConfigUtils.java:流控规则 nacos 命名以及分组相关配置;

  4. com.alibaba.csp.sentinel.dashboard.rule.nacos路径下创建NacosProperties.java参数配置类,从配置文件中获取参数

    @Component
    @ConfigurationProperties(prefix = "sentinel.nacos")
    public class NacosProperties {
    
        /**
         * nacos地址
         */
        private String serverAddr;
        /**
         * nacos命名空间
         */
        private String namespace;
    
        public String getServerAddr() {
            return serverAddr;
        }
    
        public void setServerAddr(String serverAddr) {
            this.serverAddr = serverAddr;
        }
    
        public String getNamespace() {
            return namespace;
        }
    
        public void setNamespace(String namespace) {
            this.namespace = namespace;
        }
    
    } 
    
  5. 修改NacosConfig.java

    @Configuration
    public class NacosConfig {
    
        @Autowired
        NacosProperties nacosProperties;
    
        @Bean
        public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
            return JSON::toJSONString;
        }
    
        @Bean
        public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
            return s -> JSON.parseArray(s, FlowRuleEntity.class);
        }
    
        @Bean
        public ConfigService nacosConfigService() throws Exception {
            // 修改前
            // return ConfigFactory.createConfigService("localhost");
            // 修改后
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, nacosProperties.getServerAddr());
            properties.put(PropertyKeyConst.NAMESPACE, nacosProperties.getNamespace());
            return ConfigFactory.createConfigService(properties);
        }
    }
    
  6. 修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2文件,指定对应的 Bean

        @Autowired
    //    @Qualifier("flowRuleDefaultProvider")
        @Qualifier("flowRuleNacosProvider")
        private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
        @Autowired
    //    @Qualifier("flowRuleDefaultPublisher")
        @Qualifier("flowRuleNacosPublisher")
        private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
    
  7. 修改webapp/resources/app/scripts/directives/sidebar/sidebar.html文件,找到下述代码进行修改(注释掉的为旧代码)

    <!--          <li ui-sref-active="active" ng-if="!entry.isGateway">-->
    <!--            <a ui-sref="dashboard.flowV1({app: entry.app})">-->
    <!--              <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>-->
    <!--          </li>-->
              <li ui-sref-active="active" ng-if="!entry.isGateway">
                <a ui-sref="dashboard.flow({app: entry.app})">
                  <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
              </li>
    
  8. 修改webapp/resources/app/scripts/controllers/identity.js文件,将FlowServiceV1 改成 FlowServiceV2
    在这里插入图片描述

  9. 还是步骤8描述的文件,修改其中的saveFlowRule方法

        function saveFlowRule() {
          if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {
            return;
          }
          FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {
            if (data.code === 0) {
              flowRuleDialog.close();
              // let url = '/dashboard/flow/' + $scope.app; //修改前
              let url = '/dashboard/v2/flow/' + $scope.app; //修改后
              $location.path(url);
            } else {
              alert('失败:' + data.msg);
            }
          }).error((data, header, config, status) => {
              alert('未知错误');
          });
        }
    
  10. 添加 nacos 配置,启动 sentinel dashboard 项目即可,记得清理一下浏览器缓存

    #nacos配置
    sentinel.nacos.serverAddr=<nacos-server-ip>:8848
    sentinel.nacos.namespace=bafd6448-e6f8-4b8a-8c6e-1b91a15389d4
    
  11. 修改 sentinel 客户端服务配置,添加持久化的配置项,启动项目

    server:
      port: 8401
    spring:
      application:
        name: cloudalibaba-sentinel-service
      cloud:
        nacos:
          discovery:
            #Nacos服务注册中心地址
            server-addr: 47.92.146.85:8848
        sentinel:
          transport:
            #配置Sentinel Dashboard地址
            dashboard: localhost:8080
            #默认8719端口,假如被占用会自动从8719开始一依次+1扫描,知道找到未占用的端口
            port: 8719
          datasource:
            flow:
              nacos:
                server-addr: 47.92.146.85:8848
                namespace: bafd6448-e6f8-4b8a-8c6e-1b91a15389d4
                dataId: ${spring.application.name}-flow-rules
                groupId: SENTINEL_GROUP
                dataType: json
                rule-type: flow
          #关闭链路合并
          web-context-unify: false
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
  12. 点击左侧流控规则菜单,然后点击右上角+新增流控规则即可为链路配置流控规则,完成后可在 nacos 对应的命名空间下看到相应的流控配置;对配置进行修改并发布后,也可以观察到 dashboard 配置动态改变。

自定义异常

一般情况下,发生限流、降级、熔断、权限拦截时,都会抛出异常给调用方,如果需要自定义异常的返回结果则需要实现BlockExceptionHandle接口。

Sentinel 为我们提供了很多基本的异常类:

异常说明
FlowException一般流控异常
ParamFlowException热点参数限流异常
DegradeException降级异常
AuthorityException授权规则异常
SystemBlockException系统规则异常

下述代码可以对不同异常情况下的返回信息以及响应码进行修改

@Component
public class MyException implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        String msg = "未知异常";
        int status = 429;
        if (e instanceof FlowException){
            msg = "请求被限流了";
        } else if (e instanceof ParamFlowException){
            msg = "请求被热点参数限流了";
        } else if (e instanceof DegradeException){
            msg = "请求被降级了";
        } else if (e instanceof AuthorityException){
            msg = "没有权限访问";
            status = 401;
        }
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setStatus(status);
        httpServletResponse.getWriter().println("{message>>>" + msg +",status>>>" + status + "}");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186081.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三十五、Kubernetes1.25中Ingress使用

1、概述在上篇文章中我们详解销售了Ingress是什么、有什么用以及怎么安装&#xff0c;如果没有看的建议先看下&#xff0c;然后再来看这篇文章&#xff0c;上篇文章地址&#xff1a;https://blog.csdn.net/u011837804/article/details/128564606这篇文章我们用实际操作&#xf…

vs鼠标右键“管理NuGet程序包”无响应

其他C#开发问题参考我的&#xff1a;C#基础知识体系框架图&#xff0c;及起对应我发过的博客 问题&#xff1a;下载SharpDX的开源示例代码&#xff0c;鼠标右键无法正常打开NuGet下载依赖&#xff0c;而在本地自己创建的项目中可以正常下载 原因&#xff1a;缺少.sln文件 解决…

定时任务Demo总结(推荐最后一种)

方法一&#xff1a; 线程实现 Runnable 接口 Thread thread new Thread(new Runnable() {Overridepublic void run() {while (true) {SimpleDateFormat sdf new SimpleDateFormat("HH:mm:ss.SSS");String dateStr sdf.format(new Date());System.out.println(&quo…

1.【SpringBoot源码】如何搭建SpringBoot源码调试环境?

目录 一、环境安装要求 二、从github上将SpringBoot源码项目下载下来 三、将SpringBoot源码项目导入到IDEA中 四、编译构建SpringBoot源码项目 五、运行SpringBoot自带的sample 一、环境安装要求 IntelliJ IDEAJDK1.8Maven3.5以上 二、从github上将SpringBoot源码项目下载…

嵌入式开发者的选择——用RTOS,还是不用RTOS?

多年来&#xff0c;随着新技术的引入以及用户和应用程序的不同需求&#xff0c;嵌入式开发人员发生了变化。正如所有嵌入式东西一样&#xff0c;没有两次流程是相同的。然而&#xff0c;我们应对设计挑战的方式有一些共同之处。你总是从系统级别开始&#xff0c;需要从最终应用…

深入 ServiceWorker,消息推送,后台同步,一网打尽

上一章讲到了ServiceWorker的基础使用&#xff0c;但是它的功能不仅仅只有这些&#xff0c;还有很多很多&#xff0c;比如消息推送&#xff0c;后台同步&#xff0c;甚至还有WebRTC&#xff0c;这一章我们来进阶ServiceWorker。 前期准备 在开始之前&#xff0c;我们先做一下…

【分布式微服务】SpringBoot启动原理

一、SpringBoot启动类 SpringBootApplication(exclude DataSourceAutoConfiguration.class) public class PracticeApplication {public static void main(String[] args) {SpringApplication.run(PracticeApplication.class, args);} }二、源码解析 /*** Static helper that …

云原生安全方案介绍

方案介绍 安全狗云原生应用安全解决方案(CNAPP, Cloud-Native Application Protection Platform)采用主机安全Agent和安全容器相结合的技术&#xff0c;既落地了“安全左移”的概念&#xff0c;又能对云原生容器做全面保护&#xff0c;同时能灵活地跟容器编排体系相结合&#x…

map find/count源码分析

参考博客&#xff1a;c map find方法源码解析 众所周知&#xff0c;如果一个数据结构想作为map的key&#xff0c;则必须重载 operator < 否则编译将会报错。但是operator 则是不需要的。那么当查找某个key是否存在的时候&#xff0c;map内部是怎么实现的呢&#xff1f; 结…

1301738-40-8,Bis-maleimides-amine,双马来酰亚胺-氨基

一、基础产品数据&#xff08;Basic Product Data&#xff09;&#xff1a;CAS号&#xff1a;1301738-40-8中文名&#xff1a;三臂双马来酰亚胺-氨基&#xff0c;双马来酰亚胺-氨基&#xff0c;活性交联剂&#xff0c;异官能团三臂交联剂英文名&#xff1a;Bis-Maleimides amin…

【C语言进阶】柔性数组

目录一&#xff1a;柔性数组的特点二&#xff1a;柔性数组的使用三&#xff1a;模拟实现柔性数组在C99中&#xff0c;结构中的最后一个元素允许是未知大小的数组&#xff0c;这就叫做柔性数组成员 以下是柔性数组的两种写法&#xff1a; //写法一&#xff1a; struct S {int n…

Python笔记 · 鸭子类型 / Duck Typing

1. 问题的由来 我初次意识到鸭子类型在存在是在学习Sklearn时&#xff0c;在《Hands-On Machine Learing》一书的第二章&#xff0c;作者提供了一个自定义的Tansformer&#xff0c;使用自定义Transformer的好处在于&#xff1a;你既可以实现自己需要的数据处理逻辑&#xff0c…

【应用】SpringCloud -- Gateway

SpringCloud -- GatewayGateway 网关概述Gateway 的功能Gateway 核心概念Gateway 网关搭建Gateway 的配置及使用predicate 断言filter 过滤器GlobalFilter 全局过滤器Gateway 的几点问题过滤器执行顺序跨域问题Gateway 网关概述 Gateway 的功能 在微服务当中&#xff0c;有很…

PG15 pg_basebackup 代码解析

背景 先前 PG 版本 pg_basebackup 的代码较为复杂&#xff0c;pg_basebackup 在备份过程中做了很多事情&#xff0c;但这部分代码逻辑没有完全解耦&#xff0c;导致一个文件里包含了很多功能的逻辑&#xff0c;影响了代码的可读性和可修改性。 因此&#xff0c;PG 15 针对这部…

模电学习3. 差模、共模干扰与安规电容

模电学习3. 差模、共模干扰与安规电容一、简介1. 安规2. 电源安规标准二、差模干扰与共模干扰1. 共模干扰2. 差模干扰&#xff08;1&#xff09;定义&#xff08;2&#xff09;来源与特性3. 测量4. 消除电源线路中的差模干扰&#xff08;1&#xff09;差模电容5. 消除电源线路中…

TOOM加强网络舆情监控制定处置预案,抓好舆情监控管理?

舆情预案是指根据对未来舆情发展趋势的预测&#xff0c;制定预先准备的舆情处置方案&#xff0c;以应对可能出现的舆情危机。舆情预案包括舆情预警体系、舆情应对策略、应对措施等内容&#xff0c;旨在在舆情危机发生前进行有效的预防和准备&#xff0c;避免舆情危机扩大&#…

95. BERT预训练数据代码

为了预训练之前实现的BERT模型&#xff0c;我们需要以理想的格式生成数据集&#xff0c;以便于两个预训练任务&#xff1a;遮蔽语言模型和下一句预测。一方面&#xff0c;最初的BERT模型是在两个庞大的图书语料库和英语维基百科的合集上预训练的&#xff0c;但它很难吸引这本书…

Zookeeper 教程

Zookeeper 教程Zookeeper 概述分布式应用Zookeeper 架构图ZooKeeper当中的主从与主备&#xff1a;Zookeeper的特性分布式应用的优点分布式应用的挑战什么是Apache ZooKeeper&#xff1f;ZooKeeper的好处Zookeeper 基础ZooKeeper的架构层次命名空间Znode的类型Sessions&#xff…

九龙证券|外资放大招,400亿巨头狂飙!这只翻倍股却突然崩了

昨日超级“开门红”之后&#xff0c;今天上午港股又有多只重磅个股大幅涨超10%。 今日上午&#xff0c;港股整体窄幅震荡&#xff0c;上午收盘&#xff0c;恒生指数微跌0.05%&#xff0c;恒生科技指数涨0.22%。 职业板块方面&#xff0c;媒体、软件服务、电信等涨幅居前&#…

【BLE】ANCS(Apple Notification Center Service)

目录1. 前言1.1 名词解释1.2 ANCS概述2. ANCS的特征2.1 通知源2.2 控制点和数据源2.3 获取通知属性2.4 获取应用属性2.5 执行通知操作2.6 通知操作3. 更多详情参考1. 前言 1.1 名词解释 NP(Notification Provider)&#xff1a;消息提供者&#xff0c;指的是ANCS服务的生产者&…