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

依賴管理工具漫談--從Maven,Gradle到Go

開發(fā) 開發(fā)工具
本文從Maven談起,分析了Maven的主要思想以及Gradle對(duì)Maven的改進(jìn),最后談了下Go語(yǔ)言面臨的依賴管理問(wèn)題。

[[184219]]

本文從Maven談起,分析了Maven的主要思想以及Gradle對(duì)Maven的改進(jìn),***談了下Go語(yǔ)言面臨的依賴管理問(wèn)題。

為什么要有依賴管理工具?

談依賴管理之前我們先談?wù)劄槭裁匆幸蕾嚬芾砉ぞ哌@東西。

我們學(xué)了一種編程語(yǔ)言,然后寫了個(gè)『Hello World』,然后宣稱自己學(xué)了一門語(yǔ)言,這時(shí)候確實(shí)不需要關(guān)心依賴問(wèn)題。

然而,當(dāng)你要寫一個(gè)稍微復(fù)雜點(diǎn)的應(yīng)用,那怕就是留言板這樣的,需要讀寫數(shù)據(jù)庫(kù),就需要依賴數(shù)據(jù)庫(kù)驅(qū)動(dòng),就會(huì)遇到依賴管理的問(wèn)題了。

再進(jìn)一步,你寫了一個(gè)庫(kù),想共享給別人使用,更需要了解依賴管理的問(wèn)題。

當(dāng)然,如果項(xiàng)目足夠簡(jiǎn)單,你可以直接將依賴方的源碼放置在自己的項(xiàng)目中,或者將依賴庫(kù)的二進(jìn)制文件(比如jar,dll)放置在項(xiàng)目的lib里。要提供給別人呢?把二進(jìn)制包提供下載或者給別人傳過(guò)去。依賴管理工具出現(xiàn)之前大多數(shù)都是這樣搞的。

但如果再?gòu)?fù)雜些,依賴庫(kù)本身也有依賴怎么弄呢?將依賴壓縮打包,然后放個(gè)readme幫助文件說(shuō)明下,貌似也可以工作。

那如果你的項(xiàng)目依賴了好幾個(gè),乃至幾十個(gè)庫(kù),而各庫(kù)又有依賴,依賴也有自己的依賴,怎么辦?怎么檢測(cè)庫(kù)的依賴是否有版本沖突?以后升級(jí)的時(shí)候怎么辦?怎么判斷l(xiāng)ib目錄下的某個(gè)文件是否被依賴了?

到這一步必須要承認(rèn)需要有個(gè)依賴管理工具了,無(wú)論你使用任何語(yǔ)言。我們大約也清楚了依賴管理要做些什么。假設(shè)還沒(méi)有依賴管理工具,我們自己要設(shè)計(jì)一個(gè),如何入手?

要有一種依賴庫(kù)的命名規(guī)則,或者叫坐標(biāo)(Coordinates)的定義規(guī)則,可以通過(guò)坐標(biāo)準(zhǔn)確找到依賴的庫(kù)。

要有對(duì)應(yīng)的配置文件規(guī)則,來(lái)描述和定義依賴。

要有中心倉(cāng)庫(kù)保存這些依賴庫(kù),以及依賴庫(kù)的元數(shù)據(jù)(metadata),供使用方拉取。

還需要一個(gè)本地工具去解析這個(gè)配置文件,實(shí)現(xiàn)依賴的拉取。

以上其實(shí)就是各依賴管理工具的核心要素。

聊聊Maven

Maven誕生于2004年(來(lái)源維基),查詢了下,應(yīng)該是各語(yǔ)言的依賴管理工具中早的。Ruby的gem也是2004年出現(xiàn)的,但gem離完備的依賴管理工具還差些,直到Ruby的bundler出現(xiàn)。Python的pip出現(xiàn)的更晚。

