Tair简介

概述

Tair是淘宝团队开源的高可用分布式KV存储引擎,采用服务端自动负载均衡方式,使客户端逻辑简单。Tair,即TaoBao Pair缩写,Pair表示一对、一双等意思,即Key-Value数据对。

Tair分为持久化和非持久化两种方式。非持久化Tair可看成是一个分布式缓存;持久化Tair将数据存放于磁盘中。支持以下4种存储引擎:

  • 非持久化:mdb
  • 持久化:fdb、kdb和ldb

这4种存储引擎分别基于四种开源的KV数据库:Memcached、Firebird、Kyoto Cabinet和LevelDB。Firebird是关系型数据库,另外三个是NoSQL数据库。

架构

Tair架构图
在这里插入图片描述
包括Client、ConfigServer和DataServer三个不同的应用。ConfigServer通过和DataServer的心跳(即HeartBeat)维护集群中可用的节点,并根据可用的节点,构建数据的在集群中的分布信息。Client在初始化时,从ConfigServer处获取数据的分布信息,根据分布信息和相应的DataServer交互完成用户的请求。DataServer负责数据的存储,并按照ConfigServer的指示完成数据的复制和迁移工作。

ConfigServer

ConfigServer维护集群内DataServer信息,可用DataServer的信息,以及用户配置的桶数量、副本数、机房信息等,构建数据分布的对照表,以达到负载均衡和高可用的目标。ConfigServer和client相互配合根据对照表决定数据的具体分布。如果DataServer宕机或扩容,ConfigServer负责协调数据迁移、管理进度,将数据迁移到负载较小的节点上。

Tair客户端和ConfigServer的交互主要是为了获取数据分布的对照表,客户端从ConfigServer拿到对照表后,会在本地缓存对照表,在需要存储/获取数据时根据对照表查找数据在哪个DataServer上。由此也可以看出,数据访问请求不需要和ConfigServer交互,所以ConfigServer本身的性能高低并不会形成集群的瓶颈。

ConfigServer维护的对照表有版本概念,由于集群变动或管理触发,构建新的对照表后,对照表的版本号递增,并通过DataServer的心跳,将新表同步给数据节点。

客户端和DataServer交互时,DataServer每次都把自己缓存的对照表版本号放入response结构中,返回给客户端,客户端将DataServer的对照表版本号和自己缓存的对照表版本号比较,如果不相同,会主动和ConfigServer通信,请求新的对照表。

ConfigServer使客户端使用时,不需要配置数据节点列表,也不需要处理节点的状态变化,这使得Tair对最终用户来说使用和配置都很简单。

DataServer

DataServer负责数据的物理存储,并根据ConfigServer构建的对照表完成数据的复制和迁移工作。DataServer具备抽象的存储引擎层,可以很方便地添加新存储引擎。DataServer还有一个插件容器,可以动态地加载/卸载插件。

DataServer逻辑架构图
在这里插入图片描述

自动复制和迁移

为了增强数据的安全性,Tair支持配置数据的备份数。比如你可以配置备份数为3,则每个数据都会写在不同的3台机器上。得益于抽象的存储引擎层,无论是作为cache的mdb,还是持久化的fdb,都支持可配的备份数。

当数据写入一个DataServer主节点后,主节点会根据对照表自动将数据写入到其他备份节点,整个过程对用户是透明的。

当有新DataServer加入或有DataServer不可用时,ConfigServer会根据当前可用的DataServer列表,重新构建对照表。DataServer获取到新的对照表时,会自动将在新表中不由自己负责的数据迁移到对照表中相应的DataServer。迁移完成后,客户端可以从ConfigServer同步到新的对照表,完成扩容或者容灾过程。整个过程对用户是透明的,服务不中断。

插件容器

Tair内置一个插件容器,支持热插拔插件。插件由ConfigServer配置,ConfigServer会将插件配置同步给各个数据节点,数据节点会负责加载/卸载相应的插件。插件分为request和response两类,可以分别在request和response时执行相应的操作,如在put前检查用户的quota信息等。插件容器也让Tair在功能方便具有更好的灵活性。

DataServer最主要组成模块有tair_serverrequest_processortair_managerstorage_manager和各种存储的具体实现实现。

源码

目录结构良好:
在这里插入图片描述

