手把手教你改造 Sentinel Dashboard 实现配置持久化

news/2024/10/4 7:25:15

一. 概述

Sentinel客户端默认情况下接收到 Dashboard 推送的规则配置后,可以实时生效。但是有一个致命缺陷,Dashboard和业务服务并没有持久化这些配置,当业务服务重启后,这些规则配置将全部丢失。

Sentinel 提供两种方式修改规则:

  • 通过 API 直接修改 (loadRules)
  • 通过 DataSource 适配不同数据源修改

通过 API 修改比较直观,可以通过以下几个 API 修改不同的规则:

FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控规则
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降级规则

手动修改规则(硬编码方式)一般仅用于测试和演示,生产上一般通过动态规则源的方式来动态管理规则。

上述 loadRules() 方法只接受内存态的规则对象,但更多时候规则存储在文件、数据库或者配置中心当中。DataSource 接口给我们提供了对接任意配置源的能力。相比直接通过 API 修改规则,实现 DataSource 接口是更加可靠的做法。

我们推荐通过控制台设置规则后将规则推送到统一的规则中心,客户端实现 ReadableDataSource 接口端监听规则中心实时获取变更,流程如下:

DataSource 扩展常见的实现方式有:

  • 拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件,甚至是 VCS 等。这样做的方式是简单,缺点是无法及时获取变更;
  • 推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。

Sentinel 目前支持以下数据源扩展:

  • Pull-based: 动态文件数据源、Consul, Eureka
  • Push-based: ZooKeeper, Redis, Nacos, Apollo, etcd

Sentinel开源版在Push模式下只实现了 路径2,也就是Nacos到业务服务之间的规则同步;路径1 Dashboard配置修改写入Nacos并没有实现,在后文中我们会介绍如何修改 Dashboard 源码完成配置的写入。

二. 从 Nacos 加载规则配置

首先,我们先来看看如何使用Sentinel官方提供的 sentinel-datasource-nacos 从Nacos加载规则配置。

第一步:引入依赖

<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><version>1.8.6</version>
</dependency>

第二步:配置规则自动加载

package cn.bigcoder.demo.sentinel.sentineldemo.demos.config;import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;
import java.util.List;
import java.util.Properties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;@Component
public class SentinelRuleConfiguration implements ApplicationListener<ContextRefreshedEvent> {private static final String remoteAddress = "10.10.10.12:8848";// nacos groupprivate static final String groupId = "SENTINEL_GROUP";// nacos dataIdprivate static final String dataId = "sentinel-demo";private static final String NACOS_NAMESPACE_ID = "SENTINEL";@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {Properties properties = new Properties();properties.put(PropertyKeyConst.SERVER_ADDR, remoteAddress);
//        properties.put(PropertyKeyConst.NAMESPACE, NACOS_NAMESPACE_ID);ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(properties, groupId,dataId,source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));FlowRuleManager.register2Property(flowRuleDataSource.getProperty());}
}