Maven的習(xí)慣是通過(guò) groupID(一般是組織的域名倒寫,遵循Java package的命名習(xí)慣)+ artifactId(庫(kù)本身的名稱) + version(版本)來(lái)定義坐標(biāo),通過(guò)xml來(lái)做配置文件,提供了中心倉(cāng)庫(kù)(repo.maven.org)以及本地工具(mvn)。

  1. 依賴定義: 
  2.  
  3. <dependency> 
  4.     <groupId>com.google.guava</groupId> 
  5.     <artifactId>guava</artifactId> 
  6.     <version>18.0</version> 
  7. </dependency> 
  8.  
  9. repo定義: 
  10.  
  11. <repository> 
  12.     <id>repo.default</id> 
  13.     <name>Internal Release Repository</name
  14.     <url>http://repo.xxxxxx.com/nexus/content/repositories/releases</url> 
  15.     <releases> 
  16.     <enabled>true</enabled> 
  17.     <updatePolicy>interval:60</updatePolicy> 
  18.     <checksumPolicy>warn</checksumPolicy> 
  19.     </releases> 
  20.     <snapshots> 
  21.     <enabled>false</enabled> 
  22.     <updatePolicy>always</updatePolicy> 
  23.     <checksumPolicy>warn</checksumPolicy> 
  24.     </snapshots> 
  25. </repository> 

同時(shí)為了避免依賴沖突的問(wèn)題,Maven的依賴配置提供了exclude配置機(jī)制,用于阻斷部分庫(kù)的傳遞依賴。

Ruby的gem,Node的npm,Python的pip,iOS的CocoaPods都類似,只是配置文件語(yǔ)法和坐標(biāo)命名規(guī)則有些差異。

至此,看起來(lái)Maven很簡(jiǎn)單啊,為啥許多人會(huì)覺得Maven復(fù)雜呢?

主要在于以下兩點(diǎn):

Java是編譯型語(yǔ)言,發(fā)布的庫(kù)是二進(jìn)制版本的jar包,發(fā)布前需要有編譯的流程,而依賴和編譯是緊密相關(guān)的。不像Ruby,Node這樣的腳本語(yǔ)言,將源碼和配置文件扔到倉(cāng)庫(kù)就可以。

Maven并沒(méi)有將自己?jiǎn)渭兊亩x為依賴管理工具,而是項(xiàng)目管理工具,它將項(xiàng)目的整個(gè)生命周期都囊括進(jìn)去了。

第二點(diǎn)也是Ant+ivy和Maven思路上的區(qū)別,ivy認(rèn)為已經(jīng)有Ant這樣的編譯打包工具了,只需在上面做個(gè)插件解決依賴問(wèn)題即可,而Maven認(rèn)為Ant本身也有改進(jìn)的地方,所以一并改造了。

Maven的改進(jìn)的核心思路是

  • Convention Over Configuration

即『約定大于配置』。既然大多數(shù)人習(xí)慣都把源碼目錄命名為src,那就約定好都用這個(gè)目錄,不用專門去配置。同樣,clean,compile,package等也約定好,不需要專門定義Ant task。這樣既簡(jiǎn)化了配置文件,同時(shí)也降低了學(xué)習(xí)成本。一個(gè)Ant定義的項(xiàng)目,你需要閱讀幫助文件或者查看build.xml文件才能了解如何編譯打包,而Maven定義的項(xiàng)目直接運(yùn)行『mvn package』即可。

Java語(yǔ)言發(fā)明的比較早,初期這種思想還不普及,所以Java本身沒(méi)有對(duì)項(xiàng)目的規(guī)范,而新的語(yǔ)言基本都吸收了這個(gè)思想,對(duì)項(xiàng)目都做了約定和規(guī)范。比如Go語(yǔ)言,如果用C/C++可能需要定義復(fù)雜的Makefile來(lái)定義編譯的規(guī)則,以及如何運(yùn)行測(cè)試用例,而在Go中,這些都是約定好的。

Maven定義為項(xiàng)目管理工具,包含了項(xiàng)目從源碼到發(fā)布的整個(gè)生命周期:

  1. validate → generate-sources → process-sources 
  2. → generate-resources → process-resources → compile  
  3. → process-classes → generate-test-sources 
  4. → process-test-sources → generate-test-resources 
  5. → process-test-resources → test-compile 
  6. → test → prepare-package → package  
  7. → pre-integration-test → integration-test  
  8. → post-integration-test → verify → install → deploy  

既然包含了這么多功能和階段,所以Maven引入了插件機(jī)制,Maven的本身的編輯打包等功能都是用插件來(lái)實(shí)現(xiàn)的,也允許用戶自己定義插件。