ConfigServer

ConfigServer源代码目录下主要有下面几个cpp文件:

  • tair_cfg_svr.cppserver_conf_thread.cpp:ConfigServer的主文件,tair_cfg_svr被执行后,会检查参数和配置,然后启动几个主要线程:
    • task_queue_thread:处理请求的具体线程;
    • packet_transport:发送和接收命令数据包的线程,其中引用tbnet公共包处理epoll;
    • server_conf_thread:ConfigServer的主要业务逻辑实现线程。包括ConfigServer之间的心跳保持,根据心跳维持DataServer存活列表,维护对照表,数据迁移过程管理等逻辑;
    • heartbeat_transport:发送和接收心跳数据包的线程,其中引用tbnet公共包处理epoll。
  • conf_server_table_manager.cpp:管理对照表的辅助类,提供对照表持久化、取得一些元信息等功能,还提供打印对照表的功能,方便调试。
  • table_builder.cpp:包括3个文件,实际的对照表构建过程是由server_conf_thread::table_builder_thread::build_table触发,其中:
    • table_builder:基类,定义构造对照表的主体逻辑,其中有几个虚函数:rebuild_tableset_available_serveris_this_node_OKcaculate_capableget_tokens_per_node用于不同的构造实现扩展不同的逻辑;
    • table_builder1:构建负载均衡策略对照表的实现类,继承table_builder类,对几个虚函数进行基于负载均衡优先的逻辑实现;
    • table_builder2:构建多数据中心策略对照表的实现类,继承table_builder类,对几个虚函数进行基于位置和负载均衡双因子的逻辑实现。
  • group_info.cppgroup_info负责处理group.conf和持久化文件$TAIR_DATA_DIR/data/group_1_server_table,通过读取配置和持久化的信息,构建DataServer位置信息,供ConfigServer主逻辑使用。
  • server_info:记录DataServer存活信息的主要数据结构,server_info会被持久化到$TAIR_DATA_DIR/data/server_info.0中。server_info由下面几个部分组成:
    • serverid:DataServer在集群里的唯一标识,由ip和port构成;
    • last_time:记录最后一次接收到该DataServer心跳时间;
    • status:表示该DataServer的状态,有三种状态:ALIVE、DOWN、FORCE_DOWN。
  • server_info_file_mapper.cpp、server_info_allocator.cpp:实现server_info持久化逻辑。持久化的文件存放在$TAIR_DATA_DIR/data目录下,server_info_allocator维护server_info持久化化文件集合和其中包含的server_info数量。如果当前文件没有空间来存储新server_info,则新建一个序列化文件。
  • stat_info_detail.cpp:存储统计信息,主要的数据结构vector<u64> data_holder,包含GETCOUNT,PUTCOUNT,EVICTCOUNT,REMOVECOUNT,HITCOUNT,DATASIZE,USESIZE,ITEMCOUNT。

DataServer

通过重载的process函数,处理put、get、range等请求。request_processor.cpp定义每种请求的最高层执行流程,每个请求的大体流程都相似,request_processor处理流程:
在这里插入图片描述
如上图,Tair接收到请求后,会循环处理每一个key,如果key在迁移,会发送数据迁移的响应给客户端,客户端重新获取数据分布后,到新的DataServer操作相应的数据。处理过程中会调用性能监控工具,统计相应的性能数据。

数据结构

mdb的存储数据结构:

struct mdb_item {uint64_t h_next;uint64_t prev;uint64_t next;uint32_t exptime;uint32_t key_len;uint32_t data_len;uint16_t version;uint32_t update_time;uint64_t item_id;char data[0];
};

mdb的存储数据结构:

struct LdbItemMetaBase {uint8_t  meta_version_;uint8_t  flag_;uint16_t version_;uint32_t cdate_; // create timeuint32_t mdate_; // modify timeuint32_t edate_; // expired time
};

kdb的存储数据结构:

struct kdb_item_meta {uint8_t  flag;uint8_t  reserved;uint16_t version;uint32_t cdate;uint32_t mdate;uint32_t edate;
};

fdb的存储数据结构:

typedef struct fdb_item_meta {uint16_t magic;uint16_t checksum;uint16_t keysize; // key size max: 64KBuint16_t version;uint32_t prefixsize;uint32_t valsize: 24;uint8_t  flag; // for extendsuint32_t cdate;uint32_t mdate;uint32_t edate;
};

