【Nacos源码分析02-服务配置】

文章目录

  • 服务配置
  • Nacos Config入门
  • Nacos服务端配置发布源码
  • Nacos 服务端监控源码

服务配置

服务配置中心介绍
首先我们来看一下,微服务架构下关于配置文件的一些问题:

  1. 配置文件相对分散。在一个微服务架构下,配置文件会随着微服务的增多变的越来越多,而且分散在各个微服务中,不好统一配置和管理。
  2. 配置文件无法区分环境。微服务项目可能会有多个环境,例如:测试环境、预发布环境、生产环境。每一个环境所使用的配置理论上都是不同的,一旦需要修改,就需要我们去各个微服务下手动维护,这比较困难。
  3. 配置文件无法实时更新。我们修改了配置文件之后,必须重新启动微服务才能使配置生效,这对一个正在运行的项目来说是非常不友好的。基于上面这些问题,我们就需要配置中心的加入来解决这些问题。

配置中心的思路是:
首先把项目中各种配置全部都放到一个集中的地方进行统一管理,并提供一套标准的接口。当各个服务需要获取配置的时候,就来配置中心的接口拉取自己的配置。当配置中心中的各种参数有更新的时候,也能通知到各个服务实时的过来同步最新的信息,使之动态更新。
在这里插入图片描述

Nacos Config入门

1.导入依赖

   <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency>

2.配置nacos-config
:1)不能使用原来的application.yml作为配置文件,而是新建一个bootstrap.yml作为配置文件;2)在bootstrap和application数据项相同时,bootstrap中的配置不会被覆盖;
配置文件优先级(由高到低):
bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml

    spring:application:name: service-namecloud:nacos:config:server-addr: localhost:8848 # nacos的服务端地址file-extension: yaml # 配置文件格式profiles:active: dev # 环境标识

3.自动装配
在这里插入图片描述

Nacos服务端配置发布源码

1.组装请求参数

