通过logstash同步elasticsearch数据

1 概述

logstash是一个对数据进行抽取、转换、输出的工具,能对接多种数据源和目标数据。本文介绍通过它来同步elasticsearch的数据。


2 环境

实验仅仅需要一台logstash机器和两台elasticsearch机器(elasticsearch v7.1.0)。本文用docker来模拟,使用docker-compose来启动elasticsearch容器。
elasticsearch机器A是192.168.243.128:9200。
elasticsearch机器B是192.168.243.128:19200。


2.1 安装elasticsearch机器A

2.1.1 编写docker-compose.yml

version: '2.2'
services:cerebro:image: lmenezes/cerebro:0.8.3container_name: cerebroports:- "9000:9000"command:- -Dhosts.0.host=http://elasticsearch:9200kibana:image: docker.elastic.co/kibana/kibana:7.1.0container_name: kibana7environment:- I18N_LOCALE=zh-CN- XPACK_GRAPH_ENABLED=true- TIMELION_ENABLED=true- XPACK_MONITORING_COLLECTION_ENABLED="true"ports:- "5601:5601"elasticsearch:image: docker.elastic.co/elasticsearch/elasticsearch:7.1.0container_name: es7_01environment:- cluster.name=xttblog- node.name=es7_01- bootstrap.memory_lock=true- "ES_JAVA_OPTS=-Xms512m -Xmx512m"- discovery.seed_hosts=es7_01- cluster.initial_master_nodes=es7_01,es7_02ulimits:memlock:soft: -1hard: -1volumes:- es7data1:/usr/share/elasticsearch/dataports:- 9200:9200elasticsearch2:image: docker.elastic.co/elasticsearch/elasticsearch:7.1.0container_name: es7_02environment:- cluster.name=xttblog- node.name=es7_02- bootstrap.memory_lock=true- "ES_JAVA_OPTS=-Xms512m -Xmx512m"- discovery.seed_hosts=es7_01- cluster.initial_master_nodes=es7_01,es7_02ulimits:memlock:soft: -1hard: -1volumes:- es7data2:/usr/share/elasticsearch/data
volumes:es7data1:driver: locales7data2:driver: local

2.1.2 修改vm.max_map_count

cat >> /etc/sysctl.conf << EOF
vm.max_map_count = 2621440
EOF
sysctl -p

2.1.3 启动

docker-compose up -d 

2.1.3 导入样例数据

localhost:5601,用于访问kibana并导入样例数据
localhost:9000,用于访问cerebro

2.2 安装elasticsearch机器B

2.2.1 编写docker-compose.yml

version: '2.2'
services:cerebro:image: lmenezes/cerebro:0.8.3container_name: cerebro-2ports:- "19000:9000"command:- -Dhosts.0.host=http://elasticsearch:9200kibana:image: docker.elastic.co/kibana/kibana:7.1.0container_name: kibana7-2environment:- I18N_LOCALE=zh-CN- XPACK_GRAPH_ENABLED=true- TIMELION_ENABLED=true- XPACK_MONITORING_COLLECTION_ENABLED="true"ports:- "15601:5601"elasticsearch:image: docker.elastic.co/elasticsearch/elasticsearch:7.1.0container_name: es7_03environment:- cluster.name=xttblog- node.name=es7_03- bootstrap.memory_lock=true- "ES_JAVA_OPTS=-Xms512m -Xmx512m"- discovery.seed_hosts=es7_03- cluster.initial_master_nodes=es7_03,es7_04ulimits:memlock:soft: -1hard: -1volumes:- es7data3:/usr/share/elasticsearch/dataports:- 19200:9200elasticsearch2:image: docker.elastic.co/elasticsearch/elasticsearch:7.1.0container_name: es7_04environment:- cluster.name=xttblog- node.name=es7_04- bootstrap.memory_lock=true- "ES_JAVA_OPTS=-Xms512m -Xmx512m"- discovery.seed_hosts=es7_03- cluster.initial_master_nodes=es7_03,es7_04ulimits:memlock:soft: -1hard: -1volumes:- es7data4:/usr/share/elasticsearch/data
volumes:es7data3:driver: locales7data4:driver: local

