redis 哨兵

来源:https://www.cnblogs.com/monkey-code/archive/2020/06/18/13155563.html
-Advertisement-
Play Games

哨兵作用 哨兵(sentinel) 是一個分散式系統,是程式高可用性的一個保障。用於監視任意多個主伺服器,以及這些主伺服器屬下的所有從伺服器,當出現故障時通過投票機制選擇新的master並將所有slave連接到新的master。 監控 不斷地檢查master和slave是否正常運行 master存活 ...


哨兵作用

哨兵(sentinel) 是一個分散式系統,是程式高可用性的一個保障。用於監視任意多個主伺服器,以及這些主伺服器屬下的所有從伺服器,當出現故障時通過投票機制選擇新的master並將所有slave連接到新的master。

監控

不斷地檢查master和slave是否正常運行 master存活檢測、master與slave運行情況檢測。

通知

當被監控地伺服器出現問題時,向其他(哨兵間,客戶端)發送通知。

自動故障轉移

斷開master與slave連接,選取一個slave作為master,將其他slave連接到新的master,並告知客戶端新的伺服器地址。

註意

哨兵也是一臺redis伺服器,只是不提供數據服務,通常哨兵配置數量為單數

啟動哨兵

配置文件

哨兵預設的配置文件 sentinel.conf

一般的以 sentinel_port.conf 命名 哨兵的配置文件

配置信息

port  26379  (埠號)
dir  /tmp  (哨兵運行信息存儲)
monitor mymaster 127.0.0.1 6379 2
# mymaster  (master 名字 隨意)
# 127.0.0.1 6379  (IP + 埠號)
# 2  (哨兵個數 //2 + 1  當有 2 個哨兵認為 master 掛了 就掛了)
down-after-milliseconds mymaster 30000 (單位 毫秒 )

parallel-syncs mymaster 1 ( 新的master 一次有多少個 slave 同步,設置的越小,完成數據同步的時間越長,響應的伺服器壓力越小。)
failover-timeout mymaster 180000( 3 分鐘 如果沒有同步完成 就判定為同步超時)

啟動

配置主從結構,以 1master 2 slave為例。

1 先啟動 master 和 slave

主從配置 參看 主從篇博客主從

redis-server config_6379.conf
redis-server config_6380.conf
redis-server config_6381.conf

2 啟動哨兵

redis-sentinel sentinel_26379.conf
redis-sentinel sentinel_26380.conf
redis-sentinel sentinel_26381.conf

Sentinel 命令

PING:PONG
SENTINEL masters :列出所有被監視的主伺服器,以及這些主伺服器的當前狀態。
SENTINEL slaves :列出給定主伺服器的所有從伺服器,以及這些從伺服器的當前狀態。
SENTINEL get-master-addr-by-name : 返回給定名字的主伺服器的 IP 地址和埠號。 如果這個主伺服器正在執行故障轉移操作, 或者針對這個主伺服器的故障轉移操作已經完成, 那麼這個命令返回新的主伺服器的 IP 地址和埠號。
SENTINEL reset : 重置所有名字和給定模式 pattern 相匹配的主伺服器。 pattern 參數是一個 Glob 風格的模式。 重置操作清楚主伺服器目前的所有狀態, 包括正在執行中的故障轉移, 並移除目前已經發現和關聯的, 主伺服器的所有從伺服器和 Sentinel 。
SENTINEL failover : 當主伺服器失效時, 在不詢問其他 Sentinel 意見的情況下, 強制開始一次自動故障遷移 (不過發起故障轉移的 Sentinel 會向其他 Sentinel 發送一個新的配置,其他 Sentinel 會根據這個配置進行相應的更新)。

初始化Sentinel

初始化伺服器

從下麵啟動代碼可以看出啟動方式由函數 checkForSentinelMode 來決定,是否使用 sentinel 的模式進行一個啟動, 添加的指令也是用的 sentinelcmds 的命令表

int checkForSentinelMode(int argc, char **argv) {
    int j;

    if (strstr(argv[0],"redis-sentinel") != NULL) return 1;
    for (j = 1; j < argc; j++)
        if (!strcmp(argv[j],"--sentinel")) return 1;
    return 0;
}

