聊聊Dubbox(一):為何選擇
1. 前言
隨著現在網際網路行業的發展,越來越多的框架、中介軟體、容器等開源技術不斷地湧現,更好地來服務於業務,解決實現業務的問題。然而面對眾多的技術選擇,我們要如何甄別出適合自己團隊業務的技術呢?對於人來說,鞋子過大,可能影響奔跑的速度,鞋子過小,可能影響身體的成長。技術對於業務也是如此的關係。
所以,相對於技術的學習、搭建、使用、運維等技能,我們對技術的甄別選擇更是重中之重。那麼本文要講的Dubbox框架,又是如何在眾多的服務框架中脫穎而出,被團隊選中踐行服務之路?
2. 服務
2.1 為什麼要做服務
技術為業務而生,架構也為業務而出現。隨著業務的發展、使用者量的增長,系統數量增多,呼叫依賴關係也變得複雜,為了確保系統高可用、高併發的要求,系統的架構也從單體時代慢慢遷移至服務SOA時代,根據不同服務對系統資源的要求不同,我們可以更合理的配置系統資源,使系統資源利用率最大化。
系統架構演進
- 單一應用架構
當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本。
此時,用於簡化增刪改查工作量的 資料訪問框架(ORM) 是關鍵。 - 垂直應用架構
當訪問量逐漸增大,單一應用增加機器帶來的加速度越來越小,將應用拆成互不相干的幾個應用,以提升效率。
此時,用於加速前端頁面開發的 Web框架(MVC) 是關鍵。 - 分散式服務架構
當垂直應用越來越多,應用之間互動不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。
此時,用於提高業務複用及整合的 分散式服務框架(RPC) 是關鍵。 - 流動計算架構
當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個排程中心基於訪問壓力實時管理叢集容量,提高叢集利用率。
此時,用於提高機器利用率的 資源排程和治理中心(SOA)
平臺隨著業務的發展從 All in One 環境就可以滿足業務需求(以Java來說,可能只是一兩個war包就解決了);發展到需要拆分多個應用,並且採用MVC的方式分離前後端,加快開發效率;在發展到服務越來越多,不得不將一些核心或共用的服務拆分出來,提供實時流動監控計算等,其實發展到此階段,如果服務拆分的足夠精細,並且獨立執行,這個時候至少可以理解為SOA(Service-Oriented Architecture)架構了。
2.2 服務帶來的挑戰
當迎來服務SOA時代,我們面臨要解決的問題會很多,比如:系統的複雜度上升、服務依賴關係、服務效能監控、全鏈路日誌、容災、斷路器、限流等。那麼面對這些問題為什麼還要做分散式服務呢?因為在未來只有砥礪前行,才能走的更高更遠。
根據現在團隊的業務系統情況,首先我們要梳理出現存的問題是什麼:
- 多種呼叫傳輸方式:HTTP方式、WebService方式;
- 服務呼叫依賴關係:人工記錄,檢視程式碼分析;
- 服務呼叫效能監控:日誌記錄,人工檢視時間;
- 服務與應用緊耦合:服務掛掉,應用無法可用;
- 服務叢集負載配置:Nginx配置,存在單點問題;
在去選擇技術框架時,技術框架最基本要解決上面現存問題,同時我們也要確認出我們的期望,要達到的目標是什麼:
- 支援當前業務需求,這是最最基本的條件;
- 服務避免單點問題,去中心化;
- 服務高可用、高併發,解耦服務依賴;
- 服務通用化,支援異構系統呼叫服務;
- 服務依賴關係自維護,視覺化;
- 服務效能監控自統計,視覺化;
- 服務需自帶註冊、發現、健康檢查、負載均衡等特性;
- 開發人員關注度高,上手快,簡單輕量,低侵入;
還有最重要一點,這也是往往很多技術人員進入的誤區,“對於技術,不要為了使用而使用,用最簡單合適的技術實現解決問題才是正道”。架構是服務於業務的,能快速方便的滿足業務需求的架構才是好的架構。沒有最好的,只有適合自己的。
沒有最好的,只有適合自己的
2.3 服務未來的趨勢
一談到服務,可能大家很多聽說過SOA、MSA等服務的概念名詞,近幾年MSA炒的比較火,其實每一個概念的背後都在解決不同的問題。此類名詞的最大特點就是 一解釋就懂,一問就不知,一討論就打架。
兩者說到底都是對外提供介面的一種架構設計方式。我倒覺得微服務其實就是隨著網際網路的發展,複雜的平臺、業務的出現,導致SOA架構向更細粒度、更通用化程度發展,就成了所謂的微服務了。以這種說法做為根據,我覺得SOA與微服務的區別在於如下幾個方面:
- 微服務相比於SOA更加精細,微服務更多的以獨立的程序的方式存在,互相之間並無影響;
- 微服務提供的介面方式更加通用化,例如HTTP RESTful方式,各種終端都可以呼叫,無關語言、平臺限制;
- 微服務更傾向於分散式去中心化的部署方式,在網際網路業務場景下更適合;
微服務與SOA有很多相同之處。兩者都屬於典型的、包含鬆耦合分散式元件的系統結構。在圍繞著服務的概念建立架構這一方面,微服務提供了一種更清晰、定義更良好的方式。微服務的原則與敏捷軟體開發思想是高度一致的,而它與SOA原則的演化的目標也是相同的,則減少傳統的企業服務匯流排開發的高複雜性。兩者之間最關鍵的區別在於,微服務專注於以自治的方式產生價值。但是兩種架構背後的意圖是不同的:SOA嘗試將應用整合,一般採用中央管理模式來確保各應用能夠互動運作。微服務嘗試部署新功能,快速有效地擴充套件開發團隊。它著重於分散管理、程式碼再利用與自動化執行。
功能 | SOA | 微服務 |
---|---|---|
元件大小 | 大塊業務邏輯 | 單獨任務或小塊業務邏輯 |
耦合 | 通常鬆耦合 | 總是鬆耦合 |
公司架構 | 任何型別 | 小型、專注於功能交叉的團隊 |
管理 | 著重中央管理 | 著重分散管理 |
目標 | 確保應用能夠互動操作 | 執行新功能,快速拓展開發團隊 |
微服務並不是一種新思想的方法。它更像是一種思想的精煉,一種SOA的精細化演進,並且更好地利用了先進的技術以解決問題,例如容器與自動化等。所以對於我們去選擇服務技術框架時,並不是非黑即白,而是針對SOA、MSA兩種架構設計同時要考慮到相容性,對於現有平臺情況架構設計,退則守SOA,進則攻MSA,階段性選擇適合的。
3. 框架
現在業界比較成熟的服務框架有很多,比如:Hessian、CXF、Dubbo、Dubbox、Spring Cloud、gRPC、thrift等技術實現,都可以進行遠端呼叫,具體技術實現優劣參考以下分析,這也是具體在技術方案選擇過程中的重要依據。
3.1 服務框架對比
Dubbo 是阿里巴巴公司開源的一個Java高效能優秀的服務框架,使得應用可通過高效能的 RPC 實現服務的輸出和輸入功能,可以和 Spring框架無縫整合。不過,略有遺憾的是,據說在淘寶內部,dubbo由於跟淘寶另一個類似的框架HSF(非開源)有競爭關係,導致dubbo團隊已經解散,反到是噹噹網的擴充套件版本Dubbox仍在持續發展,牆內開花牆外香。其它的一些知名電商如噹噹、國美維護了自己的分支或者在dubbo的基礎開發,但是官方的庫缺乏維護,相關的依賴類比如Spring,Netty還是很老的版本(Spring 3.2.16.RELEASE, netty 3.2.5.Final),倒是有些網友寫了升級Spring和Netty的外掛。
Dubbox和Dubbo本質上沒有區別,名字的含義擴充套件了Dubbo而已,以下擴展出來的功能,也是選擇Dubbox很重要的考察點。
- 支援REST風格遠端呼叫(HTTP + JSON/XML);
- 支援基於Kryo和FST的Java高效序列化實現;
- 支援基於Jackson的JSON序列化;
- 支援基於嵌入式Tomcat的HTTP remoting體系;
- 升級Spring至3.x;
- 升級ZooKeeper客戶端;
- 支援完全基於Java程式碼的Dubbo配置;
Spring Cloud完全基於Spring Boot,是一個非常新的專案,2016年推出1.0的release版本,目前Github上更新速度很快. 雖然Spring Cloud時間最短, 但是相比Dubbo等RPC框架, Spring Cloud提供的全套的分散式系統解決方案。Spring Cloud 為開發者提供了在分散式系統(配置管理,服務發現,熔斷,路由,微代理,控制匯流排,一次性token,全域性瑣,leader選舉,分散式session,叢集狀態)中快速構建的工具,使用Spring Cloud的開發者可以快速的啟動服務或構建應用.它們將在任何分散式環境中工作,包括開發人員自己的膝上型電腦,裸物理機的資料中心,和像Cloud Foundry雲管理平臺。在未來引領這微服務架構的發展,提供業界標準的一套微服務架構解決方案。
缺點是專案很年輕,很少見到國內業界有人在生產上成套使用,一般都是隻有其中一兩個元件。相關的技術文件大部分是英文的,案例也相對較少,使用的話需要摸索的時間會長一些。
下圖是Spring Cloud和Dubbo對比:
Spring Cloud和Dubbo對比
Motan是新浪微博開源的一個Java 框架。它誕生的比較晚,起於2013年,2016年5月開源。Motan 在微博平臺中已經廣泛應用,每天為數百個服務完成近千億次的呼叫。與Dubbo相比,Motan在功能方面並沒有那麼全面,也沒有實現特別多的擴充套件。用的人比較少,功能和穩定性有待觀望。對跨語言呼叫支援較差,主要支援java。
Hessian採用的是二進位制RPC協議,適用於傳送二進位制資料。但本身也是一個Web Service框架對RPC呼叫提供支援,功能簡單,使用起來也方便。基於Http協議進行傳輸。通過Servlet提供遠端服務。通過Hessain本身提供的API來發起請求。響應端根據Hessian提供的API來接受請求。
Hessian優點:
- 整個jar很小,輕量;
- 配置簡單;
- 功能強大,拋開了soap(simple object access protocal 簡單物件訪問協議)、ejb,採用二進位制來傳遞物件;
rpcx是Go語言生態圈的Dubbo, 比Dubbo更輕量,實現了Dubbo的許多特性,藉助於Go語言優秀的併發特性和簡潔語法,可以使用較少的程式碼實現分散式的RPC服務。
gRPC是Google開發的高效能、通用的開源RPC框架,其由Google主要面向移動應用開發並基於HTTP/2協議標準而設計,基於ProtoBuf(Protocol Buffers)序列化協議開發,且支援眾多開發語言。本身它不是分散式的,所以要實現上面的框架的功能需要進一步的開發。
thrift是Apache的一個跨語言的高效能的服務框架,也得到了廣泛的應用。
以上RPC框架功能比較:
功能 | Hessian | Montan | rpcx | gRPC | Thrift | Dubbo | Dubbox | Spring Cloud |
---|---|---|---|---|---|---|---|---|
開發語言 | 跨語言 | Java | Go | 跨語言 | 跨語言 | Java | Java | Java |
分散式(服務治理) | × | √ | √ | × | × | √ | √ | √ |
多序列化框架支援 | hessian | √(支援Hessian2、Json,可擴充套件) | √ | × 只支援protobuf) | ×(thrift格式) | √ | √ | √ |
多種註冊中心 | × | √ | √ | × | × | √ | √ | √ |
管理中心 | × | √ | √ | × | × | √ | √ | √ |
跨程式語言 | √ | ×(支援php client和C server) | × | √ | √ | × | × | × |
支援REST | × | × | × | × | × | × | √ | √ |
關注度 | 低 | 中 | 低 | 中 | 中 | 中 | 高 | 中 |
上手難度 | 低 | 低 | 中 | 中 | 中 | 低 | 低 | 中 |
運維成本 | 低 | 中 | 中 | 中 | 低 | 中 | 中 | 中 |
開源機構 | Caucho | Apache | Apache | Alibaba | Dangdang | Apache |
實際場景中的選擇
- Spring Cloud : Spring全家桶,用起來很舒服,只有你想不到,沒有它做不到。可惜因為釋出的比較晚,國內還沒出現比較成功的案例,大部分都是試水,不過畢竟有Spring作背書,還是比較看好。
- Dubbox:相對於Dubbo支援了REST,估計是很多公司選擇Dubbox的一個重要原因之一,但如果使用Dubbo的RPC呼叫方式,服務間仍然會存在API強依賴,各有利弊,懂的取捨吧。
- Thrift: 如果你比較高冷,完全可以基於Thrift自己搞一套抽象的自定義框架吧。
- Montan:可能因為出來的比較晚,目前除了新浪微博16年初發布的,
- Hessian:如果是初創公司或系統數量還沒有超過5個,推薦選擇這個,畢竟在開發速度、運維成本、上手難度等都是比較輕量、簡單的,即使在以後遷移至SOA,也是無縫遷移。
- rpcx/gRPC:在服務沒有出現嚴重效能的問題下,或技術棧沒有變更的情況下,可能一直不會引入,即使引入也只是小部分模組優化使用。
3.2 RPC vs REST(JAX-RS)
由於Dubbo是基礎框架,其實現的內容對於我們實施微服務架構是否合理,也需要我們根據自身需求去考慮是否要修改,比如Dubbo的服務呼叫是通過RPC實現的,但是如果仔細拜讀過Martin Fowler的microservices一文,其定義的服務間通訊是HTTP協議的REST API。那麼這兩種有何區別呢?
-
服務提供方與呼叫方介面依賴方式太強:我們為每個微服務定義了各自的service抽象介面,並通過持續整合釋出到私有倉庫中,呼叫方應用對微服務提供的抽象介面存在強依賴關係,因此不論開發、測試、整合環境都需要嚴格的管理版本依賴,才不會出現服務方與呼叫方的不一致導致應用無法編譯成功等一系列問題,以及這也會直接影響本地開發的環境要求,往往一個依賴很多服務的上層應用,每天都要更新很多程式碼並install之後才能進行後續的開發。若沒有嚴格的版本管理制度或開發一些自動化工具,這樣的依賴關係會成為開發團隊的一大噩夢。而REST介面相比RPC更為輕量化,服務提供方和呼叫方的依賴只是依靠一紙契約,不存在程式碼級別的強依賴,當然REST介面也有痛點,因為介面定義過輕,很容易導致定義文件與實際實現不一致導致服務整合時的問題,但是該問題很好解決,只需要通過每個服務整合swagger,讓每個服務的程式碼與文件一體化,就能解決。所以在分散式環境下,REST方式的服務依賴要比RPC方式的依賴更為靈活。
-
服務對平臺敏感,難以簡單複用:通常我們在提供對外服務時,都會以REST的方式提供出去,這樣可以實現跨平臺的特點,任何一個語言的呼叫方都可以根據介面定義來實現。那麼在Dubbo中我們要提供REST介面時,不得不實現一層代理,用來將RPC介面轉換成REST介面進行對外發布。若我們每個服務本身就以REST介面方式存在,當要對外提供服務時,主要在API閘道器中配置對映關係和許可權控制就可實現服務的複用了。
相信這些痛點也是為什麼噹噹網在dubbox(基於Dubbo的開源擴充套件)中增加了對REST支援的原因之一。
Dubbo實現了服務治理的基礎,但是要完成一個完備的微服務架構,還需要在各環節去擴充套件和完善以保證叢集的健康,以減輕開發、測試以及運維各個環節上增加出來的壓力,這樣才能讓各環節人員真正的專注於業務邏輯。
而Spring Cloud依然發揚了Spring Source整合一切的作風,以標準化的姿態將一些微服務架構的成熟產品與框架揉為一體,並繼承了Spring Boot簡單配置、快速開發、輕鬆部署的特點,讓原本複雜的架構工作變得相對容易上手一些。所以,如果選擇Dubbo請務必在各個環節做好整套解決方案的準備,不然很可能隨著服務數量的增長,整個團隊都將疲於應付各種架構上不足引起的困難。而如果選擇Spring Cloud,相對來說每個環節都已經有了對應的元件支援,可能有些也不一定能滿足你所有的需求,但是其活躍的社群與高速的迭代進度也會是你可以依靠的強大後盾。
微服務結構圖
4. Dubbox帶來什麼
4.1 Dubbo服務治理
Dubbo服務治理
特性 | 描述 |
---|---|
透明遠端呼叫 | 就像呼叫本地方法一樣呼叫遠端方法;只需簡單配置,沒有任何API侵入; |
負載均衡機制 | Client端LB,可在內網替代F5等硬體負載均衡器; |
容錯重試機制 | 服務Mock資料,重試次數、超時機制等; |
自動註冊發現 | 註冊中心基於介面名查詢服務提 供者的IP地址,並且能夠平滑新增或刪除服務提供者; |
效能日誌監控 | Monitor統計服務的呼叫次調和呼叫時間的監控中心; |
服務治理中心 | 路由規則,動態配置,服務降級,訪問控制,權重調整,負載均衡,等手動配置。 |
自動治理中心 | 無,比如:熔斷限流機制、自動權重調整等; |
4.2 Dubbox擴充套件特性
支援REST風格遠端呼叫(HTTP + JSON/XML);
支援基於Kryo和FST的Java高效序列化實現;
支援基於Jackson的JSON序列化;
支援基於嵌入式Tomcat的HTTP remoting體系;
升級Spring至3.x;
升級ZooKeeper客戶端;
支援完全基於Java程式碼的Dubbo配置;
5. 參考資料與推薦閱讀
相關推薦
聊聊Dubbox(一):為何選擇
1. 前言 隨著現在網際網路行業的發展,越來越多的框架、中介軟體、容器等開源技術不斷地湧現,更好地來服務於業務,解決實現業務的問題。然而面對眾多的技術選擇,我們要如何甄別出適合自己團隊業務的技術呢?對於人來說,鞋子過大,可能影響奔跑的速度,鞋子過小,可能影響身體的成長。技術對於業務也是如此的關係。 所以,相
Mongoose(一):為什麼選擇Mongoose
如果想要在NodeJS中連線MongoDB,可以選擇直接使用mongodb為NodeJS寫的驅動包,但我更推薦使用一個比較成熟的中介軟體:Mongoose。 官方定義 Mongoose: elegant mongodb object modelin
深入淺出聊聊Kubernetes儲存(一):詳解Kubernetes儲存關鍵概念
近年來一直關注雲端計算領域的人,必定知道Docker和Kubernetes的崛起。如今,世界範圍內的公有云巨頭(谷歌、亞馬遜、微軟、華為雲、阿里雲等等)都在其傳統的公共雲服務之上提供託管的Kubernetes服務。Kubernetes功能強大、擴充套件性高,在許多人看來,它正在成為
jQuery -- 光陰似箭(一):初見 jQuery -- 基本用法,語法,選擇器
jQuery -- 知識點回顧篇(一):初見jQuery -- 基本用法,語法,選擇器 1. 使用方法 jQuery 庫位於一個 JavaScript 檔案中,其中包含了所有的 jQuery 函式。 網頁需要使用到 jQuery 時,需要先在網頁中引入 jQuery 的 js檔案。
Spring Cloud Config(一):聊聊分散式配置中心 Spring Cloud Config
目錄 Spring Cloud Config(一):聊聊分散式配置中心 Spring Cloud Config Spring Cloud Config(二):基於Git搭建配置中心 Spring Cloud Config(三):基於JDBC搭建配置中心 Sprin
pyspider 爬蟲教程(一):HTML 和 CSS 選擇器
雖然以前寫過 如何抓取WEB頁面 和 如何從 WEB 頁面中提取資訊。但是感覺還是需要一篇 step by step 的教程,不然沒有一個總體的認識。不過,沒想到這個教程居然會變成一篇譯文,在這個爬蟲教程系列文章中,會以實際的例子,由淺入深討論爬取(抓取和解析)的一些關鍵
Java常用的八種排序演算法與程式碼實現(一):氣泡排序法、插入排序法、選擇排序法
這三種排序演算法適合小規模資料排序 --- 共同點:基於比較,時間複雜度均為O(n2),空間複雜度均為O(1)(原地排序演算法) 不同點:插入排序和氣泡排序是穩定的排序演算法,選擇排序不是 --- 穩定排序演算法:可以保持數值相等的兩個物件,在排序之
運維 如何選擇最合適的伺服器方案(一):伺服器硬體配置
我們拋開 租用伺服器還是自行購買伺服器進行託管的問題,在這篇文章裡,我們討論的是無論租用或託管都要面臨的一個問題,那就是選擇伺服器的硬體配置。因為上帝不會給你一臺伺服器來滿足所有需求,解決所有的問題。尤其是我們身處在這樣一個飛速變化的時代,當你還在為最新款最強勁的CPU乍舌不
大學生學程式設計(一):為什麼要選擇做一名程式設計師?
從這篇文章開始準備做一個大學生學程式設計系列文章,主要幫助一些還在程式設計路上徘徊的小夥伴,作為一個過來人的身份總結一些程式設計經驗以及心得,在自學的過程中走了很多的彎路,在此通過這個專欄分享給需要程式設計的小夥伴。 無數個人心中都有一個程式設計夢,想著自己能夠做在電腦旁邊敲程式碼,做出
機器學習(一): python三種特徵選擇方法
特徵選擇的三種方法介紹: 過濾型: 選擇與目標變數相關性較強的特徵。缺點:忽略了特徵之間的關聯性。 包裹型: 基於線性模型相關係數以及模型結果AUC逐步剔除特徵。如果剔除相關係數絕對值較小特徵後
信數金服決策引擎分享(一):來聊聊冠軍/挑戰者試驗,一個數字遊戲
自動化決策與冠軍/挑戰者試驗 自動化決策最有價值之處在於可以迅速的根據業務變化改變底層的決策邏輯。這些業務變化可能來自於監管的調整、競爭壓力或單純的商業機會。決策的修改需要有測試同步跟進——畢竟在不能確保決策能被正確執行的情況下貿然改變部分業務模式是不明智的
ActiveMQ原始碼解析(一):聊聊broker
一、Broker 訊息佇列核心,相當於一個控制中心,負責路由訊息、儲存訂閱和連線、訊息確認和控制事務 1.Broker介面 定義了一些獲取broker本身相關資訊,新增connection、destination、session、訊息生產者、控制事務
如何選擇開源許可協議(一):瞭解協議
目前國內開源專案正在逐漸升溫,中國也開始有不少優秀的開源專案突顯出來。在大家摩拳擦掌準備加入開源大軍時,也要知道這個圈子裡的規則。技術人員不能只是研究技術,任何圈子都有規則,要知道了才能玩得好。前段時間有件關於開源軟體的事情挺熱鬧的,關於國內一個開發者把自己作品開源
排序(一):選擇排序
一、步驟 1、找到陣列中的最小元素; 2、將第一步找到的最小元素與陣列第一個元素交換位置(如果第一個元素就是最小元素則和自己交換); 3、在剩下的元素中找到最小元素,將它與第二個元素交換位置。如此
八大排序(一):四種簡單的排序(直接插入排序、希爾排序、氣泡排序、選擇排序)
一、直接插入排序 直接插入排序基本思想是每一步將一個待排序的記錄,插入到前面已經排好序的有序序列中去,直到插完所有元素為止。 直接插入排序演算法的運作如下: 假設有一組無序序列 R0, R1, … , RN-1。 (1) 我們先將這個序列中下標為 0
Python 單元測試框架系列:聊聊 Python 的單元測試框架(一):unittest
作者:HelloGitHub-Prodesire HelloGitHub 的《講解開源專案》系列,專案地址:https://github.com/HelloGitHub-Team/Article 前言 說到 Python 的單元測試框架,想必接觸過 Python 的朋友腦袋裡第一個想到的就是
工作流引擎Oozie(一):workflow
觸發 line last ssa pig oozie apt cnblogs 定時任務 1. Oozie簡介 Yahoo開發工作流引擎Oozie(馭象者),用於管理Hadoop任務(支持MapReduce、Spark、Pig、Hive),把這些任務以DAG(有向無環圖)方式
Spring 事務配置實戰(一):過濾無需事務處理的查詢之類操作
log pla ssi pan spl tail gif aop img <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes
【SSH之旅】一步步學習Hibernate框架(一):關於持久化
stc localhost 對象 schema hbm.xml java let pass [] 在不引用不論什麽框架下,我們會通過平庸的代碼不停的對數據庫進行操作,產生了非常多冗余的可是又有規律的底層代碼,這樣頻繁的操作數據庫和大量的底層代碼的反復
CS231n(一):基礎知識
深度學習 highlight 自己 元組 .py [0 upper bsp python 給自己新挖個坑:開始刷cs231n深度學習。 看了一下導言的pdf,差缺補漏。 s = "hello" print s.capitalize() # 首字母大寫; prints "