高可用

Tair的高可用,主要通过对照表和数据迁移两大功能进行支撑。

对照表

分布式系统的一个核心问题:数据在集群中的分布策略,好的策略应该能将数据均衡地分布到所有节点上,且能适应集群节点的增减变化。

对照表将数据分为若干个桶,并根据机器数量、机器位置进行负载均衡和副本放置,确保数据分布均匀,并且在多机房有数据副本。在集群发生变化时,会重新计算对照表,并进行数据迁移。

Tair基于一致性Hash算法存储数据,根据配置建立固定数量的bucket,将这些bucket尽量均衡分配到DataServer节点上,并建立副本。

ConfigServer启动后,会等待4秒,然后根据有连接和心跳的状态,检查DataServer是否在线,然后决定是否重建对照表,DataServer需要在ConfigServer启动之前启动。

对照表的初始化

过程如下:在tair_cfg_svr程序启动后,会调用tair_config_server::start(),这个方法会调用my_server_conf_thread.start()my_server_conf_thread有个属性table_builder_threadbuilder_thread,这个类在构造方法里,会调用自己的start方法,把自己启动为一个线程。这个线程会每秒钟检查一次是否需要重新构造对照表。如果需要重新构造,就调用组对象的rebuild方法重新构建对照表。

ConfigServer会定期调用server_conf_thread::check_server_status()方法检查是否需要重建对照表或有节点变动。第一次启动时,由于没有原有的对照表,所以check_server_status调用group_info::is_need_rebuild()时,会固定返回true。因此第一次启动时会根据在线的服务器列表重构对照表。

重建对照表有三种策略选择,可通过group.conf中的_build_strategy=num的配置项进行配置:

  • num=1:表示所有机器不分机房;
  • num=2:表示按照机房分组;
  • num=3:表示让ConfigServer自动决定使用哪种模式。

在设置为自动选择模式时,根据_pos_mask设置的值,检测DataServer所在机房,如果有机器分布在不同机房,且不同机房的服务器数量差不大于_build_diff_ratio配置项指定的差异率,则使用策略类型2,否则使用策略类型1;如果没有分布在不同机房的机器,则使用策略类型1。

机房之间的差异算法:假设有两个机房A和B,配置差异比率_build_diff_ratio=0.5。假设机房A有8台DataServer,机房B有4台DataServer,差异比率=(8-4)/8=0.5。此时满足条件。如果后续对机房A进行扩容,增加一台DataServer,扩容后的差异比率=(9-4)/9=0.556,即对DataServer多的机房扩容会扩大差异比率。如果_build_diff_ratio配置值是0.5,那扩容后ConfigServer会拒绝再继续build新表。如果正在做数据迁移,则调用p_table_builder->build_quick_table(),否则调用p_table_builder->rebuild_table()重建对照表。

负载均衡策略

由于允许数据存放多备份,某个桶最多会存储copyCount次(本例用Y表示)​,也就是说集群中存在Y个bucket的内容是完全一样的,这些一样的数据桶,我们将其中的一个叫作主桶,下面的推演实例为方便和代码对照,主桶都存放在line0中。

采用负载均衡策略(_build_strategy=1)建出的对照表将使bucket会均衡分布到集群中的DataServer上。假设共有X个桶,Y个副本,N个节点,那么在负载均衡优先的策略下,每个节点存放桶最少的个数为:​(XY)/N。如果(XY)%N等于0,每个节点就会存放相同数量的桶。如果(XY)%N不等于0,那么将有(XY)%N个节点将负载(X*Y)/N+1个桶。也就是说,如果使用这种策略,任意两个节点存放的桶数量至多相差1。

在使用负载均衡策略构建对照表的时候,会按照约束级别调用table_builder1::is_this_node_OK函数,决定一个DataServer是否适合存储某个桶。约束级别有四种:

  • CONSIDER_ALL = 0;
  • CONSIDER_POS = 1;
  • CONSIDER_BASE = 2;
  • CONSIDER_FORCE = 3。