同時(shí)涉及構(gòu)建生命周期的不同的階段,依賴也需要確定是編譯依賴?測(cè)試依賴?運(yùn)行時(shí)依賴?于是依賴多了scope的定義。

如果僅僅是這樣把Maven理解成標(biāo)準(zhǔn)化的Ant+ivy+可擴(kuò)展的插件框架即可?但現(xiàn)實(shí)世界的項(xiàng)目往往更復(fù)雜。

我們有了function用于組合代碼塊邏輯,有了object用于組合一組方法,有了package,namespace用于組合一組相關(guān)對(duì)象,但其實(shí)還需要有更高一個(gè)層次的組合定義 —– module,或者叫子項(xiàng)目。同一個(gè)項(xiàng)目下不同的源碼目錄可能需要編譯打包成不同的二進(jìn)制文件,這些module共同構(gòu)成了一個(gè)整體的項(xiàng)目。這個(gè)其實(shí)和源碼管理習(xí)慣有關(guān)系,是每個(gè)獨(dú)立的module作為單獨(dú)的源碼倉(cāng)庫(kù)呢還是將相關(guān)的module全部放在一起?從降低溝通成本的角度考慮,還是應(yīng)該通過(guò)一個(gè)大的倉(cāng)庫(kù)組織。

于是Maven引入了module的概念,同一個(gè)項(xiàng)目下可以有多個(gè)module,每個(gè)module有單獨(dú)的pom文件來(lái)定義,但為了避免重復(fù),Maven的pom文件支持parent機(jī)制,子項(xiàng)目的pom文件繼承parent pom的基本配置。可以說(shuō),module的機(jī)制將Maven的復(fù)雜度又提升了一個(gè)層次,很多人遇到Maven的坑多栽到這里了。

這里介紹一個(gè)Maven多項(xiàng)目版本管理的***實(shí)踐:

  1. 父項(xiàng)目中配置版本號(hào),子項(xiàng)目中不要顯示配置版本號(hào),直接繼承父項(xiàng)目的版本號(hào)。
  2. 子項(xiàng)目之間的依賴通過(guò)${project.version}引用,不要明確配置版本號(hào)。
  3. 發(fā)布新版的時(shí)候,同時(shí)發(fā)布所有子項(xiàng)目,即便是該子項(xiàng)目未做變更。
  4. ***通過(guò)Maven的release插件發(fā)布,避免手動(dòng)修改版本號(hào)導(dǎo)致的不一致問(wèn)題。

即便是這樣,Maven的多項(xiàng)目版本管理經(jīng)常也會(huì)遇到問(wèn)題。主要是因?yàn)镸aven的子項(xiàng)目之間的依賴也沿用的是第三方庫(kù)依賴的配置方式,需要指定子項(xiàng)目的版本號(hào)。另外子項(xiàng)目的parent需要顯式配置,也需要明確指定parent的版本號(hào)。一旦這些版本號(hào)出現(xiàn)錯(cuò)誤,***就會(huì)導(dǎo)致各種詭異的問(wèn)題。

Maven的release插件使用也比較復(fù)雜,該插件其實(shí)做幾個(gè)事情:

先構(gòu)建一遍項(xiàng)目,確認(rèn)項(xiàng)目可以正常構(gòu)建。

修改pom文件的版本號(hào)到正式版,然后提交到源碼倉(cāng)庫(kù)并打tag。

將該tag的源碼檢出,再構(gòu)建一次,這次構(gòu)建的jar包的版本是正式版的,將jar包上傳到Maven倉(cāng)庫(kù)。

遞增版本號(hào),修改pom文件的版本號(hào)到SNAPSHOT,再次提交到源碼倉(cāng)庫(kù)。

這個(gè)過(guò)程中,由于要構(gòu)建兩次,提交兩次源碼倉(cāng)庫(kù),上傳一次jar包,任何一步出錯(cuò)都會(huì)導(dǎo)致release失敗,所以使用比較復(fù)雜。

到此,Maven的核心概念都分析完了,其他的都是插件機(jī)制上的一些擴(kuò)展。大家也應(yīng)該明白了Maven之所以***變這么復(fù)雜的原因。

