常見性能優(yōu)化策略的總結

代碼

之所以把代碼放到第一位,是因為這一點最容易引起技術人員的忽視威鹿。很多技術人員拿到一個性能優(yōu)化的需求以后剃斧,言必稱緩存、異步忽你、JVM等幼东。實際上,第一步就應該是分析相關的代碼科雳,找出相應的瓶頸根蟹,再來考慮具體的優(yōu)化策略。有一些性能問題糟秘,完全是由于代碼寫的不合理简逮,通過直接修改一下代碼就能解決問題的,比如for循環(huán)次數(shù)過多尿赚、作了很多無謂的條件判斷散庶、相同邏輯重復多次等。

數(shù)據(jù)庫

數(shù)據(jù)庫的調優(yōu)凌净,總的來說分為以下三部分:

SQL調優(yōu)

這是最常用悲龟、每一個技術人員都應該掌握基本的SQL調優(yōu)手段(包括方法、工具泻蚊、輔助系統(tǒng)等)躲舌。這里以MySQL為例,最常見的方式是性雄,由自帶的慢查詢?nèi)罩净蛘唛_源的慢查詢系統(tǒng)定位到具體的出問題的SQL没卸,然后使用explain羹奉、profile等工具來逐步調優(yōu),最后經(jīng)過測試達到效果后上線约计。這方面的細節(jié)诀拭,可以參考MySQL索引原理及慢查詢優(yōu)化

架構層面的調優(yōu)

這一類調優(yōu)包括讀寫分離煤蚌、多從庫負載均衡耕挨、水平和垂直分庫分表等方面,一般需要的改動較大尉桩,但是頻率沒有SQL調優(yōu)高筒占,而且一般需要DBA來配合參與。那么什么時候需要做這些事情蜘犁?我們可以通過內(nèi)部監(jiān)控報警系統(tǒng)(比如Zabbix)翰苫,定期跟蹤一些指標數(shù)據(jù)是否達到瓶頸,一旦達到瓶頸或者警戒值这橙,就需要考慮這些事情奏窑。通常,DBA也會定期監(jiān)控這些指標值屈扎。

連接池調優(yōu)

我們的應用為了實現(xiàn)數(shù)據(jù)庫連接的高效獲取埃唯、對數(shù)據(jù)庫連接的限流等目的,通常會采用連接池類的方案鹰晨,即每一個應用節(jié)點都管理了一個到各個數(shù)據(jù)庫的連接池墨叛。隨著業(yè)務訪問量或者數(shù)據(jù)量的增長,原有的連接池參數(shù)可能不能很好地滿足需求模蜡,這個時候就需要結合當前使用連接池的原理巍实、具體的連接池監(jiān)控數(shù)據(jù)和當前的業(yè)務量作一個綜合的判斷,通過反復的幾次調試得到最終的調優(yōu)參數(shù)哩牍。

緩存

分類

本地緩存(HashMap/ConcurrentHashMap、Ehcache令漂、Guava Cache等)膝昆,緩存服務(Redis/Tair/Memcache等)。

使用場景

什么情況適合用緩存叠必?考慮以下兩種場景:

1.短時間內(nèi)相同數(shù)據(jù)重復查詢多次且數(shù)據(jù)更新不頻繁荚孵,這個時候可以選擇先從緩存查詢,查詢不到再從數(shù)據(jù)庫加載并回設到緩存的方式纬朝。此種場景較適合用單機緩存收叶。

2.高并發(fā)查詢熱點數(shù)據(jù),后端數(shù)據(jù)庫不堪重負,可以用緩存來扛吟温。

選型考慮

1.如果數(shù)據(jù)量小宝泵,并且不會頻繁地增長又清空(這會導致頻繁地垃圾回收)井佑,那么可以選擇本地緩存洒疚。具體的話朱巨,如果需要一些策略的支持(比如緩存滿的逐出策略)呀潭,可以考慮Ehcache械蹋;如不需要俏竞,可以考慮HashMap绸硕;如需要考慮多線程并發(fā)的場景,可以考慮ConcurentHashMap魂毁。

2.其他情況玻佩,可以考慮緩存服務。目前從資源的投入度席楚、可運維性咬崔、是否能動態(tài)擴容以及配套設施來考慮,我們優(yōu)先考慮Tair酣胀。除非目前Tair還不能支持的場合(比如分布式鎖刁赦、Hash類型的value),我們考慮用Redis闻镶。

設計關鍵點