函数的四个约束:

  • c1:如果DataServer存储主桶的个数,超过计算出来的主桶容量,就会返回TOOMANY_MASTER;
  • c2:如果DataServer存储的桶的总个数,超过计算出来的容量,就会返回TOOMANY_BUCKET;
  • c3:如果DataServer存储的桶数量超过 M / N M/N M/N,且存储 M / N + 1 M/N+1 M/N+1个桶的DataServer数量超过计算出的最大个数+1,会返回TOOMANY_BUCKET;
  • c4:存储相同数据的任何两个桶不能在同一个DataServer上,如果违反此条,会返回SAME_NODE。

主桶和副本桶检查约束的规则如表

正在迁移的是主副桶?ALLPOSBASEFORCE
主桶c1、c4c1、c4c1
副本桶c2、c4c2、c4c3、c4c4

主桶没有宕机的情况下,检查当前DataServer上的主桶数量是否过多,如果数量过多,则轮询每个节点,查看如果主桶迁移到该DataServer,是否引起数量过多,或者和自己的副本在同一个节点;主数据桶宕机的情况下,副本桶进行提升,如果即将存放主桶的DataServer存放的主桶数量过多,则轮询每个节点,查看如果主桶迁移到该DataServer,是否引起数量过多,或者和自己的副本在同一个节点。

多机架支持

Tair的设计考虑多机架支持,在机架/机房灾难时,确保异地有数据备份。假设搭建Tair集群后,配置数据副本数为3,搭建5个DataServer分布在两个机架上。Tair在建立对照表时,会确保每一份数据至少在两个机架的DataServer上至少有一个副本,如果数据在某一个机架上有两份副本,Tair会尽量使这两份副本分布在不同的DataServer上。

多机架情况下,调用is_this_node_OK判断某个副本是否适合存放在某个DataServer上时,Tair会考虑机架信息和所在机架各个DataServer之间的负载均衡。主要考虑点如下:

  • DataServer存储主副本数量不超过master_server_capable中计算的上限;
  • DataServer存储副本总数量不超过server_capable中计算的上限;
  • 某个机架上总共存储N个副本,共有C个DataServer,存储 N / C + 1 N/C+1 N/C+1个副本的DataServer个数不超过上限;
  • 主副本与其备份副本不能存储在同一个DataServer上;
  • 主副本与其备份副本不能存储在同一个机架上。

数据迁移

Tair每次重新构造对照表之后,会将新的对照表发送给DataServer,DataServer拿到新的对照表后,通过计算,如果发现需要迁移的数据列表不为空,则通过migrate_manager::set_migrate_server_list方法,把迁移列表写入migrate_manager的迁移列表里。迁移完成后,DataServer向ConfigServer发送迁移完成的消息。

migrate_manager是DataServer启动后就启动的一个线程,不断扫描自己的迁移表,发现迁移表不为空的时候,就进行数据迁移工作。具体迁移逻辑在migrate_manager::do_migrate_one_bucket方法里,主要逻辑是,开始迁移数据时,设置current_migrating_bucket为当前正在迁移的桶id,之后DataServer写入这个桶时,都会写入redolog。然后migrate_manager开始迁移内存中桶的数据(或ldb文件中的数据)​。数据迁移完成后,迁移redolog。redolog迁移完成后,将这个桶标记为迁移完成,并把迁移完成信息发送给ConfigServer。

存储引擎

storage_manager,Tair的抽象存储引擎层​,只要满足存储引擎需要的接口,便可以很方便地替换Tair底层的存储引擎。如有需要,可对bdb、tc甚至MySQL进行包装作为存储引擎,而同时使用Tair的分布式、同步等特性。

Tair默认包含四个存储引擎:

  • mdb:高效的缓存存储引擎,它有着和Memcached类似的内存管理方式。mdb支持使用share memory,使得在重启Tair数据节点的进程时不会导致数据丢失,使应用升级更平滑,不会导致命中率的较大波动。
  • fdb:简单高效的持久化存储引擎,使用树的方式根据数据key的Hash值索引数据,加快查找速度。索引文件和数据文件分离,尽量保持索引文件在内存中,以便减小IO开销。使用空闲空间池管理被删除的空间。
  • ldb:LevelDB是Google开源的快速轻量级的单机KV存储引擎。基本特性:
    • 提供KV支持,key和value是任意的字节数组;
    • 数据按key内部排序;
    • 支持批量修改(原子操作)​。
  • kdb:Kyoto Cabinet是一个数据库管理的lib,是Tokyo Cabinet的改进版本。数据库是一个简单的包含记录的数据文件,每个记录是一个键值对,key和value都是变长的字节序列。key和value可以是二进制、文本字符串。数据库中的key必须唯一。数据库既没有表的概念,也不存在数据类型。所有的记录被组织为Hash表或B+树。Kyoto Cabinet的运行速度非常快,例如保存一百万记录到Hash数据库中只需要0.9秒,保存到B+ tree数据库只需要1.1秒。且数据库本身还非常小。Hash数据库的每个记录头只有16字节,B+ tree数据库是4字节。Kyoto Cabinet的伸缩性非常好,数据库大小可以增长到8EB​。