@PostMapping@TpsControl(pointName = "ConfigPublish")@Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG)public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,@RequestParam(value = "dataId") String dataId,@RequestParam(value = "group") String group,@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,@RequestParam(value = "content") String content, @RequestParam(value = "tag", required = false) String tag,@RequestParam(value = "appName", required = false) String appName,@RequestParam(value = "src_user", required = false) String srcUser,@RequestParam(value = "config_tags", required = false) String configTags,@RequestParam(value = "desc", required = false) String desc,@RequestParam(value = "use", required = false) String use,@RequestParam(value = "effect", required = false) String effect,@RequestParam(value = "type", required = false) String type,@RequestParam(value = "schema", required = false) String schema) throws NacosException {// //加密Pair<String, String> pair = EncryptionHandler.encryptHandler(dataId, content);content = pair.getSecond();// 参数校验ParamUtils.checkTenant(tenant);ParamUtils.checkParam(dataId, group, "datumId", content);ParamUtils.checkParam(tag);//组装请求参数ConfigForm configForm = new ConfigForm();configForm.setDataId(dataId);configForm.setGroup(group);configForm.setNamespaceId(tenant);configForm.setContent(content);configForm.setTag(tag);configForm.setAppName(appName);configForm.setSrcUser(srcUser);configForm.setConfigTags(configTags);configForm.setDesc(desc);configForm.setUse(use);configForm.setEffect(effect);configForm.setType(type);configForm.setSchema(schema);if (StringUtils.isBlank(srcUser)) {configForm.setSrcUser(RequestUtil.getSrcUserName(request));}if (!ConfigType.isValidType(type)) {configForm.setType(ConfigType.getDefaultType().getType());}ConfigRequestInfo configRequestInfo = new ConfigRequestInfo();configRequestInfo.setSrcIp(RequestUtil.getRemoteIp(request));configRequestInfo.setRequestIpApp(RequestUtil.getAppName(request));configRequestInfo.setBetaIps(request.getHeader("betaIps"));String encryptedDataKey = pair.getFirst();return configOperationService.publishConfig(configForm, configRequestInfo, encryptedDataKey);}

2.添加或者更新配置

*** Adds or updates non-aggregated data.** @throws NacosException NacosException.*/public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo, String encryptedDataKey)throws NacosException {//配置信息转mapMap<String, Object> configAdvanceInfo = getConfigAdvanceInfo(configForm);//参数校验ParamUtils.checkParam(configAdvanceInfo);if (AggrWhitelist.isAggrDataId(configForm.getDataId())) {LOGGER.warn("[aggr-conflict] {} attempt to publish single data, {}, {}", configRequestInfo.getSrcIp(),configForm.getDataId(), configForm.getGroup());throw new NacosApiException(HttpStatus.FORBIDDEN.value(), ErrorCode.INVALID_DATA_ID,"dataId:" + configForm.getDataId() + " is aggr");}final Timestamp time = TimeUtils.getCurrentTime();// 构建ConfigInfo配置信息,发布配置最基本的五个参数: nameSpaceId、groupId、dataId、应用名称、配置内容ConfigInfo configInfo = new ConfigInfo(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(),configForm.getAppName(), configForm.getContent());configInfo.setType(configForm.getType());configInfo.setEncryptedDataKey(encryptedDataKey);// 判断是否是beta测试版本if (StringUtils.isBlank(configRequestInfo.getBetaIps())) {// 正常发布,大部分情况下,我们都没有指定tagif (StringUtils.isBlank(configForm.getTag())) {// 1、插入 or 更新配置信息// 这里分为内置数据库(EmbeddedConfigInfoPersistServiceImpl)和外置数据库(ExternalConfigInfoPersistServiceImpl)操作,通常我们都是使用MySQL进行持久化存储configInfoPersistService.insertOrUpdate(configRequestInfo.getSrcIp(), configForm.getSrcUser(),configInfo, time, configAdvanceInfo, false);ConfigChangePublisher.notifyConfigChange(new ConfigDataChangeEvent(false, configForm.getDataId(), configForm.getGroup(),configForm.getNamespaceId(), time.getTime()));} else {configInfoTagPersistService.insertOrUpdateTag(configInfo, configForm.getTag(),configRequestInfo.getSrcIp(), configForm.getSrcUser(), time, false);ConfigChangePublisher.notifyConfigChange(new ConfigDataChangeEvent(false, configForm.getDataId(), configForm.getGroup(),configForm.getNamespaceId(), configForm.getTag(), time.getTime()));}} else {// 数据插入或者更新configInfoBetaPersistService.insertOrUpdateBeta(configInfo, configRequestInfo.getBetaIps(),configRequestInfo.getSrcIp(), configForm.getSrcUser(), time, false);ConfigChangePublisher.notifyConfigChange(new ConfigDataChangeEvent(true, configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(),time.getTime()));}// 日志跟踪ConfigTraceService.logPersistenceEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(),configRequestInfo.getRequestIpApp(), time.getTime(), InetUtils.getSelfIP(),ConfigTraceService.PERSISTENCE_EVENT_PUB, configForm.getContent());return true;}
    public void insertOrUpdateBeta(final ConfigInfo configInfo, final String betaIps, final String srcIp,final String srcUser, final Timestamp time, final boolean notify) {// 没有直接判断是新增还是更新,而且依赖数据库唯一性做检查,重复了(报主键冲突,说明已存在)就做更新try {//往数据库中添加信息addConfigInfo4Beta(configInfo, betaIps, srcIp, null, time, notify);} catch (DataIntegrityViolationException ive) { // Unique constraint conflict//报错则更新信息updateConfigInfo4Beta(configInfo, betaIps, srcIp, null, time, notify);}}
    public void addConfigInfo4Beta(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser, Timestamp time,boolean notify) {String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName();String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant();String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE);String encryptedDataKey = StringUtils.isBlank(configInfo.getEncryptedDataKey()) ? StringUtils.EMPTY: configInfo.getEncryptedDataKey();try {ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(),TableConstant.CONFIG_INFO_BETA);jt.update(configInfoBetaMapper.insert(Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "content", "md5", "beta_ips","src_ip", "src_user", "gmt_create", "gmt_modified", "encrypted_data_key")),configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5,betaIps, srcIp, srcUser, time, time, encryptedDataKey);} catch (CannotGetJdbcConnectionException e) {LogUtil.FATAL_LOG.error("[db-error] " + e, e);throw e;}}
  public void addConfigInfo(final String srcIp, final String srcUser, final ConfigInfo configInfo,final Timestamp time, final Map<String, Object> configAdvanceInfo, final boolean notify) {tjt.execute(status -> {try {// jdbcTemplate操作,自动插入到数据库表(config_info)中,返回主键idlong configId = addConfigInfoAtomic(-1, srcIp, srcUser, configInfo, time, configAdvanceInfo);String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags");addConfigTagsRelation(configId, configTags, configInfo.getDataId(), configInfo.getGroup(),configInfo.getTenant());// 插入历史数据到表中(his_config_info)historyConfigInfoPersistService.insertConfigHistoryAtomic(0, configInfo, srcIp, srcUser, time, "I");} catch (CannotGetJdbcConnectionException e) {LogUtil.FATAL_LOG.error("[db-error] " + e, e);throw e;}return Boolean.TRUE;});}

addConfigInfoAtomic方法

public long addConfigInfoAtomic(final long configId, final String srcIp, final String srcUser,final ConfigInfo configInfo, Map<String, Object> configAdvanceInfo) {// 取出配置信息final String appNameTmp =StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName();final String tenantTmp =StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant();final String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc");final String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use");final String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect");final String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type");final String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema");final String encryptedDataKey =configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey();// 将配置内容进行MD5加密final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE);KeyHolder keyHolder = new GeneratedKeyHolder();// 根据数据库表获取对应的mapper, 通过插件化的形式, 灵活应对使用不同数据库的场景ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(),TableConstant.CONFIG_INFO);// 将参数转换成对应数据库类型的sql语句,拼接insert into config_info values(....)插入语句final String sql = configInfoMapper.insert(Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_ip", "src_user","gmt_create", "gmt_modified", "c_desc", "c_use", "effect", "type", "c_schema","encrypted_data_key"));// 获取主键名称,默认值为idString[] returnGeneratedKeys = configInfoMapper.getPrimaryKeyGeneratedKeys();try {jt.update(new PreparedStatementCreator() {@Overridepublic PreparedStatement createPreparedStatement(Connection connection) throws SQLException {Timestamp now = new Timestamp(System.currentTimeMillis());// 通过预编译的PreparedStatement,设置每个字段的值PreparedStatement ps = connection.prepareStatement(sql, returnGeneratedKeys);ps.setString(1, configInfo.getDataId());ps.setString(2, configInfo.getGroup());ps.setString(3, tenantTmp);ps.setString(4, appNameTmp);ps.setString(5, configInfo.getContent());ps.setString(6, md5Tmp);ps.setString(7, srcIp);ps.setString(8, srcUser);ps.setTimestamp(9, now);ps.setTimestamp(10, now);ps.setString(11, desc);ps.setString(12, use);ps.setString(13, effect);ps.setString(14, type);ps.setString(15, schema);ps.setString(16, encryptedDataKey);return ps;}}, keyHolder);Number nu = keyHolder.getKey();if (nu == null) {throw new IllegalArgumentException("insert config_info fail");}return nu.longValue();} catch (CannotGetJdbcConnectionException e) {LogUtil.FATAL_LOG.error("[db-error] " + e, e);throw e;}
}

Nacos 服务端监控源码

在这里插入图片描述

1.客户端进行长轮询其实是使用定时线程来定时调用/v1/cs/configs/listener接口实现
路径 Nacos-config服务模块下的ConfigController.java
inner.doPollingConfig执行长轮询请求

     @PostMapping("/listener")@Secured(action = ActionTypes.READ, signType = SignType.CONFIG)public void listener(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);String probeModify = request.getParameter("Listening-Configs");if (StringUtils.isBlank(probeModify)) {LOGGER.warn("invalid probeModify is blank");throw new IllegalArgumentException("invalid probeModify");}//获取客户端需要监听的可能发送变化的配置,计算MD5值probeModify = URLDecoder.decode(probeModify, Constants.ENCODE);Map<String, String> clientMd5Map;try {clientMd5Map = MD5Util.getClientMd5Map(probeModify);} catch (Throwable e) {throw new IllegalArgumentException("invalid probeModify");}// 发送长轮训inner.doPollingConfig(request, response, clientMd5Map, probeModify.length());}

2.发送长轮询请求

/*** long polling the config.*/public String doPollingConfig(HttpServletRequest request, HttpServletResponse response,Map<String, String> clientMd5Map, int probeRequestSize) throws IOException {// 判断当前请求是否为长轮询,如果是,调用LongPollingService的addLongPollingClient()方法if (LongPollingService.isSupportLongPolling(request)) {longPollingService.addLongPollingClient(request, response, clientMd5Map, probeRequestSize);return HttpServletResponse.SC_OK + "";}//如果不是长轮训,就直接返回结果// Compatible with short polling logic.List<String> changedGroups = MD5Util.compareMd5(request, response, clientMd5Map);// Compatible with short polling result.String oldResult = MD5Util.compareMd5OldResult(changedGroups);String newResult = MD5Util.compareMd5ResultString(changedGroups);String version = request.getHeader(Constants.CLIENT_VERSION_HEADER);if (version == null) {version = "2.0.0";}int versionNum = Protocol.getVersionNumber(version);// Before 2.0.4 version, return value is put into header.if (versionNum < START_LONG_POLLING_VERSION_NUM) {response.addHeader(Constants.PROBE_MODIFY_RESPONSE, oldResult);response.addHeader(Constants.PROBE_MODIFY_RESPONSE_NEW, newResult);} else {request.setAttribute("content", newResult);}// Disable cache.response.setHeader("Pragma", "no-cache");response.setDateHeader("Expires", 0);response.setHeader("Cache-Control", "no-cache,no-store");response.setStatus(HttpServletResponse.SC_OK);return HttpServletResponse.SC_OK + "";}

通过scheduler.schedule启动一个定时任务,并延时时间为29.5s
将ClientLongPolling实例本身添加到allSubs队列中,它主要维护一个长轮询的订阅关系。
定时任务执行后,先把ClientLongPolling实例本身从allSubs队列中移除。
通过MD5比较客户端请求的groupKeys是否发生变更,并将变更结果通过response返回给客户端
所谓长轮询就是服务端收到请求之后,不立即返回,而是在延29.5s才把请求结果返回给客户端,这使得客户端和服务端之间在30s之内数据没有发生变化的情况下一直处于连接状态。

/*** Add LongPollingClient.** @param req              HttpServletRequest.* @param rsp              HttpServletResponse.* @param clientMd5Map     clientMd5Map.* @param probeRequestSize probeRequestSize.*/public void addLongPollingClient(HttpServletRequest req, HttpServletResponse rsp, Map<String, String> clientMd5Map,int probeRequestSize) {String str = req.getHeader(LongPollingService.LONG_POLLING_HEADER);String noHangUpFlag = req.getHeader(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER);//获取客户端请求的超时时间,减去500ms后赋值给timeout变量。int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500);// Add delay time for LoadBalance, and one response is returned 500 ms in advance to avoid client timeout.long timeout = -1L;//判断isFixedPolling,如果为true,定时任务将会在30s后开始执行,否则在29.5s后开始执行if (isFixedPolling()) {timeout = Math.max(10000, getFixedPollingInterval());// Do nothing but set fix polling timeout.} else {timeout = Math.max(10000, Long.parseLong(str) - delayTime);long start = System.currentTimeMillis();List<String> changedGroups = MD5Util.compareMd5(req, rsp, clientMd5Map);//和服务端的数据进行MD5对比,如果发送变化则直接返回if (changedGroups.size() > 0) {generateResponse(req, rsp, changedGroups);LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "instant",RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize,changedGroups.size());return;} else if (noHangUpFlag != null && noHangUpFlag.equalsIgnoreCase(TRUE_STR)) {LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "nohangup",RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize,changedGroups.size());return;}}String ip = RequestUtil.getRemoteIp(req);ConnectionCheckResponse connectionCheckResponse = checkLimit(req);if (!connectionCheckResponse.isSuccess()) {generate503Response(req, rsp, connectionCheckResponse.getMessage());return;}// Must be called by http thread, or send response.final AsyncContext asyncContext = req.startAsync();// AsyncContext.setTimeout() is incorrect, Control by oneselfasyncContext.setTimeout(0L);String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER);String tag = req.getHeader("Vipserver-Tag");//scheduler.execute 执行ClientLongPolling线程ConfigExecutor.executeLongPolling(new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag));}
@Override
public void run() {asyncTimeoutFuture = scheduler.schedule(new Runnable() {@Overridepublic void run() {try {//将 ClientLongPolling 实例本身添加到 allSubs 队列中,它主要维护一个长轮询的订阅关系getRetainIps().put(ClientLongPolling.this.ip, System.currentTimeMillis());//定时任务执行后,先把 ClientLongPolling 实例本身从 allSubs 队列中移除allSubs.remove(ClientLongPolling.this);//判断是否为固定轮询if (isFixedPolling()) {LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}",(System.currentTimeMillis() - createTime),"fix", RequestUtil.getRemoteIp((HttpServletRequest)asyncContext.getRequest()),"polling",clientMd5Map.size(), probeRequestSize);//比较数据的 MD5 值,判断是否发生变更List<String> changedGroups = MD5Util.compareMd5((HttpServletRequest)asyncContext.getRequest(),(HttpServletResponse)asyncContext.getResponse(), clientMd5Map);//并将变更的结果通过response返回给客户端if (changedGroups.size() > 0) {sendResponse(changedGroups);} else {sendResponse(null);}} else {LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}",(System.currentTimeMillis() - createTime),"timeout", RequestUtil.getRemoteIp((HttpServletRequest)asyncContext.getRequest()),"polling",clientMd5Map.size(), probeRequestSize);sendResponse(null);}} catch (Throwable t) {LogUtil.defaultLog.error("long polling error:" + t.getMessage(), t.getCause());}}}, timeoutTime, TimeUnit.MILLISECONDS);allSubs.add(this);
}

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

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

相关文章

8.22 PowerBI系列之DAX函数专题-盈亏平衡分析

需求 实现 一、用参数设置固定成本&#xff0c;单位变动成本&#xff0c;与毛利率 1 单位变动成本 generateseries(0,100,1) 2 固定成本 generateseries(0,50000,1) 3 毛利率 generateseries(0,0.4,0.01) 二、度量值 1 总变动成本 [单位变动成本 值]*[销量 值] 2 总成本…

各类电机数学模型相关公式总结 —— 集成芯片驱动

0、背景技术概述 永磁直流电机&#xff08;PMDC&#xff09;、永磁同步电机&#xff08;PMSM&#xff09;、无刷直流电机&#xff08;BLDC&#xff09;以及混合式两相步进电机在小功率应用场景中多采用集成芯片驱动&#xff08;如二合一、三合一驱动芯片&#xff09;的原因主要…

深度学习之非极大值抑制NMS介绍

1. 基本介绍 非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;NMS&#xff09;是深度学习中一种常用的目标检测算法&#xff0c;用于在检测结果中去除冗余的边界框。 在目标检测任务中&#xff0c;通常会使用候选框&#xff08;bounding boxes&#xff09;来表示可…

王道408数据结构CH2_线性表

概述 2 线性表 2.1 基本操作 2.2 顺序表示 线性表的元素从1开始&#xff0c;数组元素下标从0开始 2.2.1 结构体定义 #define Maxsize 50typedef struct{ElemType data[Maxsize];int length; }SqList;#define Initsize 100typedef struct{ElemType *data;int Maxsize ,length;…

Ansible部署 之 zookeeper集群

简介 Ansible是近年来越来越火的一款轻量级运维自动化工具&#xff0c;主要功能为帮助运维实现运维工作的自动化、降低手动操作的失误、提升运维工作效率。常用于自动化部署软件、自动化配置、自动化管理&#xff0c;支持playbook编排。配置简单&#xff0c;无需安装客户端&am…

Github 2024-06-06 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2024-06-06统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Ollama: 本地大型语言模型设置与运行 创建周期:248 天开发语言:Go协议类型:MIT LicenseStar数量:42421 个Fork数量:2724 次关注人…

详细分析Mysql中的SQL_MODE基本知识(附Demo讲解)

目录 前言1. 基本知识2. Demo讲解2.1 ONLY_FULL_GROUP_BY2.2 STRICT_TRANS_TABLES2.3 NO_ZERO_IN_DATE2.4 NO_ENGINE_SUBSTITUTION2.5 ANSI_QUOTES 前言 了解Mysql内部的机制有助于辅助开发以及形成整体的架构思维 对于基本的命令行以及优化推荐阅读&#xff1a; 数据库中增…

完美解决 mysql 报错ERROR 1524 (HY000): Plugin ‘mysql_native_password‘ is not loaded

文章目录 错误描述错误原因解决步骤 跟着我下面的步骤走&#xff0c;解决你的问题&#xff0c;如果解决不了 私信我来给你解决 错误描述 执行ALTER USER root% IDENTIFIED WITH mysql_native_password BY 123456;报错ERROR 1524 (HY000): Plugin mysql_native_password is not …

AI炒股:获取个股的历史成交价格并画出K线图

任务&#xff1a;获取贵州茅台的近几个月的价格数据&#xff0c;绘制k线图&#xff1b; 在deepseek中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个编写Python脚本的任务&#xff0c;具体步骤如下&#xff1a; 用AKShare库获取股票贵州茅台&#xf…

PID算法在电机速度控制上的应用

目录 概述 1 系统硬件框架 1.1 框架介绍 1.2 硬件实物图 2 STM32Cub生成工程 2.1 软件版本信息 2.2 配置参数 ​编辑2.3 生成项目 3 PID算法实现 3.1 概念 3.2 代码实现 4 其他功能实现 4.1 设置电机速度 4.2 PID算法控制电机 4.3 功能函数的调用 5 测试 5.1 …

3072. 将元素分配到两个数组中 II Rust 线段树 + 离散化

题目 给你一个下标从 1 开始、长度为 n 的整数数组 nums 。 现定义函数 greaterCount &#xff0c;使得 greaterCount(arr, val) 返回数组 arr 中 严格大于 val 的元素数量。 你需要使用 n 次操作&#xff0c;将 nums 的所有元素分配到两个数组 arr1 和 arr2 中。在第一次操…

winscp无法上传,删除,修改文件并提示权限不够的分析

使用winscp删除文件,报了个错如下 根据这个错就去百度,网上大部分都是通过下面这种方法解决: 在winscp端进行设置 输入主机名(即IP地址)、用户名和密码,然后点击高级 在箭头所指位置输入sudo + sftp应用程序的路径 先查询 sudo find / -name sftp-server -print点击Sh…

如何让 AI 自动阅读文档样例,编写符合你需求的代码?

&#xff08;注&#xff1a;本文为小报童精选文章。已订阅小报童或加入知识星球「玉树芝兰」用户请勿重复付费&#xff09; 痛点 我本科读的计算机专业。当时编程&#xff0c;讲究的就是个扎实。例如哈夫曼编码用来压缩解压文件&#xff0c;那真的是自己一行行代码写过来的。更…

【Pytorch】计算机视觉项目——卷积神经网络TinyVGG模型图像分类(如何使用自定义数据集)

目录 一、前言二、工作流程回顾三、详细步骤流程1. 环境配置2. 数据准备数据集下载数据存储结构&路径查看图片 3. 数据转换4. 自定义数据集&#xff08;Custom Dataset &#xff09;4.1 方法一&#xff1a;使用ImageFolder加载数据集信息查看张量转图片创建DataLoader 4.2 …

Java Web学习笔记12——JavaScript字符串

String&#xff1a; String字符串对象创建方法有两种&#xff1a; 方式一&#xff1a; var str new String("Hello String"); 方式二&#xff1a; var str "Hello String"; 常见的属性和方法&#xff1a; <!DOCTYPE html> <html lang"…

SwiftUI中ContentUnavailableView的使用(iOS 17、tvOS 17推出的新组件)

iOS 17为SwiftUI带来了一个新的组件ContentUnavailableView&#xff0c;它允许我们向用户呈现一个空状态&#xff0c;而不需要创建自定义错误或者无内容视图。 ContentUnavailableView易于使用&#xff0c;可自定义&#xff0c;并且具有用于空搜索状态的预定义视图。 建议在无…

【C语言】详解函数(下)(庖丁解牛版)

文章目录 1. 前言2. 数组做函数形参3. 函数嵌套调用和链式访问3.1 嵌套调用3.2 链式访问 1. 前言 详解C语言函数(上)的链接&#xff1a;http://t.csdnimg.cn/EGsfe 经过对函数的初步了解之后,相信大家已经对C语言标准库里的函数已经有初步的认知了&#xff0c;并且还学会了如…

设计模式-工厂方法(创建型)

创建型-工厂方法 简单工厂 将被创建的对象称为“产品”&#xff0c;将生产“产品”对象称为“工厂”&#xff1b;如果创建的产品不多&#xff0c;且不需要生产新的产品&#xff0c;那么只需要一个工厂就可以&#xff0c;这种模式叫做“简单工厂”&#xff0c;它不属于23中设计…

nvme-cli常见命令分析

一、背景 nvme-cli命令常常用于获取或者设置SSD参数&#xff0c;比如常见的nvme list&#xff0c;nvme id-ctrl等&#xff0c;都是获取SSD的基本信息&#xff0c;也有nvme admin-passthru用于读取或者设置自定义命令。作为使用者&#xff0c;我们并不知道nvme-cli源码怎么实现…

光波长 深入程度

UV深入程度&#xff08;UVC&#xff0c; UVB&#xff0c; UVA&#xff09;https://mp.weixin.qq.com/s?__bizMzkwNTM0Njk3MA&mid2247483934&idx1&sn92d1ba67ead404e7714af11ec0526786&chksmc0f868ebf78fe1fd0610493e6f49a5d90835a20a829a900746906cda12f2fa12…