第三步:往Nacos中写入配置

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import java.util.Properties;public class NacosConfigSender {public static void main(String[] args) throws Exception {final String groupId = "SENTINEL_GROUP";final String dataId = "sentinel-demo-flow-rules";// 创建ConfigService实例Properties properties = new Properties();properties.put(PropertyKeyConst.SERVER_ADDR, "10.10.10.12:8848");// 指定namespaceproperties.put(PropertyKeyConst.NAMESPACE, "SENTINEL");final String rule = "[\n"+ "  {\n"+ "    \"resource\": \"GET:/user/getById\",\n"+ "    \"controlBehavior\": 0,\n"+ "    \"count\": 1,\n"+ "    \"grade\": 1,\n"+ "    \"limitApp\": \"default\",\n"+ "    \"strategy\": 0\n"+ "  }\n"+ "]";ConfigService configService = NacosFactory.createConfigService(properties);System.out.println(configService.publishConfig(dataId, groupId, rule));}
}

执行完后,Nacos中就会出现对应的配置:

第四步:启动项目,验证规则配置是否生效

访问 http://127.0.0.1:8719/getParamRules?type=flow 即可看到业务服务内存中加载到的规则配置

并发执行 /user/getById 接口,可以发现接口被成功限流,1s内的10次请求,只有一次成功。

三. Dashboard存在的问题

使用此方案虽然解决了配置规则配置持久化的问题,但是在Dashboard上修改配置仍然是通过业务服务暴露的接口进行的配置同步。业务服务既可以接收 Nacos 配置变更,又可以接收Dashboard的配置变更,控制台的变更的配置并没有同步到Nacos,应用重启后Sentinel控制台修改的配置仍然会全部丢失:

一个理想的情况是Sentinel控制台规则配置读取至 Nacos 而不是内存,在控制台修改/新增的配置写入Nacos,当Nacos配置发生变更时,配置进而自动同步至业务服务:

当然存储媒介可以根据情况选用别的组件:ZooKeeper, Redis, Apollo, etcd

很可惜的是,阿里官方开源的Sentinel控制台并没有实现将规则配置写入其他中间件的能力。它默认只支持将配置实时推送至业务服务,所以我们在生产环境中想要使用 Sentinel Dashboard 需要自行修改其源码,将其配置同步逻辑改为写入我们所需要的中间件中。

四. 修改Sentinel Dashboard源码

4.1 准备工作

首先通过git拉取下载源码,导入idea工程:

https://github.com/alibaba/Sentinel

本文源码修改基于 Sentinel 1.8.8 版本,所有修改的源码可参考:

https://github.com/bigcoder84/Sentinel

4.1.1 流控规则接口

Sentinel Dashboard的流控规则下的所有操作,都会调用Sentinel-Dashboard源码中的FlowControllerV1类,这个类中包含流控规则本地化的CRUD操作:

在com.alibaba.csp.sentinel.dashboard.controller.v2包下存在一个FlowControllerV2;类,这个类同样提供流控规则的CURD,与V1不同的是,它可以实现指定数据源的规则拉取和发布

@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);@Autowiredprivate InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;@Autowired@Qualifier("flowRuleNacosProvider")private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;@Autowired@Qualifier("flowRuleNacosPublisher")private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;//....
}

官方说明:从 Sentinel 1.4.0 开始,我们抽取出了接口用于向远程配置中心推送规则以及拉取规则。

  • DynamicRuleProvider<T>: 拉取规则
  • DynamicRulePublisher<T>: 推送规则

以 Nacos 为例,若希望使用 Nacos 作为动态规则配置中心,用户可以提取出相关的类,然后只需在 FlowControllerV2 中指定对应的 bean 即可开启 Nacos 适配

FlowControllerV2依赖两个非常重要的类

  • DynamicRuleProvider:动态规则的拉取,从指定数据源中获取控制后在Sentinel Dashboard中展示。
  • DynamicRulePublisher:动态规则发布,将在Sentinel Dashboard中修改的规则同步到指定数据源中。

只需要扩展这两个类,然后集成Nacos来实现Sentinel Dashboard规则同步

4.1.2 需要改造的页面入口

簇点链路:

由于该页面的“流控”配置是对单节点进行配置的,所以理论上该页面的URL是不用改的

流控规则:

上述位置我们都需要改造对应前端代码,使之调用的接口更改为我们新的V2接口上。

4.2 源码改造

4.2.1 在pom.xml文件中去掉test scope注释

这是因为官方提供的Nacos持久化用例都是在test目录下,所以scope需要去除test,需要sentinel-datasource-nacos包的支持。之后将修改好的源码放在源码主目录下,而不是继续在test目录下。

<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><!--<scope>test</scope>-->
</dependency>

4.2.2 创建Nacos配置

我们采用官方的约束,即默认 Nacos 适配的 dataId 和 groupId 约定如下:

  • groupId: SENTINEL_GROUP
  • 流控规则 dataId: {appName}-flow-rules,比如应用名为 appA,则 dataId 为 appA-flow-rules

