在阿里,我們如何管理測試環境

為什么80%的碼農都做不了架構師?>>> ??hot3.png

作者:林帆(花名金戟),阿里巴巴研發效能部技術專家

相關閱讀:在阿里,我們如何管理代碼分支

前言

阿里的許多實踐看似簡單,背后卻蘊涵著許多思考,譬如測試環境的管理。

互聯網產品的服務通常是由Web應用、中間件、數據庫和許多后臺業務程序組成的,一套運行環境就是一個自成一體的小生態。最基本的運行環境是線上環境,部署產品的正式發布版本,為用戶提供持續可靠的服務。

除此以外,還有許多不對外部用戶開放的運行環境,用于產品團隊日常的開發和驗證,統稱為測試環境。正式環境的穩定性,除去軟件自身的質量因素,主要與運行的主機、網絡等基礎設施相關,而測試環境的穩定性則更多受到人為因素影響。由于頻繁的版本變更,以及部署未經充分驗證的代碼,測試環境出故障的情況屢見不鮮。

良好的代碼提交習慣、適當的變更前檢查有助于減少故障的發生,但無法徹底杜絕后患。增加多套測試環境副本能夠有效控制故障的影響范圍,然而企業的資源終歸有限,降低測試環境成本和提高測試環境穩定性成為了矛盾的兩面。

在這個領域里,獨具匠心的阿里研發效能團隊設計了一種服務級復用的虛擬化技術,稱為“特性環境”,其巧妙的思路令人贊嘆。本文將圍繞測試環境管理的話題,聊聊這種具有阿里特色的工作方式。

測試環境管理的困局

測試環境的用途很廣泛,常見的測試環境譬如系統集成測試環境、用戶驗收測試環境、預發布測試環境、灰度測試環境等,它們體現了產品的交付生命周期,也間接反映出整個團隊的組織結構。

小作坊型產品團隊的測試環境管理起來十分簡單,每個工程師本地就能啟動全套軟件組件進行調試,倘若不放心,再加上一個公共的集成測試環境也就足夠。

1

隨著產品規模擴大,本地啟動所有服務組件逐漸變得既費時又費事,工程師們只能在本地運行一部分待調試的組件,然后利用公共測試環境上的其余組件組成完整系統。

與此同時,團隊規模的擴張,使得每個團隊成員的職責進一步細分,新的子團隊被劃分出來,這意味著項目的溝通成本增加,公共測試環境的穩定性開始變得難以控制。在這個過程中,測試環境管理復雜性帶來的影響,不僅體現在服務聯調變得繁瑣,更直接反映在交付流程和資源成本的變化上。

在交付流程方面,一個顯著的變化是測試環境種類增多。出于不同的用途和目的,工程師們設計出了各式各樣的專用測試環境。這些測試環境的組合形成了各個企業獨具特色的交付流程。下圖展示了一種用于大型項目的復雜交付流程。

2

從單獨服務的角度來看,環境與環境之間是由流水線相連的,再加上自動化測試或手工審批操作組成關卡,實現環境之間的傳遞。通常越高級別環境的部署頻率越低,因此相對穩定性也越高。與之相反,在級別較低的環境上,就隨時可能存在新的部署,會打擾正在使用該環境的其他人。有時為了復現某些特殊的問題場景,一些開發者不得不直接登錄到服務器上面去“搞事情”,進一步影響環境的穩定性和可用性。

面對隨時可能崩潰的測試環境,小企業會試著去“堵”:約束服務變更時間、設立嚴格的變更規范,大企業則善于用“疏”:增加測試環境副本,隔離故障影響范圍。顯然,不堪重負的測試環境一定越“堵”越“漏”,千年以前大禹治水的故事早就揭示了的道理,刻意的管控拯救不了脆弱的測試環境。

