【BlossomConfig】配置中心Core核心功能代码的编写

文章目录

  • Core
  • 什么是配置中心?以及如何实现一个配置中心?
  • SpringBoot如何实现配置的管控?
  • SpringCloud项目是如何对bootstrap配置文件进行加载的?
  • Nacos是如何实现配置文件的读取加载的?
  • 开发配置中心前必须了解的前置知识
  • 配置中心Server和Client端代码的编写
  • 配置中心Core核心功能代码的编写
  • 配置中心源码优化---本地缓存与读写锁

网关项目源码
RPC项目源码
配置中心项目源码

Core

Core模块是我们项目最核心最重要的模块,当别人需要使用我们的配置中心的时候,只需要引入Core模块,在项目启动的时候就会自动连接我们的配置中心获取配置,并刷新本地的配置。
接下来我们来看看Core模块是如何实现的。
这里,按照我们的前置知识可以知道,我们自上到下,需要完成如下几件事情,我们在复习一下:

  1. bootstrap配置的获取
  2. 配置中心的连接与配置的获取
  3. Locator的实现,加载配置中心的配置
    所以我写了一个启动类,也是按照完成这三个事情的顺序,对Bean进行加载。
(proxyBeanMethods = false)
(name = "spring.cloud.blossom.config.enabled", matchIfMissing = true)
public class BlossomConfigBootstrapConfiguration {/*** 项目对配置中心配置* @return*/public BlossomConfigProperties blossomConfigProperties(){return new BlossomConfigProperties();}/*** 配置中心管理器* @param blossomConfigProperties* @return*/public BlossomConfigManager blossomConfigManager(BlossomConfigProperties blossomConfigProperties) {return new BlossomConfigManager(blossomConfigProperties);}public BlossomConfigChangePublisher blossomConfigChangePublisher(ApplicationEventPublisher applicationEventPublisher){return new BlossomConfigChangePublisher(applicationEventPublisher);}/*** 配置中心配置加载器* @param blossomConfigManager* @return*/public BlossomPropertySourceLocator blossomPropertySourceLocator(BlossomConfigManager blossomConfigManager) {return new BlossomPropertySourceLocator(blossomConfigManager);}public BlossomConfigChangeEventSubscriber blossomConfigChangeEventSubscriber(BlossomConfigManager manager,BlossomConfigChangePublisher publisher){return new BlossomConfigChangeEventSubscriber(manager.getConfigService(),publisher);}}

代码实现起来其实非常非常简单。
一切的难点都是在于代码的编写顺序。
这里我们跳过对boostrap配置文件的获取的代码,在前面我们已经提到过了,如果你是SpringCloud项目,直接引入spring-cloud-starter-boostrap依赖就可以直接帮助你完成对bootstrap配置文件的解析,如果你非要手写,那么你就按照前面的方式,编写一个事件监听器来读取你的配置文件。这里我追求效率就不手写了。
那么我们的配置文件就很快的完成了加载。
然后很简单的,我们依赖Client模块提供的配置中心的创建工程来创建Core模块的配置中心。

public class BlossomConfigFactory {/*** 用于创建ConfigService配置中心* @param properties 配置中心的创建需要用到配置文件* @return* @throws BlossomException*/public static ConfigService createConfigService(Properties properties) throws BlossomException {try {Class<?> configServiceClass = Class.forName("blossom.project.config.client.BlossomConfigService");Constructor constructor = configServiceClass.getConstructor(Properties.class);ConfigService configService = (ConfigService) constructor.newInstance(properties);return configService;} catch (Throwable e) {throw new BlossomException(BlossomException.REFLECT_CREATE_ERROR,e.getMessage(), e);}}}

到此为止,我们其实就完成了配置的加载和配置中心的创建。
那么接下来要做的就是初始化配置中心的配置,拉取配置中心的配置。
并加载到本地,作为PropertySource返回。
也是一样,我们重写Locator方法。

