【微服务即时通讯系统】——etcd一致性键值存储系统、etcd的介绍、etcd的安装、etcd使用和功能测试

文章目录

  • etcd
    • 1. etcd的介绍
      • 1.1 etcd的概念
    • 2. etcd的安装
      • 2.1 安装etcd
      • 2.2 安装etcd客户端C/C++开发库
    • 3. etcd使用
      • 3.1 etcd接口介绍
    • 4. etcd使用测试
      • 4.1 原生接口使用测试
      • 4.2 封装etcd使用测试

etcd

在这里插入图片描述

  

1. etcd的介绍

1.1 etcd的概念

   Etcd 是一个基于GO实现的 分布式、高可用、一致 的 一致性键值存储系统 用于配置共享和服务发现等。它使用 Raft 一致性算法来保持集群数据的一致性,且客户端通过长连接watch 功能,能够及时收到数据变化通知,相较于 Zookeeper 框架更加轻量化。

  

  为什么需要 etcd?

  管理共享配置信息:etcd 可以将配置信息集中存储,服务端将配置信息存储于 etcd 中,客户端可以通过 etcd 方便地获取这些配置信息。

  服务发现:etcd 可以作为服务注册中心,将服务信息注册到 etcd 中,其他服务可以通过查询 etcd 来获取这些信息。且当服务发生变化时,etcd 可以及时通知,实现服务的自动发现和动态调整。

  防止单点故障:为了防止单点故障,可以启动多个 etcd 组成集群。etcd 集群使用 raft 一致性算法来处理日志复制,保证多节点数据的强一致性。

  

  etcd 使用 raft 算法来实现数据的一致性:Raft 算法用于分布式系统,包含主节点选举和数据更新两部分。主节点选举中,从节点在未收到主节点心跳包时可成为候选主节点发起投票,获超半数响应则成为新主节点。数据更新分两阶段,先主节点记录并复制日志,超半数响应后通知客户端,再主节点提交修改并通知从节点提交。

  

2. etcd的安装

2.1 安装etcd

  这是在 Linux 系统(ubuntu) 上安装 Etcd 的基本步骤:
  
  安装 Etcd:

sudo apt-get install etcd

  启动 Etcd 服务:

sudo systemctl start etcd

  设置 Etcd 开机自启:

sudo systemctl enable etcd

  运行验证:

etcdctl put mykey "this is awesome"

  
  如果出现报错:No help topic for 'put'

  则 sudo vi /etc/profile 在末尾声明环境变量 ETCDCTL_API=3 以确定 etcd 版本:

export ETCDCTL_API=3

  完毕后,加载配置文件,并重新执行测试指令:

dev@Crocodile:~/workspace$ source /etc/profile
dev@Crocodile:~/workspace$ etcdctl put mykey "this is awesome"
OK
dev@Crocodile:~/workspace$ etcdctl get mykey
mykey
this is awesome
dev@Crocodile:~/workspace$ etcdctl del mykey

  

2.2 安装etcd客户端C/C++开发库

  etcd 由 golang 编写,v3 版本使用 grpc API(HTTP2+protobuf)通信,官方仅维护了 go 语言的 client 库。若要使用 C/C++ 语言,需寻找非官方的 etcd client 开发库。

  etcd-cpp-apiv3 是一个 etcd 的 C++版本客户端 API。它依赖于 mipsasm, boost, protobuf, gRPC, cpprestsdk 等库。
  
  依赖安装:

sudo apt-get install libboost-all-dev libssl-dev
sudo apt-get install libprotobuf-dev protobuf-compiler-grpc
sudo apt-get install libgrpc-dev libgrpc++-dev 
sudo apt-get install libcpprest-dev

  api 框架安装:

git clone https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.gitcd etcd-cpp-apiv3mkdir build && cd buildcmake .. -DCMAKE_INSTALL_PREFIX=/usrmake -j$(nproc) && sudo make install

  

3. etcd使用

  etcd工作原理:
  