// 檢查伺服器是否以 Sentinel 模式啟動
server.sentinel_mode = checkForSentinelMode(argc,argv);

// 初始化伺服器
initServerConfig();  // 在第二步介紹該函數

// 如果伺服器以 Sentinel 模式啟動,那麼進行 Sentinel 功能相關的初始化
// 併為要監視的主伺服器創建一些相應的數據結構
if (server.sentinel_mode) {
    initSentinelConfig();
    initSentinel();
}

從源碼我們可以看出哨兵的啟動有兩種方式

redis-sentinel sentinel_xxx.conf
redis-server sentinel_xxx.conf --sentinel

無論哪種方式啟動redis,都會執行 initServerConfig ,不同的是 Sentinel 還會 執行initSentinelConfiginitSentinel 兩個初始化函數。接下來看看這兩個函數都幹了什麼~ 。

替換 Sentinel 的專用代碼

initSentinelConfig() 這個函數會用 Sentinel 配置的屬性覆蓋伺服器預設的屬性。

void initSentinelConfig(void) {
    server.port = REDIS_SENTINEL_PORT;//26379
}

initSentinel() 會進行一個命令表的載入。一個主要的查詢命令 INFO 也不同於普通伺服器,而是使用一個特殊的版本。

// 初始化伺服器 Sentinel 伺服器
void initSentinel(void) {
    int j;

    // 刪除 普通 Redis 伺服器的命令表(該表用於普通模式)
    dictEmpty(server.commands,NULL);

    //  添加 sentinel 模式專用的命令。
    for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {
        int retval;
        struct redisCommand *cmd = sentinelcmds+j;

        retval = dictAdd(server.commands, sdsnew(cmd->name), cmd);
        redisAssert(retval == DICT_OK);
    }

    /* 初始化 Sentinel 的狀態 這是為了故障轉移階段選取 切換執行者 記錄的狀態 */
    sentinel.current_epoch = 0;

    // 保存 主伺服器 信息的字典 (這裡記錄了監測的主伺服器的信息)
    sentinel.masters = dictCreate(&instancesDictType,NULL);

    // 初始化 TILT 模式的相關選項
    sentinel.tilt = 0;
    sentinel.tilt_start_time = 0;
    sentinel.previous_time = mstime();

    // 初始化腳本相關選項
    sentinel.running_scripts = 0;
    sentinel.scripts_queue = listCreate();
}

