微服务系列四:热更新措施与配置共享

news2024/11/27 8:48:20

目录

前言

一、基于Nacos的管理中心整体方案

二、配置共享动态维护

2.1 分析哪些配置可拆,需要动态提供哪些参数

2.2 在nacos 分别创建共享配置

创建jdbc相关配置文件

 创建日志相关配置文件

创建接口文档配置文件

2.3 拉取本地合并配置文件

2.3.1 拉取出现的先后顺序问题——SpringCloud 和 SpringBoot 读取顺序问题

2.3.2 解决措施——添加引导配置文件,放在Springcloud初始化时读取

.2.3.3 引入依赖

2.3.4 新增bootstrap.yaml文件

2.3.5 重启测试购物车功能

2.3.6 同理修改其他微服务的配置文件

三、配置热更新(不停机更新)

3.1 前提条件

3.2 业务实践——热更新购物车容量

3.3 测试热更新效果

四、网关动态路由监听

监听Nacos配置变更

第一步:注入Nacos依赖

第二步:配置bootstrap.yaml文件

第三步:修改application.yaml文件,删除路由

第四步:编写动态路由加载器框架

第五步:更新路由方法拆解

【问题】如何获取RouteDefinition对象?如何更好的转换RouteDefinition对象?

第六步:动态路由加载器完整实现

第七步:在Nacos配置初始化的JSON格式路由

第八步:无需重启,测试网关生效

五、Nacos配置相关知识追问巩固


实验环境说明

本文有部分地方需要实验进行。首先对于看过黑马微服务的同学应该会比较熟悉。如果没有你也可以参考我实验的环境搭个简单的测试环境,用于实验。实验的目的也是为了更好的理解业务、理解知识点。

本地环境部分:

服务名端口号备注
nginx18080 / 18081前端运行环境
hm-gateway8080后端项目网关模块
item-service8081后端商品服务模块
cart-service8082 后端购物车服务模块
item-service28083后端商品服务模块
item-service38084后端商品服务模块
user-service8085后端用户管理模块
trade-service8086后端交易服务模块
pay-service8087后端支付服务模块

远程服务器环境部分:

服务名端口号备注
nacos8848注册中心及配置中心,非容器镜像
nginx18080/18081线上前端运行环境,容器镜像
mysql3306线上数据库,容器镜像
docker-hm8080线上后端运行环境,容器镜像

注意事项:

  • 远程环境中非容器镜像指nacos是后单独配置的容器,而mysql、nginx、docker-hm是使用compose统一部署的。
  • 在本节实验中,线上环境的nginx和docker-hm我们不会使用到,而是使用本地的nginx和后端项目
  • 请你确保在配置线上环境时,mysql必须先比nacos启动。如果nacos先启动将无法连接数据库。此时你需要停止nacos容器,先启动mysql。


前言

这是微服务相关技术学习记录的第四篇文章啦!到此为止,微服务开发相关的技术其实已经分享了七七八八了。本篇则是着重介绍微服务配置文件管理相关、配置更新相关的内容。

回顾先前我们学习的微服务开发技术,我们已经解决了以下几个问题:

  • 实现微服务远程调用 (OpenFeign)

  • 实现微服务注册、发现  (nacos)

  • 微服务请求路由、负载均衡  (nacos)

  • 微服务登录用户信息传递  (网关 + 拦截器)

还有哪些问题需要解决呢?

试想一下,假如网关路由发生了更新,你是打算重新启动网关模块更新配置么?那么停机更新会导致服务不可用怎么办?

再来,目前每个微服务都需要编写配置文件,但是实际上里面很多内容是一样的。如果对这一部分公共内容进行修改,是不是又得大规模停机更新微服务呢?

这就概括出了目前微服务配置方面存在的弊端和我们本篇改进的方向:

  • 如何简化重复配置,降低维护成本?
  • 如何实现项目配置的不停机维护?
  • 如何实现网关路由的动态维护?

一、基于Nacos的管理中心整体方案

上述问题我们不难想到,那就将所有配置文件统一管理起来,成立一个管理中心即可。确实,这个管理中心还可以由我们的Nacos一并担任!

整体方案图示

微服务共享的配置可以统一交给Nacos保存和管理,在Nacos控制台修改配置后,Nacos会将配置变更推送给相关的微服务,并且无需重启即可生效,实现配置热更新。

网关的路由同样是配置,因此同样可以基于这个功能实现动态路由功能,无需重启网关即可修改路由配置。