但無(wú)論如何,Maven基本上是項(xiàng)目管理工具的標(biāo)桿了,有的語(yǔ)言直接通過(guò)擴(kuò)展插件來(lái)用Maven管理,比如C++,C#(NMaven),或者做了移植Byldan(C#),不過(guò)貌似都是不太成功,估計(jì)主要原因應(yīng)該是Maven是用Java寫的,有社區(qū)隔膜。

Gradle對(duì)Maven的改進(jìn)

聊了Maven的思路和優(yōu)勢(shì),那Maven的缺點(diǎn)呢?這個(gè)我們和Gradle一起聊聊。Gradle就是在Maven的基礎(chǔ)上進(jìn)行的改進(jìn)。優(yōu)勢(shì)主要體現(xiàn)在以下方面:

1.配置語(yǔ)言

Maven使用的是XML,受限于XML的表達(dá)能力以及XML本身的冗余,會(huì)使pom.xml文件顯得冗長(zhǎng)而笨重。而Gradle是基于Groovy定義的一種DSL語(yǔ)言,簡(jiǎn)潔并且表達(dá)能力強(qiáng)大。在Maven中,任何擴(kuò)展都需要通過(guò)Maven插件實(shí)現(xiàn),但Gradle的配置文件本身就是一種語(yǔ)言,可以直接依賴任意Java庫(kù),可以直接在build.gradle文件中像Ant一樣定義task,比Ant的表達(dá)能力更強(qiáng)(Ant本身也是XML定義的)。

Gradle的配置文件中可以直接獲取到Project對(duì)象以及環(huán)境變量,可以通過(guò)程序?qū)uild過(guò)程進(jìn)行更細(xì)致的自定義控制,這個(gè)功能對(duì)于復(fù)雜的項(xiàng)目來(lái)說(shuō)非常有用。

2.項(xiàng)目自包含(Self Provisioning Build Environment)

用戶下載了一個(gè)Maven定義的項(xiàng)目,如果沒(méi)用過(guò)Maven,還需要下載Maven工具包,了解Maven。但Gradle可以給項(xiàng)目生成一個(gè)gradlew腳本,用戶直接運(yùn)行g(shù)radlew腳本即可,該腳本會(huì)自動(dòng)檢測(cè)本地是否已經(jīng)有Gradle,沒(méi)有則從網(wǎng)絡(luò)下載,對(duì)用戶透明(當(dāng)然,國(guó)內(nèi)網(wǎng)絡(luò)下***還是自己先下載好)。

對(duì)倉(cāng)庫(kù)的配置,Maven提供了一個(gè)本地的settings.xml配置文件,用于定義私有倉(cāng)庫(kù)以及倉(cāng)庫(kù)密碼這樣的敏感的不應(yīng)該放源碼倉(cāng)庫(kù)里的文件。但這樣帶來(lái)的不便就是這些信息項(xiàng)目中沒(méi)有自包含,所以Gradle干掉了這種本地配置的機(jī)制,所有的定義都在項(xiàng)目里。私有倉(cāng)庫(kù)密碼這樣的可以放在項(xiàng)目下的gradle.properties文件里不提交上去,通過(guò)其他方式分享給內(nèi)部成員。這點(diǎn)可能各有優(yōu)劣。

3.任務(wù)依賴以及執(zhí)行機(jī)制

Maven的構(gòu)建生命周期的每一步都是預(yù)定義好的(參看前文),插件任務(wù)只能在預(yù)留的生命周期中的某個(gè)階段切入,雖然Maven的生命周期階段考慮很充分,但有時(shí)候也不能滿足需求。Maven會(huì)嚴(yán)格按照生命周期的階段從開始線性執(zhí)行任務(wù),而Gradle則使用了Directed Acyclic Graph來(lái)檢測(cè)任務(wù)的依賴關(guān)系,決定哪些任務(wù)可以并行執(zhí)行,這樣使任務(wù)的定義以及執(zhí)行都更靈活。

4.依賴管理更為靈活