在这里插入图片描述
  

  etcd 是分布式高可用的 一致性键值存储系统 用于配置共享和服务发现(Raft 一致性算法来保持集群数据的一致性,且客户端通过长连接 watch 功能,能够及时收到数据变化通知,相较于 Zookeeper 框架更加轻量化)

  先创建一个etcd服务器存储键值对数据, 主机1可以向服务器进行服务注册,该注册是以一个键值对存储,保存了服务和主机的地址端口,主机2可以获取又主机1向服务器中添加的服务数据, 同时主机1对这个服务有一个长连接保活(服务器每个3s进行保活确认),如果服务下线了,etcd服务器就会通知主机2。

  
在这里插入图片描述

  etcd 服务器有设置租约的功能 在上面介绍了 client和etcd服务器直接有一个长连接保活(KeepAlive) 这个长连接可以Lease()获取一个租约, 同时这个租约和client提供服务设置的租约id一致, etcd 服务器和client直接通过KeepALive长连接保活 ,如果长连接断开,那么client的键值对数据就无法获取到租约, etcd就会将没有租约的数据进行删除。

  租约机制类似房租,没有租约就是房子属于自己,有租约就要定时通过房东续租,否则无法租房。

  

3.1 etcd接口介绍

//pplx::task 并行库异步结果对象
//阻塞方式 get(): 阻塞直到任务执行完成,并获取任务结果
//非阻塞方式 wait(): 等待任务到达终止状态,然后返回任务状态
namespace etcd {
class Value {bool is_dir()//判断是否是一个目录std::string const& key() //键值对的 key 值std::string const& as_string()//键值对的 val 值int64_t lease() //用于创建租约的响应中,返回租约 ID
}//etcd 会监控所管理的数据的变化,一旦数据产生变化会通知客户端
//在通知客户端的时候,会返回改变前的数据和改变后的数据
class Event {enum class EventType {PUT, //键值对新增或数据发生改变DELETE_,//键值对被删除INVALID,};enum EventType event_type() const Value& kv()const Value& prev_kv()
} class Response {bool is_ok()std::string const& error_message()Value const& value()//当前的数值 或者 一个请求的处理结果Value const& prev_value()//之前的数值Value const& value(int index)//std::vector<Event> const& events();//触发的事件
} 
class KeepAlive {KeepAlive(Client const& client, int ttl, int64_t lease_id = 
0);//返回租约 IDint64_t Lease();//停止保活动作void Cancel();
} class Client {// etcd_url: "http://127.0.0.1:2379"Client(std::string const& etcd_url,std::string const& load_balancer = "round_robin");//Put a new key-value pair 新增一个键值对pplx::task<Response> put(std::string const& key, std::string const& value);//新增带有租约的键值对 (一定时间后,如果没有续租,数据自动删除)pplx::task<Response> put(std::string const& key, std::string const& value,const int64_t leaseId);//获取一个指定 key 目录下的数据列表pplx::task<Response> ls(std::string const& key);//创建并获取一个存活 ttl 时间的租约pplx::task<Response> leasegrant(int ttl);//获取一个租约保活对象,其参数 ttl 表示租约有效时间pplx::task<std::shared_ptr<KeepAlive>> leasekeepalive(int 
ttl);//撤销一个指定的租约pplx::task<Response> leaserevoke(int64_t lease_id);//数据锁pplx::task<Response> lock(std::string const& key);
} class Watcher {Watcher(Client const& client, std::string const& key, //要监控的键值对 keystd::function<void(Response)> callback, //发生改变后的回调bool recursive = false); //是否递归监控目录下的所有数据改变Watcher(std::string const& address, std::string const& key,std::function<void(Response)> callback, bool recursive = false);//阻塞等待,直到监控任务被停止bool Wait();bool Cancel();
}

  

4. etcd使用测试

4.1 原生接口使用测试

  put.cc

#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <thread>int main(int argc, char *argv[])
{std::string etcd_host="http://127.0.0.1:2379";// 实例化客户端对象etcd::Client client(etcd_host);// 获取租约保活对象,伴随着创建一个指定有效时长的租约auto keep_alive=client.leasekeepalive(3).get();// 获取租约idauto lease_id=keep_alive->Lease();// 向ectd新增数据auto resp1=client.put("/service/user","127.0.0.1:8080",lease_id).get();if(resp1.is_ok()==false){std::cout<<"新增数据失败: "<<resp1.error_message()<<std::endl;return -1;}auto resp2=client.put("/service/friend","127.0.0.1:9090").get();if(resp2.is_ok()==false){std::cout<<"新增数据失败: "<<resp2.error_message()<<std::endl;return -1;}std::this_thread::sleep_for(std::chrono::seconds(10));return 0;
}

  get.cc

#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include <thread>// 监控事件发生的回调函数
void callback(const etcd::Response &resp)
{if(resp.is_ok()==false){std::cout<<"收到一个错误的事件通知:"<<resp.error_message()<<std::endl;return;}for(auto const& ev:resp.events()){if(ev.event_type()==etcd::Event::EventType::PUT){std::cout<<"数据发生了改变:\n";std::cout<<"当前的值:"<<ev.kv().key()<<"-"<<ev.kv().as_string()<<std::endl;std::cout<<"原来的值:"<<ev.prev_kv().key()<<"-"<<ev.prev_kv().as_string()<<std::endl;}else if(ev.event_type()==etcd::Event::EventType::DELETE_){std::cout<<"服务信息下线被删除:\n";std::cout<<"当前的值:"<<ev.kv().key()<<"-"<<ev.kv().as_string()<<std::endl;std::cout<<"原来的值:"<<ev.prev_kv().key()<<"-"<<ev.prev_kv().as_string()<<std::endl;}}
}int main(int argc, char* argv[])
{std::string etch_host="http://127.0.0.1:2379";// 实例化客户端对象 etcd::Client client(etch_host);// 获取指定的键值对信息// 初次先用 ls 获取所有能够提供指定服务/service的实例信息auto resp=client.ls("/service").get(); if(resp.is_ok()==false){std::cout<<"获取键值对数据失败:"<<resp.error_message()<<std::endl;return -1;}int sz=resp.keys().size();for(int i=0;i<sz;++i) // 遍历所有可以提供的服务{std::cout<<resp.value(i).as_string()<<"可以提供"<<resp.key(i)<<"服务\n";}// 实例化一个键值对事件监控对象auto watcher=etcd::Watcher(client,"/service",callback,true); // true递归监控路径下所有函数watcher.Wait(); // 开始事件监控return 0;
}

  makefile

all:put get
put:put.ccg++ -std=c++17 $^ -o $@ -letcd-cpp-api -lcpprest
get:get.ccg++ -std=c++17 $^ -o $@ -letcd-cpp-api -lcpprest

  
在这里插入图片描述
  
在这里插入图片描述

  

4.2 封装etcd使用测试

  etcd.hpp

#pragma once
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include <functional>
#include "logger.hpp"// 封装etcd-client-api 实现两种类型的客户端
// 1. 服务注册客户端:向服务器新增服务信息数据,并进行保活
// 2. 服务发现客户端:从服务器查找服务信息数据,并进行改变世界监控
// etcd本质是一个键值存储系统,并不是专门用于作为注册中心进行服务注册和发现的// 实现:
// 1. 封装服务注册客户端类,提供接口向服务器新增数据进行保活
//     参数:注册中心地址(etcd服务器地址) 新增的服务信息(服务名-主机地址键值对)
// 2. 封装服务发现客户端类,提供服务上线事件接口,服务下线事件接口,一个设置根目录接口namespace wh_im{//服务注册客户端类
class Registry 
{public:using ptr = std::shared_ptr<Registry>;// 服务注册客户端类初始化Registry(const std::string &host):_client(std::make_shared<etcd::Client>(host)) ,_keep_alive(_client->leasekeepalive(3).get()),_lease_id(_keep_alive->Lease()){}~Registry() {_keep_alive->Cancel(); }// 注册服务键值对,val路径可以提供的服务keybool registry(const std::string &key, const std::string &val) {// 进行服务键值对注册auto resp = _client->put(key, val, _lease_id).get();if (resp.is_ok() == false) {LOG_ERROR("注册数据失败:{}", resp.error_message());return false;}return true;}private:std::shared_ptr<etcd::Client> _client;        // 客户端std::shared_ptr<etcd::KeepAlive> _keep_alive; // 长连接uint64_t _lease_id;                           // 租约id
};//服务发现客户端类
class Discovery 
{public:using ptr = std::shared_ptr<Discovery>;using NotifyCallback = std::function<void(std::string, std::string)>;// 服务发现客户端类初始化Discovery(const std::string &host,   // 注册中心地址const std::string &basedir,      // 基础根目录const NotifyCallback &put_cb,    // 上线通知处理回调函数const NotifyCallback &del_cb):   // 下线通知处理回调函数_client(std::make_shared<etcd::Client>(host)) ,  // 构造客户端对象_put_cb(put_cb), _del_cb(del_cb){//先进行服务发现,先获取到当前已有的数据auto resp = _client->ls(basedir).get();if (resp.is_ok() == false) {LOG_ERROR("获取服务信息数据失败:{}", resp.error_message());}int sz = resp.keys().size();for (int i = 0; i < sz; ++i) {if (_put_cb) _put_cb(resp.key(i), resp.value(i).as_string());}// 然后进行事件监控,监控数据发生的改变并调用回调进行处理_watcher = std::make_shared<etcd::Watcher>(*_client.get(), basedir,std::bind(&Discovery::callback, this, std::placeholders::_1), true);}~Discovery() {_watcher->Cancel();}private:// 设置回调函数void callback(const etcd::Response &resp) {if (resp.is_ok() == false) {LOG_ERROR("收到一个错误的事件通知: {}", resp.error_message());return;}for (auto const& ev : resp.events()) {if (ev.event_type() == etcd::Event::EventType::PUT) {if (_put_cb) _put_cb(ev.kv().key(), ev.kv().as_string());LOG_DEBUG("新增服务:{}-{}", ev.kv().key(), ev.kv().as_string());}else if (ev.event_type() == etcd::Event::EventType::DELETE_) {if (_del_cb) _del_cb(ev.prev_kv().key(), ev.prev_kv().as_string());LOG_DEBUG("下线服务:{}-{}", ev.prev_kv().key(), ev.prev_kv().as_string());}}}private:NotifyCallback _put_cb;                   // 上线通知回调函数NotifyCallback _del_cb;                   // 下线通知回调函数std::shared_ptr<etcd::Client> _client;    // 客户端std::shared_ptr<etcd::Watcher> _watcher;  // Watcher对象
};} // end namespace