二、配置共享动态维护

我们可以把微服务共享的配置抽取到Nacos中统一管理,这样就不需要每个微服务都重复配置了。

主要步骤如下:

  • 在Nacos中添加共享配置

  • 微服务拉取配置

配置共享最大的好处就是将重复配置封装成一份交Nacos统一管理。其他微服务只需要提供一些配置参数就行了,极大的简化了开发。接下来我们以cart-service为例,实现配置文件拆分。

2.1 分析哪些配置可拆,需要动态提供哪些参数

2.2 在nacos 分别创建共享配置

在 配置管理->配置列表 中点击 新建一个配置:


创建jdbc相关配置文件

spring:
  datasource:
    url: jdbc:mysql://${hm.db.host:192.168.186.140}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: ${hm.db.un:root}
    password: ${hm.db.pw:123}
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto
  • 数据库ip:通过${hm.db.host:192.168.150.101}配置了默认值为192.168.150.101,同时允许通过${hm.db.host}来覆盖默认值

  • 数据库端口:通过${hm.db.port:3306}配置了默认值为3306,同时允许通过${hm.db.port}来覆盖默认值

  • 数据库database:可以通过${hm.db.database}来设定,无默认值


 创建日志相关配置文件

logging:
  level:
    com.hmall: debug
  pattern:
    dateformat: HH:mm:ss:SSS
  file:
    path: "logs/${spring.application.name}"

创建接口文档配置文件

knife4j:
  enable: true
  openapi:
    title: ${hm.swagger.title:黑马商城接口文档}
    description: ${hm.swagger.description:黑马商城接口文档}
    email: ${hm.swagger.email:weizhicong@stu.gpnu.cn}
    concat: ${hm.swagger.concat:weizhicong}
    url: https://www.weizhicong.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - ${hm.swagger.package}
  • title:接口文档标题,我们用了${hm.swagger.title}来代替,将来可以有用户手动指定

  • email:联系人邮箱,我们用了${hm.swagger.email:weizhicong@stu.gpnu.cn},默认值是weizhicong@stu.gpnu.cn,同时允许用户利用${hm.swagger.email}来覆盖。

2.3 拉取本地合并配置文件

现在线上的配置文件创建好了,我们需要想办法让微服务项目接下来拉取共享配置。将拉取到的共享配置与本地的application.yaml配置合并,完成项目上下文的初始化。但是这其中存在一些拉取的问题,让我们逐步分析。

2.3.1 拉取出现的先后顺序问题——SpringCloud 和 SpringBoot 读取顺序问题

读取Nacos配置是发生在SpringCloud上下文初始对象(ApplicationContext)时发生的,而Nacos地址配置在application.yaml却是SpringBoot启动时才发生的。也就是说在读取Nacos配置时,根本不知道去哪里读取Nacos地址信息。从而造成无法加载Nacos配置文件的问题。

2.3.2 解决措施——添加引导配置文件,放在Springcloud初始化时读取

.2.3.3 引入依赖

在cart-service模块引入依赖:

  <!--nacos配置管理-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
  <!--读取bootstrap文件-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
  </dependency>

2.3.4 新增bootstrap.yaml文件

在cart-service中的resources目录新建一个bootstrap.yaml文件:

spring:
  application:
    name: cart-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.186.140 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-jdbc.yaml # 共享mybatis配置
          - dataId: shared-log.yaml # 共享日志配置
          - dataId: shared-swagger.yaml # 共享日志配置

2.3.5 重启测试购物车功能

2.3.6 同理修改其他微服务的配置文件

user-service成功!

trade-service成功!

item-service成功!

pay-service成功!

联调测试成功!


三、配置热更新(不停机更新)

配置热更新 :当我们修改配置文件中的参数属性时,无需重启服务即可生效。

3.1 前提条件

3.2 业务实践——热更新购物车容量

购物车业务,购物车数量有一个上限,默认是10,对应代码如下:

第一步:创建容器配置类,并添加@ConfigurationProperties注解指定服务配置

第二步:注入使用修改业务代码

第三步:增加Nacos配置

文件名称由三部分组成:[服务名]-[spring.active.profile].[后缀名]

  • 服务名:我们是购物车服务,所以是cart-service

  • spring.active.profile:就是spring boot中的spring.active.profile,可以省略,则所有profile共享该配置

  • 后缀名:例如yaml

3.3 测试热更新效果

四、网关动态路由监听

