自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一次教科書級別的Redis高可用架構(gòu)設計實踐

開發(fā) 架構(gòu) Redis
本文主要介紹了 Qunar Redis 高可用架構(gòu)設計原理、安全機制及集群自動化運維方面的內(nèi)容。

 一、高可用架構(gòu)設計原理

1、概述

Qunar Redis 集群是一個分布式的高可用架構(gòu),整個架構(gòu)主要由以下幾個重要部分組成:

  •  Redis Server 節(jié)點:每個節(jié)點有一主一從兩個實例,多個節(jié)點組成一份完整的集群數(shù)據(jù),其中每個節(jié)點只有主庫對外提供服務,從庫僅僅用于節(jié)點高可用、數(shù)據(jù)持久化及定時備份。
  •  Zookeeper 集群:由五個 zk 節(jié)點組成,Redis 集群配置變更后,通知客戶端進行重連。
  •  Redis Sentinel 集群:由五個 Sentinel 節(jié)點組成,用于 Reids Server 節(jié)點的高可用,主從切換、故障轉(zhuǎn)移、配置更新等。
  •  配置中心集群:由五個 MySQL 節(jié)點組成的 PXC 集群,用于存儲 Redis 集群的分片信息,即每個節(jié)點的 Master 實例信息及分配 key 的一致性 hash 值范圍。
  •  應用程序客戶端:監(jiān)聽 zk 變化,在配置中心獲取 Redis 實例信息進行連接。

2、架構(gòu)原理圖

3、客戶端實現(xiàn)

1)當客戶端根據(jù) Redis 集群的 namespace 建立連接時,會先從 zk 中查找/config_addr 節(jié)點, 該節(jié)點下存放的是配置中心集群的實例信息,從中隨機選擇一個數(shù)據(jù)庫實例進行連接。

2)在配置中心的特定庫表中,根據(jù) Redis 的 namespace 查詢集群的節(jié)點的連接配置,然后建立 Redis 連接。

3)客戶端建立 Redis 連接后,會啟動了兩個線程:

  •  一個用于監(jiān)聽 zk 的地址的變化。每個 Redis 集群在 zk 中都會有一個/redis/namespace 的節(jié)點 ,如果集群配置發(fā)生變化,哨兵會通知 zk 更新此節(jié)點的值,客戶端感知到 zk 配置變化, 將會去配置中心獲取新的連接配置,重新建立連接。
  •  一個用于輪詢配置中心的連接配置。為了防止 zk 通知失敗,客戶端會通過這個線程,每隔 10s 去輪詢配置中心的配置信息,如果發(fā)現(xiàn)配置中心的配置和本地緩存的不一樣,就會使用配置中心的配置建立新的連接。

客戶端與其他組件的關系示意圖如下:

4、數(shù)據(jù)分片方法

開發(fā)人員提交 Redis 集群申請工單信息后,DBA 會依據(jù)工單中的內(nèi)存大小、QPS 大小等幾項主要的數(shù)據(jù),規(guī)劃集群分片節(jié)點數(shù)量為 N,所有節(jié)點平均分配 0~4294967295 范圍內(nèi)的值,即共有 2 的 32 次方個 key 的值,某一個 key 使用 murmurhash2 算法計算哈希值后,只會落在集群的一個節(jié)點上。

分片節(jié)點示意圖如下:

分片節(jié)點信息在配置中心的存儲信息如下:

5、架構(gòu)特點

Quanr Redis 高可用架構(gòu)具有以下特點:

  •  實現(xiàn)自己的 Redis 客戶端,客戶端不再訪問 Sentinel, Sentinel 只負責高可用。
  •  通過 ZK 集群和配置中心來實現(xiàn)配置的集中管理。
  •  將端口視作一種資源,即集群的一個節(jié)點的主從實例使用一個端口,下線的集群端口可復用。
  •  弱化了哨兵機器的地位, 降低了哨兵和集群之間直接的耦合度。
  •  減少了哨兵機器的使用量, 目前只使用了 5 臺哨兵機器組成集群。
  •  客戶端使用 namespace 訪問集群, 將端口和 namespace 對應,namespace 和業(yè)務部門對應,方便 DBA 管理和運維,對應用透明。

6、架構(gòu)局限性