什么時候更新緩存甚脉?如何保障更新的可靠性和實時性?

更新緩存的策略铆农,需要具體問題具體分析牺氨。這里以門店POI的緩存數(shù)據(jù)為例,來說明一下緩存服務型的緩存更新策略是怎樣的墩剖?目前約10萬個POI數(shù)據(jù)采用了Tair作為緩存服務猴凹,具體更新的策略有兩個:

1.接收門店變更的消息,準實時更新岭皂。

2.給每一個POI緩存數(shù)據(jù)設置5分鐘的過期時間郊霎,過期后從DB加載再回設到DB。這個策略是對第一個策略的有力補充爷绘,解決了手動變更DB不發(fā)消息书劝、接消息更新程序臨時出錯等問題導致的第一個策略失效的問題。通過這種雙保險機制土至,有效地保證了POI緩存數(shù)據(jù)的可靠性和實時性购对。

緩存是否會滿,緩存滿了怎么辦陶因?

對于一個緩存服務骡苞,理論上來說,隨著緩存數(shù)據(jù)的日益增多,在容量有限的情況下解幽,緩存肯定有一天會滿的贴见。如何應對?

1.給緩存服務亚铁,選擇合適的緩存逐出算法蝇刀,比如最常見的LRU。

2.針對當前設置的容量徘溢,設置適當?shù)木渲低趟觯热?0G的緩存,當緩存數(shù)據(jù)達到8G的時候然爆,就開始發(fā)出報警站粟,提前排查問題或者擴容。

3.給一些沒有必要長期保存的key曾雕,盡量設置過期時間奴烙。

緩存是否允許丟失?丟失了怎么辦剖张?

根據(jù)業(yè)務場景判斷切诀,是否允許丟失。如果不允許搔弄,就需要帶持久化功能的緩存服務來支持幅虑,比如Redis或者Tair。更細節(jié)的話顾犹,可以根據(jù)業(yè)務對丟失時間的容忍度倒庵,還可以選擇更具體的持久化策略,比如Redis的RDB或者AOF炫刷。

緩存被“擊穿”問題

對于一些設置了過期時間的key擎宝,如果這些key可能會在某些時間點被超高并發(fā)地訪問,是一種非郴肼辏“熱點”的數(shù)據(jù)绍申。這個時候,需要考慮另外一個問題:緩存被“擊穿”的問題顾彰。

概念:緩存在某個時間點過期的時候失晴,恰好在這個時間點對這個Key有大量的并發(fā)請求過來,這些請求發(fā)現(xiàn)緩存過期一般都會從后端DB加載數(shù)據(jù)并回設到緩存拘央,這個時候大并發(fā)的請求可能會瞬間把后端DB壓垮。

如何解決:業(yè)界比較常用的做法书在,是使用mutex灰伟。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db栏账,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key帖族,當操作返回成功時,再進行l(wèi)oad db的操作并回設緩存挡爵;否則竖般,就重試整個get緩存的方法。

異步

使用場景

針對某些客戶端的請求茶鹃,在服務端可能需要針對這些請求做一些附屬的事情涣雕,這些事情其實用戶并不關心或者用戶不需要立即拿到這些事情的處理結果,這種情況就比較適合用異步的方式處理這些事情闭翩。

作用

1.縮短接口響應時間挣郭,使用戶的請求快速返回,用戶體驗更好疗韵。

2.避免線程長時間處于運行狀態(tài)兑障,這樣會引起服務線程池的可用線程長時間不夠用,進而引起線程池任務隊列長度增大蕉汪,從而阻塞更多請求任務流译,使得更多請求得不到技術處理。

3.線程長時間處于運行狀態(tài)者疤,可能還會引起系統(tǒng)Load福澡、CPU使用率、機器整體性能下降等一系列問題宛渐,甚至引發(fā)雪崩竞漾。異步的思路可以在不增加機器數(shù)和CPU數(shù)的情況下,有效解決這個問題窥翩。

常見做法

一種做法业岁,是額外開辟線程,這里可以采用額外開辟一個線程或者使用線程池的做法寇蚊,在IO線程(處理請求響應)之外的線程來處理相應的任務笔时,在IO線程中讓response先返回。

如果異步線程處理的任務設計的數(shù)據(jù)量非常巨大仗岸,那么可以引入阻塞隊列BlockingQueue作進一步的優(yōu)化允耿。具體做法是讓一批異步線程不斷地往阻塞隊列里扔數(shù)據(jù),然后額外起一個處理線程扒怖,循環(huán)批量從隊列里拿預設大小的一批數(shù)據(jù)较锡,來進行批處理(比如發(fā)一個批量的遠程服務請求),這樣進一步提高了性能盗痒。