近年來,DevOps文化的興起,端到端解放了開發者的雙手,這對于測試環境的管理而言卻是一把雙刃劍。一方面,DevOps鼓勵開發人員參與運維,了解產品的完整生命周期,有助于減少不必要的低級運維事故;另一方面,DevOps讓更多的手伸向測試環境,更多的變更、更多的Hotfix出現了。這些實踐從全局來看利大于弊,然而并不能緩解測試環境的動蕩。單純的流程疏通同樣拯救不了脆弱的測試環境。

那么該投入的還得投入。將不同團隊所用的低級別測試環境各自獨立,此時每個團隊看到的都是線性流水線,從整體上觀察,則會程現出河流匯聚的形狀。

3

由此推廣,理想情況下,每位開發者都應該得到獨占且穩定的測試環境,各自不受干擾的完成工作。然而由于成本因素,現實中在團隊內往往只能共享有限的測試資源,不同成員在測試環境相互干擾成為影響軟件開發質量的隱患。增加測試環境副本數本質上是一種提高成本換取效率的方法,然而許多試圖在成本和效率之間尋找最優平衡的探索者們,似乎都在同一條不歸路上越行越遠。

由于客觀的規模和體量,上述這些測試環境管理的麻煩事兒,阿里的產品團隊都無法幸免。

首先是測試環境種類的管理。

在阿里內部,同樣有十分豐富的測試環境區分。各種測試環境的命名與其作用息息相關,雖然業界有些常用的名稱,但都未形成權威的標準。實際上,環境的名稱只是一種形式,關鍵還在于各種測試環境應當分別適配于特定應用場景,且場景之間應當或多或少存在一些差異。

這種差異有些在于運行的服務種類,譬如性能測試環境很可能只需要運行與壓力測試相關的那部分訪問量最大的關鍵業務服務,其他服務運行了也是浪費資源。有些差異在于接入數據的來源,譬如開發自測的環境的數據源與正式環境肯定不一樣,這樣測試使用的假數據就不會污染線上用戶的請求;預發布環境(或用戶驗收測試環境)會用與正式環境一致的數據源(或正式數據源的拷貝),以便反映新功能在真實數據上運行的情況;自動化測試相關的環境會有單獨的一套測試數據庫,以避測試運行過程中受到其他人為操作的干擾。

還有些差異在于使用者的不同,譬如灰度和預發布環境都使用正式的數據源,但灰度環境的使用者是一小撮真實的外部用戶,而預發布環境的使用者都是內部人員。總之,沒必要為一個不存在業務特殊性的測試場景專門發明一種測試環境。

在集團層面,阿里對流水線形式的約束相對寬松。客觀的講,只有在一線的開發團隊知道最適合團隊的交付流程應該是什么樣子。阿里的開發平臺只是規范了一些推薦的流水線模板,開發者可在此基礎上進行發揮。列舉幾個典型的模板例子:
4

這里出現了幾種外界不太常見的環境類型名稱,稍后會詳細介紹。

其次是測試環境成本的管理。

成本管理的問題十分棘手且十分值得深究。與測試環境相關的成本主要包括管理環境所需的“人工成本”和購買基礎設施所需的“資產成本”。通過自動化以及自服務化的工具可以有效降低人工相關的成本,自動化又是個很大的話題,宜另起一篇文章討論,此處暫且收住。

資產購買成本的降低依賴技術的改良和進步(排除規模化采購帶來的價格變化因素),而基礎設施技術的發展史包括兩大領域:硬件和軟件。硬件發展帶來的成本大幅下降,通常來自于新的材料、新的生產工藝、以及新的硬件設計思路;軟件發展帶來的基礎設施成本大幅下降,目前看來,大多來自于虛擬化(即資源隔離復用)技術的突破。

最早的虛擬化技術是虛擬機,早在20世紀50年代,IBM就開始利用這種硬件級的虛擬化方法獲得成倍的資源利用率提升。虛擬機上的不同隔離環境之間各自運行完整操作系統,具有很好的隔離性,通用性強,但對于運行業務服務的場景,顯得略為笨重。2000年后,KVM、XEN等開源項目使得硬件級虛擬化廣泛普及。