  registry.cc

#include "../../common/logger.hpp"
#include "../../common/etcd.hpp"
#include <gflags/gflags.h>
#include <thread>DEFINE_bool(run_mode, false, "程序的运行模式, false-调试; true-发布;");
DEFINE_string(log_file,"","发布模式下,用于指定日志的输出文件");
DEFINE_int32(log_level,0,"发布模式下,用于指定日志输出等级");DEFINE_string(etch_host,"http://127.0.0.1:2379","服务注册中心地址");
DEFINE_string(base_service,"/service","服务监控根目录");
DEFINE_string(instance_name,"/user/instance1","当前实例名称");
DEFINE_string(access_host,"127.0.0.1:8880","当前实例的外部访问地址");int main(int argc, char *argv[])
{google::ParseCommandLineFlags(&argc, &argv, true);wh_im::init_logger(FLAGS_run_mode,FLAGS_log_file,FLAGS_log_level);wh_im::Registry::ptr rclient=std::make_shared<wh_im::Registry>(FLAGS_etch_host);LOG_DEBUG("服务名称:{}",FLAGS_base_service+FLAGS_instance_name);rclient->registry(FLAGS_base_service+FLAGS_instance_name,FLAGS_access_host);std::this_thread::sleep_for(std::chrono::seconds(600));return 0;
}