mdb

Tair默认使用MDB存储数据,MDB是一个内存K/V存储引擎,有着类似Memcached的内存管理模式。

mdb结构图
在这里插入图片描述
四个主要的数据结构为:

  • mem_pool:用于管理内存;
  • mem_cache:用于管理slab;
  • cache_hash_map:用于存储Hash表;
  • mdb_area_stat:用于维护area状态。

mem_pool主要用于内存管理,Tair通过将内存分为若干个page管理内存。每个page的大小是1MB,page的个数由Tair根据slab_mem_size配置设置,单位MB。上图中的例子设置为2048,即2GB。Tair代码中定义最大page数量MAX_PAGES_NO = 65536限制,单个DataServer节点最多可使用64GB内存。mem_pool里存储当前已经占用的page、未分配的page。

mem_cache中主要存放slab_manager列表。slab_manager主要用于管理各个item,每个slab_manager中管理相同大小的数据块,存储在Tair中的数据,最终存储在这些块里,也就是item。

Tair中限制最大slab个数为100(TAIR_SLAB_LARGEST)​,每个slab的数据块大小按照mdb_param::factor(值为1.1)递增。最小slab中数据块大小cache_info->base_size=ALIGN(sizeof(mdb_item)+16),为64字节,可存储16字节数据,slab最大可存储881920约800kB字节每个item的数据。slab_manager中会分配page,然后将数据写入page中。

cache_hash_map,主要存储一个HashTable,按照数据key进行Hash,Hash冲突时,产生一个链表。

mdb_area_stat:维护area的相关数据,主要记录area的数据量限制和属于某个area的所有数据的链表。在写入数据时,会检查area数据量限制,如果数据量达到上限,则会循环50次;检查是否有过期数据,如果找到,则逐出。如果50次都没有找到过期数据,则将最后一个数据逐出。

被逐出的数据,有两种可能:

  • 如果配置evict_data_path选项,被逐出的数据会记入文件;
  • 没有配置,数据直接被逐出。

area中记录的所有数据链表,用于执行clear操作。

API

Tair为客户端提供丰富的API支持,主要分为:

  • KV操作API:普通KV操作,和Redis操作很相似;
  • Prefix操作API:类似Redis Hash数据结构。

KV

几个示例(没必要完全列举):

  • getHidden(short ns, byte[​] key, TairOption opt):用于获取被标记为隐藏的key;
  • put(short ns, byte[​] key, byte[​] value, TairOption opt):设置KV;
  • hideByProxy(short ns, byte[​] key, TairOption opt):隐藏某个key。

解读:ns表示namespace或area,K和V都是byte数组,TairOption表示参数设置(如version、expire)。

Prefix

几个示例:

  • prefixPut(short ns, byte[​] pkey, byte[​] skey, byte[​] value,TairOption opt):设置KV;
  • prefixGetHidden(short ns, byte[​] pkey, byte[​] skey,TairOption opt):取得隐藏的KV;

原理:Tair在接收到含有prefix的请求后,会按照prefix计算Hash,因此同一个namespace下的同一个prefix,会Hash到同一个HashTable位置,形成一个链表。后续通过prefix操作时,都是操作这个链表。

Range

几个示例:

  • getRange(short ns, byte[​] pkey, byte[​] begin, byte[​] end, int offset, int maxCount, boolean reverse, TairOption opt):按照前缀匹配取得prefix的子KV;
  • getRangeKey(short ns, byte[​] pkey, byte[​] begin, byte[​] end, int offset, int maxCount, boolean reverse, TairOption opt):按照前缀匹配取得prefix的子key;
  • getRangeValue:参数同上,按照前缀匹配取得prefix的子key对应的value。