與此同時,另一種更輕量的虛擬化技術出現了,以OpenVZ、LXC為代表的早期容器技術,實現了建立于操作系統內核之上的運行環境虛擬化,減少了獨立操作系統的資源消耗,以犧牲一定隔離性為代價,獲得更高的資源利用率。

之后誕生的Docker以其鏡像封裝和單進程容器的理念,將這種內核級虛擬化技術推上百萬人追捧的高度。阿里緊隨技術前進的步伐,早早的就用上了虛擬機和容器,在2017年雙十一時,在線業務服務的容器化比例已經達到100%。然而,接下來的挑戰是,基礎設施資源利用率還能做得更高嗎?

甩掉了虛擬機的硬件指令轉換和操作系統開銷,運行在容器中的程序與普通程序之間只有一層薄薄的內核Namespace隔離,完全沒有運行時性能損耗,虛擬化在這個方向上似乎已經發展到了極限。唯一的可能是,拋開通用場景,專注到測試環境管理的特定場景上,繼續尋找突破。終于,阿里在這個領域里發現了新的寶藏:服務級虛擬化。

所謂服務級虛擬化,本質上是基于消息路由的控制,實現集群中部分服務的復用。在服務級虛擬化方式下,許多外表龐大的獨立測試環境實際只需要消耗極小的額外基礎設施資源,即使給每個開發者配備一套專用的測試環境集群都不再是吹牛。

具體來說,在阿里的交付流程上,包含兩種特殊類型的測試環境:“公共基礎環境”和“特性環境”,它們形成了具有阿里特色的測試環境使用方法。公共基礎環境是一個全套的服務運行環境,它通常運行一個相對穩定的服務版本,也有些團隊將始終部署各服務的最新版本的低級別環境(稱為“日常環境”)作為公共基礎環境。

特性環境是這套方法中最有意思的地方,它是虛擬的環境。從表面上看,每個特性環境都是一套獨立完整的測試環境,由一系列服務組成集群,而實際上,除了個別當前使用者想要測試的服務,其余服務都是通過路由系統和消息中間件虛擬出來的,指向公共基礎環境的相應服務。由于在阿里通常的開發流程中,開發任務需要經過特性分支、發布分支和諸多相關環節最后發布上線,大多數環境都從發布分支部署,唯獨這種開發者自用的虛擬環境部署來自代碼特性分支的版本,故可稱為“特性環境”(阿里內部叫“項目環境”)。

舉個具體例子,某交易系統的完整部署需要由鑒權服務、交易服務、訂單服務、結算服務等十幾種小系統以及相應的數據庫、緩存池、消息中間件等組成,那么它的公共基礎環境就是這樣一套具備所有服務和周邊組件的完整環境。假設此時有兩套特性環境在運行,一套只啟動了交易服務,另一套啟動了交易服務、訂單服務和結算服務。對于第一套特性環境的使用者而言,雖然除交易服務外的所有服務實際上都由公共基礎環境代理,但在使用時就像是自己獨占一整套完整環境:可以隨意部署和更新環境中交易服務的版本,并對它進行調試,不用擔心會影響其他用戶。對于第二套特性環境的使用者,則可以對部署在該環境中的三個服務進行聯調和驗證,倘若在場景中使用到了鑒權服務,則由公共基礎環境的鑒權服務來響應。

5

咋看起來,這不就是動態修改域名對應的路由地址、或者消息主題對應的投遞地址么?實事并沒那么簡單,因為不能為了某個特性環境而修改公共基礎環境的路由,所以單靠正統路由機制只能實現單向目標控制,即特性環境里的服務主動發起調用能夠正確路由,若請求的發起方在公共基礎環境上,就無法知道該將請求發給哪個特性環境了。對于HTTP類型的請求甚至很難處理回調的情況,當處于公共基礎環境的服務進行回調時,域名解析會將目標指向公共基礎環境上的同名服務。

