作者: ethan

  • 规则怪谈生成器:三步创作属于你自己的恐怖故事

    你是否曾想过亲手创作一个令人脊背发凉的规则怪谈故事?现在,只需短短几分钟,就能轻松实现。

    规则怪谈生成器(guizegt.com)是一款完全免费的在线创作工具,专为喜爱恐怖文学、悬疑故事和创意写作的朋友设计。无论你是想为朋友编一个惊悚小故事,还是为短视频寻找灵感,这个工具都能帮你快速搭建一个完整的怪谈世界。

    为什么规则怪谈让人着迷?

    规则怪谈是一种近年来越来越受欢迎的恐怖故事形式。它不像传统鬼故事那样直接描写恐怖场面,而是通过列出一条条看似平常、实则暗藏玄机的“规则”,让读者在阅读过程中自己发现不对劲的地方。

    比如,一条规则可能写着:“晚上十一点后,无论听到什么声音,都不要打开卧室门。”另一条又写着:“如果半夜听到敲门声,请先确认门外是否有人影。若没有人影,请立即关灯躺回床上。”这些规则之间往往存在矛盾或漏洞,读者需要仔细推敲,才能拼凑出真相。

    这种独特的叙事方式,让恐怖感在读者脑海中自行生长,比直接描写更让人印象深刻。

    如何使用这个生成器?

    整个创作过程被简化为直观的四步操作,不需要任何写作基础,也不需要安装任何软件,打开网页就能使用。

    第一步:选择场景

    工具内置了12种经典场景,包括学校、医院、公寓、地铁、酒店、老宅、游乐园、图书馆、地下车库、博物馆、便利店和养老院。每个场景都有其独特的氛围和可能性,你可以选择自己最感兴趣的一个。

    第二步:选择氛围

    接下来,从8种恐怖氛围中挑选一种。有的偏向诡异压抑,有的走极度恐怖路线,还有的强调烧脑推理或绝望循环。不同的氛围会直接影响规则的内容和风格。

    第三步:选择难度

    三档难度级别可供选择,新手可以从简单模式开始,熟悉后逐步挑战更高难度。难度越高,规则之间的逻辑关系往往越复杂,故事也越耐人寻味。

    第四步:一键生成

    完成以上选择后,点击生成按钮,系统会立即组合出一套完整的规则怪谈。你可以直接阅读,也可以保存下来继续修改。

    每天都能免费使用

    目前,这个工具对所有用户完全免费开放。每天都有一定次数的免费生成额度,足够日常创作使用。如果你想保存自己的生成记录,或者去除分享时的水印,可以注册登录,解锁更多实用功能。

    根据公开数据,目前已经有超过四万九千条规则怪谈通过这个工具被生成出来,每天仍有不少用户在使用它寻找创作灵感。

    规则怪谈的更多可能

    除了个人娱乐,规则怪谈还有很多有趣的用途。

    如果你是短视频创作者,可以用生成的规则作为脚本素材,拍摄成短剧或解说视频。规则怪谈本身带有强烈的悬疑感和互动性,非常适合视频平台的传播特点。

    如果你是桌游爱好者,这些规则也可以用作跑团游戏(TRPG)中的剧本框架。玩家需要在规则中寻找生路,每一次判断都关乎角色的命运,紧张感十足。

    即使只是和朋友分享,一套精心生成的规则怪谈也能成为聚会时的话题焦点。大家一起来找规则中的漏洞,讨论“如果是我该怎么办”,本身就是一种有趣的社交互动。

    现在就试试吧

    规则怪谈的魅力在于,读者永远无法在第一遍就完全看透所有规则。每一条看似普通的提示,背后都可能隐藏着致命的真相。

    打开规则怪谈生成器,选择你喜欢的场景和氛围,看看系统会为你呈现一个怎样的诡异世界。也许你会惊讶地发现,那些看似随机的规则组合起来,竟然构成了一篇完整、自洽且细思极恐的故事。

    创作从来都不只是专业作家的事。只要有一个合适的起点,每个人都能编织出属于自己的恐怖传说。而这个生成器,就是你开启怪谈创作之旅的第一步。

  • 规则怪谈生成器:三步打造你的专属怪谈宇宙

    在恐怖创作与跑团游戏日益兴起的今天,规则怪谈作为一种独特的叙事形式,正吸引着越来越多的爱好者。规则怪谈的魅力在于,它通过一系列看似平常却暗藏危机的规则,营造出一种细思极恐的氛围——那些被禁止的行为、必须遵守的条款,以及违反规则后可能遭遇的不可名状的恐怖

    规则怪谈生成器(https://guizegt.com/ 正是为这一创作需求量身打造的在线工具,让你能够三步选择,一键生成完整的规则怪谈场景

    什么是规则怪谈生成器?

    规则怪谈生成器是一款专为恐怖创作者、跑团主持人和怪谈爱好者设计的在线工具。它通过纯模板拼接的方式,无需任何AI成本,即可生成结构完整、氛围到位的规则怪谈文本。无论你是想为自己的故事寻找灵感,还是需要为跑团游戏快速生成场景,这个工具都能满足你的需求。

    核心特点

    • 零AI成本:完全基于精心设计的模板库,不依赖AI生成,确保文本质量稳定可控
    • 纯模板拼接:由专业设计的模板组合而成,保证规则怪谈的独特韵味和逻辑自洽
    • 无限制生成:不限次数,随时创作,让你的创意源源不断
    • 完全免费:无需付费,即可享受完整的生成功能

    如何使用:三步生成完整规则怪谈

    规则怪谈生成器的使用流程极其简洁,只需三步即可完成创作:

    第一步:选择场景

    12个经典场景中选择一个作为你的怪谈舞台。不同的场景设定将直接影响规则的内容和氛围基调,为你的怪谈奠定基础。例如废弃医院、幽灵邮轮、午夜学校等经典恐怖场景,都能在工具中找到。

    第二步:选择氛围

    8种恐怖风格中选择你想要营造的氛围。是偏向心理恐怖的压抑感,还是克苏鲁式的不可名状恐惧?是都市传说的现实诡异,还是更加超自然的惊悚?不同的氛围选择将决定规则的措辞风格和整体调性。

    第三步:选择难度

    选择3档规则数量之一,控制怪谈的复杂程度。较低的难度适合快速创作或新手尝试,较高的难度则能生成更加丰富、细致的规则体系,适合深度创作需求。

    完成以上三步后,点击一键生成,规则便会逐条浮现,呈现出类似真实规则书或告示牌的视觉效果,营造出紧张有序的阅读节奏。

    谁需要规则怪谈生成器?

    恐怖故事创作者

    如果你正在创作恐怖小说、短篇故事或系列怪谈,规则怪谈生成器可以为你提供高质量的灵感素材和完整的规则框架。生成的怪谈文本可以直接作为故事的核心设定,也可以作为创作的起点进行二次加工。

    TRPG跑团主持人

    对于主持克苏鲁的呼唤、密室逃脱或其他恐怖主题跑团的主持人来说,规则怪谈是营造氛围、推动剧情的重要工具。快速生成符合场景的规则书,为玩家带来沉浸式的游戏体验

    都市传说与怪谈爱好者

    如果你只是单纯喜欢阅读和收集各种规则怪谈,这个工具也能为你提供源源不断的新内容。每次生成都可能带来意想不到的惊喜,满足你对细思极恐故事的渴望。

    脑洞练习与灵感拓展

    规则怪谈生成器也是进行创意写作练习的优秀工具。通过组合不同的场景、氛围和难度,你可以探索各种创作可能性,拓展自己的叙事技巧。

    为什么选择规则怪谈生成器?

    易用性

    无需任何写作经验或专业知识,三步选择,一键生成,即可获得高质量的规则怪谈文本。界面直观,操作简单,任何人都能轻松上手。

    专业性

    工具中的模板由熟悉规则怪谈叙事的专业人士设计,确保生成的文本在结构、逻辑和氛围上都符合规则怪谈的独特风格。不会出现AI生成内容常见的逻辑混乱或风格偏差问题。

    效率性

    从构思到获得完整成品,整个过程只需几分钟。相比从零开始构思和撰写规则,这个工具能极大地提升创作效率,让你有更多时间专注于故事的打磨和润色。

    创造性

    虽然基于模板,但通过场景、氛围、难度的自由组合,你可以创造出千变万化的规则怪谈。无限制生成功能让你可以不断探索新的可能性,保持创作的新鲜感。

    立即开始创作

    规则怪谈生成器为恐怖创作者提供了一个高效、专业、免费的创作平台。无论你是经验丰富的创作者,还是刚刚对规则怪谈产生兴趣的新手,这个工具都能帮助你快速将脑海中的恐怖想象转化为完整的文字作品。

    现在就访问 https://guizegt.com/ ,开始构建你的第一个规则怪谈吧!

  • Mysql集群部署(主从复制)

    基础环境准备

    # 获取centos镜像,注:以下镜像为整合了 ansible 的centos镜像,可以解决大量的依赖问题
    docker pull ansible/centos7-ansible

    容器中安装基础工具

    启动容器:

    docker run -itd --privileged --name centos-mysql ansible/centos7-ansible /usr/sbin/init

    进入容器:

    docker exec -it centos-mysql bash
    yum install wget 
    yum install net-tools
    yum install initscripts
    yum install openssh-server
    yum install libaio
    yum install numactl

    安装Mysql

    cd /usr/local/
    # 下载mysql
    wget http://mirrors.163.com/mysql/Downloads/MySQL-8.0/mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz
    # 解压
    tar xvJf mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz
    mv mysql-8.0.26-linux-glibc2.12-x86_64 mysql
    # 创建data文件
    mkdir /usr/local/mysql/data
    # 创建用户组与用户并授权
    groupadd mysql
    useradd -g mysql mysql
    chown -R mysql.mysql /usr/local/mysql
    # 初始化基本信息
    cd /usr/local/mysql/bin/
    ./mysqld --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data/ --initialize

    记住生成的临时用户名和密码,保存下来,之后会用到

    编辑配置文件

    查看my.cnf 文件位置 ./mysql –help | grep my.cnf

    这个实际上就是mysql尝试去读取配置文件的路径顺序,没有找到话,mysql会走默认配置

    所以,看下这几处位置是否有配置文件,有的话,编辑该文件,没有的话,直接在第一读取位置,创建配置文件,如: 创建 /etc/my.cnf 配置文件。

    /etc/my.cnf:

    [client]
    default-character-set=utf8
    [mysqld]
    basedir=/usr/local/mysql
    datadir=/usr/local/mysql/data
    port=3306
    socket=/tmp/mysql.sock
    character-set-server=utf8
    collation-server=utf8_general_ci
    sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

    添加mysqld服务到系统

    # cp -a 保留原文件属性
    cp -a /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql
    # 授予执行权限
    chmod +x /etc/init.d/mysql
    chkconfig --add mysql

    启动mysql

    service mysql start

    将mysql命令添加到服务

    ln -s /usr/local/mysql/bin/mysql /usr/bin/

    登录mysql 使用之前随机生成的密码

    修改root密码

    ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';    #其中123456是新的密码自己设置
    flush privileges; # 使新密码生效

    使支持远程连接

    use mysql;
    update user set host='%' where user='root';
    flush privileges;
    exit

    制作镜像

    退出容器,制作mysql镜像

    docker stop centos-mysql
    docker commit centos-mysql centos-mysql-80

    创建Master

    docker run -itd -p 3306:3306 --privileged --name mysql-master centos-mysql-80 /usr/sbin/init

    进入容器

    docker exec -it mysql-master /bin/bash

    修改/etc/my.cnf, 增加以下配置

    # master
    log_bin=master-bin
    log_bin_index=master-bin.index
    server-id=1
    sync-binlog=1
    binlog-ignore-db=information_schema
    binlog-ignore-db=performance_schema
    binlog-ignore-db=sys
    gtid_mode = on
    enforce_gtid_consistency = 1
    log_slave_updates = 1
    
    plugin_load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
    loose_rpl_semi_sync_master_enabled = 1
    loose_rpl_semi_sync_slave_enabled = 1
    loose_rpl_semi_sync_master_timeout = 5000

    重启mysql

    service mysql restart

    授权

    # 登录mysql,配置slave访问master的用户的ip权限,注:identified by 后需要填写你root账户对应的密码
    # 创建账户
    create user 'root'@'%' identified by  '123456'
    # 赋予权限,with grant option这个选项表示该用户可以将自己拥有的权限授权给别人
    grant all privileges on *.* to 'root'@'%' with grant option
    # 使生效
    flush privileges;

    创建Slave

    docker run -itd -p 3307:3306 --privileged --name mysql-slave-1 centos-mysql-80 /usr/sbin/init

    进入容器

    docker exec -it mysql-slave-1 /bin/bash

    修改/etc/my.cnf, 增加以下配置

    # slave
    server_id=2
    relay_log_index = slave_relay_bin.index
    relay_log= slave_relay_bin
    innodb_log_file_size= 256M
    expire-logs-days = 1
    read_only=1
    gtid_mode = on
    enforce_gtid_consistency = 1
    log_slave_updates = 1
    binlog-ignore-db=information_schema
    binlog-ignore-db=performance_schema
    binlog-ignore-db=sys
    # 候选时需要设置参数
    log-bin = mysql-bin
    log-bin-index = mysql-bin.index
    plugin_load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
    loose_rpl_semi_sync_master_enabled = 1
    loose_rpl_semi_sync_slave_enabled = 1
    loose_rpl_semi_sync_master_timeout = 5000

    重启mysql

    # 删除auto.cnf文件,否则连接master会失败
    rm /usr/local/mysql/data/auto.cnf
    service mysql restart

    设置master相关信息

    配置前,我们需要先获取master相关信息,进入mysql-master容器,登录mysql,执行 show master status 查看:

    获取mysql-master容器ip地址:

    docker inspect --format='{{.NetworkSettings.IPAddress}}' mysql-master

    进行从库的master配置

    change master to master_host='172.17.0.2', master_port=3306, master_user='root', master_password='123456',master_log_file='master-bin.000001', master_log_pos=550;
    
    start slave;
    
    show slave status \G
    # show slave satus 这两项为Yes,则成功
    Slave_IO_Running: Yes
    Slave_SQL_Running: Yes

    使用同样的方式创建salve2,注:需要保证my.inf配置中server_id保持唯一

    测试主从复制

    master创建数据库 test

    mysql-slave-1

    mysql-slave-2

    至此,mysql主从集群部署完成~

    参考链接

    https://blog.csdn.net/qq_15350581/article/details/114090794

    https://blog.csdn.net/fhdsece/article/details/104920422

  • Redis Cluster集群节点间通信

    Redis 的集群节点之间的通信采取 gossip 协议进行通信,在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加10000 的端口号,比如 16379,16379 端口号是用来进行节点间通信的。

    Gossip协议

    Gossip protocol 也叫 Epidemic Protocol (流行病协议),顾名思义,就像流言蜚语一样,利用一种随机、带有传染性的方式,将信息传播到整个网络中,并在一定时间内,使得系统内的所有节点数据一致。这里简单介绍下Gossip协议的执行过程:

    当一个种子节点有状态需要更新到网络中的其他节点时,它会随机的选择周围几个节点散播消息,收到消息的节点也会重复该过程,直至最终网络中所有的节点都收到了消息。

    Gossip protocol

    Gossip协议优势:

    • 扩展性 网络可以允许节点的任意增加和减少,新增加的节点的状态最终会与其他节点一致。
    • 容错 网络中任何节点的宕机和重启都不会影响 Gossip 消息的传播,Gossip 协议具有天然的分布式系统容错特性。
    • 去中心化 Gossip 协议不要求任何中心节点,所有节点都可以是对等的,任何一个节点无需知道整个网络状况,只要网络是连通的,任意一个节点就可以把消息散播到全网。
    • 一致性收敛 Gossip 协议中的消息会以一传十、十传百一样的指数级速度在网络中快速传播,因此系统状态的不一致可以在很快的时间内收敛到一致。消息传播速度达到了 logN。
    • 简单 Gossip 协议的过程极其简单,实现起来几乎没有太多复杂性。

    Gossip协议的缺陷:

    • 消息的延迟 由于 Gossip 协议中,节点只会随机向少数几个节点发送消息,消息最终是通过多个轮次的散播而到达全网的,因此使用 Gossip 协议会造成不可避免的消息延迟。不适合用在对实时性要求较高的场景下。
    • 消息冗余 Gossip 协议规定,节点会定期随机选择周围节点发送消息,而收到消息的节点也会重复该步骤,因此就不可避免的存在消息重复发送给同一节点的情况,造成了消息的冗余,同时也增加了收到消息的节点的处理压力。而且,由于是定期发送,因此,即使收到了消息的节点还会反复收到重复消息,加重了消息的冗余。

    Redis Cluster的Gossip机制

    Redis Cluster 是在 3.0 版本引入集群功能。为了让让集群中的每个实例都知道其他所有实例的状态信息,Redis 集群规定各个实例之间按照 Gossip 协议来通信传递信息。

    在这里插入图片描述

    上图展示了主从架构的 Redis Cluster 示意图,其中实线表示节点间的主从复制关系,而虚线表示各个节点之间的 Gossip 通信。

    Redis Cluster 中的每个节点都维护一份自己视角下的当前整个集群的状态,主要包括:

    1. 当前集群状态
    2. 集群中各节点所负责的 slots信息,及其migrate状态
    3. 集群中各节点的master-slave状态
    4. 集群中各节点的存活状态及怀疑Fail状态

    Redis Cluster 的节点之间会相互发送多种消息,较为重要的如下所示:

    消息说明
    meet通过「cluster meet ip port」命令,已有集群的节点会向新的节点发送邀请,加入现有集群,然后新节点就会开始与其他节点进行通信
    ping节点按照配置的时间间隔向集群中其他节点发送 ping 消息,消息中带有自己的状态,还有自己维护的集群元数据,和部分其他节点的元数据
    pong返回ping和meet,包含自己的状态和其他信息,也可以用于信息广播和更新
    fail某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机了
    redis cluster通信消息

    通过上述这些消息,集群中的每一个实例都能获得其它所有实例的状态信息。这样一来,即使有新节点加入、节点故障、Slot 变更等事件发生,实例间也可以通过 PING、PONG 消息的传递,完成集群状态在每个实例上的同步。

    通过上述这些消息,集群中的每一个实例都能获得其它所有实例的状态信息。这样一来,即使有新节点加入、节点故障、Slot 变更等事件发生,实例间也可以通过 PING、PONG 消息的传递,完成集群状态在每个实例上的同步。下面,我们依次来看看几种常见的场景:

    定时ping/pong消息

    Redis Cluster 中的节点都会定时地向其他节点发送 PING 消息,来交换各个节点状态信息,检查各个节点状态,包括在线状态、疑似下线状态 PFAIL 和已下线状态 FAIL。

    Redis 集群的定时 PING/PONG 的工作原理可以概括成两点:

    • 一是,每个实例之间会按照一定的频率,从集群中随机挑选一些实例,把 PING 消息发送给挑选出来的实例,用来检测这些实例是否在线,并交换彼此的状态信息。PING 消息中封装了发送消息的实例自身的状态信息、部分其它实例的状态信息,以及 Slot 映射表。
    • 二是,一个实例在接收到 PING 消息后,会给发送 PING 消息的实例,发送一个 PONG 消息。PONG 消息包含的内容和 PING 消息一样。

    下图显示了两个实例间进行 PING、PONG 消息传递的情况,其中实例一为发送节点,实例二是接收节点

    在这里插入图片描述

    新节点上线

    Redis Cluster 加入新节点时,客户端需要执行 CLUSTER MEET 命令,如下图所示。

    meet

    节点一在执行 CLUSTER MEET 命令时会首先为新节点创建一个 clusterNode 数据,并将其添加到自己维护的 clusterState 的 nodes 字典中。有关 clusterState 和 clusterNode 关系,我们在最后一节会有详尽的示意图和源码来讲解。

    然后节点一会根据据 CLUSTER MEET 命令中的 IP 地址和端口号,向新节点发送一条 MEET 消息。新节点接收到节点一发送的MEET消息后,新节点也会为节点一创建一个 clusterNode 结构,并将该结构添加到自己维护的 clusterState 的 nodes 字典中。

    接着,新节点向节点一返回一条PONG消息。节点一接收到节点B返回的PONG消息后,得知新节点已经成功的接收了自己发送的MEET消息。

    最后,节点一还会向新节点发送一条 PING 消息。新节点接收到该条 PING 消息后,可以知道节点A已经成功的接收到了自己返回的P ONG消息,从而完成了新节点接入的握手操作。

    MEET 操作成功之后,节点一会通过定时 PING 机制将新节点的信息发送给集群中的其他节点,让其他节点也与新节点进行握手,最终,经过一段时间后,新节点会被集群中的所有节点认识。

    节点疑似下线和真正下线

    Redis Cluster 中的节点会定期检查已经发送 PING 消息的接收方节点是否在规定时间 ( cluster-node-timeout ) 内返回了 PONG 消息,如果没有则会将其标记为疑似下线状态,也就是 PFAIL 状态,如下图所示。

    在这里插入图片描述

    然后,节点一会通过 PING 消息,将节点二处于疑似下线状态的信息传递给其他节点,例如节点三。节点三接收到节点一的 PING 消息得知节点二进入 PFAIL 状态后,会在自己维护的 clusterState 的 nodes 字典中找到节点二所对应的 clusterNode 结构,并将主节点一的下线报告添加到 clusterNode 结构的 fail_reports 链表中。

    PING_FAIL

    随着时间的推移,如果节点十 (举个例子) 也因为 PONG 超时而认为节点二疑似下线了,并且发现自己维护的节点二的 clusterNode 的 fail_reports 中有半数以上的主节点数量的未过时的将节点二标记为 PFAIL 状态报告日志,那么节点十将会把节点二将被标记为已下线 FAIL 状态,并且节点十会立刻向集群其他节点广播主节点二已经下线的 FAIL 消息,所有收到 FAIL 消息的节点都会立即将节点二状态标记为已下线。如下图所示。

    fail

    需要注意的是,报告疑似下线记录是由时效性的,如果超过 cluster-node-timeout *2 的时间,这个报告就会被忽略掉,让节点二又恢复成正常状态。

    参考

    https://zhuanlan.zhihu.com/p/41228196

    https://www.cnblogs.com/yufeng218/p/13688582.html

    https://blog.csdn.net/makyan/article/details/104798725

    https://www.cnblogs.com/phyger/p/14108738.html

  • Redis Cluster “cluster nodes”命令

    redis cluster集群部署后,可以通过redis-cli的 cluster nodes 命令查看集群的节点信息。

    此图像的alt属性为空;文件名为image-16-1024x156.png

    输出的每行,都代表一个节点,下面我们讲解下这些信息的含义,为了更直观些,我们将这些信息放入表格里:

    idip:portflagsmasterping-sentpong-recvconfig-epochconfig-epochslot
    46dc4de072aad1e44548cfde5b56239001eaff5a127.0.0.1:6380@16380master016234029173022connected5461-10922
    18cc5a352ba7a567bdbad5d777b3712a7b81b0f8127.0.0.1:6384@16384slave99b3c660b15114ef55247e5b07cbf8f34621bee3016234029163273connected
    99b3c660b15114ef55247e5b07cbf8f34621bee3127.0.0.1:6381@16381master016234029150003connected10923-16383
    3e2cb9b12cf802fec29424dad133e42bc9a5f24c127.0.0.1:6382@16382slave08e3c37dbf40c32af18d2c6c75fce04e1fe41920016234029153181connected
    08e3c37dbf40c32af18d2c6c75fce04e1fe41920127.0.0.1:6379@16379myself,master016234029140001connected0-5460
    954e52a5ec121a68214ca0cede98b727c572727b127.0.0.1:6383@16383slave46dc4de072aad1e44548cfde5b56239001eaff5a016234029160002connected

    每项含义如下:

    id 节点ID,是一个40字节的随机字符串,这个值在节点启动的时候创建,并且永远不会改变(除非使用CLUSTER RESET HARD命令)
    
    ip:port 客户端与节点通信使用的地址
    
    flags 逗号分割的标记位,可能的值有: myselfmasterslavefail?failhandshakenoaddrnoflags. 之后将详细介绍这些标记
    
    master 如果节点是slave,并且已知master节点,则这里列出master节点ID,否则的话这里列出"-"
    
    ping-sent 最近一次发送ping的时间,这个时间是一个unix毫秒时间戳,0代表没有发送过
    
    pong-recv 最近一次收到pong的时间,使用unix时间戳表示
    
    config-epoch 节点的epoch值(如果该节点是从节点,则为其主节点的epoch值)。每当节点发生失败切换时,都会创建一个新的,独特的,递增的epoch。如果多个节点竞争同一个哈希槽时,epoch值更高的节点会抢夺到
    
    link-state node-to-node集群总线使用的链接的状态,我们使用这个链接与集群中其他节点进行通信.值可以是 connected 和 disconnected
    
    slot 哈希槽值或者一个哈希槽范围. 从第9个参数开始,后面最多可能有16384个 数(limit never reached)。代表当前节点可以提供服务的所有哈希槽值。如果只是一个值,那就是只有一个槽会被使用。如果是一个范围,这个值表示为起始槽-结束槽,节点将处理包括起始槽和结束槽在内的所有哈希槽

    各flags的含义

    myself: 当前连接的节点
    master: 节点是master
    slave: 节点是slave
    fail?: 节点处于PFAIL 状态。 当前节点无法联系,但逻辑上是可达的 (非 FAIL 状态)
    fail: 节点处于FAIL 状态. 大部分节点都无法与其取得联系将会将改节点由 PFAIL 状态升级至FAIL状态
    handshake: 还未取得信任的节点,当前正在与其进行握手
    noaddr: 没有地址的节点(No address known for this node)
    noflags: 连个标记都没有(No flags at all)
  • Redis Cluster集群为何是16384个哈希槽

    我们都知道,对于客户端请求的key,redis是根据公式 HASH_SLOT=CRC16(key) mod 16384,计算出映射到哪个分片上,然后Redis会去相应的节点进行操作。

    CRC16算法产生的hash值有16bit,该算法可以产生65536(2^16)个值。换句话说,值是分布在0~65535之间。那作者在做mod运算的时候,为什么不mod 65536,而选择mod 16384?这个问题,作者是给出了回答的,详见: https://github.com/antirez/redis/issues/2576,下面我们来具体的讲一下。

    1. 如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。

    redis集群的节点之间会定期发送ping/pong消息,交换数据信息的,交换的数据信息,由消息体和消息头组成。

    消息体无外乎是一些节点标识,IP,端口号,发送时间等等,这里我们主要关注的是消息头:

    src/cluster.h (redis-6.0.9)

    可以看到有个字段名为 myslots 的char数组,长度为 CLUSTER_SLOTS/8,CLUSTER_SLOTS为常量16384,所以该数组长度为16384/8,这其实是一个bitmap,每一个位代表一个槽,如果该位为1,表示这个槽是属于这个节点的。

    该数组所占空间大小为 16384÷8÷1024=2kb,当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb。消息体中会携带一定数量的其他节点的信息,节点信息里同样包含其槽位信息,节点数大约占集群节点总数量的十分之一,至少是3个节点的信息。节点数量越多,消息体内容越大。

    因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。

    2. redis的集群主节点数量基本不可能超过1000个

    如上所述,集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。

    3. 槽位越小,节点少的情况下,压缩率高

    Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。

    综上所述,作者决定取16384个槽,不多不少,刚刚好!

    相关链接

    https://www.cnblogs.com/rjzheng/p/11430592.html

    https://zhuanlan.zhihu.com/p/99037321

  • Redis Cluster集群部署

    Redis Cluster是Redis官方提供的Redis集群功能。

    下载镜像

    docker pull redis:6.2.4

    运行镜像

    我们需要创建6个redis容器(redis集群,最少必须有6个节点,3主3从)

    • redis-node1:6379
    • redis-node2:6380
    • redis-node3:6381
    • redis-node4:6382
    • redis-node5:6383
    • redis-node6:6384

    创建挂载目录

    mkdir -p ~/Ethan/Docker/redis/node1/data
    mkdir -p ~/Ethan/Docker/redis/node1/conf
    mkdir -p ~/Ethan/Docker/redis/node2/data
    mkdir -p ~/Ethan/Docker/redis/node2/conf
    mkdir -p ~/Ethan/Docker/redis/node3/data
    mkdir -p ~/Ethan/Docker/redis/node3/conf
    mkdir -p ~/Ethan/Docker/redis/node4/data
    mkdir -p ~/Ethan/Docker/redis/node4/conf
    mkdir -p ~/Ethan/Docker/redis/node5/data
    mkdir -p ~/Ethan/Docker/redis/node5/conf
    mkdir -p ~/Ethan/Docker/redis/node6/data
    mkdir -p ~/Ethan/Docker/redis/node6/conf

    分别在每个conf目录下创建文件redis.conf

    node1/conf/redis.conf :

    #端口号,写文件夹对映的端口
    Port 6379
    #设置登录密码
    requirepass 123456
    #设置节点密码,集群必设
    masterauth 123456
    #开启aof存储
    appendonly yes
    # 关闭保护,外网可直接访问
    protected-mode no
    #开启集群
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 15000
    # 宿主机ip
    cluster-announce-ip 10.252.187.163
    # 集群节点映射端口
    cluster-announce-port 6379
    # 集群总线端口 port + 10000
    cluster-announce-bus-port 16379

    同样,node1 ~ 6端口号依次为: 6379,6380,6381,6382,6383,6384

    创建虚拟网卡

    创建虚拟网卡,主要是用于redis-cluster能于外界进行网络通信,一般常用桥接模式。

    docker network create redis-net

    编辑docker-compose.yml文件

    version: "3.8"
    
    services:
      redis1:
        image: redis:6.2.4
        restart: "always"
        network_mode: "redis-net"
        ports:
          - "6379:6379"
          - "16379:16379"
        container_name: redis-node1
        command: redis-server /usr/local/etc/redis/redis.conf
        volumes:
          - /Users/ethanxu/Ethan/Docker/redis/node1/data:/data
          - /Users/ethanxu/Ethan/Docker/redis/node1/conf/redis.conf:/usr/local/etc/redis/redis.conf
    
      redis2:
        image: redis:6.2.4
        restart: "always"
        network_mode: "redis-net"
        ports:
          - "6380:6380"
          - "16380:16380"
        container_name: redis-node2
        command: redis-server /usr/local/etc/redis/redis.conf
        volumes:
          - /Users/ethanxu/Ethan/Docker/redis/node2/data:/data
          - /Users/ethanxu/Ethan/Docker/redis/node2/conf/redis.conf:/usr/local/etc/redis/redis.conf
    
      redis3:
        image: redis:6.2.4
        restart: "always"
        network_mode: "redis-net"
        ports:
          - "6381:6381"
          - "16381:16381"
        container_name: redis-node3
        command: redis-server /usr/local/etc/redis/redis.conf
        volumes:
          - /Users/ethanxu/Ethan/Docker/redis/node3/data:/data
          - /Users/ethanxu/Ethan/Docker/redis/node3/conf/redis.conf:/usr/local/etc/redis/redis.conf
    
      redis4:
        image: redis:6.2.4
        restart: "always"
        network_mode: "redis-net"
        ports:
          - "6382:6382"
          - "16382:16382"
        container_name: redis-node4
        command: redis-server /usr/local/etc/redis/redis.conf
        volumes:
          - /Users/ethanxu/Ethan/Docker/redis/node4/data:/data
          - /Users/ethanxu/Ethan/Docker/redis/node4/conf/redis.conf:/usr/local/etc/redis/redis.conf
    
      redis5:
        image: redis:6.2.4
        restart: "always"
        network_mode: "redis-net"
        ports:
          - "6383:6383"
          - "16383:16383"
        container_name: redis-node5
        command: redis-server /usr/local/etc/redis/redis.conf
        volumes:
          - /Users/ethanxu/Ethan/Docker/redis/node5/data:/data
          - /Users/ethanxu/Ethan/Docker/redis/node5/conf/redis.conf:/usr/local/etc/redis/redis.conf
    
      redis6:
        image: redis:6.2.4
        restart: "always"
        network_mode: "redis-net"
        ports:
          - "6384:6384"
          - "16384:16384"
        container_name: redis-node6
        command: redis-server /usr/local/etc/redis/redis.conf
        volumes:
          - /Users/ethanxu/Ethan/Docker/redis/node6/data:/data
          - /Users/ethanxu/Ethan/Docker/redis/node6/conf/redis.conf:/usr/local/etc/redis/redis.conf

    启动容器

    docker-compose up -d

    组建 Redis 集群

    进入任意一个redis容器

    docker exec -it redis-node1 /bin/bash

    执行组件集群的命令

    # cluster-replicas 1 表示每个主节点有一个从节点
    redis-cli -a 123456 --cluster create 10.252.187.163:6379 10.252.187.163:6380 10.252.187.163:6381 10.252.187.163:6382 10.252.187.163:6383 10.252.187.163:6384 --cluster-replicas 1

    执行结果

    从上面的命令执行输出,可以清楚的看到redis对于哈希槽(slots)的分配。redis集群,固定共计 16384 个哈希槽,集群启动时,就会将这些哈希槽,分配给所有的主节点。从上图输出可知,三个主节点,各自负责了 [0, 5460], [5461, 10922], [10923, 16383]三段哈希槽。

    输入“yes”确认

    可以看到 “All 16384 slots covered”,只有主节点会分配哈希槽。

    可以通过 redis-cli 查看一下集群节点信息

    通过集群节点信息,我们也可以看到节点间的主从关系

    master 10.252.187.163:6379 redis-node1
    slave  10.252.187.163:6382 redis-node4
    
    master 10.252.187.163:6380 redis-node2
    slave  10.252.187.163:6383 redis-node5
    
    master 10.252.187.163:6381 redis-node3
    slave  10.252.187.163:6384 redis-node6

    至此,我们的redis cluster集群就已经部署完成啦~

    附录

    为何是16384个哈希槽?

    Redis Cluster “cluster nodes”命令

    相关链接

  • 两数相加 – LeetCode2

    题目

    给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
    请你将两个数相加,并以相同形式返回一个表示和的链表。
    你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
    示例 1:
    输入:l1 = [2,4,3], l2 = [5,6,4]
    输出:[7,0,8]
    解释:342 + 465 = 807.
    示例 2:
    输入:l1 = [0], l2 = [0]
    输出:[0]
    示例 3:
    输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
    输出:[8,9,9,9,0,0,0,1]
    提示:
    每个链表中的节点数在范围 [1, 100] 内
    0 <= Node.val <= 9
    题目数据保证列表表示的数字不含前导零

    题解

    golang

    func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
    	// 定义结果链表头指针
    	var head *ListNode
    
    	// 定义结果链表尾指针
    	tail := &ListNode{}
    
    	// carry 用于保存进位
    	carry := 0
    	for l1 != nil || l2 != nil {
    		// 依次获取l1, l2 相同位置的值,n1, n2
    		n1, n2 := 0, 0
    		if l1 != nil {
    			n1 = l1.Val
    			l1 = l1.Next
    		}
    		if l2 != nil {
    			n2 = l2.Val
    			l2 = l2.Next
    		}
    
    		// n1, n2 以及 carry(上一位计算的进位) 求和,与10的余数,则为最终结果该位上的值,除10的值,则为进位,用carry存储
    		// 例如 19 + 6, 第一步: n1 = 9, n2 = 6, carry = 0, 最终结果个位为 5,进位1 (carry = 1) 第二步: n1 = 1, n2 = 0, n1 + n2 + carry = 2
    		// 最终结果: 19 + 6 = 25
    		sum := n1 + n2 + carry
    		sum, carry = sum % 10, sum / 10
    		if head == nil {
    			head = &ListNode{Val: sum}
    			tail = head
    		} else {
    			tail.Next = &ListNode{Val: sum}
    			tail = tail.Next
    		}
    	}
    
    	// 若各位皆已经计算,但carry中还有值,则最高位为carry. 例如: 98 + 9 = 107 (carry = 1)
    	if carry > 0{
    		tail.Next = &ListNode{Val: carry}
    	}
    	return head
    }

    相关链接

    https://leetcode-cn.com/problems/add-two-numbers

  • Grafana配置Prometheus

    结合grafana,展示prometheus的数据。

    步骤一

    Grafana, 操作 Add data source,选择Prometheus

    步骤二

    配置Prometheus相关信息,primetheus服务地址,以及抓取频率等

    ps: HTTP-Access选择Server(default)的话,URL就不要填写127.0.0.1或localhost这种,因为Server是直接从Grafana容器内访问,找不到该地址,可以填写实际IP地址

    步骤三

    导入prometheus模板

    模板导入后,就可以看到导入的模板了,点击进入模板,就可以看到相应模板的图表

    步骤四

    我们也可以创建自己的图表

  • Grafana本地部署

    在本地mac上,进行Grafana服务的部署,便于开发, 学习和测试。

    下载镜像

    docker pull grafana/grafana:7.5.7

    配置

    mkdir -p ~/Ethan/Docker/grafana-7.5.7
    cd ~/Ethan/Docker/grafana-7.5.7

    启动镜像

    docker run -d \
    -p 3000:3000 \
    --name=grafana \
    -v /Users/ethanxu/Ethan/Docker/grafana:/var/lib/grafana \
    grafana/grafana:7.5.7

    访问

    http://127.0.0.1:3000/login

    用户名: admin 密码: admin

    grafana