所以不需要修改NacosConfigUtil.java了,但这是展示是为了步骤的完整性。

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "sentinel.nacos")
public class NacosPropertiesConfiguration {private String serverAddr;private String groupId = "SENTINEL_GROUP"; // 默认分组private String namespace;// 省略 getter/setter  
}

然后配置sentinel-dashboar/resources/application.properties中配置nacos配置,以为sentinel.nacos为前缀:

# nacos config server
sentinel.nacos.serverAddr=127.0.0.1:8848
sentinel.nacos.namespace=
sentinel.nacos.group-id=SENTINEL-GROUP

4.2.3 改造NacosConfig,创建NacosConfigService

package com.alibaba.csp.sentinel.dashboard.config;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import java.util.List;
import java.util.Properties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@Configuration
public class NacosConfig {@Beanpublic Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {return s -> JSON.parseArray(s, FlowRuleEntity.class);}@Beanpublic ConfigService nacosConfigService(NacosPropertiesConfiguration nacosPropertiesConfiguration)throws Exception {Properties properties = new Properties();properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());return ConfigFactory.createConfigService(properties);}
}

NacosConfig主要做两件事:

1) 注入Convert转换器,将 FlowRuleEntity 使用序列化为JSON字符串,以及将JSON字符串反序列化为 FlowRuleEntity

2) 注入Nacos配置服务ConfigService

4.2.4 实现 DynamicRulePublisher 和 DynamicRuleProvider 接口完成配置的持久化和远程加载

在 test 包下,已经有Sentinel官方的实现了,我们只需要将其拷贝至 src 目录下即可:

FlowRuleNacosProvider:用于从Nacos中拉取规则配置。

package com.alibaba.csp.sentinel.dashboard.rule;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.util.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** @author Eric Zhao* @since 1.4.0*/
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<FlowRuleEntity>> converter;@Overridepublic List<FlowRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

FlowRuleNacosPublisher:用于将配置保存至Nacos中

package com.alibaba.csp.sentinel.dashboard.rule;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.util.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** @author Eric Zhao* @since 1.4.0*/
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<FlowRuleEntity>, String> converter;@Overridepublic void publish(String app, List<FlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, converter.convert(rules));}
}

4.2.5 修改FlowControllerV2类将Nacos实现类注入进去

@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);@Autowiredprivate InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;@Autowired@Qualifier("flowRuleNacosProvider")private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;@Autowired@Qualifier("flowRuleNacosPublisher")private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;// 省略      
}

到这里所有流控规则相关的后端接口都已经改造完毕,我们需要接着改造前端页面,将页面请求的接口全部换成V2新接口。

4.2.6 修改前端“流控规则”路由配置(sidebar.html)

找到 resources/app/scripts/directives/sidebar/sidebar.html 文件,该文件是用来渲染左侧路由的:

我们需要将 “流控规则” 路由跳转的页面由 app/views/flow_v1.html 更换为 app/views/flow_v2.html,因为 flow_v2.html 页面中调用的后端接口全部都是 v2接口。

修改flowV1为flow,去掉V1,这样的话会调用FlowControllerV2接口

<!--<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>-->
<!-- 修改为flow,直接调用FlowControllerV2 -->
<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>

这样就可以通过js跳转至FlowControllerV2了

      .state('dashboard.flow', {templateUrl: 'app/views/flow_v2.html',url: '/v2/flow/:app',controller: 'FlowControllerV2',resolve: {loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {return $ocLazyLoad.load({name: 'sentinelDashboardApp',files: ['app/scripts/controllers/flow_v2.js',]});}]}})

4.2.7 修改前端“簇点链路”中流控配置的接口

根据 app/scripts/directives/sidebar/sidebar.html 触点链路路由调用js方法可知,最终路由转发到了 app/views/identity.html 页面:

<!-- app/scripts/directives/sidebar/sidebar.html -->
<li ui-sref-active="active" ng-if="!entry.isGateway"><a ui-sref="dashboard.identity({app: entry.app})"><i class="glyphicon glyphicon-list-alt"></i>&nbsp;&nbsp;簇点链路</a>
</li>
// app/scripts/app.js
.state('dashboard.identity', {templateUrl: 'app/views/identity.html',url: '/identity/:app',controller: 'IdentityCtl',resolve: {loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {return $ocLazyLoad.load({name: 'sentinelDashboardApp',files: ['app/scripts/controllers/identity.js',]});}]}
})

app/views/identity.html 页面中,我们需要将“流控”弹窗的保存按钮调用的接口换成V2版本,

addNewFlowRule 方法在 app/scripts/controllers/identity.js 文件中:

    $scope.addNewFlowRule = function (resource) {if (!$scope.macInputModel) {return;}var mac = $scope.macInputModel.split(':');flowRuleDialogScope = $scope.$new(true);flowRuleDialogScope.currentRule = {enable: false,strategy: 0,grade: 1,controlBehavior: 0,resource: resource,limitApp: 'default',clusterMode: false,clusterConfig: {thresholdType: 0},app: $scope.app,ip: mac[0],port: mac[1]};flowRuleDialogScope.flowRuleDialog = {title: '新增流控规则',type: 'add',confirmBtnText: '新增',saveAndContinueBtnText: '新增并继续添加',showAdvanceButton: true};// $scope.flowRuleDialog = {//     showAdvanceButton : true// };flowRuleDialogScope.saveRule = saveFlowRule;flowRuleDialogScope.saveRuleAndContinue = saveFlowRuleAndContinue;flowRuleDialogScope.onOpenAdvanceClick = function () {flowRuleDialogScope.flowRuleDialog.showAdvanceButton = false;};flowRuleDialogScope.onCloseAdvanceClick = function () {flowRuleDialogScope.flowRuleDialog.showAdvanceButton = true;};flowRuleDialog = ngDialog.open({template: '/app/views/dialog/flow-rule-dialog.html',width: 680,overlay: true,scope: flowRuleDialogScope});};

在这个方法中,会调用 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;$location.path(url);} else {alert('失败:' + data.msg);}}).error((data, header, config, status) => {alert('未知错误');});}

在这个方法中,会调用 FlowService.newRule 方法发送HTTP请求新建规则,成功后会将页面重定向至 '/dashboard/flow/' + $scope.app,所以我们需要改两个地方:

  1. 将FlowService改成V2版本

  2. 将重定向页面跳转至 '/dashboard/v2/flow/' + $scope.app

4.3.8 重新打包项目

进入 sentinel-dashboard 目录,执行下列命令重新打包:

mvn clean package -Dmaven.test.skip

4.3 测试

可以看到修改后的Dashboard成功将配置写入Nacos中,Nacos配置发生变更,也同时通知了订阅这些配置的客户端,使得业务服务能实时更新流控配置,即使业务服务重启,之前仍能正常从Nacos中拉取配置。

五. 总结

本文详细介绍了如何利用Nacos实现Sentinel Dashboard配置的持久化,解决了业务服务重启后配置丢失的问题。通过以下几个步骤,我们成功实现了配置的动态管理和持久化存储:

  1. 引入Nacos依赖:在项目中添加了sentinel-datasource-nacos依赖,为后续集成打下基础。
  2. 配置自动加载:通过实现ReadableDataSource接口,配置了规则自动从Nacos加载到Sentinel的流程。
  3. Nacos配置写入:通过编写NacosConfigSender类,实现了向Nacos写入配置的功能。
  4. Dashboard源码改造:针对Dashboard存在的问题,通过修改前后端源码,实现了配置的持久化存储和同步更新。

通过这一系列的改造,我们不仅提高了Sentinel Dashboard的可用性和稳定性,还增强了其在生产环境中的实用性。现在,即使在业务服务重启的情况下,配置也不会丢失,确保了服务的连续性和一致性。

