haproxy實現會話保持(2):stick table

来源:https://www.cnblogs.com/f-ck-need-u/archive/2018/03/13/8558514.html
-Advertisement-
Play Games

本文目錄:1.stickiness和stick table簡介2.使用stick table 2.1 創建stick table 2.2 查看stick table 2.3 使用客戶端源IP作為客戶端標識符 2.4 使用cookie作為客戶端標識符 2.5 使用String作為客戶端標識符 2.6 ...


本文目錄:
1.stickiness和stick table簡介
2.使用stick table
 2.1 創建stick table
 2.2 查看stick table
 2.3 使用客戶端源IP作為客戶端標識符
 2.4 使用cookie作為客戶端標識符
 2.5 使用String作為客戶端標識符
 2.6 stick on、stick match、stick store
 2.7 使用stick table統計狀態信息

在上一篇文章中,分析了haproxy如何通過cookie實現會話保持,本文討論haproxy另一種實現會話保持的方式:stick table。

1.stickiness和stick table簡介

stick table是haproxy的一個非常優秀的特性,這個表裡面存儲的是stickiness記錄,stickiness記錄了客戶端和服務端1:1對應的引用關係。通過這個關係,haproxy可以將客戶端的請求引導到之前為它服務過的後端伺服器上,也就是實現了會話保持的功能。這種記錄方式,俗稱會話粘性(stickiness),即將客戶端和服務端粘連起來。

stick table中使用key/value的方式映射客戶端和後端伺服器,key是客戶端的標識符,可以使用客戶端的源ip(50位元組)、cookie以及從報文中過濾出來的部分String。value部分是服務端的標識符。

stick table實現會話粘性的過程如下圖:

除了存儲key/value實現最基本的粘性,stick table還可以額外存儲每個stickiness記錄對應的狀態統計數據。比如stickiness記錄1目前建立了多少和客戶端的連接、平均建立連接的速度是多少、流入流出了多少位元組的數據、建立會話的數量等等。

stick table可以在"雙主模型"下進行複製(replication)。只要設置好對端haproxy節點,haproxy就會自動將新插入的、剛更新的記錄通過TCP連接推送到對端節點上。這樣一來,粘性記錄不會丟失,即使某haproxy節點出現了故障,其他節點也能將客戶端按照粘性映射關係引導到正確的後端伺服器上。而且每條stickiness記錄占用空間都很小(平均最小50位元組,最大166位元組,由是否記錄額外統計數據以及記錄多少來決定占用空間大小),使得即使在非常繁忙的環境下在幾十個節點之間推送都不會出現壓力瓶頸和網路阻塞(可以按節點數量、stickiness記錄的大小和平均併發量來計算每秒在網路間推送的數據流量)。

此外,stick table還可以在haproxy重啟時,在新舊兩個進程間進行複製,這是本地複製。當haproxy重啟時,舊haproxy進程會和新haproxy進程建立TCP連接,將其維護的stick table推送給新進程。這樣新進程不會丟失粘性信息,和其他節點也能最大程度地保持同步,使得其他節點只需要推送該節點重啟過程中新增加的stickiness記錄就能完全保持同步。

2.使用stick table

下圖是本文測試時的環境:

2.1 創建stick table

首先看創建stick table的語法:

stick-table type {ip | integer | string [len <length>] | binary [len <length>]}
            size <size> [expire <expire>] [nopurge] [peers <peersect>]
            [store <data_type>]*

其中

  • type ip | integer | string:使用什麼類型的key作為客戶端標識符。可以是客戶端的源IP,可以是一個整數ID值,也可以是一段從請求報文或響應報文中匹配出來的字元串。
  • size:表中允許的最大stickiness記錄數量。單位使用k、m和g表示,分別表示1024、2^20和2^30條記錄。
  • expire:stickiness記錄的過期時長。當某記錄被操作後,過了一段時間就會過期,過期的記錄會自動從stick table中移除,釋放表空間。
  • nopurge:預設情況下,當表滿後,如果還有新的stickiness記錄要插入進來,haproxy會自動將一部分老舊的stickiness記錄flush掉,以釋放空間存儲新紀錄。指定nopurge後,將不進行flush,只能通過記錄過期來釋放表空間,因此該選項必須配合expire選項同時使用。
  • peers:指定要將stick table中的記錄replication到對端haproxy節點。
  • store:指定要存儲在stick table中的額外狀態統計數據。其中代表後端伺服器的標識符server ID(即key/value的value部分)會自動插入,無需顯式指定。