另一種做法蚂蕴,是使用消息隊列(MQ)中間件服務低散,MQ天生就是異步的。一些額外的任務骡楼,可能不需要我這個系統(tǒng)來處理熔号,但是需要其他系統(tǒng)來處理。這個時候可以先把它封裝成一個消息鸟整,扔到消息隊列里面引镊,通過消息中間件的可靠性保證把消息投遞到關心它的系統(tǒng),然后讓這個系統(tǒng)來做相應的處理篮条。

比如C端在完成一個提單動作以后弟头,可能需要其它端做一系列的事情,但是這些事情的結果不會立刻對C端用戶產(chǎn)生影響兑燥,那么就可以先把C端下單的請求響應先返回給用戶亮瓷,返回之前往MQ中發(fā)一個消息即可。而且這些事情理應不是C端的負責范圍降瞳,所以這個時候用MQ的方式嘱支,來解決這個問題最合適。

NoSQL

和緩存的區(qū)別

先說明一下挣饥,這里介紹的和緩存那一節(jié)不一樣除师,雖然可能會使用一樣的數(shù)據(jù)存儲方案(比如Redis或者Tair),但是使用的方式不一樣扔枫,這一節(jié)介紹的是把它作為DB來用汛聚。如果當作DB來用,需要有效保證數(shù)據(jù)存儲方案的可用性短荐、可靠性倚舀。

使用場景

需要結合具體的業(yè)務場景,看這塊業(yè)務涉及的數(shù)據(jù)是否適合用NoSQL來存儲忍宋,對數(shù)據(jù)的操作方式是否適合用NoSQL的方式來操作痕貌,或者是否需要用到NoSQL的一些額外特性(比如原子加減等)。

如果業(yè)務數(shù)據(jù)不需要和其他數(shù)據(jù)作關聯(lián)糠排,不需要事務或者外鍵之類的支持舵稠,而且有可能寫入會異常頻繁,這個時候就比較適合用NoSQL(比如HBase)入宦。

比如哺徊,美團點評內(nèi)部有一個對exception做的監(jiān)控系統(tǒng),如果在應用系統(tǒng)發(fā)生嚴重故障的時候乾闰,可能會短時間產(chǎn)生大量exception數(shù)據(jù)落追,這個時候如果選用MySQL,會造成MySQL的瞬間寫壓力飆升涯肩,容易導致MySQL服務器的性能急劇惡化以及主從同步延遲之類的問題轿钠,這種場景就比較適合用Hbase類似的NoSQL來存儲雹熬。

JVM調優(yōu)

什么時候調?

通過監(jiān)控系統(tǒng)(如沒有現(xiàn)成的系統(tǒng)谣膳,自己做一個簡單的上報監(jiān)控的系統(tǒng)也很容易)上對一些機器關鍵指標(gc time、gc count铅乡、各個分代的內(nèi)存大小變化继谚、機器的Load值與CPU使用率、JVM的線程數(shù)等)的監(jiān)控報警阵幸,也可以看gc log和jstat等命令的輸出花履,再結合線上JVM進程服務的一些關鍵接口的性能數(shù)據(jù)和請求體驗,基本上就能定位出當前的JVM是否有問題挚赊,以及是否需要調優(yōu)诡壁。

怎么調?

1.如果發(fā)現(xiàn)高峰期CPU使用率與Load值偏大荠割,這個時候可以觀察一些JVM的thread count以及gc count(可能主要是young gc count)妹卿,如果這兩個值都比以往偏大(也可以和一個歷史經(jīng)驗值作對比),基本上可以定位是young gc頻率過高導致蔑鹦,這個時候可以通過適當增大young區(qū)大小或者占比的方式來解決夺克。

2.如果發(fā)現(xiàn)關鍵接口響應時間很慢,可以結合gc time以及gc log中的stop the world的時間嚎朽,看一下整個應用的stop the world的時間是不是比較多铺纽。如果是,可能需要減少總的gc time哟忍,具體可以從減小gc的次數(shù)和減小單次gc的時間這兩個維度來考慮狡门,一般來說,這兩個因素是一對互斥因素锅很,我們需要根據(jù)實際的監(jiān)控數(shù)據(jù)來調整相應的參數(shù)(比如新生代與老生代比值其馏、eden與survivor比值、MTT值粗蔚、觸發(fā)cms回收的old區(qū)比率閾值等)來達到一個最優(yōu)值尝偎。