Maven對(duì)依賴管理比較嚴(yán)格,依賴必須是源碼倉(cāng)庫(kù)的坐標(biāo)。雖然也支持system scope的本地路徑配置,但還是有許多不方便之處(system scope的依賴,打包的時(shí)候不包含進(jìn)來(lái))。如果世界上所有的庫(kù)都通過(guò)Maven發(fā)布,當(dāng)然沒(méi)有問(wèn)題,但現(xiàn)實(shí)往往不是這樣的。這里要吐槽一下國(guó)內(nèi)的各大廠發(fā)布的sdk之類的庫(kù),幾乎都不提供倉(cāng)庫(kù)地址,就給個(gè)壓縮包放一堆jar包進(jìn)來(lái),讓用戶自己搞定依賴管理問(wèn)題。而Gradle在這方面比較靈活,比如支持:

  1. compile fileTree(dir: 'libs', include: '*.jar'

這樣的配置規(guī)則。

另外由于Gradle本身是一種語(yǔ)言,可以用編程的方式來(lái)管理依賴。比如大多數(shù)子項(xiàng)目都依賴某個(gè)庫(kù),除了個(gè)別幾個(gè),就可以這樣寫:

  1. configure(subprojects.findAll {it.name != 'xxx1’ && it.name != ‘xxx2’}) {   
  2.      dependencies {   
  3.          compile("com.google.guava:guava:18.0”)   
  4.      }   
  5.  } 

5.子項(xiàng)目以及動(dòng)態(tài)依賴機(jī)制

動(dòng)態(tài)依賴主要是用來(lái)解決幾個(gè)互相依賴的庫(kù)都在快速開發(fā)期間的依賴問(wèn)題,不能每次地層庫(kù)修改發(fā)布新版本,上層庫(kù)都要修改依賴配置文件,所以需要?jiǎng)討B(tài)設(shè)置依賴***版本。

Maven的解決方案是SNAPSHOT機(jī)制,子項(xiàng)目之間也是通過(guò)這個(gè)機(jī)制來(lái)實(shí)現(xiàn)依賴的。遇到的問(wèn)題我們前面也分析了。

Gradle的雖然也兼容Maven倉(cāng)庫(kù)的SNAPSHOT機(jī)制,但它自己的版本管理機(jī)制上,并沒(méi)有引入SNAPSHOT機(jī)制。它的依賴支持4.x,2.+這樣的配置規(guī)則,實(shí)現(xiàn)動(dòng)態(tài)依賴。而子項(xiàng)目之間的依賴采用特殊的依賴配置,和第三方庫(kù)的配置規(guī)則有區(qū)別。它直接使用:

  1. compile project(“:subpoject-name”); 

這樣的配置,無(wú)需配置版本號(hào),明確指定是子項(xiàng)目,避免Maven的子項(xiàng)目依賴帶來(lái)的版本號(hào)問(wèn)題。子項(xiàng)目的配置中也不需要顯示配置父項(xiàng)目,只需要父項(xiàng)目單向中配置子項(xiàng)目列表即可。

同時(shí)Gradle的release機(jī)制也更為靈活,支持release到各種倉(cāng)庫(kù)(包括Maven倉(cāng)庫(kù)),但不控制release過(guò)程中的版本號(hào)生成,修改源碼倉(cāng)庫(kù)等步驟,留給用戶自己通過(guò)手動(dòng)或者CI工具,或者腳本去解決。

關(guān)于Gradle相對(duì)Maven的改進(jìn)這里主要列舉這幾點(diǎn),其他的可以參看Gradle官方的比較表格:maven_vs_gradle,這里不再詳述。

Go語(yǔ)言的多項(xiàng)目以及依賴管理問(wèn)題

***再談?wù)凣o語(yǔ)言的多項(xiàng)目以及依賴管理問(wèn)題。Go官方對(duì)這兩方面并未做約定或者提供工具,于是只能各自想辦法解決。多項(xiàng)目問(wèn)題一般就是回歸到了Makefile+腳本的解決方案,比如kubernetes。依賴管理,開源社區(qū)多用Godeps,kubernetes用的也是這個(gè)。Godeps通過(guò)源碼倉(cāng)庫(kù)路徑以及源碼tag來(lái)確定庫(kù)的坐標(biāo),只管理依賴,有點(diǎn)像ivy,不關(guān)心構(gòu)建過(guò)程。Godepes會(huì)將依賴庫(kù)的依賴也添加到當(dāng)前項(xiàng)目的依賴配置中,不是動(dòng)態(tài)的依賴傳遞機(jī)制。沒(méi)有scope,不區(qū)分是否是單元測(cè)試的依賴。一個(gè)倉(cāng)庫(kù)只支持一個(gè)配置,沒(méi)有子項(xiàng)目概念,項(xiàng)目大了管理就比較復(fù)雜。另外它對(duì)傳遞依賴以及版本沖突的問(wèn)題當(dāng)前還是沒(méi)有解決太好(有一些相關(guān)Issue)。

一個(gè)語(yǔ)言的多項(xiàng)目以及依賴管理方案對(duì)這個(gè)語(yǔ)言的生態(tài)發(fā)展有很大的影響,Java發(fā)展到現(xiàn)在,Maven以及Gradle功不可沒(méi),所以感覺Go官方應(yīng)該對(duì)這兩方面有所作為。Go語(yǔ)言遲遲沒(méi)出依賴管理工具個(gè)人覺得有幾方面考慮:

  1. Go尚未確定動(dòng)態(tài)庫(kù)的機(jī)制。編譯型語(yǔ)言依賴***也是二進(jìn)制的,而不是源碼。一方面可以加快編譯速度,另外一方面也可以實(shí)現(xiàn)源碼保護(hù),方便分發(fā)以及代理緩存,讓語(yǔ)言的適用范圍更廣。許多商業(yè)上的庫(kù)是不方便提供源碼的。所以依賴管理工具的實(shí)現(xiàn)需要?jiǎng)討B(tài)庫(kù)的機(jī)制。而動(dòng)態(tài)庫(kù)尚未確定的原因我覺得是Go語(yǔ)言不想過(guò)早的引入二進(jìn)制動(dòng)態(tài)庫(kù)的格式兼容問(wèn)題,初期全部用源碼是最省事的。
  2. 先讓社區(qū)試試水,看看效果和反饋。