註意,每個後端組只能建立一張stick table,每個stick table的id或名稱等於後端組名。例如在backend static_group後端創建stick table,則該表的id為"static_group"。也有特殊方法建立多張,但無必要,可翻官方手冊找方法。

例如,創建一個以源IP地址為key的stick table,該表允許100W條記錄,5分鐘的記錄過期時長,並且不記錄任何額外數據。

stick-table type ip size 1m expire 5m

這張表由於沒有記錄額外的統計數據,每條stickiness記錄在記憶體中只占用50位元組左右的空間,表滿後整張表在記憶體中占用50MB(2^20*50/1024/1024=50MB)。看上去很大,但檢索速度是極快的,完全不用擔心性能問題。

如果還要存儲和客戶端建立的連接數量計數器(conn_cnt),則:

stick-table type ip size 1m expire 5m store conn_cnt

conn_cnt占用32個bit位,即4位元組,因此每條stickiness記錄占用54位元組,100W條記錄占用54M記憶體空間。

2.2 查看stick table

haproxy沒有直接的介面可以顯示stick table的相關信息,只能通過stats socket進行查看。該指令表示開啟一個本地unix套接字監聽haproxy的信息,通過這個套接字可以查看haproxy的很多信息,且能動態調整haproxy配置。

首先在haproxy的配置文件中開啟"stats socket"狀態信息,如下:

global
    stats socket /var/run/haproxy.sock mode 600 level admin
    stats timeout 2m

預設stats timeout的過期時長為10s,建議設置長一點。上面還設置了socket的許可權級別,表示能訪問(600)這個套接字的人具有所有許可權(admin)。level還有兩種許可權級別更低一點的值"read"和"operator"(預設),前者表示只有讀取信息的許可權,不能設置或刪除、清空某些信息,後者表示具備讀和某些設置許可權。

本地套接字監聽haproxy後,可以通過"socat"工具(socket cat,很強大的工具,在epel源中提供)從套接字來操作haproxy。

# 方式一:直接傳遞要執行的操作給套接字
echo "help" | socat unix:/var/run/haproxy.sock -

# 方式二:進入互動式模式,然後在互動式模式下執行相關操作
socat readline unix:/var/run/haproxy.sock

如果要監控某些狀態信息的實時變化,可以使用watch命令。

watch -n 1 '"echo show table" | socat unix:/var/run/haproxy.sock -'

haproxy支持以下列出的所有操作命令:

[root@xuexi ~]# echo "help" | socat unix:/var/run/haproxy.sock -
  help           : this message
  prompt         : toggle interactive mode with prompt
  quit           : disconnect
  show tls-keys [id|*]: show tls keys references or dump tls ticket keys when id specified
  set ssl tls-key [id|keyfile] <tlskey>: set the next TLS key for the <id> or <keyfile> listener to <tlskey>
  set maxconn global : change the per-process maxconn setting
  set rate-limit : change a rate limiting value
  set timeout    : change a timeout setting
  show env [var] : dump environment variables known to the process
  show resolvers [id]: dumps counters from all resolvers section and
                     associated name servers
  add acl        : add acl entry
  clear acl <id> : clear the content of this acl
  del acl        : delete acl entry
  get acl        : report the patterns matching a sample for an ACL
  show acl [id]  : report available acls or dump an acl's contents
  add map        : add map entry
  clear map <id> : clear the content of this map
  del map        : delete map entry
  get map        : report the keys and values matching a sample for a map
  set map        : modify map entry
  show map [id]  : report available maps or dump a map's contents
  show pools     : report information about the memory pools usage
  show sess [id] : report the list of current sessions or dump this session
  shutdown session : kill a specific session
  shutdown sessions server : kill sessions on a server
  clear counters : clear max statistics counters (add 'all' for all counters)
  show info      : report information about the running process
  show stat      : report counters for each proxy and server
  show errors    : report last request and response errors for each proxy
  clear table    : remove an entry from a table
  set table [id] : update or create a table entry's data
  show table [id]: report table usage stats or dump this table's contents
  disable frontend : temporarily disable specific frontend
  enable frontend : re-enable specific frontend
  set maxconn frontend : change a frontend's maxconn setting
  show servers state [id]: dump volatile server information (for backend <id>)
  show backend   : list backends in the current running config
  shutdown frontend : stop a specific frontend
  disable agent  : disable agent checks (use 'set server' instead)
  disable health : disable health checks (use 'set server' instead)
  disable server : disable a server for maintenance (use 'set server' instead)
  enable agent   : enable agent checks (use 'set server' instead)
  enable health  : enable health checks (use 'set server' instead)
  enable server  : enable a disabled server (use 'set server' instead)
  set maxconn server : change a server's maxconn setting
  set server     : change a server's state, weight or address
  get weight     : report a server's current weight
  set weight     : change a server's weight (deprecated)