6

如何才能實現數據雙向的正確路由和投遞呢?不妨先回到這個問題的本質上來:請求應該進入哪個特性環境,是與請求的發起人相關的。因此實現雙向綁定的關鍵在于,識別請求發起人所處的特性環境和進行端到端的路由控制。這個過程與“灰度發布”很有幾分相似,可采用類似的思路解決。

得益于阿里在中間件領域的技術積累,和鷹眼等路由追蹤工具的廣泛使用,識別請求發起人和追溯回調鏈路都不算難事。如此一來,路由控制也就水到渠成了。當使用特性環境時,用戶需要“加入”到該環境,這個操作會將用戶標識(如IP地址或用戶ID)與指定的特性環境關聯起來,每個用戶只能同時屬于一個特性環境。當數據請求經過路由中間件(消息隊列、消息網關、HTTP網關等),一旦識別到請求的發起人當前處在特性環境中,就會嘗試把請求路由給該環境中的服務,若該環境沒有與目標一致的服務,才路由或投遞到公共基礎環境上。

特性環境并不是孤立存在的,它可以建立在容器技術之上,從而獲得更大的靈活性。正如將容器建立在虛擬機之上得到基礎設施獲取的便利性一樣,在特性環境中,通過容器快速而動態的部署服務,意味著用戶可以隨時向特性環境中增加一個需要修改或調試的服務,也可以將環境中的某個服務隨時銷毀,讓公共基礎環境的自動接替它。

還有一個問題是服務集群調試。

配合AoneFlow的特性分支工作方式,倘若將幾個服務的不同特性分支部署到同一個特性環境,就可以進行多特性的即時聯調,從而將特性環境用于集成測試。不過,即使特性環境的創建成本很低,畢竟服務是部署在測試集群上的。這意味著每次修改代碼都需要等待流水線的構建和部署,節約了空間開銷,卻沒有縮短時間開銷。

為了進一步的降低成本、提高效率,阿里團隊又搗鼓出了一種開腦洞的玩法:將本地開發機加入特性環境。在集團內部,由于開發機和測試環境都使用內網IP地址,稍加變通其實不難將特定的測試環境請求直接路由到開發機。這意味著,在特性環境的用戶即使訪問一個實際來自公共基礎環境的服務,在后續處理鏈路上的一部分服務也可以來自特性環境,甚至來自本地環境。現在,調試集群中的服務變得非常簡單,再也不用等待漫長的流水線構建,就像整個測試環境都運行在本地一樣。

DIY體驗特性環境

覺得服務級虛擬化太小眾,離普通開發者很遠?實事并非如此,我們現在就可以動手DIY個體驗版的特性環境來玩。

阿里的特性環境實現了包括HTTP調用、RPC調用、消息隊列、消息通知等各類常用服務通信方式的雙向路由服務級虛擬化。要完成這樣的功能齊全的測試環境有點費勁,從通用性角度考慮,咱不妨從最符合大眾口味的HTTP協議開始,做個支持單向路由的簡易款。

為了便于管理環境,最好得有一個能跑容器的集群,在開源社區里,功能齊全的Kubernetes是個不錯的選擇。在Kubernetes中有些與路由控制有關的概念,它們都以資源對象的形式展現給用戶。

簡單介紹一下,Namespace對象能隔離服務的路由域(與容器隔離使用的內核Namespace不是一個東西,勿混淆),Service對象用來指定服務的路由目標和名稱,Deployment對象對應真實部署的服務。類型是ClusterIP(以及NodePort和LoadBalancer類型,暫且忽略它們)的Service對象可路由相同Namespace內的一個真實服務,類型是ExternalName的Service對象則可作為外部服務在當前Namespace的路由代理。這些資源對象的管理都可以使用YAML格式的文件來描述,大致了解完這些,就可以開始動工了。

