每一個可以努力的日子,都是一份厚禮。
Amazon S3 雲存儲服務Cloud Storage編程實踐
Amazon Simple Storage Service (S3) 是一個雲端存儲平台,這是現在蓬勃發展的雲計算的典型應用之一。用戶可以將自己的數據上傳到雲端服務器,便可以隨時隨地地訪問到這些數據,靈活高效。它按需收費,也就是說使用相應容量的存儲空間,就花相應的錢。這裡有具體的資費標準。對於企業用戶來說,使用這項服務實際上可以大大降低成本,這些成本不僅僅包括自己購置服務器硬件、軟件成本,還包括電力、為IT設施維護而僱傭的人力成本等等。
在Amazon S3中有如下幾個概念,通過分別介紹,我們可以大致理解雲存儲的基本原理。
Buckets:一個bucket是一個用於存儲的容器,我們可以不太恰當地理解為就是雲端的文件夾。文件夾要求一個獨特唯一的名字,這和註冊郵箱名差不多,可以加前綴或者後綴來避免重名。bucket使得我們在一個高層級上組織命名空間,並在數據的訪問控制上扮演重要角色。下面舉個例子,假設一個名為photos/puppy.jpg的文件對象存儲在名為johnsmith的bucket里,那麼我們就可以通過這樣一個url訪問到這個對象:http://johnsmith.s3.amazonaws.com/photos/puppy.jpg
Objects:對象,也就是存儲在S3里的基本實體。一個object包括object data和metadata。metadata是一系列的name-value對,用來描述這個object。默認情況下包括文件類型、最後修改時間等等,當然用戶也可以自定義一些metadata。
Keys:即bucket中每一個object的獨一無二的標識符。上面例子中提到的photos/puppy.jpg就是一個key。
Access Control Lists:訪問控制表ACL。在S3中每一個bucket和object都有一個ACL,並且bucket和object的ACL是互相獨立的。當用戶發起一個訪問請求,S3會檢查ACL來核實請求發送者是否有權限訪問這個bucket或object。
Regions:我們可以指定bucket的具體物理存儲區域(Region)。選擇適當的區域可以優化延遲、降低成本。Amazon在世界各地建立了數據中心,目前S3支持下列區域:US Standard,US (Northern California),EU (Ireland),APAC (Singapore)。
雲端為了提高數據可靠性,常用手段是在多個不同的服務器建立同一份數據的冗餘備份(replica)。這樣即使某一個服務器掛了,用戶仍然能夠從別的服務器取得他的數據。使用多份數據副本將帶來數據一致性的問題,如何保證每一份副本的內容是一致的?如何保證多個用戶可以並發讀寫?這在分布式系統設計中是一個經典的問題,我將另寫文章討論。Amazon的US Standard Region為所有的requests提供了最終一致性(eventual consistency),EU 和 Northern California Regions 提供了寫後讀一致性(read-after-write consistency)。
回到應用層面上來。希望開通試用S3雲存儲服務的同學,可以去看看這篇帖子,有詳細步驟和截圖。雖然Amazon給用戶提供了十分友好的Web界面控制台來管理雲端數據和應用,作為開發人員,我們也可以使用boto提供的API建立與Amazon雲計算存儲平台S3交互。boto是一個Amazon雲計算服務的python接口,當然也有其他語言比如C++的接口libAWS,Java接口,Ruby接口,PHP接口,等等。這些API不僅僅用於S3,也可以用於EC2等其他雲計算服務的調用。下面是一個示例程序,擁有連接Amazon S3上傳下載文件等基本功能。
#!/usr/bin/python # # Amazon S3 Interface # Author: Zeng, Xi # SID: 1010105140 # Email: [email protected] connected = 0 def connect(): access_key = raw_input('Your access key:').strip() secret_key = raw_input('Your secret key:').strip() from boto.s3.connection import S3Connection global conn conn = S3Connection(access_key, secret_key) global connected connected = 1 def creat(): if connected == 0: print 'Not connected!' elif connected == 1: bucket_name = raw_input('Bucket name:').strip() bucket = conn.create_bucket(bucket_name) def put(): if connected == 0: print 'Not connected!' elif connected == 1: local_file = raw_input('Local filename:').strip() bucket = raw_input('Target bucket name:').strip() from boto.s3.key import Key b = conn.get_bucket(bucket) k = Key(b) k.key = local_file k.set_contents_from_filename(local_file) def ls(): if connected == 0: print 'Not connected!' elif connected == 1: rs = conn.get_all_buckets() for b in rs: print b.name def lsfile(): if connected == 0: print 'Not connected!' elif connected == 1: bucket = raw_input('Bucket name:').strip() from boto.s3.key import Key b = conn.get_bucket(bucket) file_list = b.list() for l in file_list: print l.name def info(): if connected == 0: print 'Not connected!' elif connected == 1: bucket = raw_input('Bucket name:').strip() filename = raw_input('Filename:').strip() from boto.s3.bucketlistresultset import BucketListResultSet b = conn.get_bucket(bucket) brs = BucketListResultSet(bucket=b) for f in brs: key = b.lookup(f.name) print 'File: ' + f.name print 'size: ' + str(key.size) print 'last modified: ' + str(key.last_modified) print 'etag (md5): ' + str(key.etag) def permission(): if connected == 0: print 'Not connected!' elif connected == 1: while True: bucket = raw_input('Bucket name:').strip() permission = raw_input('Permission (private or public-read):').strip() if permission not in ['private', 'public-read']: print 'Input error!' elif permission in ['private', 'public-read']: break b = conn.get_bucket(bucket) b.set_acl(permission) def get(): if connected == 0: print 'Not connected!' elif connected == 1: bucket = raw_input('Source bucket name:').strip() s_file = raw_input('Source filename:').strip() d_file = raw_input('Local directory path and filename:').strip() from boto.s3.key import Key b = conn.get_bucket(bucket) key = b.lookup(s_file) key.get_contents_to_filename(d_file) def delete(): if connected == 0: print 'Not connected!' elif connected == 1: bucket = raw_input('Bucket name:').strip() conn.delete_bucket(bucket) def delfile(): if connected == 0: print 'Not connected!' elif connected == 1: bucket = raw_input('Bucket name:').strip() filename = raw_input('Filename:').strip() b = conn.get_bucket(bucket) b.delete_key(filename) def showMenu(): title = ''' Amazon S3 Service connect Get user credential and connect to Amazon S3 creat Creat bucket put Upload file to S3 ls List buckets lsfile List files in a bucket info Display information of a file permission Set bucket permissions get Download file from S3 delete Delete bucket delfile Delete file quit Quit Enter choice:''' while True: choice = raw_input(title).strip().lower() choices = ['connect','creat','put','ls','lsfile','info','permission','get','delete','delfile','quit'] if choice not in choices: print('Input Error!') else: if choice == 'quit': break elif choice == 'connect': connect() elif choice == 'creat': creat() elif choice == 'put': put() elif choice == 'ls': ls() elif choice == 'lsfile': lsfile() elif choice == 'info': info() elif choice == 'permission': permission() elif choice == 'get': get() elif choice == 'delete': delete() elif choice == 'delfile': delfile() if __name__ == '__main__': showMenu() |
對於個人用戶來說,文件同步是一個很實用的功能。如果我們的電腦被竊或硬盤損壞,我們仍可以通過同步文件夾從雲端獲取以前的文件。雲存儲也帶來了移動便利,在一些緊急場合,我們甚至可以使用手機來編輯文檔。事實上已經有很多這方面的應用,國外的同步工具Dropbox十分流行,它其實就是以Amazon S3為存儲後台的。國內115網盤之類應用也是層出不窮,金山發布了快盤、T盤,迅雷又宣布發布P盤……
下面的python代碼就是使用boto API寫的一個同步文件夾的示例程序。程序通過檢查文件名、大小、MD5來判斷雲端的文件和本地文件夾中的是否相同。如果不同,則下載到本地文件夾。
#!/usr/bin/python # # Synchronize files between local machine and the cloud storage. # Author: Zeng, Xi # SID: 1010105140 # Email: [email protected] connected = 0 downloaded_files = "" total_size = 0 def connect(): access_key = raw_input('Your access key:').strip() secret_key = raw_input('Your secret key:').strip() from boto.s3.connection import S3Connection global conn conn = S3Connection(access_key, secret_key) global connected connected = 1 def sync(): if connected == 0: print 'Not connected!\n' connect() if connected == 1: bucket = raw_input('Bucket name:').strip() local_path = raw_input('Local directory path:').strip() from boto.s3.key import Key from hashlib import md5 b = conn.get_bucket(bucket) file_list = b.list() for l in file_list: try: F = open(local_path + l.name,"rb") except IOError, e: get(bucket, l.name, local_path, l.size) else: s = md5(F.read()).hexdigest() if "\""+str(s)+"\"" == str(l.etag): import os local_size = os.path.getsize(local_path + l.name) if int(local_size) == int(l.size): continue else: get(bucket, l.name, local_path, l.size) else: get(bucket, l.name, local_path, l.size) global downloaded_files global total_size print "Downloaded files:\n" print downloaded_files print "Total size:" print total_size def get(bucket, filename, local_path, size): global downloaded_files global total_size downloaded_files += filename + "\n" total_size += size from boto.s3.key import Key b = conn.get_bucket(bucket) key = b.lookup(filename) key.get_contents_to_filename(local_path + filename) if __name__ == '__main__': sync() |
下載以上程序源代碼:S3接口、同步工具。
運行前請確認你已經安裝了python和boto
Amazon的雲計算不僅僅是S3數據存儲,還包括EC2虛擬機,SimpleDB數據庫等等很多服務。如果你有興趣,可以查看下面的相關文章。
關於作者:我目前是一名在讀研究生,如果你覺得我的文章對你有用,或我了解的知識對貴公司項目開發有幫助,或許你會有興趣與我聯繫。
這篇文章由lovelucy於2011-01-04 18:48發表在雲計算。你可以訂閱RSS 2.0 也可以發表評論或引用到你的網站。除特殊說明外文章均為本人原創,並遵從署名-非商業性使用-相同方式共享創作協議,轉載或使用請註明作者和來源,尊重知識分享。 |
批評不自由
則讚美無意義
Google Chrome 25.0.1364.97 Windows XP 大約11年前
博主,小弟一直不理解pool與bucket到底什麼關係
Google Chrome 28.0.1500.68 Linux 大約11年前
我沒研究過 ceph,剛看了下官方文檔,也是會有 replicas,好像和 S3 裡面的 bucket 類似。
Google Chrome 25.0.1364.97 Windows XP 大約11年前
博主,請問pool如何通過url訪問?
Google Chrome 28.0.1500.63 Linux 大約11年前
你說的 pool 是 Amazon S3 服務中的概念么?不是很明白
Google Chrome 25.0.1364.97 Windows XP 大約11年前
奧,pool是ceph中的概念,因為ceph有S3的兼容接口,看到你這有講S3的概念,就直接問了
Google Chrome 28.0.1500.68 Linux 大約11年前
哦,好像 ceph 會集成到 openstack 里。