其中和stick table相關的命令有:

  clear table    : remove an entry from a table
  set table [id] : update or create a table entry's data
  show table [id]: report table usage stats or dump this table's contents

例如:

# on haproxy
backend static_group
    stick-table type ip size 5k expire 1m

backend dynamic_group
    stick-table type ip size 5k expire 1m


[root@xuexi ~]# echo "show table" | socat unix:/var/run/haproxy.sock -
# table: static_group, type: ip, size:5120, used:0
# table: dynamic_group, type: ip, size:5120, used:0

本文只是引入stats socket的操作方式,至於各命令的作用,參見官方手冊:http://cbonte.github.io/haproxy-dconv/1.7/management.html#9.3

2.3 使用客戶端源IP作為客戶端標識符

配置文件部分內容如下:

frontend http-in
    bind             *:80
    mode             http
    log              global

    acl url_static   path_beg  -i /static /images /stylesheets
    acl url_static   path_end  -i .jpg .jpeg .gif .png .ico .bmp .html

    use_backend      static_group   if url_static
    default_backend  dynamic_group

backend dynamic_group
    stick-table type ip size 5k expire 1m
    stick on src
    balance roundrobin
    option http-server-close
    option httpchk  GET /index.php
    http-check expect  status 200
    server app1 192.168.100.60:80 check rise 1 maxconn 3000
    server app2 192.168.100.61:80 check rise 1 maxconn 3000

backend static_group
    stick-table type ip size 5k expire 1m
    stick on src
    balance            roundrobin
    option             http-keep-alive
    http-reuse         safe
    option httpchk     GET /index.html
    http-check expect  status 200
    server staticsrv1  192.168.100.62:80 check rise 1 maxconn 5000
    server staticsrv2  192.168.100.63:80 check rise 1 maxconn 5000

上面的配置中,設置了acl,當滿足靜態訪問時,使用static_group後端組,否則使用dynamic_group後端組。在兩個後端組中,都設置了stick-tablestick on,其中stick on是存儲指定內容,併在請求到達時匹配該內容,它的具體用法見後文。只有配置了stick on後,haproxy才能根據匹配的結果決定是否存儲到stick table中,以及如何篩選待分派的後端。

總之,上面的兩個後端組都已經指定了要向stick table中存儲源ip地址作為key。當客戶端請求到達時,haproxy根據調度演算法分配一個後端,但請求交給後端成功後,Haproxy立即向stick table表中插入一條stickiness記錄。當客戶端請求再次到達時,haproxy發現能匹配源ip,於是按照該stickiness記錄,將請求分配給對應的後端。

以下是分別使用兩台機器測試192.168.100.59/index.html192.168.100.59/index.php後,stick table記錄的數據。

[root@xuexi ~]# echo "show table static_group" | socat unix:/var/run/haproxy.sock - 
# table: static_group, type: ip, size:5120, used:2
0x1bc0024: key=192.168.100.1 use=0 exp=48013 server_id=2
0x1bbec14: key=192.168.100.59 use=0 exp=27994 server_id=1

[root@xuexi ~]# echo "show table dynamic_group" | socat unix:/var/run/haproxy.sock -
# table: dynamic_group, type: ip, size:5120, used:2
0x1bc00c4: key=192.168.100.1 use=0 exp=53686 server_id=2
0x1bbeb04: key=192.168.100.59 use=0 exp=34309 server_id=1

其中server_id預設是從1自增的,它可以在server指令中用"id"選項進行顯式指定。例如:

server staticsrv1 192.168.100.62:80 id 111 check rise 1 max conn 6500

如果,在使用stickiness的同時,haproxy還設置了cookie,誰的優先順序高呢?

2.4 使用cookie作為客戶端標識符

一般會話保持考慮的對象是應用程式伺服器,因此此處我們忽略後端的靜態伺服器,只考慮php應用伺服器。在dynamic_group兩個後端server app1和app2的index.php中分別設置好PHPSESSID作為測試。例如:

<h1>response from webapp 192.168.100.60</h1>
<?php
        session_start();
        echo "Server IP: "."<font color=red>".$_SERVER['SERVER_ADDR']."</font>"."<br>";
        echo "Server Name: "."<font color=red>".$_SERVER['SERVER_NAME']."</font>"."<br>";
        echo "SESSIONNAME: "."<font color=red>".session_name()."</font>"."<br>";
        echo "SESSIONID: "."<font color=red>".session_id()."</font>"."<br>";