基礎設施和Kubernetes集群搭建的過程略過,下面直接進正題。先得準備路由兜底的公共基礎環境,這是一個全量測試環境,包括被測系統里的所有服務和其他基礎設施。暫不考慮對外訪問,公共基礎環境中的所有服務相應的Service對象都可以使用ClusterIP類型,假設它們對應的Namespace名稱為pub-base-env。這樣一來,Kubernetes會為此環境中的每個服務自動賦予Namespace內可用的域名“服務名.svc.cluster”和集群全局域名“服務名.pub-base-env.svc.cluster”。有了兜底的保障后,就可以開始創建特性環境了,最簡單的特性環境可以只包含一個真實服務(例如trade-service),其余服務全部用ExternalName類型的Service對象代理到公共基礎環境上。假設它使用名稱為feature-env-1的Namespace,其描述的YAML如下(省略了非關鍵字段的信息):

kind: Namespace

metadata:

name: feature-env-1


kind: Service

metadata:

name: trade-service

namespace: feature-env-1

spec:

type: ClusterIP

...


kind: Deployment

metadata:

name: trade-service

namespace: feature-env-1

spec:

...


kind: Service

metadata:

name: order-service

namespace: feature-env-1

spec:

type: ExternalName

externalName: order-service.pub-base-env.svc.cluster

...


kind: Service

...

注意其中的order-service服務,它在當前特性環境Namespace中可以使用局部域名order-service.svc.cluster訪問,請求會路由到它配置的全局域名order-service.pub-base-env.svc.cluster,即公共基礎環境的同名服務上處理。處于該Namespace中的其它服務感知不到這個差異,而是會覺得這個Namespace中部署了所有相關的服務。

若在特性的開發過程中,開發者對order-service服務也進行了修改,此時應該將修改過的服務版本添加到環境里來。只需修改order-service的Service對象屬性(使用Kubernetes的patch操作),將其改為ClusterIP類型,同時在當前Namespace中創建一個Deployment對象與之關聯即可。

由于修改Service對象只對相應Namespace(即相應的特性環境)內的服務有效,無法影響從公共基礎環境回調的請求,因此路由是單向的。在這種情況下,特性環境中必須包含待測調用鏈路的入口服務和包含回調操作的服務。例如待測的特性是由界面操作發起的,提供用戶界面的服務就是入口服務。即使該服務沒有修改,也應該在特性環境中部署它的主線版本。

通過這種機制也不難實現把集群服務局部替換成本地服務進行調試開發的功能,倘若集群和本地主機都在內網,將ExternalName類型的Service對象指向本地的IP地址和服務端口就可以了。否則需要為本地服務增加公網路由,通過動態域名解析來實現。

與此同時,云效也正在逐步完善基于Kubernetes的特性環境解決方案,屆時將會提供更加全面的路由隔離支持。值得一提的是,由于公有云的特殊性,在聯調時將本地主機加入云上集群是個必須克服的難題。為此云效實現了通過隧道網絡+kube-proxy自身路由能力,將本地局域網主機(無需公網IP地址)加入到不在同一內網Kubernetes集群進行聯調的方式。其中的技術細節也將在近期的云效公眾號向大家揭曉,敬請留意。

小結

當許多人還在等待,在虛擬機和容器之后,下一輪虛擬化技術的風口何時到來的時候,阿里已經給出了一種答案。創業者的心態讓阿里人懂得,能省必須省。其實,限制創新的往往不是技術而是想象力,服務級虛擬化的理念突破了人們對環境副本的傳統認知,以獨特的角度化解了測試環境成本與穩定性的矛盾。

作為一種頗具特色的技術載體,特性環境的價值不僅僅在于輕量的測試環境管理體驗,更在于為每位開發人員帶來流暢的工作方式,實則是“簡約而不簡單”。

實踐出真知,阿里巴巴云效平臺致力于解決大型項目協作、敏捷高速迭代、海量代碼托管、高效測試工具、分布式秒級構建、大規模集群部署發布等世界級業務和技術難題,為阿里巴巴集團內部、生態伙伴以及云上開發者服務。誠摯歡迎業界同行與我們探討交流。

