Dubbo加载配置文件方式,加载流程,加载配置文件源码解析
配置方法
API配置
以Java编码的方式组织配置,Dubbo3配置API详解 :https://dubbo.apache.org/zh/docs3-v2/java-sdk/reference-manual/config/api/#bootstrap-api
public static void main(String[] args) throws IOException {ServiceConfig<GreetingsService> service = new ServiceConfig<>();service.setApplication(new ApplicationConfig("first-dubbo-provider"));service.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));service.setInterface(GreetingsService.class);service.setRef(new GreetingsServiceImpl());service.export();System.out.println("first-dubbo-provider is running.");System.in.read();
}
xml配置
以XML方式配置各种组件,支持与Spring无缝集成
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder/><dubbo:application name="demo-provider"/><dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/><dubbo:provider token="true"/><bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/><dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService"/></beans>
Annotation 结合SpringBoot配置
以注解方式暴露服务和引用服务接口,支持与Spring无缝集成
- 注解有 @DubboService、@DubboReference 与 @EnableDubbo。
其中 @DubboService 与 @DubboReference 用于标记 Dubbo 服务
@EnableDubbo 启动 Dubbo 相关配置并指定 Spring Boot 扫描包路径。
- 配置文件 application.properties 或 application.yml
@Service 与 @Reference注解从 3.0 版本开始就已经废弃,改用 @DubboService,@DubboReference,以区别于 Spring 的 @Service,@Reference 注解
@DubboService(version = "1.0.0", group = "dev", timeout = 5000,registry="zk-registry")
@DubboReference(version = "1.0.0", group = "dev", timeout = 5000)
注解中的参数有限,如果需要可以使用dubbo.properties 或者javaconfig代替,或者通过SpringBoot的配置文件结合
SpringBoot配置文件
spring.application.name=dubbo--provider-sample
dubbo.scan.base-packages=com.alibaba.boot.dubbo.demo.provider.service# Dubbo Application
## The default value of dubbo.application.name is ${spring.application.name}
## dubbo.application.name=${spring.application.name}# Dubbo Protocol
dubbo.protocol.name=dubbo
dubbo.protocol.port=12345## Dubbo Registry
dubbo.registry.address=N/A## DemoService version
demo.service.version=1.0.0
JavaConfig
JavaConfig即使用 @Bean的方式添加到Spring容器中
@Configuration
public class Configuration {@Beanpublic ServiceConfig demoService() {ServiceConfig service = new ServiceConfig();service.setInterface(DemoService.class);service.setRef(new DemoServiceImpl());service.setGroup("dev");service.setVersion("1.0.0");Map<String, String> parameters = new HashMap<>();service.setParameters(parameters);return service;}
}
dubbo.properties
参考
dubbo.service.org.apache.dubbo.springboot.demo.DemoService.timeout=5000
dubbo.service.org.apache.dubbo.springboot.demo.DemoService.parameters=[{myKey:myValue},{anotherKey:anotherValue}]
dubbo.reference.org.apache.dubbo.springboot.demo.DemoService.timeout=6000
配置加载流程
- JVM System Properties,JVM -D 参数
- System environment,JVM进程的环境变量
- Externalized Configuration,外部化配置,从配置中心读取
- Application Configuration,应用的属性配置,从Spring应用的Environment中提取"dubbo"打头的属性集
- API / XML /注解等编程接口采集的配置可以被理解成配置来源的一种,是直接面向用户编程的配置采集方式
- 从classpath读取配置文件 dubbo.properties
关于dubbo.properties属性:
- 如果在 classpath 下有超过一个 dubbo.properties 文件,比如,两个 jar 包都各自包含了 dubbo.properties,dubbo 将随机选择一个加载,并且打印错误日志。
- Dubbo 可以自动加载 classpath 根目录下的 dubbo.properties,但是你同样可以使用 JVM 参数来指定路径:
-Ddubbo.properties.file=xxx.properties。
常用配置组件如下:
- application: Dubbo应用配置
- registry: 注册中心
- protocol: 服务提供者RPC协议
- config-center: 配置中心
- metadata-report: 元数据中心
- service: 服务提供者配置
- reference: 远程服务引用配置
- provider: service的默认配置或分组配置
- consumer: reference的默认配置或分组配置
- module: 模块配置
- monitor: 监控配置
- metrics: 指标配置
- ssl: SSL/TLS配置
属性覆盖
发生属性覆盖可能有两种情况,并且二者可能是会同时发生的:
- 不同配置源配置了相同的配置项
- 相同配置源,但在不同层次指定了相同的配置项
不同配置源
按配置加载流程 从上到下覆盖, JVM -D 优先级最高,dubbo.properties 优先级最低
相同配置源
属性覆盖是指用配置的属性值覆盖config bean实例的属性,类似Spring PropertyOverrideConfigurer 的作用。
但与PropertyOverrideConfigurer的不同之处是,Dubbo的属性覆盖有多个匹配格式,优先级从高到低依次是:
#1. 指定id的实例级配置 dubbo.{config-type}s.{config-id}.{config-item}={config-item-value}
#2. 指定name的实例级配置 dubbo.{config-type}s.{config-name}.{config-item}={config-item-value}
#3. 应用级配置(单数配置) dubbo.{config-type}.{config-item}={config-item-value}
属性覆盖处理流程:
按照优先级从高到低依次查找,如果找到此前缀开头的属性,则选定使用这个前缀提取属性,忽略后面的配置。
配置加载源码解析
DefaultApplicationDeployer#initialize()
@Override
public void initialize() {if (initialized) {return;}// Ensure that the initialization is completed when concurrent callssynchronized (startLock) {if (initialized) {return;}// register shutdown hookregisterShutdownHook();startConfigCenter();loadApplicationConfigs();initModuleDeployers();// @since 2.7.8startMetadataCenter();initialized = true;if (logger.isInfoEnabled()) {logger.info(getIdentifier() + " has been initialized!");}}
}
始化过程中会先启动配置中心配置信息处理,然后 调用加载初始化应用程序配置方法loadApplicationConfigs()进行配置加载
Dubbo框架的配置项比较繁多,为了更好地管理各种配置,将其按照用途划分为不同的组件,最终所有配置项都会汇聚到URL中,传递给后续处理模块。
配置信息的初始化回顾
在创建dubbo环境时,ModuleEnvironment环境信息 getApplicationModel().getModelEnvironment()获取对象时,使用spi扩展加载ModuleEnvironment,会触发 ExtensionLoader#initExtension ()
对象如下代码所示:
public ModuleEnvironment(ModuleModel moduleModel) {super(moduleModel);this.moduleModel = moduleModel;this.applicationDelegate = moduleModel.getApplicationModel().getModelEnvironment();
}@Override
public ModuleEnvironment getModelEnvironment() {if (moduleEnvironment == null) {moduleEnvironment = (ModuleEnvironment) this.getExtensionLoader(ModuleExt.class).getExtension(ModuleEnvironment.NAME);}return moduleEnvironment;
}private void initExtension(T instance) {if (instance instanceof Lifecycle) {Lifecycle lifecycle = (Lifecycle) instance;lifecycle.initialize();}}
属性加载
Environment中属性的初始化方法 initialize()
这个初始化方法对应ModuleEnvironment的父类型Environment中的初始化方法如下
@Override
public void initialize() throws IllegalStateException {if (initialized.compareAndSet(false, true)) {//加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.propertiesthis.propertiesConfiguration = new PropertiesConfiguration(scopeModel);//系统JVM参数的配置无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getProperty(key)来调用this.systemConfiguration = new SystemConfiguration();//系统环境变量的配置,无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getenv(key)来获取就可以this.environmentConfiguration = new EnvironmentConfiguration();//从远程配置中心的全局配置获取对应配置this.externalConfiguration = new InmemoryConfiguration("ExternalConfig");//从远程配置中心的应用配置获取对应配置this.appExternalConfiguration = new InmemoryConfiguration("AppExternalConfig");//应用内的配置比如: Spring Environment/PropertySources/application.propertiesthis.appConfiguration = new InmemoryConfiguration("AppConfig");//加载迁移配置,用户在JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定测尝试加载类路径下的dubbo-migration.yamlloadMigrationRule();}
}
dubbo.properties配置文件加载解析原理
如前面所示:
//加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.properties
this.propertiesConfiguration = new PropertiesConfiguration(scopeModel);
下面就直接提构造器的PropertiesConfiguration代码了:
public PropertiesConfiguration(ScopeModel scopeModel) {this.scopeModel = scopeModel;refresh();}public void refresh() {//配置获取的过程是借助工具类ConfigUtils来获取的properties = ConfigUtils.getProperties(scopeModel.getClassLoaders());}
继续看ConfigUtils的getProperties方法:
public static Properties getProperties(Set<ClassLoader> classLoaders) {//这个配置的KEY是dubbo.properties.file System.getProperty是从JVM参数中获取配置的 一般情况下我们在启动Java进程的时候会指定Dubbo配置文件 如配置://-Ddubbo.properties.file=/dubbo.propertiesString path = System.getProperty(CommonConstants.DUBBO_PROPERTIES_KEY);if (StringUtils.isEmpty(path)) {//优先级最高的JVM参数拿不到数据则从 环境变量中获取,这个配置key也是dubbo.properties.file System.getenv是从环境变量中获取数据//例如我们在环境变量中配置 dubbo.properties.file=/dubbo.propertiespath = System.getenv(CommonConstants.DUBBO_PROPERTIES_KEY);if (StringUtils.isEmpty(path)) {//如果在JVM参数和环境变量都拿不到这个配置文件的路径我们就用默认的吧//默认的路径是类路径下的资源文件 这个路径是: dubbo.properties path = CommonConstants.DEFAULT_DUBBO_PROPERTIES;}}return ConfigUtils.loadProperties(classLoaders, path, false, true);}
路径获取之后加载详细的配置内容:
ConfigUtils的loadProperties代码如下:
ConfigUtils.loadProperties(classLoaders, path, false, true);
代码如下:
public static Properties loadProperties(Set<ClassLoader> classLoaders, String fileName, boolean allowMultiFile, boolean optional) {Properties properties = new Properties();// add scene judgement in windows environment Fix 2557//检查文件是否存在 直接加载配置文件如果加载到了配置文件则直接返回if (checkFileNameExist(fileName)) {try {FileInputStream input = new FileInputStream(fileName);try {properties.load(input);} finally {input.close();}} catch (Throwable e) {logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);}return properties;}//为什么会有下面的逻辑呢,如果仅仅使用上面的加载方式只能加载到本系统下的配置文件,无法加载封装在jar中的根路径的配置Set<java.net.URL> set = null;try {List<ClassLoader> classLoadersToLoad = new LinkedList<>();classLoadersToLoad.add(ClassUtils.getClassLoader());classLoadersToLoad.addAll(classLoaders);//这个方法loadResources在扩展加载的时候说过set = ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad).values().stream().reduce(new LinkedHashSet<>(), (a, i) -> {a.addAll(i);return a;});} catch (Throwable t) {logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);}if (CollectionUtils.isEmpty(set)) {if (!optional) {logger.warn("No " + fileName + " found on the class path.");}return properties;}if (!allowMultiFile) {if (set.size() > 1) {String errMsg = String.format("only 1 %s file is expected, but %d dubbo.properties files found on class path: %s",fileName, set.size(), set);logger.warn(errMsg);}// fall back to use method getResourceAsStreamtry {properties.load(ClassUtils.getClassLoader().getResourceAsStream(fileName));} catch (Throwable e) {logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);}return properties;}logger.info("load " + fileName + " properties file from " + set);for (java.net.URL url : set) {try {Properties p = new Properties();InputStream input = url.openStream();if (input != null) {try {p.load(input);properties.putAll(p);} finally {try {input.close();} catch (Throwable t) {}}}} catch (Throwable e) {logger.warn("Fail to load " + fileName + " file from " + url + "(ignore this file): " + e.getMessage(), e);}}return properties;}
完整的配置加载流程这里用简单的话描述下:
-
项目内配置查询
-
- 路径查询
-
-
- 从JVM参数中获取配置的 dubbo.properties.file配置文件路径
- 如果前面未获取到路径则从环境变量参数中获取配置的dubbo.properties.file配置文件路径
- 如果前面未获取到路径则使用默认路径dubbo.propertie
-
-
- 配置加载
-
-
- 将路径转为FileInputStream 然后使用Properties加载
-
-
依赖中的配置扫描查询
-
- 使用类加载器扫描所有资源URL
- url转InputStream 如 url.openStream() 然后使用Properties加载
加载JVM参数的配置
这里我们继续看SystemConfiguration配置的加载 这个直接看下代码就可以了:
这个类型仅仅是使用System.getProperty来获取JVM配置即可
public class SystemConfiguration implements Configuration {@Overridepublic Object getInternalProperty(String key) {return System.getProperty(key);}public Map<String, String> getProperties() {return (Map) System.getProperties();}
}
加载环境变量参数的配置
这里我们来看EnvironmentConfiguration
public class EnvironmentConfiguration implements Configuration {@Overridepublic Object getInternalProperty(String key) {String value = System.getenv(key);if (StringUtils.isEmpty(value)) {value = System.getenv(StringUtils.toOSStyleKey(key));}return value;}public Map<String, String> getProperties() {return System.getenv();}
}
内存配置的封装:InmemoryConfiguration
这里我们看下InmemoryConfiguration的设计,这个直接看代码吧内部使用了一个LinkedHashMap来存储配置
public class InmemoryConfiguration implements Configuration {private String name;// stores the configuration key-value pairsprivate Map<String, String> store = new LinkedHashMap<>();public InmemoryConfiguration() {}public InmemoryConfiguration(String name) {this.name = name;}public InmemoryConfiguration(Map<String, String> properties) {this.setProperties(properties);}@Overridepublic Object getInternalProperty(String key) {return store.get(key);}/*** Add one property into the store, the previous value will be replaced if the key exists*/public void addProperty(String key, String value) {store.put(key, value);}/*** Add a set of properties into the store*/public void addProperties(Map<String, String> properties) {if (properties != null) {this.store.putAll(properties);}}/*** set store*/public void setProperties(Map<String, String> properties) {if (properties != null) {this.store = properties;}}public Map<String, String> getProperties() {return store;}}
Dubbo迁移新版本的配置文件加载dubbo-migration.yaml
这个配置文件的文件名字为:dubbo-migration.yaml
private void loadMigrationRule() {//JVM参数的dubbo.migration.file配置String path = System.getProperty(CommonConstants.DUBBO_MIGRATION_KEY);if (StringUtils.isEmpty(path)) {//环境变量的dubbo.migration.file配置path = System.getenv(CommonConstants.DUBBO_MIGRATION_KEY);if (StringUtils.isEmpty(path)) {//默认的迁移配置文件 dubbo-migration.yamlpath = CommonConstants.DEFAULT_DUBBO_MIGRATION_FILE;}}this.localMigrationRule = ConfigUtils.loadMigrationRule(scopeModel.getClassLoaders(), path);}
初始化加载应用配置
加载配置涉及到了配置优先级的处理,
下面来看加载配置代码 loadApplicationConfigs()方法
private void loadApplicationConfigs() {//发布器还是不处理配置加载的逻辑还是交给配置管理器configManager.loadConfigs();
}
配置管理器加载配置:
@Overridepublic void loadConfigs() {// application config has load before starting config center// load dubbo.applications.xxx//加载应用配置loadConfigsOfTypeFromProps(ApplicationConfig.class);// load dubbo.monitors.xxx//加载监控配置loadConfigsOfTypeFromProps(MonitorConfig.class);// load dubbo.metrics.xxx//加载指标监控配置loadConfigsOfTypeFromProps(MetricsConfig.class);// load multiple config types:// load dubbo.protocols.xxx//加载协议配置loadConfigsOfTypeFromProps(ProtocolConfig.class);// load dubbo.registries.xxxloadConfigsOfTypeFromProps(RegistryConfig.class);// load dubbo.metadata-report.xxx//加载元数据配置loadConfigsOfTypeFromProps(MetadataReportConfig.class);// config centers has bean loaded before starting config center//loadConfigsOfTypeFromProps(ConfigCenterConfig.class);//刷新配置refreshAll();//检查配置checkConfigs();// set model nameif (StringUtils.isBlank(applicationModel.getModelName())) {applicationModel.setModelName(applicationModel.getApplicationName());}}
相关文章:
Dubbo加载配置文件方式,加载流程,加载配置文件源码解析
配置方法 API配置 以Java编码的方式组织配置,Dubbo3配置API详解 :https://dubbo.apache.org/zh/docs3-v2/java-sdk/reference-manual/config/api/#bootstrap-api public static void main(String[] args) throws IOException {ServiceConfig<Greet…...
十大开源测试工具和框架,一定有你需要的
目录 前言 Katalon Studio Selenium Appium JMeter SOAP UI Robot Framework Watir JUnit Robotium Citrus 总结 前言 免费的开源框架和工具由于其开源特性,现在逐渐成为自动化测试的首选解决方案。区别在于,你是喜欢使用类库编写一个全新的…...
加密技术在android中的应用
1、算法基础 算法基础参照linux的全盘加密与文件系统加密在android中的应用 消息摘要算法 对称加密算法 非对称加密算法...
备战蓝桥杯【一维前缀和】
🌹作者:云小逸 📝个人主页:云小逸的主页 📝Github:云小逸的Github 🤟motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前…...
研报精选230214
目录 【行业230214艾瑞股份】中国增强现实(AR)行业研究报告【行业230214国信证券】信息安全深度剖析5:密评和信创双催化,密码产业开启从1到N【行业230214民生证券】磁性元器件深度报告:乘新能源之风,磁性元…...
【SSL/TLS】准备工作:证书格式
证书格式1. 格式说明1.1 文件编码格式1.2 文件后缀格式2. xca导出格式1. 格式说明 1.1 文件编码格式 1. PEM格式: 使用Base 64 ASCII进行编码的纯文本格式。后缀为“.pem”, ".cer", ".crt", ".key" 2. DER格式 二进制编码格式,文件…...
Linux常用命令---系统常用命令
Linux系统常用命令场景一: 查看当前系统内核版本相关信息场景二: sosreport 命令场景三: 如何定位并确定命令?场景四:查看当前系统运行负载怎场景五: 查看当前系统的内存可用情况场景六:查看网卡…...
C 结构体
C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。结构用于表示一条记录,假设您想要跟踪图书馆中书本的动态,您可能需要跟踪每本书的下列属性ÿ…...
手语检测识别
论文:Real-Time Sign Language Detection using Human Pose Estimation Github:https://github.com/google-research/google-research/tree/master/sign_language_detection SLRTP 2020 手语识别任务包括手语检测(Sign language detection&a…...
android fwk模块之Sensor架构
本文基于Android 12源码整理,包含如下内容: 通信架构应用层实现使用方式SensorManager抽象接口具体实现fwk层的实现native中的SensorManager的初始化流程native中的消息队列初始化与数据读取sensorservice实现HAL层的实现通信架构 应用层实现 涉及代码&…...
安装less-loader5出现webpack版本不兼容
今天遇到一个问题: 安装less-loader5之后其它包提示peerDependencies WARNING,意思是包版本不兼容。 【难题】 虽然NPM已经很自动化了,但依赖问题真的是一个难题,无法自动解决,需要人工干预调整。 【解决办法】 去查…...
Java 网络编程
1.UDP和TCPUDP和TCP是传输层协议中最核心的两种协议他们的特点分别是UDP: 无连接,不可靠传输,面向数据报,全双工TCP: 有连接,是可靠传输,面向字节流,全双工有无连接有连接:就好比两个人打电话,打电话的一方发出连接请求,被打电话的一方选择确认连接,此时双方才能进行通话无连接…...
BEV学习记录
近期可能要经常性的开展BEV工作,打算把自己觉着不错的网站拿出来记录一下。 首先贴上来我还没有细读的一篇觉着不错的文章。 自动驾驶感知新范式——BEV感知经典论文总结和对比(上)_苹果姐的博客-CSDN博客_bev视角 开山之作--LSS ECCV 202…...
Webrtc Native C++切换音频输入源
modules/audio_device/audio_device_impl.cc #include “api/audio_options.h” #include “modules/audio_device/include/factory.h” // 创建一个 AudioDeviceModule 对象 auto audio_device_module = webrtc::AudioDeviceModule::Create( webrtc::AudioDeviceModule::kPl…...
裸辞5个月,面试了37家公司,终于找到理想工作了
上半年裁员,下半年裸辞,有不少人高呼裸辞后躺平真的好快乐!但也有很多人,裸辞后的生活五味杂陈。 面试37次终于找到心仪工作 因为工作压力大、领导PUA等各种原因,今年2月下旬我从一家互联网小厂裸辞,没想…...
Mybatis-plus@DS实现动态切换数据源应用
目录1 DS实现动态切换数据源原理2 不可在事务中切换数据库分析解决3 原因解析1 DS实现动态切换数据源原理 首先mybatis-plus使用com.baomidou.dynamic.datasource.AbstractRoutingDataSource继承 AbstractDataSource接管数据源;具体实现类为com.baomidou.dynamic.d…...
SpringBoot的创建和使用
SpringBoot是什么?SpringBoot诞生的目的就是为了简化Spring开发,而相对于Spring,SpringBoot算是一个很大的升级,就如同汽车手动挡变成了自动挡。Spring:SpringBoot:SpringBoot的优点SpringBoot让Spring开发…...
居家电话客服宝典
客服分类从销售的流程来分,客服分为售前和售后。售前一般都带有销售性质,工资主要靠提成,售后一般是解答问题,工资主要看服务质量和差评量。从工作模式来分,客服分为在线客服和热线客服。在线客服以打字聊天为主&#…...
开发方案设计
1、开发流程产品需求设计-->需求粗评-->做设计方案-->粗估时-->需求细评-->排期-->开发-->提测、修bug-->code review-->上线设计方案主要是写实现思路、模块划分code review:完善代码,发现未考虑到的边界问题2、具体实现方案…...
文件路径模块pathlib
文件路径模块pathlib 文章目录文件路径模块pathlib1.概述2.创建路径2.1.创建非windos平台路径2.2.动态拼接路径joinpath2.3.替换文件名称 with_name2.4.创建固定目录2.5.创建文件夹和文件1.创建多级目录mkdir2.创建空文件3.路径解析3.1.根据路径分隔符解析路径parts3.2.获取父级…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