任何一個(gè)語(yǔ)言,發(fā)展到一定階段都避不開依賴管理問(wèn)題。前一段時(shí)間看到一篇寫Go語(yǔ)言的文章,嘲諷Java的Maven構(gòu)建個(gè)項(xiàng)目恨不能把半個(gè)互聯(lián)網(wǎng)下載下來(lái),我當(dāng)時(shí)腦海中就浮現(xiàn)出長(zhǎng)者的那句經(jīng)典語(yǔ)錄『圖樣圖森破』。Go當(dāng)前沒(méi)遇到這些問(wèn)題的原因只是Go還比較年輕,庫(kù)還不夠豐富,以及Go的很多項(xiàng)目還不夠復(fù)雜。而像kubernetes這樣的項(xiàng)目,當(dāng)前依賴已經(jīng)有226個(gè)了,構(gòu)建一下,也快要下載半個(gè)Github了。所以個(gè)人覺得Go社區(qū)當(dāng)前還是非常需要一個(gè)類似于Gradle的工具,來(lái)解決依賴管理,構(gòu)建,多項(xiàng)目管理等問(wèn)題。

【本文為51CTO專欄作者“王淵命”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過(guò)作者微信公眾號(hào)jolestar-blog獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專欄
相關(guān)推薦

2016-02-24 10:45:00

2023-09-04 13:26:27

PHP開發(fā)工具

2011-08-12 10:38:09

MongoDB

2009-04-24 21:13:45

服務(wù)器虛擬化Vmware

2012-12-06 11:31:40

虛擬化

2020-09-30 14:05:22

網(wǎng)絡(luò)管理

2020-10-30 11:18:47

網(wǎng)絡(luò)技術(shù)工具

2012-04-09 09:43:49

云計(jì)算云管理

2013-07-17 09:54:17

2013-07-15 15:00:26

項(xiàng)目管理工具

2014-03-28 11:15:42

phpmyadminMySQL管理

2012-03-01 10:04:02

虛擬化云計(jì)算混合云

2021-03-04 12:55:01

systemd進(jìn)程管理工具Linux

2022-08-03 08:02:46

PDM工具Python

2022-05-06 12:04:24

Ansible管理工具

2023-03-07 14:21:57

2022-06-16 11:06:07

開源Grafanaon-call

2010-05-25 18:36:54

MySQL管理工具

2009-05-21 10:38:51

MySQLphpMyAdminSun

2010-04-30 14:53:31

點(diǎn)贊
收藏

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