相關閱讀:

在阿里,我們如何管理代碼分支

當kubernetes應用遇到阿里分批發布模式

點此了解云效測試平臺

作者: 云效鼓勵師
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。

轉載于:https://my.oschina.net/yunqi/blog/3007532

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280425.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280425.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280425.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據庫_7_SQL基本操作——表操作

SQL基本操作——表操作 建表的過程就是聲明列的過程。 表與字段是密不可分的。 一、新增數據表 create table [if not exists] 表名( 字段名字 數據類型, 字段名字 數據類型 -- 最后一行不需要逗號 )[表選項];if not exists:如果表名不存在,那么就創建,…

EXT.NET 更改lable和Text的顏色

2019獨角獸企業重金招聘Python工程師標準>>> &#xfeff;&#xfeff; <ext:TextField ID"TextField1" " runat"server" FieldLabel"編號" LabelWidth"60" LabelAlign"Left" LabelStyle"color:red…

rest_framework08:分頁器/根據ip進行頻率限制

分頁器 # 查詢所有&#xff0c;才需要分頁 from rest_framework.generics import ListAPIView# 內置三種分頁方式 from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPaginationPageNumberPaginationclass MyPageNumberPagination(Pag…

NYOJ746 整數劃分

該題是一道區間DP的題目&#xff0c;做了幾道區間DP&#xff0c;說起來高大上&#xff0c;也就是DP在區間內的形式而已&#xff0c;核心思想還是要想到轉移->規劃。 題意是在n位數中間加m個稱號&#xff0c;使得最終乘積最大。 狀態轉移方程如下&#xff1a; dp[ i ][ j ]ma…

Spring MVC實現文件下載

方法一&#xff1a; RequestMapping("/testHttpMessageDown")public ResponseEntity<byte[]> download(HttpServletRequest request) throws IOException {File file new File(request.getSession().getServletContext().getClassLoader().getResource("…

[MobX State Tree數據組件化開發][3]:選擇正確的types.xxx

?系列文章目錄? 定義Model時&#xff0c;需要正確地定義props中各字段的類型。本文將對MST提供的各種類型以及類型的工廠方法進行簡單的介紹&#xff0c;方便同學們在定義props時挑選正確的類型。 前提 定義props之前&#xff0c;有一個前提是&#xff0c;你已經明確地知道這…

ubuntu系統備份和還原_如何使用Aptik在Ubuntu中備份和還原您的應用程序和PPA

ubuntu系統備份和還原If you need to reinstall Ubuntu or if you just want to install a new version from scratch, wouldn’t it be useful to have an easy way to reinstall all your apps and settings? You can easily accomplish this using a free tool called Apti…

rest_framework09:自動生成接口文檔(簡略)

coreapi 參考 python/Django-rest-framework框架/8-drf-自動生成接口文檔 | Justin-劉清政的博客 Swagger 很多語言都支持&#xff0c;看起來用的人多。 參考fastapi的界面

AppDomainManager后門的實現思路

本文講的是AppDomainManager后門的實現思路&#xff0c;0x00 前言從Casey SmithsubTee學到的一個技巧&#xff1a;針對.Net程序&#xff0c;通過修改AppDomainManager能夠劫持.Net程序的啟動過程。 如果劫持了系統常見.Net程序如powershell.exe的啟動過程&#xff0c;向其添加…

所有內耗,都有解藥。

你是否常常會有這種感覺&#xff1a;剛開始接手一件事情&#xff0c;腦海中已經幻想出無數個會發生的問題&#xff0c;心里也已篤定自己做不好&#xff1b;即使別人不經意的一句話&#xff0c;也會浮想一番&#xff0c;最終陷入自我懷疑&#xff1b;隨便看到點什么&#xff0c;…

ABAP 通過sumbit調用另外一個程序使用job形式執行-簡單例子