  discovery.cc

#include "../../common/logger.hpp"
#include "../../common/etcd.hpp"
#include <gflags/gflags.h>
#include <thread>DEFINE_bool(run_mode, false, "程序的运行模式, false-调试; true-发布;");
DEFINE_string(log_file,"","发布模式下,用于指定日志的输出文件");
DEFINE_int32(log_level,0,"发布模式下,用于指定日志输出等级");DEFINE_string(etch_host,"http://127.0.0.1:2379","服务注册中心地址");
DEFINE_string(base_service,"/service","服务监控根目录");void online(const std::string &service_name, const std::string &service_host)
{LOG_DEBUG("上线服务:{}-{}",service_name,service_host);
}void offline(const std::string &service_name, const std::string &service_host)
{LOG_DEBUG("下线服务:{}-{}",service_name,service_host);
}int main(int argc, char *argv[])
{google::ParseCommandLineFlags(&argc, &argv, true);wh_im::init_logger(FLAGS_run_mode,FLAGS_log_file,FLAGS_log_level);wh_im::Discovery::ptr rclient=std::make_shared<wh_im::Discovery>(FLAGS_etch_host,FLAGS_base_service,online,offline);std::this_thread::sleep_for(std::chrono::seconds(600));return 0;
}

  makefile

all:discovery registry
discovery:discovery.ccg++ -std=c++17 $^ -o $@ -lspdlog -lfmt -lgflags -letcd-cpp-api -lcpprest
registry:registry.ccg++ -std=c++17 $^ -o $@ -lspdlog -lfmt -lgflags -letcd-cpp-api -lcpprest

  
在这里插入图片描述
  
在这里插入图片描述

            

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

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

相关文章

分库分表还是分布式?如何用 OceanBase的单机分布式一体化从根本上解决问题

随着企业业务规模的不断增长&#xff0c;单机集中式的数据库系统逐渐难以承载企业日益增长的数据存储与处理需求。因此&#xff0c;MySQL 的分库分表方案成为了众多企业应对数据存储量激增及数据处理能力需求扩张的“止痛药”。尽管这一方案短期内有效缓解了企业面临的大规模数…

在新ARM板上移植U-Boot和Linux指南

序言 从支持一个定制板子在U-Boot和Linux中的过程中得到经验以一个带有知名SoC&#xff08;i.MX6&#xff09;且IP已经得到支持的板子为例&#xff0c;这次讨论几乎不涉及编码技能&#xff0c;更多地聚焦在U-Boot部分 一般原则 如果您有您的BSP&#xff08;板级支持包&#…

【全新课程】正点原子《基于GD32 ARM32单片机项目实战入门》培训课程上线!

正点原子《基于GD32 ARM32单片机项目实战入门》全新培训课程上线啦&#xff01;正点原子工程师手把手教你学&#xff01;彻底解决ARM32单片机项目入门难的问题&#xff01; 一、课程介绍 本课程专为ARM32单片机的入门学习者设计&#xff0c;涵盖了环境搭建、编程软件使用、模…

【数学分析笔记】第3章第4节闭区间上的连续函数(2)

3. 函数极限与连续函数 3.4 闭区间上的连续函数 3.4.4 中间值定理 【定理3.4.4】若 f ( x ) f(x) f(x)在 [ a , b ] [a,b] [a,b]上连续&#xff0c;则它一定能取到最大值 M M M与最小值 m m m之间的任何一个值。 M max ⁡ f ( x ) , x ∈ [ a , b ] , m min ⁡ f ( x ) , …

【前端框架对比和选择】React 与 Vue 框架设计思路对比

框架总览 前端框架繁多&#xff0c;在学习的时候也会陷入困惑&#xff0c;我们应该抓住最主流的内容 Vue/React&#xff0c;深入底层&#xff0c;尝试揣摩框架作者的设计思路&#xff0c;开阔前端培训自己的视野&#xff0c;大家也不要把自己限制在框架之中&#xff0c;认为工…

常见区块链数据模型介绍

除了加密技术和共识算法&#xff0c;区块链技术还依赖于一种数据模型&#xff0c;它决定了信息如何被结构化、验证和存储。数据模型定义了账户如何管理&#xff0c;状态转换如何发生&#xff0c;以及用户和开发者如何与系统交互。 在区块链技术的短暂历史中&#xff0c;数据…

数据治理005-血缘关系

数据血缘是元数据产品的核心能力&#xff0c;但数据血缘是典型的看起来很美好但用起来门槛很高的技术&#xff0c;只要你采买过元数据产品就知道了。这篇文章对数据血缘的特征、价值、用途和方法做了系统阐述&#xff1a; 1、特征&#xff1a;归属性、多源性、可追溯及层次性 2…

SAP已知事务码查询关联角色

运维期间客户就出现没有某些事务码的权限&#xff0c;要求添加&#xff1b; 想要添加事务码就必须知道这个事务码属于哪个角色&#xff1b;使用SUIM-角色-按菜单中的事务分配&#xff0c;输入事务码&#xff0c;点击执行就可以查看 找到相关的角色之后&#xff0c;用SU01添加至…

动态规划算法:12.简单多状态 dp 问题_打家劫舍_C++

目录 题目链接&#xff1a;LCR 089. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 一、题目解析 题目&#xff1a; 解析&#xff1a; 二、算法原理 1、状态表示 状态表示&#xff1a; 2、状态转移方程 状态转移方程推理&#xff1a; 3、初始化 dp表初始化: 特殊…

【抓包工具】如何下载抓包工具Fiddler

目录 Fiddler简介 Fiddler下载步骤 Fiddler安装步骤 配置Fiddler抓取HTTPS Fiddler简介 Fiddler是一个http协议调试代理工具&#xff0c;它能够记录并检查所有你的电脑和互联网之间的http通讯&#xff0c;设置断点&#xff0c;查看所有的“进出”Fiddler的数据&#xff08…

【BurpSuite】SQL注入 | SQL injection(1-2)

&#x1f3d8;️个人主页&#xff1a; 点燃银河尽头的篝火(●’◡’●) 如果文章有帮到你的话记得点赞&#x1f44d;收藏&#x1f497;支持一下哦 【BurpSuite】SQL注入 | SQL injection&#xff08;1-2&#xff09; 实验一 Lab: SQL injection vulnerability in WHERE clause…

大数据新视界 --大数据大厂之数据压缩算法比较与应用:节省存储空间

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

2-105 基于matlab的GA-WNN预测算法

基于matlab的GA-WNN预测算法。遗传算法优化小波神经网络的步骤&#xff1a;1设种群规模为M。随机生成初始种群N , 采用实数编码对个体Ni编码。2、用1中的种群N训练, WNN参数由初始化获得。3、计算种群N中个体适应度值。满足终止条件则跳至6, 不满足执行4。4、适应度大的个体, 选…

携手SelectDB,观测云实现性能与成本的双重飞跃

在刚刚落下帷幕的2024云栖大会上&#xff0c;观测云又一次迎来了全面革新。携手SelectDB&#xff0c;实现了技术的飞跃&#xff0c;这不仅彰显了观测云在监控观测领域的技术实力&#xff0c;也预示着我们可以为全球用户提供更加高效、稳定的数据监测与分析服务。这一技术升级&a…

智慧园区建设,构建智能监控和安防体系

智慧园区是指运用先进的信息技术和互联网思维&#xff0c;以提升园区管理和服务水平为目标&#xff0c;通过整合各类资源、优化园区运营&#xff0c;打造智能化、智能、绿色、低碳的现代园区。在智慧园区中&#xff0c;智慧楼宇、智能监控、智慧消防和智慧安防是不可或缺的重要…

SpringBoot整合JPA实现CRUD详解

SpringBoot版本是2.0以上(2.6.13) JDK是1.8 一、依赖 <dependencies><!-- jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId></dependency><!--…

【ADC】SAR 型 ADC 和 ΔΣ ADC 的选型决策方法

本文学习于TI 高精度实验室课程&#xff0c;介绍如何选择 SAR 或 delta-sigma 型 ADC。 文章目录 一、选型决策树二、特定传感器的应用三、需要 DC 精度但分辨率较低的应用四、需要 DC 精度且分辨率较高的应用五、极低噪声的 DC 精密测量六、需要捕获瞬态信号值的应用七、需要高…

vue单点登录异步执行请求https://xxx.com获取并处理数据

一、请求一个加密地址获取access_token再拼接字符串再次请求 接口返回数据 异步执行请求该地址获取数据并处理 二、请求代码第二步使用 access_token 获取 auth_key // 第二步&#xff1a;使用 access_token 获取 auth_keyconst access_token tokenData.access_token;const …

13年408计算机考研-计算机网络

第一题&#xff1a; 解析&#xff1a;OSI体系结构 OSI参考模型&#xff0c;由下至上依次是&#xff1a;物理层-数据链路层-网络层-运输层-会话层-表示层-应用层。 A.对话管理显然属于会话层&#xff0c; B.数据格式转换&#xff0c;是表示层要解决的问题&#xff0c;很显然答案…

代理模式简介:静态代理VS与动态代理

代理模式&#xff1a;静态代理VS动态代理 1、定义2、分类2.1 静态代理2.2 动态代理 3、使用场景4、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、定义 代理模式是一种设计模式&#xff0c;通过代理对象控制对目标对象的访问。简而…