目前,我们的网关路由是写死在配置文件的。一旦路由有所变动,必须重启网关才能生效。对此我们希望网关路由也能和上一小节的配置热更新一样,动态维护,无需停机即可更新。

但是,网关的路由配置全部是在项目启动时由org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator在项目启动的时候加载,并且一经加载就会缓存到内存中的路由表内(一个Map),不会改变。也不会监听路由变更,

所以,我们无法利用上节课学习的配置热更新来实现路由更新。

因此,我们必须监听Nacos的配置变更,然后手动把最新的路由更新到路由表中。这里有两个难点:

  • 如何监听Nacos配置变更?

  • 如何把路由信息更新到路由表?

监听Nacos配置变更

官方文档:Nacos 监听配置 Java SDKicon-default.png?t=O83Ahttps://nacos.io/zh-cn/docs/sdk.html

第一步:注入Nacos依赖

第二步:配置bootstrap.yaml文件

spring:
  application:
    name: hm-gateway # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.186.140:8848 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-log.yaml # 共享日志配置

第三步:修改application.yaml文件,删除路由

server:
  port: 8080 # 网关端口 前端请求统一处理

hm:
  jwt:
    location: classpath:hmall.jks
    alias: hmall
    password: hmall123
    tokenTTL: 30m
  auth:
    excludePaths:
      - /search/**
      - /users/login
      - /items/**
      - /hi

第四步:编写动态路由加载器框架


@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    // 用来获取ConfigServer中的配置信息,与nacos建立连接
    private final NacosConfigManager nacosConfigManager;

    private final String dataId = "gateway-routers.json";

    private final String group = "DEFAULT_GROUP";

    @PostConstruct // 初始化方法,这个Bean一初始化就会执行
    public void initRouteConfigListener() throws NacosException {

        //1. 项目启动,先拉取一次配置,并且添加监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 3000, new Listener() {
                    @Override
                    // 线程池
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override

                    public void receiveConfigInfo(String s) {
                        //TODO 2. 监听器,当配置发生变化时,需要更新路由表

                    }
                });

        // 3.第一次读取到配置,也需要更新到路由表
        updateConfigInfo(configInfo);
    }

    public void updateConfigInfo(String configInfo) {
        // TODO 更新路由表
    }
}

第五步:更新路由方法拆解

更新路由要用到org.springframework.cloud.gateway.route.RouteDefinitionWriter这个接口:

【问题】如何获取RouteDefinition对象?如何更好的转换RouteDefinition对象?

我们知道通过前面编写的这个方法可以获取到字符串格式的配置文件,保存在configInfo。它就和我们的配置文件一模一样,只不过是字符串表示。

所以第一个问题:如何获取RouteDefinition对象?我们需要解析字符串形式的yaml文件,将它转成java对象。

但是啊,将yaml文件转成Java的对象比较困难,那么什么格式转java对象比较方便呢?——JSON格式。

所以第二个问题:如何更好的转换RouteDefinition对象?我们将路由等信息存储成JSON格式而不是yaml格式,这样可以更好地转换成Java对象

第六步:动态路由加载器完整实现

package com.hmall.gateway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.hmall.common.utils.CollUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    private final RouteDefinitionWriter writer;
    // 用来获取ConfigServer中的配置信息,与nacos建立连接
    private final NacosConfigManager nacosConfigManager;

    // 配置文件id 和 分组
    private final String dataId = "gateway-routers.json";
    private final String group = "DEFAULT_GROUP";

    // 保存更新过的路由id
    private final Set<String> routeIds = new HashSet<>();

    
    @PostConstruct // 初始化方法,这个Bean一初始化就会执行
    public void initRouteConfigListener() throws NacosException {

        //1. 项目启动,先拉取一次配置,并且添加监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 3000, new Listener() {
                    @Override
                    // 线程池
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override

                    public void receiveConfigInfo(String configInfo) {
                        //2. 监听器,当配置发生变化时,需要更新路由表
                        updateConfigInfo(configInfo);
                    }
                });

        // 3.第一次读取到配置,也需要更新到路由表
        updateConfigInfo(configInfo);
    }

    /**
     * configInfo 是nacos中配置的json字符串
     * @param configInfo
     */
    public void updateConfigInfo(String configInfo) {
        // 更新路由表
        log.debug("监听到路由配置变更,{}", configInfo);
        // 1.反序列化
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        // 2.更新前先清空旧路由
        // 2.1.清除旧路由
        for (String routeId : routeIds) {
            writer.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();
        // 2.2.判断是否有新的路由要更新
        if (CollUtils.isEmpty(routeDefinitions)) {
            // 无新路由配置,直接结束
            return;
        }
        // 3.更新路由
        routeDefinitions.forEach(routeDefinition -> {
            // 3.1.更新路由
            writer.save(Mono.just(routeDefinition)).subscribe();
            // 3.2.记录路由id,方便将来删除
            routeIds.add(routeDefinition.getId());
        });
    }
}