// sentinel 的指令集合
struct redisCommand sentinelcmds[] = {
    {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0},
    {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0},
    {"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0},
    {"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
    {"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0},
    {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
    {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0},
    {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0},
    {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0}
};

初始化 Sentinel 狀態

在完成命令表載入之後,緊接著會進行 sentinelStatesentinelRedisInstance 結構的一個初始化。

Sentinel 狀態中的 masters 字典記錄了所有被監視的主伺服器信息,鍵為伺服器名字,值為被監視主伺服器對應的sentinel.c/sentinelRedisInstance結構。每個sentinelRedisInstance實例結構代表監視一個Redis伺服器實例,這個實例可以是主伺服器,也可以是從伺服器,或者另外一個sentinel伺服器。

對於sentinelState的初始化將引發對masters字典的初始化,而masters字典的初始化是根據被該入的sentinel配置文件(sentinel_26379.conf)來進行的。主要為被監控 masterip port

註意 這些都是有 sentinel 來維護和使用的。

sentinelState

struct sentinelState {

    // 當前紀元 用做故障轉移
    uint64_t current_epoch;     /* Current epoch. */

    // 保存了所有被這個 sentinel 監視的主伺服器
    // 字典的鍵是主伺服器的名字
    // 字典的值則是一個指向 sentinelRedisInstance 結構的指針,可以是主伺服器,從伺服器或者其他sentinel節點
    dict *masters;      /* Dictionary of master sentinelRedisInstances.
                           Key is the instance name, value is the
                           sentinelRedisInstance structure pointer. */

    // 是否進入了 TILT 模式?
    int tilt;           /* Are we in TILT mode? */

    // 目前正在執行的腳本的數量
    int running_scripts;    /* Number of scripts in execution right now. */

    // 進入 TILT 模式的時間
    mstime_t tilt_start_time;   /* When TITL started. */

    // 最後一次執行時間處理器的時間
    mstime_t previous_time;     /* Last time we ran the time handler. */

    // 一個 FIFO 隊列,包含了所有需要執行的用戶腳本
    list *scripts_queue;    /* Queue of user scripts to execute. */

} sentinel;

sentinelRedisInstance

name
實例的名字
主伺服器的名字由用戶在配置文件中設置
從伺服器以及 Sentinel 的名字由 Sentinel 自動設置
格式為 ip:port ,例如 "127.0.0.1:26379"

runid
實例的運行 ID

sentinelAddr
實例的地址

主伺服器實例特有的屬性

sentinels
其他同樣監控這個主伺服器的所有 sentinel

slaves
如果這個實例代表的是一個主伺服器
那麼這個字典保存著主伺服器屬下的從伺服器
字典的鍵是從伺服器的名字,字典的值是從伺服器對應的 sentinelRedisInstance 結構

quorum
判斷這個實例為客觀下線(objectively down)所需的支持投票數量

parallel_syncs
SENTINEL parallel-syncs 選項的值
在執行故障轉移操作時,可以同時對新的主伺服器進行同步的從伺服器數量

auth_pass
連接主伺服器和從伺服器所需的密碼

從伺服器實例特有的屬性

master_link_down_time
主從伺服器連接斷開的時間

slave_priority
從伺服器優先順序

slave_reconf_sent_time
執行故障轉移操作時,從伺服器發送 SLAVEOF 命令的時間

master
主伺服器的實例(在本實例為從伺服器時使用)

slave_master_host
INFO 命令的回覆中記錄的主伺服器 IP

slave_master_port
INFO 命令的回覆中記錄的主伺服器埠號

slave_master_link_status
INFO 命令的回覆中記錄的主從伺服器連接狀態

slave_repl_offset
從伺服器的複製偏移量

結構中的 sentinelAddr 保存著對象的 地址和埠。

/* Address object, used to describe an ip:port pair. */
/* 地址對象,用於保存 IP 地址和埠 */
typedef struct sentinelAddr {
    char *ip;
    int port;
} sentinelAddr;

建立連接

sentinel 會先去連接 sentinel masters 中的每一個 master,併在每一個 mastersentinel之間創建兩個非同步連接 一個 命令連接 一個 訂閱鏈接。此時 sentinel將成為 master 的客戶端它可以向主伺服器發送命令,並從命令回覆中獲取相關信息。

命令連接

專門用於向主伺服器發送命令,並接收命令回覆。比如sentinel向主伺服器發送INFO命令。

訂閱連接

專門用於訂閱主伺服器的 _sentinel_:hello頻道。 比如 sentinel向主,從,其它sentinel發送sentinel本身和主庫信息。

redis在發佈與訂閱功能中,被髮送的信息都不會保存在redis伺服器中,若消息到來時,需要接收的客戶端不線上或者斷線,那麼這個客戶端就會丟失這條信息。為了不丟失_sentinel_:hello頻道的任何信息,sentinel必須專門的用一個訂閱連接來接收該頻道的信息。

獲取主伺服器信息

Sentinel 預設會以每10秒一次的頻率向主伺服器發送INFO命令,通過分析命令回覆來獲取主伺服器的當前信息。Sentinel可以獲取以下兩方面的信息:

1主伺服器本身的信息,包括伺服器run_id,role的伺服器角色。

2 主伺服器對應的所有從伺服器的信息(從伺服器IP和埠)。

獲取從伺服器信息

Sentinel發現有新的從伺服器出現時,Sentinel除了會為這個新的從伺服器創建相應的實例結構(sentinelRedisInstance)之外,還會創建到從伺服器的命令連接訂閱連接

Sentinel依然會像對待主伺服器那樣,每10s 發送一個INFO命令來獲取從伺服器的當前信息。

run_id、role、ip、port 、master_link_status(主從伺服器的連接狀態)、slave_priority(從伺服器的優先順序)等信息。

向主從伺服器發送信息

在預設情況下, Sentinel會以每2秒一次的頻率,通過命令連接向,所有被監視的主伺服器和從伺服器發送以下格式的命令:

PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"

這條命令向伺服器的_sentinel_:hello頻道發送了一條信息,信息的內容由多個參數組成:

(1) s_開頭的參數記錄的是sentinel本身的信息。

(2) m_開頭的參數記錄的則是主伺服器的信息,如果sentinel正在監視的是主伺服器,那麼這些參數就是主伺服器的信息,如果sentinel正在監視的是從伺服器,那麼這些參數記錄就是從伺服器正在複製的主伺服器的信息。

參數 描述
S_ip Sentinel的ip地址
S_port Sentinel的埠號
S_runid Sentinel的運行ID
S_epoch Sentinel 的當前配置紀元
m_name 主伺服器的名字
M_ip 主伺服器的IP地址
M_port 主伺服器的埠號
M_epoch 主伺服器的當前配置紀元

例如

"127.0.0.1,26379,e955b4c77398ef6b5f055bc7ebfd5e828dbed4fc,0,mymaster,127.0.0.1,6379,0"
# --------------------------------解釋------------------------------------------
127.0.0.1  # sentinel ip 地址
26379  # sentinel 埠號
e955b4c77398ef6b5f055bc7ebfd5e828dbed4fc  # sentinel的運行 id
0 # sentinel 當前配置紀元
mymaster # sentinel 監控的 master name
127.0.0.1 # master ip 地址
6379 # master 埠號
0 # master 當前配置紀元

接收來自主從伺服器的頻道信息

Sentinel與一個主伺服器或者從伺服器建立起訂閱連接之後,Sentinel就會通過訂閱連接向伺服器發送 subscribe_sentinel_:hello

對於每個與 Sentinel 連接的伺服器,Sentinel既通過命令連向伺服器的_sentinel_:hello頻道發送信息,又通過訂閱連接從伺服器的_sentinel_:hello頻道接收信息。

因此當有新的Sentinel 連接進來時, 向訂閱連接中發送的 subscribe_sentinel_:hello 被已有的Sentinel 接收(同時自己也會接受到來自自己的這條消息)。

// 發送 PUBLISH 命令的間隔
#define SENTINEL_PUBLISH_PERIOD 2000

if ((now - ri->last_pub_time) > SENTINEL_PUBLISH_PERIOD) {
        /* PUBLISH hello messages to all the three kinds of instances. */
        sentinelSendHello(ri);
    }

/* 接收來自主伺服器和從伺服器的頻道信息
當 sentinel 與一個主伺服器或者從伺服器建立起訂閱連接之後, sentinel 就會通過訂閱連接,向伺服器發送以下命令:
*/
SUBSCRIBE __sentinel__:hello

/* Now we subscribe to the Sentinels "Hello" channel. */
// 發送 SUBSCRIBE __sentinel__:hello 命令,訂閱頻道
retval = redisAsyncCommand(ri->pc,
        sentinelReceiveHelloMessages, NULL, "SUBSCRIBE %s",
        SENTINEL_HELLO_CHANNEL);

當一個Sentinel_sentinel_:hello頻道收到一條信息時,Sentinel會對這條信息進行分析,提取出信息中 ip 、port、run_id 等8個參數,併進行以下檢查:如果這條消息是自己發的,就直接忽略。如果是新進來的Sentinel , 此時Sentinel 會對 對應的主伺服器實例結構進行更新,即將新加進來的 Sentinel 添加到 sentinels 字典中。

每個Sentinel都有自己的一個sentinels字典,Sentinels字典信息保存了除自己之外的所有Sentinel信息。

下線狀態

對於Redis的Sentinel中關於下線有兩個不同的概念:(1)主觀下線(Subjectively Down, 簡稱 Sdown) 指的是單個 Sentinel 實例對伺服器做出的下線判斷,此時不會進行故障轉移。(2) 客觀下線(Objectively Down, 簡稱 Odown)指的是多個 Sentinel 實例在對同一個伺服器做出 Sdown 判斷,此時目標sentinel會對主伺服器進行故障轉移。本篇具體詳細介紹。

主觀下線狀態

預設的Sentinel會以每秒一次的頻率向所有與它創建命令連接的實例(包括主、從、其他sentinel在內)發送PING命令,並通過實例回覆來判斷實例是否線上。

合法的回覆

+pong-loading -masterdown

無效回覆

除此之外的所有回覆或者無回覆都被視作無效回覆。無回覆指在指定的時間內沒有回覆就認為是無回覆。

down-after-milliseconds  # 指定的時間 未收到回覆 視為無效

用戶設置down-after-milliseconds選項的值,不僅會被sentinel用來判斷主伺服器的主觀下線狀態,還會被用於判斷主伺服器下的所有從伺服器,以及同樣監視主伺服器的其他sentinel的主觀下線狀態。

-- 例如用戶向sentinel設置以了下配置:
sentinel  monitor master 127.0.0.1 6379 2
sentinel  down-after-milliseconds master 50000

這裡的master是主伺服器的名稱, 埠預設63792代表sentinel集群中有2sentinel認為master 狀態下線時,才能真正認為該master已經不可用了(也就是客觀下線)。

這50000毫秒不僅會成為sentinel判斷master進入主觀下線的標準,還會判斷所有從庫、其它sentinel進入主觀下線的標準。

當多個sentinel設置的主觀下線時長可能不同

對於多個sentinel共同監視同一個主伺服器時,這些sentinel在配置文件sentinle.conf中所設置的down-after-milliseconds值也可能不同,因此當一個sentinel將主伺服器判斷為主觀下線時,其它sentinel可能仍然會認為主伺服器處於線上狀態。只有全部的sentine都判斷進入了主觀下線狀態時,才會認為主master進入了主觀下線狀態。

客觀下線狀態

Sentinel將一個主伺服器判斷為主觀下線之後,為了確認這個主伺服器是否真的下線了,會向同樣監視這一主伺服器的其它Sentinel進行詢問,當有半數以上(看具體配置, 一般的是半數以上 例如sentinel monitor mymaster 127.0.0.1 6379 2 中 就為當 2 個判定下線時,就認為時客觀下線了)

master, 被確定客觀下線之後sentinel 們 會選出一個 決策者 去執行故障轉移操作。客觀下線條件只適用於主伺服器

is-master-down-by-addr命令用來判斷是否客觀下線

sentinel is-master-down-by-addr  ip  port  current_epoch  run_id

sentinel當前的配置紀元 current_epoch 用於選舉 決策者 sentinel, run_id可以是*或者sentinel的 運行id。

決策者選取

假設現在有4個sentinel 這四個sentinel 既是投票者,也是候選者(這四個必須時健康的)。

1 不能有下麵三個標記中的一個:SRI_S_DOWN|SRI_O_DOWN|SRI_DISCONNECTED

2 ping 心跳正常

3 優先順序不能為 0(slave->slave_priority)

4 INFO 數據不能超時

5 主從連接斷線會時間不能超時

投票的過程很簡單,每個sentinel 都將自己的ipportcurrent_epochrun_idis-master-down 發送到 hello 頻道。

sentinel 第一個獲取到誰的 is-master-down 信息, 就將自己的票投給對應的sentinel

一次過後 current_epoch 最大的,且超過了半數以上。則被選為決策者 否則再來一輪,每增加一輪 current_epoch + 1, 直到選出為止。

故障轉移

選取候選Slave

1 線上的

2 響應速度快的

3 與原 master 斷開連接最短的

4 優先原則

優先順序>offset>runid

最終選取出 新的 master 之後向新的 master 發送

slaveof no one  # 斷開主從

然後聲明新的master

slaveof ip port  # 發送新的IP 和  新的port

最後將原來的 master 作為從機。當重新上線時,sentinel 會發送 salveof 命令使其成為從機。

總結

  • sentinel只是一個運行在特殊模式下的redis伺服器,它使用了和普通模式不同的命令表,以及區別與普通模式下使用的命令不同。

  • sentinel向主伺服器發送INFO命令來獲得主伺服器屬下所有從伺服器的地址信息,併為這些從伺服器創建相應的實例結構,以及連向這些從伺服器的命令連接和訂閱連接。

  • 一般情況下,sentinel以每10秒一次的頻率向被監視的主伺服器和從伺服器發送INFO命令,當主伺服器處於下線狀態,或者sentinel正在對主伺服器進行故障轉移操作時,sentinel向從伺服器發送INFO命令的頻率會改為1秒一次。

  • 對於監視同一個主伺服器和從伺服器的多個sentinel來說,它們會以每2秒一次的頻率,通過向被監視的_sentinel_:hello頻道發送消息來向其他sentinel宣告自己的存在。

  • 每個sentinel也會從_sentinel_:hello中頻道中接收其他sentinel發來的信息,並根據這些信息為其他sentinel創建相應的實例結構,以及命令連接。

  • sentinel只會與主伺服器和從伺服器創建命令連接和訂閱連接,sentinelsentinel之間則只創建命令連接。

  • sentinel以每秒一次的頻率向實例(包括主,從,其它sentinel)發送PING命令,並根據實例的回覆來判斷實例是否線上,當一個實例在指定的時長中連續向sentinel發送無效回覆時,sentinel會將這個實例判斷為主觀下線。

  • sentinel將一個主伺服器判斷為主觀下線時,它會向同樣的監視這個主伺服器的其他sentinel進行詢問,看它們是否同意這個主伺服器已經進入主觀下線狀態。

  • sentinel收集到足夠多的主觀下線投票之後,它會將主伺服器判斷為客觀下線,併發起一次針對主伺服器的故障轉移操作。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 感謝 Vatsan Madhavan 小伙伴推薦的 MSBuild 輸出日誌可視化工具,這個工具可以使用漂亮的 WPF 界面預覽 MSBuild 複雜的輸出內容 ...
  • 你是不是有遇到過這樣的場景?使用 SSH 命令進入到伺服器,然後再用 cd 命令進入到對應目錄,再繼續進行你的工作。 這種操作對於新手來講特別常見,良許之前也是這樣。在本文,老司機將帶你來進行更高效的操作,只需一步即可達到你想要的效果。 而且,不僅僅是實現快速進入到 Linux 伺服器特定的目錄,還 ...
  • 進入黑客世界,跟我學習日常寫的shell腳本-設置系統selinux ...
  • 首先來看一下並口和串口的區別:引腳的區別: 串口SRAM(或其它存儲器)通常有如下的示意圖: 串口SRAM引腳 引腳只有SCK,CS#,SI,SO,HOLDB,VCC,VSS不到8個,一般遵循SPI協議,並口SRAM引腳很多,串口SRAM引腳很少。大部分SRAM是並口(parallel)操作的,也有 ...
  • https://blog.csdn.net/m0_37321987/article/details/903447621、右擊任務欄,啟動任務管理器; 2、選擇“性能“選項卡,點擊“資源監視器”; 3、點擊“CPU”選項卡,在“關聯的句柄”右側的“搜索句柄”輸入框輸入文件名或文件夾名並點擊搜索;win ...
  • 問題:常用命令“ll”失效或命令未找到 原因: "ll"命令不是linux的基本命令, 它是"ls -l"的別名, 部分版本並不直接支持“ll”命令輸出。 ###解決方法: 運行“vi ~/.bashrc” 查看該文件里是否有“alias ll='ls -l'”這樣的數據, 如有,將數據前的“#”去 ...
  • basename命令用於獲取路徑中的文件名或路徑名,還可以對末尾字元進行刪除。 ...
  • 晶元生態很重要,接觸到的一些進口晶元,比如ST、TI、NORDIC、AVR等,有論壇,網上能找到資料,晶元容易買到,SDK不停更新。這也就是ST的晶元、Arduino IDE市場很大、用戶基數多的原因,穩定性、功能、開發速度缺一不可。 從使用上來說,開發產品最喜歡用的還是STM8S003,但是因為價... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...