2.2.2 修改vm.max_map_count

cat >> /etc/sysctl.conf << EOF
vm.max_map_count = 2621440
EOF
sysctl -p

2.2.3 启动

docker-compose up -d 

2.2 logstash机器上准备python环境

在centos7机器上执行如下命令,可安装python环境:

yum install python2
yum install python-pip

由于python脚本需要导入requests模块哈yaml模块,因此需要执行如下命令在机器上安装python模块:

pip install pyyaml
pip install requests

2.2 logstash机器上准备python脚本

2.2.1 准备migrateConfig.yaml

本文件用于描述源端和目标端是谁,是否开启SSL连接。
一般只需要修改src_ip、dest_ip。

cat > /tmp/migrateConfig.yaml << EOF
es_cluster_new:# 源集群的名称clustername: es_cluster_new# 源Elasticsearch集群的访问地址,加上“http://”。src_ip: http://x.x.x.x:9200# 访问源Elasticsearch集群的用户名和密码,如果为非安全集群则设置为""。src_username: ""src_password: ""# 目标Elasticsearch集群的访问地址,加上“http://”。dest_ip: http://x.x.x.x:9200# 访问目标Elasticsearch集群的用户名和密码,如果为非安全集群则设置为""。dest_username: ""dest_password: ""# only_mapping 可以不定义,默认值为false,需要搭配“migrateMapping.py”使用,表示是否只处理这个文件中mapping地址的索引。#  当设置成false时,则迁移源集群中除“.kibana”和“.*”之外的所有索引数据。#  当设置成true时,则只迁移源集群中和下面mapping的key一致的索引数据;迁移过程中会将索引名称与下面的mapping匹配,如果匹配一致,则使用mapping的value作为目标集群的索引名称;如果匹配不到,则使用源集群原始的索引名称。only_mapping: false# 当only_mapping = true时,mapping用于设置要迁移的索引,key为源集群的索引名字,value为目标集群的索引名字。mapping:test_index_1: test_index_1# only_compare_index 可以不定义,默认值为false,需要搭配“checkIndices.py”使用,当设置为false会比较所有的索引和文档数量,当设置为true只比较索引数量。only_compare_index: false  
EOF

2.2.1 准备migrateTemplate.py

用于迁移索引模板,此文件不需要修改,直接复制。

cat > /tmp/migrateTemplate.py << EOF
# -*- coding:UTF-8 -*-
import sys
import yaml
import requests
import json
import osdef printDividingLine():print("<=============================================================>")def loadConfig(argv):if argv is None or len(argv) != 2:config_yaml = "migrateConfig.yaml"else:config_yaml = argv[1]config_file = open(config_yaml)# config = yaml.load(config_file, Loader=yaml.FullLoader)return yaml.load(config_file)def put_template_to_target(url, template, cluster, template_name, dest_auth=None):headers = {'Content-Type': 'application/json'}create_resp = requests.put(url, headers=headers, data=json.dumps(template), auth=dest_auth, verify=False)if not os.path.exists("templateLogs"):os.makedirs("templateLogs")if create_resp.status_code != 200:print("create template " + url + " failed with response: " + str(create_resp) + ", source template is " + template_name)print(create_resp.text)filename = "templateLogs/" + str(cluster) + "#" + template_namewith open(filename + ".json", "w") as f:json.dump(template, f)return Falseelse:return Truedef main(argv):requests.packages.urllib3.disable_warnings()print("begin to migration template!")config = loadConfig(argv)src_clusters = config.keys()print("process cluster name:")for name in src_clusters:print(name)print("cluster total number:" + str(src_clusters.__len__()))for name, value in config.items():printDividingLine()source_user = value["src_username"]source_passwd = value["src_password"]source_auth = Noneif source_user != "":source_auth = (source_user, source_passwd)dest_user = value["dest_username"]dest_passwd = value["dest_password"]dest_auth = Noneif dest_user != "":dest_auth = (dest_user, dest_passwd)print("start to process cluster name:" + name)source_url = value["src_ip"] + "/_template"response = requests.get(source_url, auth=source_auth, verify=False)if response.status_code != 200:print("*** get all template failed. resp statusCode:" + str(response.status_code) + " response is " + response.text)continueall_template = response.json()migrate_itemplate = []for template in all_template.keys():if template.startswith(".") or template == "logstash":continueif "index_patterns" in all_template[template]:for t in all_template[template]["index_patterns"]:# if "kibana" in template:if t.startswith("."):continuemigrate_itemplate.append(template)for template in migrate_itemplate:dest_index_url = value["dest_ip"] + "/_template/" + templateresult = put_template_to_target(dest_index_url, all_template[template], name, template, dest_auth)if result is True:print('[success] delete success, cluster: %-10s, template %-10s ' % (str(name), str(template)))else:print('[failure] delete failure, cluster: %-10s, template %-10s ' % (str(name), str(template)))if __name__ == '__main__':main(sys.argv)EOF