第七步:在Nacos配置初始化的JSON格式路由

当前还没配置Nacos时,此时的网关没有任何路由,可以测试一下:

第八步:无需重启,测试网关生效

五、Nacos配置相关知识追问巩固

1. 如何简化重复配置,降低维护成本?谈谈具体实施步骤

2. 如何解决读取远程Nacos配置时无法从Springboot中获取到Nacos地址的配置信息?

3. 如何实现配置热更新?谈谈具体的实施步骤

4. 如何实现动态路由热更新?谈谈具体的实施步骤

5. 请你谈谈 @PostConstruct 注解的作用?

6. 请你谈谈监听Nacos配置中如何获取Nacos的ConfigServer中的配置信息?

7. 谈谈更新路由监听器方法是怎么实现的?你是否赞同采用先删除后更新的策略,为什么?

8. Nacos提供了RouteDefinitionWriter接口用于实现添加路由,路由对象是RouteDefinition类型的。请问你在路由监听中如何获取该对象?如何更好、更快的获取该对象?

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

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

相关文章

NoETL自动化指标平台为数据分析提质增效,驱动业务决策

直觉判断往往来源于多年的经验和专业知识&#xff0c;能够在复杂和不确定的环境中快速做出决策反应。但这种方式普遍存在主观偏见&#xff0c;缺乏合理的科学依据&#xff0c;无法全面、客观、精准地评估和识别市场趋势与用户需求&#xff0c;从而造成决策失误&#xff0c;给业…

使用亚马逊 S3 连接器为 PyTorch 和 MinIO 创建地图式数据集

在深入研究 Amazon 的 PyTorch S3 连接器之前&#xff0c;有必要介绍一下它要解决的问题。许多 AI 模型需要使用无法放入内存的数据进行训练。此外&#xff0c;许多为计算机视觉和生成式 AI 构建的真正有趣的模型使用的数据甚至无法容纳在单个服务器附带的磁盘驱动器上。解决存…

基于MATLAB的实现垃圾分类Matlab源码

⼀、垃圾分类 如何通过垃圾分类管理&#xff0c;最⼤限度地实现垃圾资源利⽤&#xff0c;减少垃圾处置量&#xff0c;改善⽣存环境质量&#xff0c;是当前世界各国共同关注的迫切问题之⼀。根据国家制定的统⼀标准&#xff0c;现在⽣活垃圾被⼴泛分为四类&#xff0c;分别是可…

硬件基础10 逻辑门电路——CMOS

目录 一、门电路类型 二、CMOS逻辑门 1、CMOS基础 2、MOS管开关电路 &#xff08;1&#xff09;、基础理论分析 &#xff08;2&#xff09;、开关动态特性 3、CMOS反相器 4、与非、或非门 三、逻辑门的不同输出结构与参数 1、CMOS的保护和缓冲 2、漏极开路与三态输出…

新手散户如何避免被割?有量化策略适应暴涨暴跌行情吗?|附代码

这是邢不行第 124 期量化小讲堂的分享 作者 | 邢不行 大A今年上半年的行情较为坎坷&#xff0c;市场持续下跌&#xff0c;导致诸多投资者风格大变&#xff0c;从倾向于高风险的进攻策略转为低风险的防御策略&#xff0c;尤以高股息策略和杠铃策略最为火爆。 本文给大家介绍一…

数据链路层Mac协议与ARP协议

Mac帧 ​ ​ 如何将有效载荷和报头分离&#xff1f; 根据固定大小 ​​ 报头固定大小&#xff0c;按报头大小分离 如何分用&#xff1f; ​​​​ 类型为0800&#xff0c;代表为IP报文&#xff0c;应该交给网络层IP协议 目的地址 原地址为Mac地址 局域网通信 ​ 局…

轮椅车、医用病床等康复类器具检测设备的介绍

康复类器具检测设备是指用于检测、评估和测试康复类器具的设备。康复类器具包括轮椅、助行器、假肢、矫形器等。这些器具在使用前需要经过检测和评估以确保其满足质量、性能、安全和有效性的要求。 康复类器具的测试项目及其设备主要包括以下几种&#xff1a; 1、力学测试设…