?>

cookie是string的一種特殊情況,因此創建stick table時,指定type為string。以下是在haproxy上的配置:

backend dynamic_group
    stick-table type string len 32 size 5k expire 2m
    stick on req.cook(PHPSESSID)
    stick store-response res.cook(PHPSESSID)
    balance roundrobin
    option http-server-close
    option httpchk  GET /index.php
    http-check expect  status 200
    server app1 192.168.100.60:80 check rise 1 maxconn 3000
    server app2 192.168.100.61:80 check rise 1 maxconn 3000

stick store-response指令表示從響應報文中匹配某些數據出來,然後存儲到stick table中,此處表示截取響應報文中"Set-Cookie"欄位中名為"PHPSESSID"的cookie名進行存儲。stick on req.cook(PHPSESSID)表示從請求報文的"Cookie"欄位中匹配名為PHPSESSID的cookie。如果能和存儲在stick table中的PHPSESSID匹配成功,則表示該客戶端被處理過,於是將其引導到對應的後端伺服器上。嚴格地說,這裡不是識別客戶端,而是通過PHPSESSID來識別後端。

某次瀏覽器的請求得到如下結果:之後每次請求也都是分配到192.168.100.61上。註意,不要使用curl命令來測試,因為這裡是根據PHPSESSID匹配的,curl每次接收到響應後進程就直接退出了,無法緩存cookie,因此curl每次請求都相當於一次新請求。

在haproxy上查看stick table。

[root@xuexi ~]# echo "show table dynamic_group" | socat unix:/var/run/haproxy.sock - 
# table: dynamic_group, type: string, size:5120, used:1
0x12163d4: key=g5ossskspc96aecp4hvmsehoh4 use=0 exp=50770 server_id=2

2.5 使用string作為客戶端標識符

上面的cookie是string的一種特殊用法。使用string篩選內容進行存儲,靈活性非常大,可以通過它實現某些複雜、特殊的需求。

例如,從請求報文中截取Host欄位的值作為key存儲起來。

backend dynamic_group
    stick-table type string size 5k expire 2m
    stick on req.hdr(Host)
    balance roundrobin
    option http-server-close
    option httpchk  GET /index.php
    http-check expect  status 200
    server app1 192.168.100.60:80 check rise 1 maxconn 3000
    server app2 192.168.100.61:80 check rise 1 maxconn 3000

找一臺linux客戶端使用curl進行測試,發現所有請求都將引導到同義後端伺服器上。

[root@xuexi ~]# for i in `seq 1 5`;do grep "response" <(curl 192.168.100.59/index.php 2>/dev/null);done        
<h1>response from webapp 192.168.100.60</h1>
<h1>response from webapp 192.168.100.60</h1>
<h1>response from webapp 192.168.100.60</h1>
<h1>response from webapp 192.168.100.60</h1>
<h1>response from webapp 192.168.100.60</h1>

查看stick table也只能看到一條記錄,而且其key部分正是捕獲到的Host欄位的值。

[root@xuexi ~]# echo "show table dynamic_group" | socat unix:/var/run/haproxy.sock -       
# table: dynamic_group, type: string, size:5120, used:1
0xf0d904: key=192.168.100.19 use=0 exp=46308 server_id=1

2.6 stick on、stick match、stick store

在前面haproxy的配置中出現過stick onstick store-response,除此之外,還有兩個指令stick matchstick store-request。語法如下:

stick store-request <pattern> [table <table>] [{if | unless} <condition>]
stick store-response <pattern> [table <table>] [{if | unless} <condition>]
stick match <pattern> [table <table>] [{if | unless} <cond>]
stick on <pattern> [table <table>] [{if | unless} <condition>]

其中stick store指令是從請求或響應報文中截取一部分字元串出來,並將其作為stickiness的key存儲到stick table中。例如:

# 截取響應報文中名為PHPSESSID的cookie作為key
stick store-response res.cook(PHPSESSID)

# 截取請求報文中Host欄位的值作為key
stick store-request req.hdr(Host)

# 對請求的源ip地址進行匹配,若不是兄弟網路中的主機時,就寫入stick table中,且該table名為dynamic_group
stick store-request src table dynamic_group if !my_brother

stick match是將請求報文中的指定部分和stick table中的記錄進行匹配。例如:

# 截取請求報文中名為PHPSESSID的cookie,去stick table中搜索是否存在對應的記錄
stick match req.cook(PHPSESSID)

# 當源IP不是本機時,去dynamic_group表中搜索是否有能匹配到源IP地址的記錄
stick match src table dynamic_group if !localhost