2.2.1 准备migrateMapping.py

脚本用于迁移索的表结构,此文件不需要修改,直接复制。

cat > /tmp/migrateMapping.py << EOF
# -*- coding:UTF-8 -*-
import sys
import yaml
import requests
import re
import json
import osdef printDividingLine():print("<=============================================================>")def loadConfig(argv):if argv is None or len(argv) != 2:config_yaml = "migrateConfig.yaml"else:config_yaml = argv[1]config_file = open(config_yaml)# config = yaml.load(config_file, Loader=yaml.FullLoader)return yaml.load(config_file)def get_cluster_version(url, auth=None):response = requests.get(url, auth=auth)if response.status_code != 200:print("*** get ElasticSearch message failed. resp statusCode:" + str(response.status_code) + " response is " + response.text)return Falsecluster = response.json()version = cluster["version"]["number"]return Truedef process_mapping(index_mapping, dest_index):# remove unnecessary keysdel index_mapping["settings"]["index"]["provided_name"]del index_mapping["settings"]["index"]["uuid"]del index_mapping["settings"]["index"]["creation_date"]del index_mapping["settings"]["index"]["version"]if "lifecycle" in index_mapping["settings"]["index"]:del index_mapping["settings"]["index"]["lifecycle"]# check aliasaliases = index_mapping["aliases"]for alias in list(aliases.keys()):if alias == dest_index:print("source index " + dest_index + " alias " + alias + " is the same as dest_index name, will remove this alias.")del index_mapping["aliases"][alias]# if index_mapping["settings"]["index"].has_key("lifecycle"):if "lifecycle" in index_mapping["settings"]["index"]:lifecycle = index_mapping["settings"]["index"]["lifecycle"]opendistro = {"opendistro": {"index_state_management":{"policy_id": lifecycle["name"],"rollover_alias": lifecycle["rollover_alias"]}}}index_mapping["settings"].update(opendistro)# index_mapping["settings"]["opendistro"]["index_state_management"]["rollover_alias"] = lifecycle["rollover_alias"]del index_mapping["settings"]["index"]["lifecycle"]# replace synonyms_pathif "analysis" in index_mapping["settings"]["index"]:analysis = index_mapping["settings"]["index"]["analysis"]if "filter" in analysis:filter = analysis["filter"]if "my_synonym_filter" in filter:my_synonym_filter = filter["my_synonym_filter"]if "synonyms_path" in my_synonym_filter:index_mapping["settings"]["index"]["analysis"]["filter"]["my_synonym_filter"]["synonyms_path"] = "/rds/datastore/elasticsearch/v7.10.2/package/elasticsearch-7.10.2/plugins/analysis-dynamic-synonym/config/synonyms.txt"return index_mappingdef getAlias(source, source_auth):# get all indicesresponse = requests.get(source + "/_alias", auth=source_auth)if response.status_code != 200:print("*** get all index failed. resp statusCode:" + str(response.status_code) + " response is " + response.text)exit()all_index = response.json()system_index = []create_index = []for index in list(all_index.keys()):if (index.startswith(".")):system_index.append(index)else:create_index.append(index)return system_index, create_indexdef put_mapping_to_target(url, mapping, cluster, source_index, dest_auth=None):headers = {'Content-Type': 'application/json'}create_resp = requests.put(url, headers=headers, data=json.dumps(mapping), auth=dest_auth, verify=False)if not os.path.exists("mappingLogs"):os.makedirs("mappingLogs")if create_resp.status_code != 200:print("create index " + url + " failed with response: " + str(create_resp) +", source index is " + str(source_index))print(create_resp.text)filename = "mappingLogs/" + str(cluster) + "#" + str(source_index)with open(filename + ".json", "w") as f:json.dump(mapping, f)return Falseelse:return Truedef main(argv):requests.packages.urllib3.disable_warnings()print("begin to migrate index mapping!")config = loadConfig(argv)src_clusters = config.keys()print("begin to process cluster name :")for name in src_clusters:print(name)print("cluster count:" + str(src_clusters.__len__()))for name, value in config.items():printDividingLine()source = value["src_ip"]source_user = value["src_username"]source_passwd = value["src_password"]source_auth = Noneif source_user != "":source_auth = (source_user, source_passwd)dest = value["dest_ip"]dest_user = value["dest_username"]dest_passwd = value["dest_password"]dest_auth = Noneif dest_user != "":dest_auth = (dest_user, dest_passwd)print("start to process cluster:   " + name)# only deal with mapping listif 'only_mapping' in value and value["only_mapping"]:for source_index, dest_index in value["mapping"].iteritems():print("start to process source index" + source_index + ", target index: " + dest_index)source_url = source + "/" + source_indexresponse = requests.get(source_url, auth=source_auth)if response.status_code != 200:print("*** get ElasticSearch message failed. resp statusCode:" + str(response.status_code) + " response is " + response.text)continuemapping = response.json()index_mapping = process_mapping(mapping[source_index], dest_index)dest_url = dest + "/" + dest_indexresult = put_mapping_to_target(dest_url, index_mapping, name, source_index, dest_auth)if result is False:print("cluster name:" + name + ", " + source_index + ":failure")continueprint("cluster name:" + name + ", " + source_index + ":success")else:# get all indicessystem_index, create_index = getAlias(source, source_auth)success_index = 0for index in create_index:source_url = source + "/" + indexindex_response = requests.get(source_url, auth=source_auth)if index_response.status_code != 200:print("*** get ElasticSearch message failed. resp statusCode:" + str(index_response.status_code) + " response is " + index_response.text)continuemapping = index_response.json()dest_index = indexif 'mapping' in value:if index in value["mapping"].keys():dest_index = value["mapping"][index]index_mapping = process_mapping(mapping[index], dest_index)dest_url = dest + "/" + dest_indexresult = put_mapping_to_target(dest_url, index_mapping, name, index, dest_auth)if result is False:print("[failure]: migrate mapping cluster name: " + name + ", " + index)continueprint("[success]: migrate mapping cluster name: " + name + ", " + index)success_index = success_index + 1print("create index mapping success total: " + str(success_index))if __name__ == '__main__':main(sys.argv)EOF