WiFi一直获取不到IP地址是怎么回事?

在当今这个信息化时代&#xff0c;WiFi已成为我们日常生活中不可或缺的一部分。无论是家庭、办公室还是公共场所&#xff0c;WiFi都为我们提供了便捷的无线互联网接入。然而&#xff0c;有时我们可能会遇到WiFi连接后无法获取IP地址的问题&#xff0c;这不仅影响了我们的网络使…

基于SSM+VUE儿童接种疫苗预约管理系统JAVA|VUE|Springboot计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…

城镇住房保障:SpringBoot系统架构解析

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

软件测试—功能测试详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、测试项目启动与研读需求文档 &#xff08;一&#xff09; 组建测试团队 1、测试团队中的角色 2、测试团队的基本责任 尽早地发现软件程序、系统或产…

第十五届蓝桥杯C/C++B组题解——数字接龙

题目描述 小蓝最近迷上了一款名为《数字接龙》的迷宫游戏&#xff0c;游戏在一个大小为N N 的格子棋盘上展开&#xff0c;其中每一个格子处都有着一个 0 . . . K − 1 之间的整数。游戏规则如下&#xff1a; 从左上角 (0, 0) 处出发&#xff0c;目标是到达右下角 (N − 1, N …

【9695】基于springboot+vue的学生就业管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取免费源码 项目描述 本学生就业管理系统以springboot作为框架&#xff…

Android使用scheme方式唤醒处于后台时的App场景

场景&#xff1a;甲App唤醒处于后台时的乙App的目标界面Activity&#xff0c;且乙App的目标界面Activity处于最上层&#xff0c;即已经打开状态&#xff0c;要求甲App使用scheme唤醒乙App时&#xff0c;达到跟从桌面icon拉起App效果一致&#xff0c;不能出现只拉起了乙App的目标…

centos7,yum安装mongodb

yum安装mongodb 1.配置MongoDB的yum源2.安装Mongodb3.启动Mongodb4.配置远程访问5.设置mongo密码 1.配置MongoDB的yum源 1.创建yum源文件&#xff0c;输入命令&#xff1a; vim /etc/yum.repos.d/mongodb-org-5.0.repo然后在文件中输入以下内容并保存&#xff1a; [mongodb-…

SpringBoot项目集成ONLYOFFICE

ONLYOFFICE 文档8.2版本已发布&#xff1a;PDF 协作编辑、改进界面、性能优化、表格中的 RTL 支持等更新 文章目录 前言ONLYOFFICE 产品简介功能与特点Spring Boot 项目中集成 OnlyOffice1. 环境准备2. 部署OnlyOffice Document Server3. 配置Spring Boot项目4. 实现文档编辑功…

【华为HCIP实战课程31(完整版)】中间到中间系统协议IS-IS路由汇总详解,网络工程师

一、IS-IS的汇总 1、可以有效减少在LSP中发布的路由条目,减小对系统资源的占用。 2、会减少LSP报文的扩散,接收到该LSP报文的其他设备路由表中只会出现一条聚合路由。 3、可以避免网络中的路由震荡,提高了网络的稳定性。 4、被聚合的路由可以是IS-IS路由,也可以是被引入…

LabVIEW编程过程中为什么会出现bug?

在LabVIEW编程过程中&#xff0c;Bug的产生往往源自多方面原因。以下从具体的案例角度分析一些常见的Bug成因和调试方法&#xff0c;以便更好地理解和预防这些问题。 ​ 1. 数据流错误 案例&#xff1a;在一个LabVIEW程序中&#xff0c;多个计算节点依赖相同的输入数据&#…

Vatee万腾平台:让企业数字化转型更轻松、更高效

在数字化浪潮席卷全球的今天&#xff0c;企业数字化转型已成为不可逆转的趋势。然而&#xff0c;对于许多企业来说&#xff0c;数字化转型并非易事&#xff0c;它涉及到技术、人才、流程等多个方面的变革。为了帮助企业顺利实现数字化转型&#xff0c;Vatee万腾平台应运而生&am…

STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入

STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入 参考例程例程说明一、存储到Flash中的数据二、Flash最底层操作(解锁&#xff0c;加锁&#xff0c;擦除&#xff0c;读写)三、从Flash块中读取数据五、测试验证 参考例程 STM32G0xx HAL和LL库Flash读写擦除操…