package blossom.project.config.core;import blossom.project.config.client.ConfigService;
import blossom.project.config.common.constants.BlossomConstants;
import blossom.project.config.common.enums.ConfigType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;import static blossom.project.config.common.constants.BlossomConstants.DOT;
import static blossom.project.config.common.constants.BlossomConstants.SEPARATOR;/*** @author: ZhangBlossom* @date: 2023/12/28 17:35* @contact: QQ:4602197553* @contact: WX:qczjhczs0114* @blog: https://blog.csdn.net/Zhangsama1* @github: https://github.com/ZhangBlossom* BlossomPropertySourceLocator类* 在编写这个类之前应该先将ConfigService实现类编写完毕* 然后在这个类里面得到ConfigService之后* 调用里面的方法获取到配置中心的配置之后* 将配置加载到本地 同时考虑编写一套缓存*/

(0)
public class BlossomPropertySourceLocator implements PropertySourceLocator {private BlossomConfigManager manager;private BlossomConfigProperties properties;//使用builder的方式得到来自各种地方的BlossomPropertySource//最后将BlossomPropertySource放入到CompositePropertySource即可private BlossomPropertySourceBuilder blossomPropertySourceBuilder;public BlossomPropertySourceLocator(BlossomConfigManager manager) {this.manager = manager;this.properties = manager.getProperties();}public PropertySource<?> locate(Environment environment) {this.properties.setEnvironment(environment);ConfigService configService = manager.getConfigService();if (Objects.isNull(configService)) {log.warn("No instance of ConfigService was found,can not load config from ConfigService");return null;}this.blossomPropertySourceBuilder = new BlossomPropertySourceBuilder(configService, properties);CompositePropertySource ps = new CompositePropertySource(BlossomConstants.BLOSSOM_PROPERTY_SOURCE_NAME);loadApplicationConfig(ps);loadConfigLists(ps);return ps;}/*** 加载项目所有配置* @param ps*/private void loadConfigLists(CompositePropertySource ps) {List<BlossomConfigProperties.BlossomConfig> configLists = this.properties.getConfigLists();configLists.forEach(config -> {loadConfigIfPresent(ps, config.getConfigId(), config.getGroup(), this.properties.getFileExtension());});}/*** 在项目项目原生配置* @param ps*/private void loadApplicationConfig(CompositePropertySource ps) {Environment env = this.properties.getEnvironment();String applicationName = env.getProperty("spring.application.name");String group = this.properties.getGroup();String fileExtension = this.properties.getFileExtension();for (String profile : env.getActiveProfiles()) {//blossom-core-dev.yamlString configId = applicationName + SEPARATOR + profile + DOT + this.properties.getFileExtension();loadConfigIfPresent(ps, configId, group, fileExtension);}}private void loadConfigIfPresent(CompositePropertySource ps, String configId, String group, String fileExtension) {if (StringUtils.isBlank(configId)) {return;}if (StringUtils.isBlank(group)) {return;}Boolean validType = ConfigType.isValidType(fileExtension);//the file extension is unvalid;if (!validType) {return;}this.loadBlossomConfig(ps, configId, group, fileExtension);}private void loadBlossomConfig(CompositePropertySource ps, String configId, String group, String fileExtension) {//1:从配置中心获取配置 并且封装为BlossomPropertySourceBlossomPropertySource blossomPropertySource =this.blossomPropertySourceBuilder.buildBlossomPropertySource(configId, group, fileExtension);//2:将配置转换为PropertySource ---能得到Properties类型即可//3:将配置添加到CompositePropertySourceps.addFirstPropertySource(blossomPropertySource);}}

然后在代码中按照我们所说的,完成对配置中心配置的加载和获取。
下面的代码,才是真正的连接到配置中心,并且对配置中心的配置进行拉取以及解析的地方。
那么到此,我们就已经完成了项目启动的时候的初始化属性的配置了。
接下来我们要思考一下,如何在项目的配置文件发生变更的时候,能刷新本地的配置呢?

package blossom.project.config.core;import blossom.project.config.client.ConfigService;
import blossom.project.config.common.exception.BlossomException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.PropertySource;import java.util.Collections;
import java.util.Date;
import java.util.List;/*** @author: ZhangBlossom* @date: 2023/12/29 20:16* @contact: QQ:4602197553* @contact: WX:qczjhczs0114* @blog: https://blog.csdn.net/Zhangsama1* @github: https://github.com/ZhangBlossom* BlossomPropertySourceBuilder类*/


public class BlossomPropertySourceBuilder {private ConfigService configService;private BlossomConfigProperties properties;public BlossomPropertySourceBuilder(ConfigService configService, BlossomConfigProperties properties) {this.configService = configService;this.properties = properties;}/*** 当前方法会完成BlossomPropertySource的构建** @param configId* @param group* @param fileExtension* @return*/BlossomPropertySource buildBlossomPropertySource(String configId, String group, String fileExtension) {//从配置中心得到配置并且封装为List类型的PropertySourceList<PropertySource<?>> propertySources = loadBlossomConfigData(configId, group, fileExtension);//将List转换为最后我们需要的BlossomPropertySourceBlossomPropertySource blossomPropertySource = new BlossomPropertySource(group, configId, new Date(),propertySources);return blossomPropertySource;}/*** 当前方法完成对配置中心配置的加载和解析* 并且最终返回PropertySource集合** @param configId* @param group* @param fileExtension* @return*/private List<PropertySource<?>> loadBlossomConfigData(String configId, String group, String fileExtension) {List<PropertySource<?>> propertySources = Collections.emptyList();try {//得到配置文件的内容String configData = this.configService.getConfig(configId, group, fileExtension);if (StringUtils.isBlank(configData)) {log.warn("the data from ConfigService is empty, configId: {}, group:{}", configId, group);return Collections.emptyList();}//在spring中想要将配置解析为PropertySource可以用自带的解析器--只提供了yaml和properties//也就是说json/xml等其他格式需要自己实现propertySources =BlossomConfigDataHandler.getInstance().parseConfigData(configId, configData, fileExtension);} catch (BlossomException e) {log.error("get the config data from ConfigService failed, configId: {}, group:{},Exception:{}", configId,group, e);} catch (Exception e) {log.error("parse the config data failed. Exception:{}", e);}return propertySources;}}

我们知道,其实对于配置变更事件,两种实现方法,一种push,一种pull。
push就是让Server端主动的通知Client,实现起来相比pull更加复杂,因此这里我们选择使用pull的方式,也就是让CLient主动的去Server端拉取配置变更。
那么这里就会用到我们上面所说的长轮询了,以及我们的事件发布机制。
因为我们得让Client这个普通的Java项目能通知道Core模块这个Spring项目,并且让我们轻松的利用到Spring项目中提供的强大的事件监听机制。
重点就是,Client模块我说到的Publish接口。
在Core模块中


public BlossomConfigChangeEventSubscriber blossomConfigChangeEventSubscriber(BlossomConfigManager manager,BlossomConfigChangePublisher publisher){return new BlossomConfigChangeEventSubscriber(manager.getConfigService(),publisher);
}

有如下的一个类,这个类的作用就是在Core模块启动的时候,主动的根据配置中心的信息,去发起一个长轮询监听请求。


public class BlossomConfigChangeEventSubscriber {private ConfigService configService;private Properties properties;private Publish publish;public BlossomConfigChangeEventSubscriber(ConfigService configService, BlossomConfigChangePublisher publisher) {this.configService = configService;this.properties = configService.getProperties();this.publish = publisher;}public void listen() {//得到当前项目所有生效的配置List<BlossomConfigProperties.BlossomConfig> configLists =(List<BlossomConfigProperties.BlossomConfig>) this.properties.get(BlossomConfigPropertiesKeyConstants.CONFIG_LISTS);if (configLists.isEmpty()) {return;}//对这些配置进行遍历,为他们添加监听器//使得这些配置发生变更之后我能监听到对应的事件 从而对这些事件进行处理configLists.stream().forEach(config -> {this.configService.subscribeConfigChangeEvent(config.getGroup(),config.getConfigId(),this.publish);});}}

而这里的subscribeConfigChangeEvent就是Client端实现的代码。
那么这里,我们想要让Core项目能知道配置变更,那么我们只要确保我们的Core模块提供的Publish实现能发送事件即可。

public class BlossomConfigChangePublisher extends AbstractConfigChangePublish {private final ApplicationEventPublisher applicationEventPublisher;public BlossomConfigChangePublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}public void publishRemoveEvent(String key) {BlossomConfigChangeEvent event = new BlossomConfigChangeEvent(this, key);applicationEventPublisher.publishEvent(event);}public void publishPublishEvent(String key, ConfigCache configCache) {BlossomConfigChangeEvent event = new BlossomConfigChangeEvent(this, key, configCache);applicationEventPublisher.publishEvent(event);}
}

这样子,一旦我们的Core模块能监听到事件,那么如何刷新@Value注解对应的值,其实就简单了,我们在文章开篇就已经讲解了。
这里我们来看看监听器的实现即可。

package blossom.project.config.core;import blossom.project.config.common.exception.BlossomException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Collections;
import java.util.List;import static blossom.project.config.common.constants.BlossomConstants.SEPARATOR;/*** @author: ZhangBlossom* @date: 2023/12/30 22:49* @contact: QQ:4602197553* @contact: WX:qczjhczs0114* @blog: https://blog.csdn.net/Zhangsama1* @github: https://github.com/ZhangBlossom*/


public class BlossomConfigChangeListener implements ApplicationListener<BlossomConfigChangeEvent> {private static final String REFRESH_SCOPE = "RefreshScope";private ConfigurableApplicationContext applicationContext;private BeanDefinitionRegistry beanDefinitionRegistry;private Environment environment;public void init() {ScopeRegistry scopeRegistry = applicationContext.getBean(ScopeRegistry.class);this.beanDefinitionRegistry = scopeRegistry.getBeanDefinitionRegistry();}public void onApplicationEvent(BlossomConfigChangeEvent event) {// 处理配置更改事件if (event.getConfigCache() != null) {// 处理发布事件System.out.println("Config published: " + event.getKey());doPublishEvent(event);} else {// 处理删除事件System.out.println("Config removed: " + event.getKey());doRemoveEvent(event);}}/*** 处理配置变更事件** @param event*/private void doPublishEvent(BlossomConfigChangeEvent event) {//1:根据event中的key 找到对应的配置String key = event.getKey();//2:根据新的content信息,解析完毕之后,重新添加到Environemnt中String content = event.getConfigCache().getContent();//得到文件解析格式String type = event.getConfigCache().getType();List<PropertySource<?>> newPropertySources = Collections.emptyList();//得到配置文件的内容if (StringUtils.isBlank(content)) {log.warn("the data from ConfigService is empty, key:{}", key);return;}//在spring中想要将配置解析为PropertySource可以用自带的解析器--只提供了yaml和properties//也就是说json/xml等其他格式需要自己实现String configId = parseKey(key);try {newPropertySources =BlossomConfigDataHandler.getInstance().parseConfigData(configId, content, type);} catch (IOException e) {throw new RuntimeException(e);}// 将新的PropertySource添加到Environment中for (PropertySource<?> propertySource : newPropertySources) {((ConfigurableEnvironment) environment).getPropertySources().addFirst(propertySource);}// 触发环境变更事件,以刷新@Value注解的值applicationContext.publishEvent(new EnvironmentChangeEvent(applicationContext, Collections.singleton(event.getKey())));// 刷新带有RefreshScope注解的BeanrefreshScopedBeans();}/*** 刷新@Value注解的值*/private void refreshScopedBeans() {String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(beanDefinitionName);if (REFRESH_SCOPE.equalsIgnoreCase(beanDefinition.getScope())) {applicationContext.getBeanFactory().destroyScopedBean(beanDefinitionName);applicationContext.getBean(beanDefinitionName);}}}/*** 处理配置删除事件** @param event*/private void doRemoveEvent(BlossomConfigChangeEvent event) {//1:删除对应的Environment//2: 不刷新@Value}/*** 根据key返回configid* @param key* @return*/private String parseKey(String key){return key.substring(key.lastIndexOf(SEPARATOR));}}

在上面的Listener代码中,我们就顺利的完成了Core模块对Client模块的整合,完成了变更事件的监听以及变更事件的处理。
代码比较好理解,不做过多的解释了。
其实,完成上面的代码之后,一个非常简易的配置中心就做完了,上面的代码已经可以完成配置的加载和变更了。
如果代码只是写到这里,那么这个项目也只是类似于一个Demo,帮助我们了解Spring与配置中心的关系。
但是亮点并不多,只能说帮助你和面试官聊天的时候聊到这一块有一些说辞。
所以,我打算在上面的版本中,进行一下简单的优化,用上一些”花里胡哨“的功能。

什么是配置中心?以及如何实现一个配置中心?

SpringBoot如何实现配置的管控?

SpringCloud项目是如何对bootstrap配置文件进行加载的?

Nacos是如何实现配置文件的读取加载的?

开发配置中心前必须了解的前置知识

配置中心Server和Client端代码的编写

配置中心Core核心功能代码的编写

配置中心源码优化—本地缓存与读写锁

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

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

相关文章

关于 MySQL 优化(详解)

文章目录 关于 MySQL 优化一、硬件方面的优化1、关于 CPU2、关于内存3、关于磁盘 二、MySQL 配置文件1、 default-time-zone8:002、interactive_timeout 1203、wait_timeout 1204、open_files_limit 102405、group_concat_max_len 1024006、usermysql7、character-set-serv…

深入探索Yarn:安装与使用指南

Yarn 是一个由 Facebook 开发的 JavaScript 包管理器&#xff0c;旨在提供更快、更可靠的包管理体验。它与 npm 类似&#xff0c;但在某些方面更加高效和可靠。本文将介绍如何安装 Yarn&#xff0c;并展示如何使用它来管理 JavaScript 项目的依赖。 1. 安装 Yarn Yarn 可以通…

Web软件测试面试总结

整理下最近遇到的Web软件测试会被问到的问题&#xff0c;来自测下你掌握得怎么样&#xff1f; 一、编程语言相关 1、字符串反转 s "I am Donny" reversed_s s[::-1] print(reversed_s)这将输出 “ynnoD ma I”。 2、基础数据类型 基本数据类型&#xff08;Prim…

三步提升IEDA下载速度——修改IDEA中镜像地址

找到IDEA的本地安装地址 D:\tool\IntelliJ IDEA 2022.2.4\plugins\maven\lib\maven3\conf 搜索阿里云maven仓库 复制https://developer.aliyun.com/mvn/guide中红框部分代码 这里也是一样的&#xff1a; <mirror><id>aliyunmaven</id><mirrorOf>*&…

【c++初阶】类与对象(下)

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

ESP32学习---ESP-NOW

ESP32学习---ESP-NOW 基于Arduino IDE环境获取mac地址单播通讯一对多通讯多对一通讯多对多通讯模块1代码模块2模块3 广播通讯 基于ESP-IDF框架 乐鑫编程指南中关于ESP-NOW的介绍&#xff1a;https://docs.espressif.com/projects/esp-idf/zh_CN/v5.2.1/esp32/api-reference/net…

7.1 Mysql shell 定时备份

直接上脚本----linu 定时任务执行 #!/bin/bash# 配置信息 DB_USER"your_username" # 数据库用户名 DB_PASSWORD"your_password" # 数据库密码 DB_NAME"your_database_name" # 要备份的数据库名 BACKUP_DIR"/path/to/backup/directory"…

深度学习训练中的种子设置

文章目录 深度学习训练中的种子设置1. 为什么需要设置随机种子2. 随机种子的设置及使用 深度学习训练中的种子设置 1. 为什么需要设置随机种子 在神经网络训练过程中&#xff0c;经常会通过随机的方式对一些数据进行初始化&#xff1a; 1、随机权重&#xff0c;网络有些部分…

C语言--实现判断输入数字是几位数的函数

用起来很方便&#xff0c;代码如下 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int digit (int x){int count 0;do {count;x x / 10;} while (x > 0); return count;} int main() {int x 0;scanf("%d", &x);int number digit(x);printf…

如何在Ubuntu系统部署Z-blog博客结合cpolar实现无公网IP访问本地网站

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…

CrossOver玩游戏会损害电脑吗 CrossOver玩游戏会卡吗 Mac玩游戏 crossover24免费激活

CrossOver是一款可以在macOS上运行Windows应用程序的软件&#xff0c;它利用了Wine技术&#xff0c;无需安装虚拟机或双系统&#xff0c;可以直接在苹果系统下运行Windows游戏。那么&#xff0c;使用CrossOver玩游戏会损害电脑吗&#xff1f;CrossOver玩游戏会卡吗&#xff1f;…

LLaMA-Factory参数的解答

打开LLaMA-Factory的web页面会有一堆参数 &#xff0c;但不知道怎么选&#xff0c;选哪个&#xff0c;这个文章详细解读一下&#xff0c;每个参数到底是什么含义这是个人写的参数解读&#xff0c;我并非该领域的人如果那个大佬看到有参数不对请反馈一下&#xff0c;或者有补充的…

【软件测试】教程及案例

软件测试是软件开发过程中的关键环节&#xff0c;它确保软件产品符合设计要求并且能够在各种条件下正常运行。以下是关于软件测试的教程和案例的详细介绍&#xff1a; ### 软件测试基础 软件测试的目的是发现和修复软件中的缺陷&#xff0c;以确保软件的质量。测试可以在软件…

面试题(六)

目录 101.RocketMQ的事务消息是如何实现的 102.为什么RocketMQ不使⽤Zookeeper作为注册中⼼呢&#xff1f; 103.RocketMQ的实现原理 104.RocketMQ为什么速度快 105.消息队列如何保证消息可靠传输 106.消息队列有哪些作⽤ 107.死信队列是什么&#xff1f;延时队列是什么&a…

【Entity Framework】EF中的增删改查

【Entity Framework】EF中的增删改查 文章目录 【Entity Framework】EF中的增删改查一、概述二、DbContext数据上下文三、EntityState五个状态值四、EF添加数据4.1 EF Add方式4.2 EF 通过改变对象的状态为 Added4.3 调用方sql4.4 调用存储过程 五、EF修改数据5.1 不查询数据库&…

Pytorch:Pytorch入门基础

文章目录 一、PyTorch概述二、Pytorch基础数据结构三、Tensorflow和Pytorch的区别TensorFlow和PyTorch的区别 四、导入Pytorch库 学习参考于&#xff1a; 与凤行——上古神君&#xff1a;Pytorch数据结构 一、PyTorch概述 PyTorch是一个开源的机器学习库&#xff0c;用于计算…

项目管理系统在制造业的应用,提高生产效率的秘诀与解决方案

缩短产品交货周期&#xff0c;提高产品交付率是当下很多制造业面临的难题&#xff0c;项目管理系统业务流程自动化&#xff0c;能够显著改善项目效率。接下来我们说一说项目管理系统在制造业的应用&#xff0c;项目管理系统制造业解决方案。 制造业典型的项目背景 随着企业体量…

学习【Redis原理篇】这一篇就够了

目录 1. 数据结构1-1. 动态字符串&#xff08;SDS&#xff09;1-2. intset1-3. Dict 2. 网络模型3. 通信协议4. 内存策略 1. 数据结构 1-1. 动态字符串&#xff08;SDS&#xff09; 我们都知道Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字…

【Java项目】基于SpringBoot的【就业信息管理系统】

在当今这个科技迅猛发展的时代&#xff0c;计算机技术在生活中扮演着至关重要的角色&#xff0c;特别是在信息管理领域。在这样的背景下&#xff0c;学习计算机知识不只是简单地掌握一项技能&#xff0c;更关键的是将所学知识应用于实际&#xff0c;以创新的思维不断简化人们的…

JAVA 100道题(24)

24.使用Java的线程池&#xff08;ExecutorService&#xff09;执行一组任务。 在Java中&#xff0c;ExecutorService是一个用于管理和控制线程的工具&#xff0c;它允许你提交任务给线程池来异步执行。下面是一个使用ExecutorService来执行一组任务的简单示例&#xff1a; java…