Quanr Redis 高可用架構(gòu)具有以下局限性:

  •  支持的客戶端比較少。目前客戶端僅支持 Java 和 Python。
  •  不支持快速水平擴容。當集群內(nèi)存不足時可以快速擴大各個節(jié)點實例的內(nèi)存大小,以此來增加整個集群大小,但單個實例的內(nèi)存大小也有一定的限度,不能無限擴展。當需要增加集群節(jié)點個數(shù)時,由于各個節(jié)點的一致性哈希范圍發(fā)生了變化,所有的 key 需要重新分配,對于比較大的集群,過程比較繁瑣和耗時。
  •  整個架構(gòu)依賴的組件比較多。雖然架構(gòu)中的 zookeeper、配置中心、Sentinel 等都是多節(jié)點的高可用集群,但依賴的組件越多,發(fā)生故障的可能性也越大,運維難度和工作量也會隨著增加,無疑對運維人員有更高的要求。
  •  部分 Redis 原生功能無法使用。由于客戶端的限制,部分 Redis 原生功能無法使用,如不支持事務、Lua 腳本等。

二、安全機制

Redis 被設計成僅供可信環(huán)境下的可信用戶才可以訪問,并沒有最大化的去優(yōu)化安全方面,而是盡量可能的去優(yōu)化高性能和易用性,因此 Redis 沒有類似關系型數(shù)據(jù)庫那樣嚴格的權(quán)限控制,因此將 Redis 實例直接暴露在網(wǎng)絡上或者讓不可信的用戶直接訪問 Redis 的 TCP 端口,是非常危險的行為。

為了提高 Redis 使用的安全性,去哪兒網(wǎng)使用的 Redis Server 是在官方 Redis 4.0.14 版本上進行了部分的源代碼改造,增加了一個白名單參數(shù) trustedip,屏蔽了部分高危指令,除了 trustedip 中配置的 IP 之外,任何其他客戶端連接都無法執(zhí)行這些高危指令,同時為了提高 Redis 的性能,對主從實例進行了差異性配置。

1、clientcipher和IP白名單

Qunar Redis 客戶端并沒有直接通過 TCP 方式去連接 Redis 實例,而是首先要通過集群 namespace 和該集群唯一的 clientcipher 的驗證,然后從配置中心獲取真正的連接信息后,才可以連接 Redis 實例。同時白名單機制對客戶端請求中的高危指令進行過濾,避免對線上 Redis 執(zhí)行不合理的操作,進一步加強了其安全性。

  •  客戶端使用 namespace 和 clientcipher 方式訪問集群。
  •  不同 namespace 對應的 clientcipher 不同,在創(chuàng)建集群時通過隨機生成的密碼再次加密生成 clientcipher。
  •  即使知道密碼,也無法使用屏蔽的危險命令,除非 IP 地址在白名單中。
  •  本地登陸和 IP 白名單登陸,命令不受限制,方便 DBA 管理和兼容各種監(jiān)控統(tǒng)計腳本。
  •  IP 白名單可以動態(tài)配置,最大支持 32 個 IP 白名單。

IP 白名單功能涉及修改代碼的地方:

1)在 config.c 文件的 configGetCommand 方法中增加參數(shù) trustedip 

  1. void configGetCommand(client *c) {  
  2. robj *o = c->argv[2];  
  3. void *replylen = addDeferredMultiBulkLength(c);  
  4. char *pattern = o->ptr;  
  5. char buf[128];  
  6. int matches = 0 
  7. serverAssertWithInfo(c,o,sdsEncodedObject(o));  
  8. ...  
  9. /* 增加trustedip參數(shù) */  
  10. if (stringmatch(pattern,"trustedip",0)) {  
  11. sds buf = sdsempty();  
  12. int j;  
  13. int numips;  
  14. numips = server.trusted_ips.numips;  
  15. for (j = 0; j < numips; j++) { 
  16. buf = sdscat(buf, server.trusted_ips.ips[j]);  
  17. if (j != numips - 1)  
  18. buf = sdscatlen(buf," ",1);  
  19.  
  20. addReplyBulkCString(c,"trustedip");  
  21. addReplyBulkCString(c,buf);  
  22. sdsfree(buf);  
  23. matches++;  
  24.  
  25. setDeferredMultiBulkLength(c,replylen,matches*2);  

2)在 server.c 文件的 processCommand 方法中增加對 issuperclient 的認證 

  1. typedef struct trustedIPArray {  
  2. int numips;  
  3. sds* ips;  
  4. } trustedIPArray; 

3)在 networking.c 文件中增加 isTrustedIP 方法 

  1. /* 判斷客戶端IP是否在IP白名單中 */  
  2. int isTrustedIP(int fd) {  
  3. char ip[128];  
  4. int i, port;  
  5. anetPeerToString(fd,ip,128,&port); 
  6. if (strcmp(ip, "127.0.0.1") == 0) {  
  7. return 1;  
  8.  
  9. for (i = 0; i < server.trusted_ips.numips; i++) {  
  10. if (strcmp(ip, server.trusted_ips.ips[i]) == 0) {  
  11. return 1;  
  12.  
  13.  
  14. return 0;  

4)在 networking.c 文件的 createClient 方法中增加 issuperclient 的設置 

  1. client *createClient(int fd) {  
  2. client *c = zmalloc(sizeof(client));  
  3. /* passing -1 as fd it is possible to create a non connected client.  
  4. * This is useful since all the commands needs to be executed  
  5. * in the context of a client. When commands are executed in other  
  6. * contexts (for instance a Lua script) we need a non connected client. */  
  7. if (fd != -1) {  
  8. anetNonBlock(NULL,fd);  
  9. anetEnableTcpNoDelay(NULL,fd);  
  10. if (server.tcpkeepalive)  
  11. anetKeepAlive(NULL,fd,server.tcpkeepalive);  
  12. if (aeCreateFileEvent(server.el,fd,AE_READABLE,  
  13. readQueryFromClient, c) == AE_ERR)  
  14.  
  15. close(fd);  
  16. zfree(c);  
  17. return NULL;  
  18.  
  19. ...  
  20. /* 設置is_super_client */  
  21. if (isTrustedIP(fd)) {  
  22. c->is_super_client = 1 
  23. } else {  
  24. c->is_super_client = 0 
  25.  
  26. ...  
  27. return c;  

5)在 server.c 文件的 processCommand 方法中增加對 issuperclient 的認證 

  1. int processCommand(client *c) {  
  2. /* The QUIT command is handled separately. Normal command procs will  
  3. * go through checking for replication and QUIT will cause trouble  
  4. * when FORCE_REPLICATION is enabled and would be implemented in  
  5. * a regular command proc. */ 
  6. if (!strcasecmp(c->argv[0]->ptr,"quit")) {  
  7. addReply(c,shared.ok);  
  8. c->flags |= CLIENT_CLOSE_AFTER_REPLY;  
  9. return C_ERR;  
  10.  
  11. ...  
  12. /* Check if the user is authenticated */  
  13. /* 增加is_super_client認證 */  
  14. if (!c->is_super_client && server.requirepass && !c->authenticated && c->cmd->proc != authCommand)  
  15. ...  
  16. return C_OK;  

6)在 db.c 文件中增加 checkCommandBeforeExec 方法 

  1. /* 如果是super client或者是master,返回1,否則返回0  
  2. * 因為在master-slave下,master(client)需要向slave執(zhí)行危險命令*/  
  3. int checkCommandBeforeExec(client *c) {  
  4. if (c->is_super_client || (server.masterhost && (c->flags & CLIENT_MASTER))) {  
  5. return 1;  
  6.  
  7. addReplyError(c,"No permission to execute this command");  
  8. return 0;  

2、屏蔽高危指令

通過修改 Redis 源代碼,在 Server 端屏蔽部分危險指令,規(guī)定只有通過白名單檢查的客戶端連接才可以執(zhí)行這些指令。在執(zhí)行高危指令前進行檢查,如需對 save 指令進行屏蔽,可對 rdb.c 文件的 saveCommand 方法的第一行增加 checkCommandBeforeExec 檢查。 

  1. void saveCommand(client *c) {  
  2. if (!checkCommandBeforeExec(c)) return; /* 執(zhí)行指令之前進行檢查,如不通過直接返回 */   
  3. if (server.rdb_child_pid != -1) {  
  4. addReplyError(c,"Background save already in progress");  
  5. return;  
  6.  
  7. rdbSaveInfo rsi, *rsiptr;  
  8. rsiptr = rdbPopulateSaveInfo(&rsi);  
  9. if (rdbSave(server.rdb_filename,rsiptr) == C_OK) { 
  10. addReply(c,shared.ok);  
  11. } else {  
  12. addReply(c,shared.err);  
  13.  

屏蔽的高危指令有:

  •  比較耗時類指令:info、keys *。
  •  清空數(shù)據(jù)類指令:shutdown、flushdb、 flushall。
  •  數(shù)據(jù)持久化類指令:save、bgsave、bgrewriteaof。
  •  配置類指令:config get、config set、config rewrite。
  •  運維管理類指令:slaveof、monitor、client list、client kill。

在 Redis 源代碼涉及這些指令的地方,都需要加上 checkCommandBeforeExec 方法進行檢查。

3、配置優(yōu)化

針對集群各個節(jié)點的主從實例進行差異化配置,由于每個節(jié)點只有主庫對外提供服務,為了最大限度的提高主庫的并發(fā)能力,一些比較耗時的操作可以放到從庫去執(zhí)行。

幾項主要的配置如下:

  •  主庫關閉 bgsave、bgrewriteaof 功能。
  •  從庫開啟 aof 功能,定時調(diào)度重寫 aof 文件,釋放服務器磁盤空間。
  •  從庫定時執(zhí)行 bgsave 操作,備份 rdb 文件。
  •  從庫開啟 slave-read-only 參數(shù),只讀。

當 Redis 集群部署完之后,會有定時任務去檢查服務器上各個 Redis 實例的角色,根據(jù)角色的不同修改相關的配置參數(shù),同時將修改后的持久化到配置文件。

三、自動化運維

1、初始化系統(tǒng)環(huán)境

在 Redis 服務器上部署集群之前,首先需要初始化系統(tǒng)環(huán)境,將這些環(huán)境配置添加到 Redis 的 rpm 打包程序的 spec 文件中,安裝 Redis 軟件包時會自動更改相關配置,主要的系統(tǒng)環(huán)境參數(shù)有以下幾個: 

  1. sed -i -r '/vm.overcommit_memory.*/d' /etc/sysctl.conf  
  2. sed -i -r '/vm.swappiness.*/d' /etc/sysctl.conf  
  3. sed -i -r '/vm.dirty_bytes.*/d' /etc/sysctl.conf  
  4. echo "vm.overcommit_memory = 1>> /etc/sysctl.conf  
  5. echo "vm.swappiness = 0>> /etc/sysctl.conf  
  6. echo "vm.dirty_bytes = 33554432>> /etc/sysctl.conf  
  7. /sbin/sysctl -q -p /etc/sysctl.conf  
  8. groupadd redis >/dev/null 2>&1 || true  
  9. useradd -M -g redis redis -s /sbin/nologin >/dev/null 2>&1 || true  
  10. sed -i -r '/redis soft nofile.*/d' /etc/security/limits.conf  
  11. sed -i -r '/redis hard nofile.*/d' /etc/security/limits.conf  
  12. echo "redis soft nofile 288000" >> /etc/security/limits.conf  
  13. echo "redis hard nofile 288000" >> /etc/security/limits.conf  
  14. sed -i -r '/redis soft nproc.*/d' /etc/security/limits.conf  
  15. sed -i -r '/redis hard nproc.*/d' /etc/security/limits.conf  
  16. echo "redis soft nproc unlimited" >> /etc/security/limits.conf  
  17. echo "redis hard nproc unlimited" >> /etc/security/limits.conf  
  18. echo never > /sys/kernel/mm/transparent_hugepage/enabled 

2、統(tǒng)一運維管理工具

Qunar Redis 集群的統(tǒng)一管理套件,封裝了系統(tǒng)環(huán)境初始化、實例安裝、實例啟動、實例關閉、監(jiān)控報警、定時任務等腳本,實現(xiàn)了監(jiān)控、統(tǒng)計、注冊等自動化操作。 

  1. /etc/cron.d/appendonly_switch  
  2. /etc/cron.d/auto_upgrade_toolkit  
  3. /etc/cron.d/bgrewriteaof  
  4. /etc/cron.d/check_maxmemory  
  5. /etc/cron.d/dump_rdb_keys  
  6. /etc/cron.d/rdb_backup  
  7. /etc/profile.d/q_redis_path.sh  
  8. /xxx/collectd/etc/collectd.d/collect_redis.conf  
  9. /xxx/collectd/lib/collectd/collect_redis.py  
  10. /xxx/collectd/share/collectd/types_redis.db  
  11. /xxx/nrpe/libexec/q-check-redis-cpu-usage  
  12. /xxx/nrpe/libexec/q-check-redis-latency  
  13. /xxx/nrpe/libexec/q-check-redis-memory-usage  
  14. /xxx/nrpe/libexec/q-check-zookeeper-ruok  
  15. /xxx/redis/tools/cron_appendonly_switch.sh  
  16. /xxx/redis/tools/cron_bgrewrite_aof.sh  
  17. /xxx/redis/tools/cron_check_maxmemory.sh  
  18. /xxx/redis/tools/cron_dump_rdb_keys.sh  
  19. /xxx/redis/tools/cron_rdb_backup.sh  
  20. /xxx/redis/tools/dump_rdb_keys.py  
  21. /xxx/redis/tools/redis-cli5  
  22. /xxx/redis/tools/redis-latency  
  23. /xxx/redis/tools/redis_install.sh  
  24. /xxx/redis/tools/redis_start.sh  
  25. /xxx/redis/tools/redis_stop.sh 

3、單機多實例多版本部署

Qunar Redis 的安裝工具包支持單機多實例安裝,安裝腳本提供選項和配置文件模板,可以自定義安裝不同版本的 Redis,目前支持的 Redis Server 版本有 2.8.6、3.0.7 以及 4.0.14。 

  1. /* 安裝包及Redis實例目錄結(jié)構(gòu) */  
  2.  
  3. ├── multi  
  4. │ ├── server_2800 /* Redis2.8.6軟件包 */  
  5. │ │ ├── bin  
  6. │ │ └── utils  
  7. │ ├── server_3000 /* Redis3.0.7軟件包 */  
  8. │ │ ├── bin  
  9. │ │ └── utils  
  10. │ └── server_4000 /* Redis4.0.14軟件包 */  
  11. │ ├── bin  
  12. │ └── utils  
  13. ├── redis10088 /* 端口為10088的Redis實例數(shù)據(jù)目錄,用于存放該實例的配置文件、日志、AOF文件及RDB文件 */  
  14. │ ├── bin  
  15. │ └── utils  
  16. ├── redis10803 /* 端口為10803的Redis實例數(shù)據(jù)目錄,用于存放該實例的配置文件、日志、AOF文件及RDB文件 */  
  17. │ ├── bin  
  18. │ └── utils  
  19. ├── redis11459 /* 端口為11459的Redis實例數(shù)據(jù)目錄,用于存放該實例的配置文件、日志、AOF文件及RDB文件 */  
  20. │ ├── bin  
  21. │ └── utils  
  22. /* Redis實例安裝程序用法 */  
  23. Usage: redis_install.sh -P <port> -v [2.8|3.0|4.0] -p <password> -m <size>  
  24. 必選參數(shù):  
  25. -P redis端口  
  26. -p redis密碼  
  27. -v 將要安裝的redis版本,強烈推薦4.0版本  
  28. -m redis實例允許的最大內(nèi)存大小,單位是G  
  29. 可選參數(shù):  
  30. --cluster 集群模式,version>=3.0  
  31. --testenv 測試環(huán)境  
  32. example:  
  33. sudo redis_install.sh -P 6379 -v 4.0 -m 20 -p 1qaz2wsx 

4、使用git管理Redis哨兵

使用 git 集中管理所有的哨兵配置,一個地方發(fā)生變更,哨兵集群的所有服務器同時拉取進行同步更新。同時詳細的 commit log,方便跟蹤配置文件修改歷史。Qunar Redis 哨兵具有以下特點:

  •  一套哨兵只管理一個節(jié)點,即只對端口號相同的一組 Redis(一主一從或一主多從)實例進行監(jiān)控和故障轉(zhuǎn)移。
  •  哨兵只負責節(jié)點的高可用,客戶端不需要通過哨兵來訪問 Redis 實例。
  •  哨兵配置文件使用 git 統(tǒng)一管理,配置文件以[節(jié)點端口號+20000]_集群 namespace.conf 方式統(tǒng)一命名,例如 30708_redis_delay_test.conf,通過集群任意一個節(jié)點的端口號或者 namespace 可以獲取集群全部節(jié)點的信息。
  •  當哨兵監(jiān)控的節(jié)點發(fā)生切換時,會更新配置中心對應節(jié)點的主庫配置和 zookeeper 中對應節(jié)點的 dataVersion,客戶端檢測到 zookeeper 的變化會去配置中心獲取節(jié)點最新的信息進行重連,同時哨兵會將切換信息發(fā)送至 DBA 和運維事件平臺。
  • 哨兵服務器的 IP 默認都添加到 Redis 實例的白名單中,即通過哨兵服務器可以訪問任何一個 Redis 實例進行所有的操作,所以哨兵服務器的權(quán)限必須嚴格控制,只有 DBA 才有權(quán)限登陸。

5、運維操作平臺化

以上幾項規(guī)范統(tǒng)一的標準化流程,為 Qunar Redis 的整個運維平臺化提供了有力的支撐,目前 Qunar Redis 的 90% 以上的運維操作都實現(xiàn)了平臺自動化,包括工單申請及審核、集群部署、實例遷移、集群垂直伸縮、不同維度(服務器、集群、實例)的信息查看等,下面主要介紹下 Qunar Redis 集群部署和實例遷移的實現(xiàn)過程。

集群部署

Qunar Redis 集群部署時主要有以下步驟:

1)開發(fā)人員通過平臺提交集群申請工單發(fā)起流程,TL 審核完成后流程扭轉(zhuǎn)到 DBA。

2)DBA 根據(jù)申請工單的信息規(guī)劃集群規(guī)模,如節(jié)點個數(shù)、內(nèi)存大小、部署機房、Redis 版本等。

3)根據(jù)集群規(guī)劃在 Redis 集群部署頁面填寫部署信息。

4)提交部署信息后平臺會自動篩選資源空閑的服務器進行集群部署。

5)集群部署完成后會在在 Qtalk 上通知 DBA,集群的 clientcipher 會通過郵件方式通知開發(fā)人員,同時會將集群部署情況推送到公司運維事件平臺,保留操作記錄。

實例遷移

運維過程中實例遷移主要分為兩大類:

1)部分實例遷移。當某臺服務器的可用資源不足時,將這臺機器上的部分實例遷移到其他資源比較空閑的服務器上。在頁面輸入實例的源主機和目前主機,提交后會自動生成遷移任務。

2)整機實例遷移。主要是替換過保服務器或者服務器需要停機維護時,將該機器上的所有實例自動遷移到其他資源比較空間的服務器上。在頁面輸入需要遷移的主機名,提交后會自動生成遷移任務。

遷移任務開始后,整個遷移過程無須人工介入,會自動更新執(zhí)行進度并輸出日志。

 

 

責任編輯:龐桂玉 來源: DBAplus社群
相關推薦

2019-06-28 09:27:20

高可用架構(gòu)支付

2019-11-29 10:15:28

GitHub代碼開發(fā)者

2021-03-09 20:52:01

架構(gòu)無狀態(tài)服務

2017-06-12 11:09:56

計數(shù)架構(gòu)數(shù)據(jù)庫

2022-05-31 08:04:03

Redis高可用集群

2018-09-11 09:33:49

Redis高可用架構(gòu)

2017-12-07 12:47:48

Serverless架構(gòu)基因

2018-05-25 14:41:56

Serverless無服務器構(gòu)造

2025-03-03 04:20:00

高可用架構(gòu)冗余法則

2020-04-22 14:25:48

云開發(fā)高可用架構(gòu)

2018-09-10 08:27:18

登錄Auth0架構(gòu)

2018-09-27 18:34:08

架構(gòu)Auth0

2021-06-29 10:18:07

Kafka宕機系統(tǒng)

2019-12-24 09:30:59

蘇寧高可用高并發(fā)

2017-05-19 15:00:05

session架構(gòu)web-server

2017-10-27 14:52:31

互聯(lián)網(wǎng)高可用架構(gòu)高可用

2009-06-22 14:48:21

DRY架構(gòu)設計

2017-01-23 12:40:45

設計演講報表數(shù)據(jù)

2021-04-28 08:52:22

高并發(fā)架構(gòu)設高并發(fā)系統(tǒng)

2021-02-24 10:05:07

架構(gòu)運維技術(shù)
點贊
收藏

51CTO技術(shù)棧公眾號