涉及到兩個程序&#xff1a; ZTEST_ZUMA02 (主程序)ZTEST_ZUMA(被調用的程序&#xff0c;需要以后臺job執行)"ztest_zuma 的代碼DATA col TYPE i VALUE 0.DO 8 TIMES.MESSAGE JOB HERE TYPE S.ENDDO.程序ZTEST_ZUMA是在程序ZTEST_ZUMA02中以job的形式調用的&#xff0c;先…

那些影響深遠的彎路

靜兒最近反思很多事情&#xff0c;不僅是當時做錯了。錯誤定式形成的思維習慣對自己的影響比事情本身要大的多。經常看到周圍的同事&#xff0c;非常的羨慕。他們都很聰明、有自己的方法。就算有些同事工作經驗相對少一些&#xff0c;但是就像在廢墟上創建一個輝煌的城市要比在…

如何使用APTonCD備份和還原已安裝的Ubuntu軟件包

APTonCD is an easy way to back up your installed packages to a disc or ISO image. You can quickly restore the packages on another Ubuntu system without downloading anything. APTonCD是將安裝的軟件包備份到光盤或ISO映像的簡便方法。 您可以在不下載任何東西的情況…

rest_framework10:base64補充/修改頭像

base64補充 # base64 變長&#xff0c;可反解 # md5 固定長度&#xff0c;不可反解# base64 編碼和解碼 import base64 import json dic{name:test,age:18} dic_strjson.dumps(dic)retbase64.b64encode(dic_str.encode(utf-8)) print(ret)# 解碼 ret2base64.b64decode(ret) pri…

next_permutation(全排列算法)

next_permutation(全排列算法) STL提供了兩個用來計算排列組合關系的算法&#xff0c;分別是next_permutation和prev_permutation。 首先解釋下全排列&#xff0c;顧名思義&#xff0c;即一組數的全部排列的情況。 next_permutation 即列出一組數的全部排列情況&#xff0c;不過…

C#自定義字符串壓縮和解壓縮源碼庫

如下的內容是關于C#自定義字符串壓縮和解壓縮庫的內容。class ZipLib{public static string Zip(string value){byte[] byteArray new byte[value.Length];int indexBA 0;foreach (char item in value.ToCharArray()){byteArray[indexBA] (byte)item;}System.IO.MemoryStrea…

使用 Visual Studio 2022 調試Dapr 應用程序

使用Dapr 編寫的是一個多進程的程序, 兩個進程之間依賴于啟動順序來組成父子進程&#xff0c;使用Visual Studio 調試起來可能會比較困難&#xff0c;因為 Visual Studio 默認只會把你當前設置的啟動項目的啟動調試。好在有Visual Studio 擴展&#xff08;Microsoft Child Proc…

卸載 cube ui_如何還原Windows 8附帶的已卸載現代UI應用程序

卸載 cube uiWindows 8 ships with built-in apps available on the Modern UI screen (formerly the Metro or Start screen), such as Mail, Calendar, Photos, Music, Maps, and Weather. Installing additional Modern UI apps is easy using the Windows Store, and unins…

rest_framework11:jwt簡單例子/自定制基于jwt認證類

jwt簡單例子 一、登陸設置 1.不需要寫login的視圖類&#xff0c;使用jwt內置的。 2.需要前置條件&#xff0c;已有繼承AbstractUser models,并且有數據&#xff0c;用于校驗&#xff0c;返回token。 urls.py from rest_framework_jwt.views import obtain_jwt_tokenurlpat…

Java各種數據類型,自己學習寫的筆記!!!

java編程規范&#xff1a; 1.良好的標識符的命名保留字不能作為標識符命名&#xff1a; class、public、static..., goto,const區分大小寫&#xff1a;helloWorld、HelloWorld 2.良好的注釋習慣 3.良好的縮進&#xff1a;沒遇到一個代碼塊縮進一次&#xff08;一個tab鍵&…