mdb、fdb、kdb引擎不支持range操作,需要更换为ldb引擎。

Version

Tair中的每个数据都包含版本号,版本号在每次更新后都会递增。这个特性可防止数据的并发更新导致的问题。

Tair使用不同的存储引擎时,存储的数据结构里,都会有一个版本号。参考上面的数据结构部分。

在执行put操作时,会首先把原来存储的数据拿出来,对比version如果version不匹配,返回错误;如果version匹配,则更新数据,并增加版本号。如果不希望使用version匹配,可以传入0:

else if(version_care && version ! = 0&& it->version ! = static_cast<uint32_t> (version)) {TBSYS_LOG(WARN, "it->version(%hu) ! = version(%hu)", it->version, key.get_version());return TAIR_RETURN_VERSION_ERROR;
}

参考

  • 深入分布式缓存:从原理到实践

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

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

相关文章

【Linux学习】【Ubuntu入门】2-1 Linux系统下运行C语言输出hello word

1.双击打开VMware软件&#xff0c;点击开启此虚拟机后&#xff0c;等待点击头像输入密码进入 2.“CtrlAltt”调出命令行终端&#xff0c;输入命令sudo apt-get install vim安装vim&#xff0c;输入命令sudo apt-get install gcc安装gcc 3.输入命令vi hello.c进入C语言编写环境&…

【D3.js in Action 3 精译_025】3.4 让 D3 数据适应屏幕(中)—— 线性比例尺的用法

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

[Python数据分析]最通俗入门Kmeans聚类分析,可视化展示附代码。

什么是k-means分析?【头条@William数据分析,看原版】 想象一下,你有一堆五颜六色的糖果,你想把它们按照颜色分成几堆。k-means分析就是这么一个自动分类的过程。它会根据糖果的颜色特征,把它们分成若干个组,每个组里的糖果颜色都比较相似。 更专业一点说,k-means分析是…

【C++并发入门】摄像头帧率计算和多线程相机读取(上):并发基础概念和代码实现

前言 高帧率摄像头往往应用在很多opencv项目中&#xff0c;今天就来通过简单计算摄像头帧率&#xff0c;抛出一个单线程读取摄像头会遇到的问题&#xff0c;同时提出一种解决方案&#xff0c;使用多线程对摄像头进行读取。同时本文介绍了线程入门的基础知识&#xff0c;讲解了…

【muduo源码分析】「阻塞」「非阻塞」「同步」「异步」

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 引言何为「muduo库」安装muduo库阻塞、非阻塞、同步、异步数据准备数据准备 引言 从本篇博客开始&#xff0c;我会陆续发表muduo库源码分析的相关文章。感谢大家的持续关注&#xff01;&#xff01;…

9.29总结

这星期学了概率和组合数学 这是我觉得的一个有趣的题目&#xff0c;每个人身上都有n-1根绳子&#xff0c;如果组不成稳定三角&#xff0c;那么肯定有两个人相邻两根绳子颜色不一样&#xff0c;那么每两个这样的人就会贡献一个不稳定三角形&#xff0c;所以只要所有三角形减去每…

64.【C语言】再议结构体(下)(未完)

本文衔接第63篇 目录 6.复习 7.修改默认对齐数 8.结构体传参 01.传递非指针参数 02.传递指针参数(传递地址) 03.对比 9.结构体实现位段 01.位段的定义 02.格式 03.例题 答案速查 分析 前置知识:位段的内存分配 解析 若按浪费空间处理 验证 6.复习 20.【C语言…

①三菱Modbus主站MELSEC转ModbusRTU/ASCII工业MELSEC网关串口服务

三菱Modbus主站MELSEC转ModbusRTU/ASCII工业MELSEC网关串口服务https://item.taobao.com/item.htm?ftt&id834634632647 MELSEC 通信单元 MELSEC 转 RS485 MS-A1-80X1 系列概述 型号&#xff1a;1路总线MELSEC网关(单网口&#xff09; MS-A1-8011 1路总线MELSEC网关(双…

A Learning-Based Approach to Static Program Slicing —— 论文笔记