本文只是讲解了“流控规则”持久化的源码修改过程,如果其它模块也有持久化的需求,也可以参考此过程进行相应的源码修改。

本文参考至:

dynamic-rule-configuration | Sentinel (sentinelguard.io)

Sentinel Dashboard(基于1.8.1)流控规则持久化到Nacos——涉及部分Sentinel Dashboard源码改造 - JJian - 博客园 (cnblogs.com)

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

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

相关文章

WPF修改MessageBox样式(.NET6版本)

.NET6版本及其以上版本,通过GlobalUsings 实现WPF中对MessageBox样式的全局应用一、问题场景 使用HandyControl简写HC 作为基础UI组件库时,希望系统中所有的MessageBox 样式都使用HC的MessageBox,常规操作如下: 在对应的xxxx.cs 顶部使用using 指定特定类的命名空间。 usin…

MISC——攻防世界

pdf 下载附件,一个PDF文件,打开,中间有一个框框,点一下,看见flag:flag{security_through_obscurity} 如来十三掌 下载附件: 夜哆悉諳多苦奢陀奢諦冥神哆盧穆皤三侄三即諸諳即冥迦冥隸數顛耶迦奢若吉怯陀諳怖奢智侄諸若奢數菩奢集遠俱老竟寫明奢若梵等盧皤豆蒙密離怯婆皤…

FreeRTOS简单内核实现4 临界段

本文内容较为简单,主要介绍如何实现临界段以及临界段如何使用@目录0、思考与回答0.1、思考一0.2、思考二0.3、思考三1、关中断1.1、带返回值1.2、不带返回值2、开中断3、临界段4、应用0、思考与回答 0.1、思考一 为什么需要临界段? 有时候我们需要部分代码一旦这开始执行,则…

第一章 进制,寄存器,汇编指令(公开课)

一、进制1、十进制:由0.1.2.3.4.5.6.7.8.9组成,逢十进一2、二进制:由0.1组成,逢二进一eg:0--01--12--103--114--1005--1016--1103、十六进制:由0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F组成,逢十六进一4、八进制eg:2-3=? 二、exe程序 1、PE文件结构:任何一个在Windows上运行的…

洛谷 P1216 数字三角形

题目链接:数字三角形思路dp:金字塔顶的元素为起点,金字塔每行的最左侧数字只能从上一层的最左侧数字到达,如7 -> 3 -> 8 -> 2 -> 4,这些数字中的每一个(除起点7外)都只能从上一层的最左侧数字到达,递推公式为dp[i][1] = max(dp[i][1], num[i][1] + dp[i - …

Qt/C++音视频开发77-获取本地有哪些摄像头名称/ffmpeg命令日志方式

一、前言 上一篇文章讲使用ffmpeg函数接口去获取本地摄像头信息,这种方式只能从ffmpeg5版本开始才具备,那ffmpeg3/4只能干瞪眼?那肯定不行的,必须要想办法打通这个功能,查阅信息发现可以执行命令 ffmpeg -f dshow -list_devices true -i dummy 去获取,会通过日志打印出来…

ants:强大的高性能与低成本 Go 协程池

ants:强大的高性能与低成本 Go 协程池 原创 K8sCat 源自开发者 2024-06-16 11:28 广东 听全文源自开发者 专注于提供关于Go语言的实用教程、案例分析、最新趋势,以及云原生技术的深度解析和实践经验分享。 256篇原创内容公众号在开发高并发程序时,管理并发的能力至关重要。在…

app专项测试

过滤: 过滤表达式: domain. 展示 domain 中的资源, *.comhas-response-header. 包含指定 HTTP 响应 headeris. 表达式larger-than. 展示大于某个尺寸的资源,1000 等于 1kmethod. 指定http请求方法,比如 get 或者 postmime-type. 资源 mime 类型,比如 application/jsonsch…