3.如果發(fā)生full gc或者old cms gc非常頻繁,通常這種情況會誘發(fā)STW的時間相應加長鹏控,從而也會導致接口響應時間變慢致扯。這種情況,大概率是出現(xiàn)了“內(nèi)存泄露”当辐,Java里的內(nèi)存泄露指的是一些應該釋放的對象沒有被釋放掉(還有引用拉著它)抖僵。那么這些對象是如何產(chǎn)生的呢?為啥不會釋放呢缘揪?對應的代碼是不是出問題了耍群?問題的關鍵是搞明白這個义桂,找到相應的代碼,然后對癥下藥蹈垢。所以問題的關鍵是轉化成尋找這些對象慷吊。怎么找?綜合使用jmap和MAT曹抬,基本就能定位到具體的代碼溉瓶。

多線程與分布式

使用場景

離線任務、異步任務谤民、大數(shù)據(jù)任務堰酿、耗時較長任務的運行,適當?shù)乩谜抛悖蛇_到加速的效果触创。

注意:線上對響應時間要求較高的場合,盡量少用多線程为牍,尤其是服務線程需要等待任務線程的場合(很多重大事故就是和這個息息相關)哼绑,如果一定要用,可以對服務線程設置一個最大等待時間吵聪。

常見做法

如果單機的處理能力可以滿足實際業(yè)務的需求凌那,那么盡可能地使用單機多線程的處理方式,減少復雜性吟逝;反之帽蝶,則需要使用多機多線程的方式。

對于單機多線程块攒,可以引入線程池的機制励稳,作用有二:

1.提高性能,節(jié)省線程創(chuàng)建和銷毀的開銷

2.限流囱井,給線程池一個固定的容量驹尼,達到這個容量值后再有任務進來,就進入隊列進行排隊庞呕,保障機器極限壓力下的穩(wěn)定處理能力在使用JDK自帶的線程池時新翎,一定要仔細理解構造方法的各個參數(shù)的含義,如core pool size住练、max pool size地啰、keepAliveTime、worker queue等讲逛,在理解的基礎上通過不斷地測試調整這些參數(shù)值達到最優(yōu)效果亏吝。

如果單機的處理能力不能滿足需求,這個時候需要使用多機多線程的方式盏混。這個時候就需要一些分布式系統(tǒng)的知識了蔚鸥。首先就必須引入一個單獨的節(jié)點惜论,作為調度器赖捌,其他的機器節(jié)點都作為執(zhí)行器節(jié)點愧膀。調度器來負責拆分任務,和分發(fā)任務到合適的執(zhí)行器節(jié)點嘴办;執(zhí)行器節(jié)點按照多線程的方式(也可能是單線程)來執(zhí)行任務弹谁。這個時候蹦掐,我們整個任務系統(tǒng)就由單擊演變成一個集群的系統(tǒng),而且不同的機器節(jié)點有不同的角色僵闯,各司其職,各個節(jié)點之間還有交互藤滥。這個時候除了有多線程鳖粟、線程池等機制,像RPC拙绊、心跳等網(wǎng)絡通信調用的機制也不可少向图。

度量系統(tǒng)(監(jiān)控、報警标沪、服務依賴管理)

嚴格來說榄攀,度量系統(tǒng)不屬于性能優(yōu)化的范疇,但是這方面和性能優(yōu)化息息相關金句,可以說為性能優(yōu)化提供一個強有力的數(shù)據(jù)參考和支撐檩赢。沒有度量系統(tǒng),基本上就沒有辦法定位到系統(tǒng)的問題违寞,也沒有辦法有效衡量優(yōu)化后的效果贞瞒。很多人不重視這方面,但我認為它是系統(tǒng)穩(wěn)定性和性能保障的基石趁曼。

關鍵流程

如果要設計這套系統(tǒng)军浆,總體來說有哪些關鍵流程需要設計呢?

1.確定指標

2.采集數(shù)據(jù)

3.計算數(shù)據(jù)挡闰,存儲結果

4.展現(xiàn)和分析

需要監(jiān)控和報警哪些指標數(shù)據(jù)乒融?需要關注哪些?

按照需求出發(fā)摄悯,主要需要二方面的指標:

1.接口性能相關赞季,包括單個接口和全部的QPS、響應時間射众、調用量(統(tǒng)計時間維度越細越好碟摆;最好是,既能以節(jié)點為維度叨橱,也可以以服務集群為維度典蜕,來查看相關數(shù)據(jù))断盛。其中還涉及到服務依賴關系的管理,這個時候需要用到服務依賴管理系統(tǒng)

2.單個機器節(jié)點相關愉舔,包括CPU使用率钢猛、Load值、內(nèi)存占用率轩缤、網(wǎng)卡流量等命迈。如果節(jié)點是一些特殊類型的服務(比如MySQL、Redis火的、Tair)壶愤,還可以監(jiān)控這些服務特有的一些關鍵指標。

數(shù)據(jù)采集方式

通常采用異步上報的方式馏鹤,具體做法有兩種:第一種征椒,發(fā)到本地的Flume端口,由Flume進程收集到遠程的Hadoop集群或者Storm集群來進行運算湃累;第二種勃救,直接在本地運算好以后,使用異步和本地隊列的方式治力,發(fā)送到監(jiān)控服務器蒙秒。

數(shù)據(jù)計算

可以采用離線運算(MapReduce/Hive)或者實時/準實時運算(Storm/Spark)的方式,運算后的結果存入MySQL或者HBase宵统;某些情況晕讲,也可以不計算,直接采集發(fā)往監(jiān)控服務器马澈。

展現(xiàn)和分析

提供統(tǒng)一的展現(xiàn)分析平臺益兄,需要帶報表(列表/圖表)監(jiān)控和報警的功能。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末箭券,一起剝皮案震驚了整個濱河市净捅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辩块,老刑警劉巖蛔六,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異废亭,居然都是意外死亡国章,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門豆村,熙熙樓的掌柜王于貴愁眉苦臉地迎上來液兽,“玉大人,你說我怎么就攤上這事∷膯” “怎么了宁玫?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長柑晒。 經(jīng)常有香客問我欧瘪,道長,這世上最難降的妖魔是什么匙赞? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任佛掖,我火速辦了婚禮,結果婚禮上涌庭,老公的妹妹穿的比我還像新娘芥被。我一直安慰自己,他們只是感情好坐榆,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布撕彤。 她就那樣靜靜地躺著,像睡著了一般猛拴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚀狰,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天愉昆,我揣著相機與錄音,去河邊找鬼麻蹋。 笑死跛溉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的扮授。 我是一名探鬼主播芳室,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼刹勃!你這毒婦竟也來了堪侯?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤荔仁,失蹤者是張志新(化名)和其女友劉穎伍宦,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乏梁,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡次洼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了遇骑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卖毁。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖落萎,靈堂內(nèi)的尸體忽然破棺而出亥啦,到底是詐尸還是另有隱情炭剪,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布禁悠,位于F島的核電站念祭,受9級特大地震影響,放射性物質發(fā)生泄漏碍侦。R本人自食惡果不足惜粱坤,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓷产。 院中可真熱鬧站玄,春花似錦、人聲如沸濒旦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尔邓。三九已至晾剖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間梯嗽,已是汗流浹背齿尽。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灯节,地道東北人循头。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像炎疆,于是被迫代替她去往敵國和親卡骂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 本文主要根據(jù)美團的技術博客《常見性能優(yōu)化策略的總結》整理而來形入。 代碼 之所以把代碼放到第一位全跨,是因為這一點最容易引...
    你是妖怪吧閱讀 4,128評論 0 15
  • 從三月份找實習到現(xiàn)在,面了一些公司亿遂,掛了不少螟蒸,但最終還是拿到小米、百度崩掘、阿里七嫌、京東、新浪苞慢、CVTE诵原、樂視家的研發(fā)崗...
    時芥藍閱讀 42,277評論 11 349
  • 我還以為你不會搜我呢~ 前言 在app開發(fā)中,我堅信,總會有小伙伴會用到友盟統(tǒng)計的绍赛,我也是(廢話)蔓纠。但是在友盟的錯...
    MQ_Twist閱讀 3,027評論 4 5
  • 綿綿秋雨淋濕了心,想望故鄉(xiāng)的小路吗蚌,南方的煙雨腿倚,北國的風霜,都化作深深的思念蚯妇!愿一切安好敷燎!
    468c63592051閱讀 109評論 0 0
  • 前面我們已經(jīng)完成了博客的分頁顯示模糊查詢刪除等功能,現(xiàn)在我們就講一下如何實現(xiàn)寫博客與修改博客的功能箩言。 1硬贯、 寫博客...
    余空啊閱讀 3,237評論 4 7