stick on等價於stick store+stick match,是它們的簡化寫法。例如:

# 存儲並匹配源IP地址
stick on src               #1 = #2 + #3
stick match src            #2
stick store-request src    #3

# 存儲並匹配源IP地址
stick on src table dynamic_group if !localhost             #1 = #2 + #3
stick match src table dynamic_group if !localhost          #2
stick store-request src table dynamic_group if !localhost  #3

# 存儲並匹配後端伺服器設置的PHPSESSID
stick on req.cook(PHPSESSID)                 #1 +#2 = #3 + #4
stick store-response res.cook(PHPSESSID)     #2
stick match req.cook(PHPSESSID)              #3
stick store-response res.cook(PHPSESSID)     #4

2.7 使用stick table統計狀態信息

stick table除了存儲基本的粘性信息,還能存儲額外的統計數據,這其實是haproxy提供的一種"採樣調查"功能。它能採集的數據種類有以下幾種:

每個stickiness記錄中可以同時存儲多個記錄類型,使用逗號分隔或多次使用store關鍵字即可。但註意,後端伺服器的server id會自動記錄,其它所有額外信息都需要顯式指定。

需要註意,每個haproxy後端組只能有一張stick table,但卻不建議統計太多額外的狀態信息,因為每多存一個類型,意味著使用更多的記憶體。

如果存儲所有上述列出的數據類型,需要116位元組,100W條記錄要用116M,這不是可以忽略的大小。此外還有50M的key,共166M。

例如下麵的示例中,使用了通用計數器累計,並記錄了每30秒內的平均連接速率。

stick-table type ip size 1m expire 5m store gpc0,conn_rate(30s)

 

回到Linux系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
回到網站架構系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7576137.html
回到資料庫系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7586194.html
轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/8558514.html

註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!


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

-Advertisement-
Play Games
更多相關文章
  • 最近有同事問道在應用程式啟動之後,再次雙擊應用程式,如何保證不再啟動新的應用程式,而是彈出之前已經啟動的進程,本質上這就是創建一個單實例的WPF應用程式。在VS的工程樹中有一個App.xaml和App.xaml.cs(這兩個文件都是VS自動生成的),在App.xaml.cs中定義了App類,該類繼承 ...
  • (基於 Linux 3.4.2 內核) 可分為以下幾個步驟來完成這個驅動: ~~~~ 1. 分配設置一個 usb_driver 結構體 2. 註冊這個 usb_driver (如果設備的 id_table 與驅動匹配的話會調用驅動程式的 probe 函數) 3. 在 probe 函數中分配 urb ...
  • #!/bin/bash#註釋#註釋#環境變數相關,如下PATH=/sbin:/bin:/usr/bin:/usr/sbin #引入庫函數,如下,類似於c語言的#include "*.h" . /etc/init.d/functions #獲取相關配置信息,不是必須的,如test -f /etc/sy ...
  • 本文最後修改時間:20180313 一、快速修改,重啟後設置就沒了 二、修改配置文件,重啟設置還在 (一)ubuntu 版本命令行設置靜態IP 修改 /etc/network/interfaces 在/etc/resolv.conf添加 重啟網卡: (二)SUSE 1、修改IP地址 修改/etc/s ...
  • linux if 命令判斷條件總結linux if命令 關於文件屬性的判斷式 -a 如果文件存在 -b 如果文件存在,且該文件是區域設備文件 -c 當file存在並且是字元設備文件時返回真 -d 當pathname存在並且是一個目錄時返回真 -e 當pathname指定的文件或目錄存在時返回真 -f ...
  • 本文最後修改時間:20180313 root預設為空密碼,預設遠程無法登錄。 mysql5.7更新了user表,網上的方法試了很多,都有點問題 \ 先停止MySQL服務 \ 跳過安全檢查 如果出現如下報錯信息 mysqld_safe Directory ‘/var/run/mysqld’ for U ...
  • #!/bin/bash step=2 for((i=0;i<60;i=i+step));do ps aux|grep apptomcat |grep -v grep >/dev/null y=`echo $?` if [ "$y" -ne "0" ];then ps aux|grep webtomc ...
  • 進程管理回顧 基本方法: 用戶進程最大記憶體640K。 問題:記憶體過小,如果應用程式需要更多記憶體怎麼辦 解決方案:overlay。僅載入當前必須的代碼和數據。 問題:P1, P2都在運行,P1需要申請更多記憶體,但記憶體已經用完 解決方案:swap(將P2臨時交換到外存)。缺點:磁碟傳輸導致swap成本極 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...