Redis多机数据库实现
Redis多机数据库实现
为《Redis设计与实现》笔记
复制
客户端可以使用SLAVEOF
命令将指定服务器设置为该服务器的主服务器
127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
127.0.0.1:6379
将被设置为127.0.0.1:123456
的主服务器
旧版复制功能的实现
Redis的复制功能分为同步(sync)和命令传播(command propagate)两个阶段
- 同步:将从服务器的数据库状态更新至主服务器当前所处的状态
- 命令传播:在主服务器的数据状态被修改,导致主服务器的数据状态出现不一致时,让主从服务器的数据重新回到一致状态
同步
从服务器对主服务器的同步操作通过向主服务器发送SYNC
命令来完成,步骤如下:
- 从服务器向主服务器发送
SYNC
命令 - 主服务器执行BGSAVE命令,生成RDB文件,并用一个缓冲区记录现在开始执行的所有写命令
- 将生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件
- 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些命令,将自己的状态更新至主服务器数据库当前所处的状态
命令传播
同步完成后,客户端对主服务器执行了某些操作导致主从服务器的数据库出现不一致,此时主服务器会将自己执行的写命令发送给从服务器,使其恢复成同步状态
旧版复制功能的缺陷
旧版复制存在两种情况:
- 初次复制:从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上次复制的主服务器不同
- 断线后重复制:主从服务器因为网络原因而中断了复制,但从服务器通过自动重连重新连上了主服务器,并继续复制主服务器
在旧版复制中,断线重连后整个复制操作都需要重新执行,导致效率过低
新版复制功能的实现
Redis2.8开始,使用PSYNC
命令代替SYNC
来执行复制时的同步操作
PSYNC
命令具有两种模式:
- 完整同步:用于初次赋值情况,执行步骤和
SYNC
命令的执行一样,也是通过主服务器创建并发送RDB文件并发送缓冲区里的写命令给从服务器来进行同步 - 部分重同步:用于短线后重复复制情况,当从服务器在断线后重连时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令即可恢复到主服务器当前所处的状态
部分重同步由以下三个部分构成:
- 主服务器的复制偏移量和从服务器的复制偏移量
- 主服务器的复制积压缓冲区
- 服务器的运行ID
复制偏移量
主服务器每次向从服务器传播N个字节的数据时,就将自己的复制哦i按一辆值加上N,而从服务器接收到N个字节的数据时,也将自己的复制偏移量加上N。如果主从服务器处于一致状态,那么主从服务器地偏移量是相同的,反之则说明主从服务器为处于一致状态
复制积压缓冲区
复制偏移量能够判断主从服务器是否初一致状态,而复制积压缓冲区则是在从服务器断线重连时执行部分重同步时发挥作用
复制积压缓冲区为主服务器维护的固定长度的先进先出队列,其默认大小为1MB。当主服务器将写命令发送给从服务器时,同时将写命令加入到复制积压缓冲区中,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量。
当从服务器重新连上主服务器时,从服务器会通过PSYNC
命令将自己的复制偏移量发送给主服务器,主服务器将根据该偏移量来决定进行那种同步方式:
- 如果偏移量之后的数据任然存在于复制挤压缓冲区中,那么主服务器将对冲服务器执行部分重同步操作
- 如果偏移量之后的数据已经不存在于复制积压缓冲区中,那么将执行完整同步操作
服务器运行ID
每个Redis服务器都拥有自己的而运行ID,其在服务器启动时自动生成,由40个随机的十六进制字符组成。当主从服务器进行初次复制时,主服务器将自己的运行ID发送给从服务器,并保存好自己的运行ID;当从服务器断连后重新连接,从服务器将告知主服务器之前保存的运行ID
- 如果从服务为保存的运行ID和当前连接的主服务器的运行ID相同,则说明从服务器断连之前复制的就是当前连接的主服务器,那么将执行部分重同步操作
- 如果如果从服务为保存的运行ID和当前连接的主服务器的运行ID不同,则说明从服务器断连之前复制的不是当前连接的主服务器,那么将执行完整的重同步操作
PSYNC 命令
PSYNC
命令的调用方法有两种:
- 如果从服务器一脸没有父之过任何主服务器,或之前执行过
SLAVEOF no one
命令,那么从服务器将在开始复制时向主服务器发送PSYNC ? -1
命令,主动请求服务器进行完整重同步 - 如果从服务器已经复制过某个主服务器,那么从服务器在开始复制时将向主服务器发送
PSYNC <runid> <offset>
命令,其中runid
为运行ID,offset
为偏移量
主服务器在接收到PSYNC
命令后会有以下三种回答:
- 如果要执行完整重同步,主服务器将返回
+FULLRESYNC <runid> <offset>
,从服务器将保存主服务器的运行ID,并将offset
作为自己的初始化偏移量 - 执行部分重同步时,主服务器将发送
+CONTIMUE
回复,这时从服务器只需要等待主服务器发送自己缺失的写命令即可 - 如果Redis版本过低,不支持
PSYNC
,则发送-ERR
回复,从服务器将发送SYNC
命令执行旧的完整同步操作
复制的实现
1. 设置主服务器ip和端口
从服务器执行SLAVEOF <master_id> <master_port>
命令时,从服务器保存有主服务器id和端口信息;SLAVEOF
为一个异步命令,在设置完上述两个属性后,向客户端发送OK
后再开始执行复制工作
struct redisServer {...// 主服务器地址char *masterhost;// 主服务器端口char *masterport;...
};
2. 建立套接字连接
在执行完SLAVEOF
后,从服务器将根据上一步设置的IP和端口,向主服务器建立套接字连接;主服务器接收(accept)从服务器的连接后,为该套接字建立相应的客户端状态,将从服务器看作是连接主服务器的一个客户端来看待
3. 发送 PING 命令
连接建立完成后,从服务器将向主服务器发送PING
命令,其有以下两个作用:
- 检查套接字的读写状态书否正常
- 检查主服务器是否能正常处理命令请求
主服务器接收到PING
命令后会有三种情况
- 主服务器返回
PONG
,表示网络连接正常,可以继续进行下面的步骤 - 主服务器正常返回,但是从服务器未能在规定事件内接收到消息,此时从服务器将断开并重新连接主服务器
- 出服务器向从服务器返回一个错误信息,表示此时主服务器暂时无法处理从服务器的命令请求,此时从服务器将断开并重新连接主服务器
4. 身份验证
从服务器接收到PONG
回复后,如果从服务器设置了masterauth
选项,则进行身份验证,此时从服务器向主服务器发送AUTH
命令,命令的参数为masterauth
的值
从服务器在进行身份验证阶段存在以下几种情况:
- 主服务器没有设置
requirepass
选项,并且从服务器也没有设置masterauth
选项,则跳过该阶段 - 如果从服务器通过
AUTH
命令发送的密码和主机的requirepass
选项相同,则进行下一步操作,否则返回invalid password
错误 - 如果主服务器设置了
requirepass
选项,但从服务器没有设置masterauth
选项,那么主服务器将返回NOAUTH
;相反地,从服务器设置了masterauth
选项而主服务器未设置时返回no password is set
5. 发送端口信息
在进行完身份验证后,从服务器将执行REPLCONF listening-port <port-number>
向主服务器发送从服务器的监听端口号,其记录在主服务器对从服务器的客户端中,起作用时在主服务器执行INFO replication
命令时打印从服务器的端口号
typedef struct redisClient {...// 从服务器的监听端口号int slave_listening_port;
} redisClient;
6. 同步
从服务器将向主服务器发送PSYNC
命令,执行同步操作,将自己的数据更新志主服务器当前所处的状态
在同步操作执行之前,只有从服务器是主服务器的客户端,而在执行同步操作后,主服务器也会成为从服务器的客户端,只有主服务器成为了从服务器的客户端,才能向从服务器发送同步需要的写命令
7. 命令传播
同步完成后,主从服务器进入命令传播阶段,此时主服务器只要一直将自己执行的写命令发送给从服务器,而从服务器只要一直接收并执行主服务器发送来的写命令,就可以保持主从一致了
心跳检测
在命令椽笔阶段,默认从服务器每秒向主服务器发送命令REPLCONF ACK <replication_offset>
,其中replication_offset
为偏移量,其作用如下:
- 检测主从服务器的连接状态:主从服务器通过发送可接收
REPLOCONF ACK
命令来检查两者之间的网络连接是否正常,如果主服务器超过以免没有收到从服务器发来的REPLOCONF ACK
命令,那么主服务器就知道与从服务器的连接出现问题 - 辅助实现
min-slaves
选项:Redis的min-slaves-to-write
和min-slaves-max-lag
两个选项可以防止主服务器在不安全的情况下执行写命令,其表示当从服务器数量少于min-slaves-to-write
或者min-slaves-to-write
个从服务器的延迟都不小于min-slaves-max-lag
时主服务器将拒绝执行写命令 - 检测命令丢失:主服务器可以通过比对
REPLCONF ACK
发送过来的偏移量和自己记录的偏移量来判断从服务器是否存在命令丢失,如果存在,主服务器将根据复制积压缓冲区中保存的数据重新发送给从服务器
Sentinel
Sentinel(哨兵)是Redis的高可用性解决方案,其由一个或多个Sentinel梳理组成的Sentinel系统可以监视任意多个主服务器及其从服务器,并在被监视的主服务器进入下线状态时自动其中的某个从服务器升级为主服务器
以上图为例,当Server1下线时长超过用户设定的下线时长时,Sentinel系统就会对Server1执行故障转移操作:
- Sentinel系统会挑选Server1属下的一个从服务器,将其升级为新的主服务器
- Sentinel向所有Server1下的从服务器发送新的复制指令,让他们成为新的主服务器的从服务器,当所有的从服务器都开始复制新的主服务器时,故障转移操作执行完毕
- Sentinel继续监视以下线的Server1,当Server1重新上线时,将其设置为新的主服务器的从服务器
故障转移过程
启动并初始化Sentinel
使用以下命令启动Sentinel:
redis-sentinel /path/to/your/sentinel.conf
或者
redis-server /path/to/your/sentinel.conf --sentinel
当Sentinel启动时,执行以下步骤
- 初始化服务器:Sentinel本质是一个特殊Redis服务器,在初始化时不会加载持久化文件
- 将普通Redis服务器使用的代码替换成Sentinel专用代码:Sentinel使用
sentinel.c/REDIS_SENTINEL_PORT
常量作为服务器端口,其默认值为26479,并将普通Redis服务器的命令表redis.c/redisCommandTable
替换为sentinel.c/sentinelcmds
命令表,所以Sentinel不能执行Redis中如GET
,SET
等命令 - 初始化Sentinel状态:初始化一个
sentinel.c/sentinelState
结构作为Sentinel状态
struct sentinelState {// 当前纪元,用于故障转移uint64_t current_epoch;// 被该Sentinel监视的主服务器列表,键为主服务器名字,值为指向sentinelRedisInstance结构的指针dict *masters;// 是否进入了TILT模式int tilt;// 目前正在执行的脚本的数量int running_start_time;// 最后一次执行时间处理器的事件mstime_t previous_time;// 包含了所有需要执行的用户脚本的FIFO队列list *scripts_queue;
};
- 根据给定的配置文件,初始化Sentinel的监视主服务器列表:Sentinel状态中的masters字典记录了所有被Sentinel监视的主服务器的相关信息,其值为
sentinel.c/sentinelRedisInstance
结构:
typedef struct sentinelRedisInstance {// 标识值,记录了实例的类型及其状态,主服务器为SRI_MASTER,从服务器为SRI_SLAVEint flags;// 实例的名称,默认为 ip:portchar *name;// 实例的运行IDchar *runid;// 配置纪元,用于实现故障转移uint64_t config_epoch;//实例的地址sentinelAddr *addr;// 实例无响应多少毫秒后会被判断为主观下线mstime_t down_after_period;// 判断该实例为客观下线所需的支持投票数量int quorum;// 在执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量int parallel_syncs;// 刷新故障迁移状态时的最大时限mstime_t failover_timeout;...} sentinelRedisInstance;
- 创建连向主服务器的网络连接,Sentinel将成为主服务器的客户端,它可以向主服务器发送命令,并从命令回复中获取相关信息,Sentinel会创建两个联想主服务器的异步网络连接:
- 命令连接,用于向主服务器发送命令并接收回复
- 订阅连接,专门用于订阅主服务器的
__sentinel__:hello
频道
Sentinel建立两个连接是因为在Redis的发布于订阅功能中,被发送的信息都不会保存在Redis服务器中秒如果在信息发送时,想要接收信息的客户端不在线或下线,则信息会丢失,因此需要建立一个专门的连接来接收该频道的信息。此外,除了订阅频道外,Sentinel还需要向主服务器发送命令,所以除了订阅连接外还需要建立命令连接
获取主服务器信息
Sentinel默认十秒一次向主服务器发送INFO
命令,并通过该命令来获取主服务器的当前信息,信息格式如下:
# Server
...
run_id: XXX
...
# Replication
role:master
...
slave0:ip=127.0.0.1,port=11111,state=online,offset=43,lag=0
slave1:ip=127.0.0.1,port=22222,state=online,offset=43,lag=0
slave2:ip=127.0.0.1,port=33333,state=online,offset=43,lag=0
...
# othersections
...
信息内容包括:
- 主服务器本身的信息,包括服务器运行ID以及role域记录的服务器角色
- 主服务器下所有的从服务器信息,包括IP地址和端口号等
Sentinel会根据该信息更新masters
字典中的主服务器信息及sentinelRedisInstance
结构中的slaves
字典中的信息
获取从服务器的信息
当Sentinel发现主服务器由新的从服务器时,会对该从服务器建立相应的实例结构和命令连接于订阅连接,同样会每个十秒发送INFO
命令,其回复内容如下:
# Server
...
run__id:XXX
...# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
slave_repl_offset:11887
slave_priority:100# Other sections
...
信息内容包括:
- 从服务器的运行ID
runID
- 从服务器的角色
role
- 主服务器的地址
master_host
和端口号master_port
- 主从服务器的连接状态
master_link_status
- 从服务器的优先级
slabe_priority
- 从服务器的复制偏移量
slave_repl_offset
根据这些信息,Sentinel会对从服务器的实例结构进行更新
向主服务器和从服务器发送信息
默认Sentinel会以两秒一次的间隔给所有监视的主从服务器发送命令:
PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"
信息内容包括:
- 以
s_
开头的参数为Sentinel本身的信息s_ip
:Sentinel的IP地址s_port
:Sentinel的端口号s_runid
:Sentinel的运行IDs_epoch
:Sentinel当前的配置纪元
- 以
m_
开头的参数为主服务器信息;如果发送的Sentinel正在监视的是主服务器,那么这些参数就是主服务器的信息,如果是从服务器,那么这些参数就是该从服务器对应的主服务器信息m_ip
:主服务器的IP地址m_port
:主服务器的端口号m_runid
:主服务器的运行IDm_epoch
:主服务器当前的配置纪元
接收来自主服务器和从服务器的频道信息
当Sentinel和主从服务器建立起连接后,会通过订阅连接发送以下命令
SUBSCRIB __sentinel__:hello
对于监视同一个服务器的多个Sentinel来说,一个Sentinel发送的信息会被其他Sentinel接收到,这些信息用于更新其他entinel对发送信息的Sentinel的认知,也会被用于更新其他Sentinel对被监视服务器的认知
当一个Sentinel从__sentinel__:hello
频道收到一条信息时,Sentinel会对该信息尽心分析,提取出信息中的Sentinel IP地址,Sentinel端口号和运行ID等八个参数,进行以下检查:
- 如果信息中记录的Sentinel的运行ID和接收信息的Sentinel的运行ID相同,那么Sentinel则丢弃这条信息不做处理
- 如果信息中记录的Sentinel的运行ID和接收信息的Sentinel的运行ID不同,则接收信息的Sentinel将根据信息中的各个参数,对相应主服务器的实例结构进行更新:
- 更新sentinels字典:Sentinel为主服务器创建的实力结构中的sentinels字典保存了其他监视该主服务器的Sentinel的信息,当收到其他Sentinel发来的消息时,会对sentinels字典进行更新
- 创建向其他Sentinel的命令连接,使用命令连接相连的各个Sentinel可以通过向其他Sentinel发送命令请求来进行命令交换
检测主观下线状态
默认情况下,Sentinel会以每秒一次的频率向所有创建了命令连接的主从服务器和Sentinel发送PING
命令,通过其返回值来判断其是否在线,如果在down-after-milliseconds
毫秒内连续向Sentinel发送无效回复,那么Sentinel就会判断该实例为主观下线状态,其flag
属性将被设置为SRI_S_DOWN
检查客观下线状态
当一个主服务器被判断为主观下线后,为了确认这个主服务器是否真的下线了,Sentinel会像同样监视这个主服务器的其他Sentinel进行询问,如果收集到了足够多的已下线判断后,Sentinel就会将其判定为客观下线,如果为主服务器则进行故障转移操作
选取零头Sentinel
当一个主服务器被判断为客观下线时,监视这个下线主服务器的Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel来执行故障转移操作,其规则如下:
- 所有Sentinel都有被选为领头Sentinel的资格
- 当进行选举时,无论是否选举成功,所有Sentinel的配置纪元(configuration epoch)都会自增一次
- 在一个配置纪元里,所有Sentinel都有一次将某个Sentinel设置为局部Sentinel的机会,并且局部领头一旦设置,在这个配置纪元里就不再更改
- 当一个源Sentinel向另一个目标Sentinel发送
SENTINEL -s-master-down-by-addr
命令,其返回值中的runid
不是*
而是是源Sentinel的运行ID时,表示源Sentinel为目标Sentinel的局部领头Sentinel - 目标Sentinel在接收到
SENTINEL -s-master-down-by-addr
命令后,其回复中的leader_runid
参数和leader_epoch
参数分别为目标Sentinel的局部头领Sentinel的运行ID和配置纪元 - 源Sentinel收到目标Sentinel的回复后,检查
Leader_runid
参数是否和源Sentinel的运行ID是否一致,如果一致测目标Sentinel将源Sentinel设置为自己的局部领头Sentinel - 如果某个Sentinel被半数以上的Sentinel设置为了局部头领Sentinel,那么该Sentinel为领头Sentinel
- 如果在规定时间内为选举出领头Sentinel,那么重新来一轮选举
故障转移
在选举出领头Sentinel后,领头Sentinel会对一下先的主服务器执行故障转移操作:
- 在以下线的主服务器的从服务器中选取一个从服务器,将其转换为主服务器,其挑选过程为:
- 删除类表中所有处于下线和短线状态的从服务器
- 删除列表中所有最近五秒内没有回复过领头Sentinel的
INFO
命令的从服务器 - 删除所有于一下先主服务器连接断开超过
down-after-milliseconds
* 10毫秒的从服务器 - 在剩余的从服务器中,根据优先级进行选择,如果具有多个最高优先级的从服务器,那么选择复制偏移量最大的,如果复制偏移量也相同,那么按照运行ID进行排序,选择运行ID最小的从服务器作为新的主服务器
- 让其他从服务器改为新的主服务器的从服务器
- 将已下线的主服务器设置为新的主服务器的从服务器
相关文章:
Redis多机数据库实现
Redis多机数据库实现 为《Redis设计与实现》笔记 复制 客户端可以使用SLAVEOF命令将指定服务器设置为该服务器的主服务器 127.0.0.1:12345> SLAVEOF 127.0.0.1 6379127.0.0.1:6379将被设置为127.0.0.1:123456的主服务器 旧版复制功能的实现 Redis的复制功能分为同步&a…...
Leangoo领歌 -敏捷任务管理软件,任务管理更轻松更透明
任务管理,简单易懂,就是对任务进行管理。那怎么可以更好进行任务管理呢?怎么样样可以让任务进度可视化,一目了然呢?有效的管理可以让我们事半功倍。 接下来我们看一下如何借助任务管理软件高效的做任务管理。 首先…...
go的iris框架进行本地资源映射到服务端
我这里使用的是HandleDirapi,有其他的请补充 package mainimport ("github.com/kataras/iris/v12" )type Hello struct{Status int json:"status"Message string json:"message" }func main(){app : iris.New()//第一个api:相当于首页app.Get(&q…...
代码随想录day46|139. 单词拆分
139. 单词拆分 class Solution:def wordBreak(self, s: str, wordDict: List[str]) -> bool:dp [False]*(len(s)1)dp[0]Truefor i in range(len(s)1):for j in wordDict:if i>len(j) and (s[i-len(j):i] in wordDict) and dp[i-len(j)]:dp[i] Truereturn dp[len(s)]多…...
MATLAB实现函数拟合
目录 一.理论知识 1.拟合与插值的区别 2.几何意义 3.误差分析 二.操作实现 1.数据准备 2.使用cftool——拟合工具箱 三.函数拟合典例 四.代码扩展 一.理论知识 1.拟合与插值的区别 通俗的说,插值的本质是根据现有离散点的信息创建出更多的离散点…...
vue优化首屏加载时间优化-cdn引入第三方包
前言 为什么要进行首屏加载优化,因为随着我们静态资源和第三方包和代码增加,压缩之后包会越来越大 随着网络的影响,在我们第一输入url请求资源时候,网络阻塞,加载时间长,用户体验不好 仔细观察后就会发现…...
lv4 嵌入式开发-3 标准IO的读写
目录 1 标准I/O – 读写流 2 标准I/O – 按字符输入 3 标准I/O – 按字符输出 4 标准I/O – 思考和练习 5 标准I/O – 按行输入 6 标准I/O – 按行输出 7 标准I/O – 思考和练习 1 标准I/O – 读写流 流支持不同的读写方式: 读写一个字符:fgetc()/fputc()一…...
iOS UIDevice设备信息
识别设备和操作系统 //获得共享设备实例 open class var current: UIDevice { get }//识别设备的名称 open var name: String { get } // e.g. "My iPhone"//设备类型 open var model: String { get } // e.g. "iPhone", "iPod touch"//本地化设…...
SLAM ORB-SLAM2(2)编译安装
SLAM ORB-SLAM2(2)编译安装 1. 软件包依赖安装2. 依赖安装2.1. Eigen2.2. Pangolin2.3. OpenCV3. ORB-SLAM23.1. 源码下载3.2. 文件修改3.3. 扩大交换空间3.4. 编译1. 软件包依赖安装 以一个纯净的ubuntu20.04桌面版为例 1.首先设置软件源为清华源 2.安装必要依赖 sudo ap…...
第11节-PhotoShop基础课程-索套工具
文章目录 前言1.索套工具 选中后按Ctrl 可以移动2.加,减,交叉 shift alt 2.多边形索套工具 手动首尾相连 或者双击空地1.单击绘制直线选区2.双击结束绘制3.加,减,交叉4. delete可以删除节点 3.磁性索套工具1.沿着边缘自动吸附2.可…...
Json字符串内容比较-超实用版
背景 之前有类似接口diff对比,数据对比的测试需求,涉及到json格式的数据对比,调研了几个大神们分享的代码,选了一个最符合自己需求的研究了下。 说明 这个对比方法,支持JsonObject和JsonArray类型的数据对比&#x…...
Redis系列之客户端Redisson
概述 官方推荐的客户端,支持Redis单实例、Redis哨兵、Redis Cluster、Redis master-slave等各种部署架构。 GitHub, 功能: 分布式锁 分布式锁 使用Redisson提供的分布式锁的一个最常见场景,应用部署为多个节点,然…...
centos 端口被占用的快速排查方式
问题笔记 centos 端口被占用的快速排查方式 centos 端口被占用的快速排查方式 这里说一个我刚刚遇到的问题,解决步骤用来记录,方便以后自己查询。 nginx配置完index.html测试文件,发现一直显示的404页面。 我跑到服务器上想重启一下nginx …...
Java“牵手”淘宝商品列表数据,关键词搜索淘宝商品数据接口,淘宝API申请指南
淘宝商城是一个网上购物平台,售卖各类商品,包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取淘宝商品列表和商品详情页面数据,您可以通过开放平台的接口或者直接访问淘宝商城的网页来获取商品详情信息。以下是两种常用方法的介绍&…...
OpenEuler/CentOS如何修改密码策略
密码策略文件: /etc/pam.d/system-auth 找到行: password requisite pam_pwquality.so try_first_pass local_users_only 为保证安全,可以将这一行注释掉,添加一行,最后结果如下: #password …...
# Spring MVC与RESTful API:如何设计高效的Web接口
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
Scrum敏捷模式的优势点、实践经验及适用企业
Scrum敏捷模式是一种灵活、适应性强的开发方法,其核心理念是以短周期、高频率的方式进行项目开发,确保团队能够快速响应变化。 Scrum包含三个角色:产品负责人(Product Owner)、Scrum Master和开发团队(Tea…...
【C++杂货铺】探索stack和queue的底层实现
文章目录 一、stack的介绍和使用1.1 stack的介绍1.2 stack的使用1.2.1 最小栈1.2.2 栈的压入、弹出序列1.2.3 逆波兰表达式求值1.2.4 用栈实现队列 二、queue的介绍和使用2.1 queue的介绍2.2 queue的使用2.2.1 二叉树的层序遍历 三、模拟实现3.1 stack模拟实现3.2 queue模拟实现…...
“系统的UI”——SystemUI
SystemUI的实现 以StatusBar为例,来分析下Android系统具体是如何实现它们的。 相关代码分为两部分,即: Service部分 代码路径:frameworks/base/services/java/com/android/server。 应用部分 代码路径:frameworks…...
类和对象:构造函数,析构函数与拷贝构造函数
1.类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现,编译器…...
谈谈Java的特点和优点以及选择Java的原因
 如果面试官问你:请你说说Java的特点和优点,为什么要选择Java?你该怎么回答? 得分点 Java的特点 Java与C的区别 Java的优点标准回答 Java是一门非常纯粹的面向对象的编程语言,它吸收了C语言的各种优…...
消息队列(MQ)面试
目录 讲一讲MQ 面试官: 在你之前的项目中,你是否使用过消息队列(MQ)?能详细介绍一下你在项目中如何使用MQ吗? 在用户和用户之间的多对多聊天通信中如何使用,请具体来讲一下。 那你可以讲一下消息的确认…...
无涯教程-JavaScript - COUPNUM函数
描述 COUPNUM函数返回结算日和到期日之间应付的息票数量,四舍五入到最接近的整数。 语法 COUPNUM (settlement, maturity, frequency, [basis])争论 Argument描述Required/OptionalSettlement 证券的结算日期。 证券结算日期是指在发行日期之后将证券交易给买方的日期。 Re…...
上海控安携汽车网络安全新研产品出席AUTOSEMO“恒以致远,共创共赢”主题研讨会
8月31日,AUTOSEMO“恒以致远,共创共赢”主题研讨会在天津成功召开。本次大会由中国汽车工业协会软件分会中国汽车基础软件生态标委会(简称:AUTOSEMO)与天津市西青区人民政府联合主办。现场汇聚了100余位来自产学研政企…...
小程序引入高德/百度地图坐标系详解
小程序引入高德/百度地图坐标系详解 官网最近更新时间:最后更新时间: 2021年08月17日 高德官网之在原生小程序中使用的常见问题 链接 目前在小程序中使用 高德地图只支持以下功能 :地址描述、POI和实时天气数据 小结:从高德api中获取数…...
英诺森 “供应链智能数据平台”荣获“科技进步奖”
近日,2023年中国物流与采购联合会科学技术奖正式公布,该奖项经国家科技部批准,在国家科学技术奖励工作办公室登记备案,是我国物流行业最具影响力的奖项之一。 英诺森联合客户申报的科技项目“英诺森供应链智能数据平台”…...
kafka 3.5 主题分区的Follower创建Fetcher线程从Leader拉取数据源码
Kakfa集群有主题,每一个主题下又有很多分区,为了保证防止丢失数据,在分区下分Leader副本和Follower副本,而kafka的某个分区的Leader和Follower数据如何同步呢?下面就是讲解的这个 首先要知道,Follower的数据…...
Golang web 项目中实现自定义 recovery 中间件
为什么需要实现自定义 recovery 中间件? 在 Golang 的 Web 项目中,自定义 recovery 中间件是一种常见的做法,用于捕获并处理应用程序的运行时错误,以避免整个应用程序崩溃并返回对应格式的响应数据。 很多三方 web 框架…...
Direct3D绘制旋转立方体例程
初始化文件见Direct3D的初始化_direct3dcreate9_寂寂寂寂寂蝶丶的博客-CSDN博客 D3DPractice.cpp #include <windows.h> #include "d3dUtility.h" #include <d3dx9math.h>IDirect3DDevice9* Device NULL; IDirect3DVertexBuffer9* VB NULL; IDirect3…...
ElementUI浅尝辄止31:Tabs 标签页
选项卡组件:分隔内容上有关联但属于不同类别的数据集合。 常见于网站内容信息分类或app内容信息tab分类 1.如何使用? Tabs 组件提供了选项卡功能,默认选中第一个标签页,你也可以通过 value 属性来指定当前选中的标签页。 <temp…...
淄博公司做网站/成都搜狗seo
Connection:用于连接数据源Command:对数据源执行命令DataReader:在只读和只写的连接模式下从数据源读取数据.DataAdpter:从数据源读取数据并使用所读取的数据填充数据集对象.建立Connection 1,用SqlConnection连接Sql Server (1)引入命名空间: using System.Data.SqlClient; (2…...
中山网站制作套餐/品牌宣传策略
转自 http://blog.csdn.net/zhengzhb/article/details/7187278 在java以及其他的面向对象设计模式中,类与类之间主要有6种关系,他们分别是:依赖、关联、聚合、组合、继承、实现。他们的耦合度依次增强。 1. 依赖(Dependence&…...
jsp和php做网站那个快/马鞍山seo
使用inBitmap前使用inBitmap后一开始哪怕我看了这个图我也不太理解,后来我猜想应该是这样: 当某个bitmap不可达的时候,这个时候就是处于了可回收的状态 所以我们如果要复用的话,应该会把不可达的bitmap先recycler,然后…...
在手机怎样使用wordpress/深圳网络营销公司
前言:电商中:我们想查看某个用户所有的订单,或者想查看某个用户在某个时间段内所有的订单,此时我们需要对订单表数据进行筛选,按照用户、时间进行过滤,得到我们期望的结果。 此时我们需要使用条件查询来对…...
柳市网站建设/网上商城网站开发
linux环境下,使用binlog模式恢复mysql数据(mysql数据库中的一张表误删了怎么找回?)。 问题:linux中开启binlog模式下,mysql数据库中的一张表误删了怎么找回? 1.首先在mysql中查看是否开启binl…...
永康市建设局网站为什么打不开/哪里有营销策划培训班
最近无聊,看一了一下朋友的大佬的项目,用腾讯的开源NCNN实现了图像识别,不过是基于C的,我不太会,于是搜了一下java的,有基于Tesseract-OCR的,有基于百度API的,不过据说Tesseract-OCR…...