3 同步数据

3.1 同步元数据

python /tmp/migrateTemplate.py
python /tmp/migrateMapping.py

3.2 同步实际数据

准备logstash.conf文件,放在/tmp/目录中即可。需要在logstash.conf指定源端和目标端,待同步数据的索引有哪些。
实际需要改动字段是input.elasticsearch.hosts 、input.elasticsearch.index,output.elasticsearch.hosts。

vim /tmp/logstash.conf,输入以下内容:

input{elasticsearch{# 源Elasticsearch的访问地址,不需要添加协议,添加HTTPS协议会导致报错。hosts =>  ["192.168.243.128:9200"]# 访问源集群的用户名和密码,非安全集群无需配置。# user => "css_logstash"# password => "*****"# 配置待迁移的索引信息,多个索引以逗号隔开,可以使用通配符设置,例如“index*”。index => "kibana_sample_data_flights,kibana_sample_data_ecommerce"docinfo => trueslices => 3size => 3000# 当源集群是HTTPS访问方式时,则设置ssl => false。# ssl => false}}# 移除一些logstash增加的字段。filter {mutate {remove_field => ["@metadata", "@version"]}}output{elasticsearch{# 目标Elasticsearch集群的访问地址hosts =>  ["192.168.243.128:19200"]# 访问目标集群的用户名和密码,非安全集群无需配置。# user => "css_logstash"# password => "*****"# 配置目标集群的索引,以下配置为索引名称和源端保持一致,保持默认。index => "%{[@metadata][_index]}"document_type => "%{[@metadata][_type]}"document_id => "%{[@metadata][_id]}"# 当目标集群是HTTPS访问方式时,则需额外配置以下信息。# 配置集群HTTPS访问证书,CSS集群保持以下不变。     #cacert => "/rds/datastore/logstash/v7.10.0/package/logstash-7.10.0/extend/certs/CloudSearchService.cer" # 是否开启HTTPS通信,HTTPS访问集群则设置为true。#ssl => true# 是否验证服务端Elasticsearch证书,设置为false表示不验证证书。#ssl_certificate_verification => false}}

执行如下命令来启动logstash进程,进程执行完后会自动退出:

docker run -it --rm -v /tmp/logstash.conf:/tmp/logstash.conf docker.elastic.co/logstash/logstash:7.1.0 logstash -f /tmp/logstash.conf

在这里插入图片描述

4 小结

logstash可用于同步elasticsearch的数据,不仅可以进行全量同步,其实还能进行增量同步(数据带时间字段,用该字段进行query即可抽取出增量的数据),虽然本文没演示增量同步。

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

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

相关文章

Puppet 部署应用(Puppet deployment application)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

FIB对芯片反向技术的贡献

目前由于国内在模拟集成电路设计领域的研究较为薄弱&#xff0c;芯片逆向分析便成为大多数模拟集成电路工程师基础实际模拟电路积累经验的有效途径&#xff0c;IC反向设计也成为推动国内集成电路设计进步的有效手段。在IC逆向分析与设计服务中&#xff0c;主要用FBI对IC线路进行…

8.4Prewitt算子边缘检测

基本原理 Prewitt算子是一种用于边缘检测的经典算子&#xff0c;它通过计算图像中像素值的&#xff08;一阶导数&#xff09;梯度来检测边缘。Prewitt算子通常包括两个3x3的卷积核&#xff0c;一个用于检测水平方向上的边缘&#xff0c;另一个用于检测垂直方向上的边缘。 示例…

深入理解数据分析的使用流程:从数据准备到洞察挖掘

数据分析是企业和技术团队实现价值的核心。 5 秒内你能否让数据帮你做出决策&#xff1f; 通过本文&#xff0c;我们将深入探讨如何将原始数据转化为有意义的洞察&#xff0c;帮助你快速掌握数据分析的关键流程。 目录 数据分析的五个核心步骤1. 数据获取常用数据获取方式 2. 数…

CentOS7更换阿里云yum更新源

目前CentOS内置的更新安装源经常报错无法更新&#xff0c;或者速度不够理想&#xff0c;这个时候更换国内的镜像源就是一个不错的选择。 备份内置更新源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 下载阿里云repo源&#xff08;需要系统…

Mysql InnoDB 存储引擎简介

InnoDB 存储引擎是 Mysql 的默认存储引擎&#xff0c;它是由 Innobase Oy 公司开发的 Mysql 为什么默认使用 InnoDB 存储引擎 InnoDB 是一款兼顾高可靠性和高性能的通用存储引擎 在 Mysql 5.5 版本之前&#xff0c;默认是使用 MyISAM 存储引擎&#xff0c;在 5.5 及其之后版…

监控系列之-prometheus部署说明

一、Prometheus介绍 Prometheus是一款开源的监控系统&#xff0c;主要用于收集、存储和查询时间序列数据&#xff0c;以便于对系统进行监控和分析Prometheus的架构由四个主要组件组成&#xff1a; 1、Prometheus Server &#xff1a;Prometheus Server是Prometheus的核心组件&a…

一步一步自制py脚本并且并且修改为exe可执行文件教学外附带SHA-1解密exe文件资源

第一步&#xff1a;安装 Python 下载 Python&#xff1a;访问 Python 官网 下载并安装最新版本的 Python。安装时选择添加到环境变量 PATH&#xff1a;在安装过程中&#xff0c;确保勾选“Add Python to PATH”选项。 第二步&#xff1a;编写 Python 脚本 创建一个新的 Pyth…

人脸防伪检测系统源码分享

人脸防伪检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

✔2848. 与车相交的点

代码实现&#xff1a; 方法一&#xff1a;哈希表 #define fmax(a, b) ((a) > (b) ? (a) : (b))int numberOfPoints(int **nums, int numsSize, int *numsColSize) {int hash[101] {0};int max 0;for (int i 0; i < numsSize; i) {max fmax(max, nums[i][1]);for …

Spring4-IoC3-手写IoC

Spring框架的IoC是基于Java反射机制实现的 Java反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法和属性&#xff0c;这种动态获取信息以及动态调用对象方法的功…

学习大数据DAY57 新的接口配置

作业  完成 API 接口和文件的接入, 并部署到生产调度平台, 每个任务最后至少 要有两条 不报错 的日志, 报错就驳回作业  作业不需要复制日志 API Appliation Program Interface 应用程序接口 > JSON 的地址 客户需求: 把 https://zhiyun.pub:9099/site/c-class…

【QT】定时器使用

文章目录 关于 Qt 定时器使用的注意细节总结实例-检查工具使用周期时间是否合理UI设计头文件 remind.h源文件 remind.cpp实现效果 关于 Qt 定时器使用的注意细节总结 一、创建与初始化 使用 QTimer 类来创建定时器。可以在构造函数中指定父对象&#xff0c;确保定时器在正确的…

电子电气架构——中央计算的软件定义汽车架构

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…

台风,也称为热带气旋,是一种在热带海洋上形成的强烈风暴系统。台风的形成需要满足以下几个条件:

台风&#xff0c;也称为热带气旋&#xff0c;是一种在热带海洋上形成的强烈风暴系统。台风的形成需要满足以下几个条件&#xff1a; 1. **温暖的海水**&#xff1a;台风通常在海面温度至少达到26.5C&#xff08;79.7F&#xff09;的海域形成&#xff0c;因为温暖的海水能够提供…

XShell快速连接虚拟机(Ubuntu系统)

目录 前言 一 (XShell)(虚拟机 )(Ubuntu)下载 二 虚拟机的ip查找 三 虚拟机中安装连接环境 四 开启ssh-server服务 五 验证是Ubuntu是否开启ssh-server服务 六 连接XShell软件 前言 对于刚开始探索 Linux 世界的新手来说&#xff0c;拥有一台自己的服务器可能并不现实。幸运的…

学习大数据DAY58 增量抽取数据表

作业 1 SQL 优化的常见写法有哪些 - 面试经常被问 使用索引&#xff1a;合理创建和使用索引是提高查询效率的关键。索引可以加速数据的检 索速度&#xff0c;但是索引也会占用额外的存储空间&#xff0c;并且在插入、删除和更新操作时会 有额外的开销。 避免全表扫描&…

【ARM】中断术语介绍

外设产生中断给到gic&#xff0c;gic通过内部判断此中断是FIQ还是IRQ&#xff0c;这个过程就称为assert&#xff08;断言&#xff09; 此中断被发到哪里面去叫target cpu跳转到此中断的中断向量表中叫做taken 整个过程就做routing distribute决定将中断发给哪个cpu&#xff08…

【STM32】DAC数字模拟转换

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 前言 DAC简介 DAC配置 DAC时钟使能 GPIO初始化 DAC配置 DAC使能 读写DAC值 驱动代码 MyDAC.h MyDAC.c main.c 前言 大容量的STM32F101xx和STM32F103xx产品才有DAC外设 大容量…

基于SpringBoot的在线考试系统【附源码】

基于SpringBoot的在线考试系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概述 4.2系统功能结构设计 4.3.2 数据库表结构设计 5 系统实现 5.1管理员功能介绍 5.1.1管理员登录 5.1.2 试卷管理 5.1.3 公告信息管理 5.1.…