A Learning-Based Approach to Static Program Slicing OOPLSA’2024 文章目录 A Learning-Based Approach to Static Program Slicing1. Abstract2. Motivation(1) 为什么需要能处理不完整代码(2) 现有方法局限性(3) 验证局限性: 初步实验研究实验设计何为不完整代码实验结果…

计算机网络基础--认识协议

目录 前言 一、IP地址与端口 二、网络协议 1.网络体系结构框架 2.网络字节序 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 计算机网络涉及非常广泛&#xff0c;这篇文章主要对计算机网络有个认识 提示&#xff1a;以下是本篇文章正文内容&#x…

C++系列-继承

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 继承的概念和定义 继承是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行拓展&#xff0c;增加功能&#xff0c;这样可以…

【PyTorch】生成对抗网络

生成对抗网络是什么 Generative Adversarial Nets&#xff0c;简称GAN GAN&#xff1a;生成对抗网络 —— 一种可以生成特定分布数据的模型 《Recent Progress on Generative Adversarial Networks (GANs): A Survey》 《How Generative Adversarial Networks and Its Varian…

基于Megatron-LM从0到1完成GPT2模型预训练、模型评估及推理

随着 ChatGPT 迅速爆火&#xff0c;引领基于Transformer架构的大模型从幕后走到台前。但 ChatGPT 的成功并不是一蹴而就&#xff0c;而是&#xff0c;经过了从早期的 GPT1 到 GPT2&#xff0c;之后到 GPT3 和 InstructGPT、然后到GPT3.5和ChatGPT&#xff0c;直到如今的多模态大…

一钉多用:自攻螺钉在家居与工业领域的广泛应用

自攻螺钉的结构要素有哪些重要特点&#xff1f; 自攻螺钉适用于非金属或软金属&#xff0c;不需要配合预先开好的孔和攻牙。自攻螺钉的尖头设计使其能够“自我攻入”材料中&#xff1b;而普通螺丝通常是平头&#xff0c;规格一致。自攻螺钉的关键在于&#xff0c;打孔时不需要进…

【JavaEE初阶】网络原理

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 ⽹络互连 IP地址 端口号 协议 协议分层 优势 TCP/IP 五层网络模型 数据在网络通信中的整体流程 封装和分用 封装 分用 ⽹络互连 随着时代的发展&#xff0c;越来越需…

Angular基础学习(入门 --> 入坑)

目录 一、Angular 环境搭建 二、创建Angular新项目 三、数据绑定 四、ngFor循环、ngIf、ngSwitch、[ngClass]、[ngStyle]、管道、事件、双向数据绑定--MVVM 五、DOM 操作 &#xff08;ViewChild&#xff09; 六、组件通讯 七、生命周期 八、Rxjs 异步数据流 九、Http …

51单片机的光照强度检测【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块光照传感器按键蜂鸣器LED等模块构成。适用于光照强度检测、光照强度测量报警等相似项目。 可实现功能: 1、LCD1602实时显示光照强度信息 2、光照强度传感器&#xff08;电位器模拟&#xff09;采集光照信息 3、可…

X86架构(九)——保护模式的进入

全局描述符表 全局描述符表(Global Descriptor Table,GDT)是保护模式下非常重要的一个数据结构。 在保护模式下&#xff0c;对内存的访问仍然使用段地址和偏移地址&#xff0c;在每个段能够访问之前&#xff0c;必须先行设置好 GDT 的地址&#xff0c;并加载全局描述符表寄存…

emp.dll丢失怎么解决,快来试试这个几个解决方法

在日常使用电脑玩游戏的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中最常见的就是“emp.dll丢失”。那么&#xff0c;emp.dll到底是什么&#xff1f;它为什么会丢失&#xff1f;丢失后会对我们的电脑产生什么影响&#xff1f;本文将为您详细解析emp.dll的概念…

[BUUCTF从零单排] Web方向 03.Web入门篇之sql注入-1(手工注入详解)

这是作者新开的一个专栏《BUUCTF从零单排》&#xff0c;旨在从零学习CTF知识&#xff0c;方便更多初学者了解各种类型的安全题目&#xff0c;后续分享一定程度会对不同类型的题目进行总结&#xff0c;并结合CTF书籍和真实案例实践&#xff0c;希望对您有所帮助。当然&#xff0…