每一個可以努力的日子,都是一份厚禮。
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 也可以發表評論或引用到你的網站。除特殊說明外文章均為本人原創,並遵從署名-非商業性使用-相同方式共享創作協議,轉載或使用請註明作者和來源,尊重知識分享。 |
批評不自由
則讚美無意義
博主,小弟一直不理解pool與bucket到底什麼關係
我沒研究過 ceph,剛看了下官方文檔,也是會有 replicas,好像和 S3 裡面的 bucket 類似。
博主,請問pool如何通過url訪問?
你說的 pool 是 Amazon S3 服務中的概念么?不是很明白
奧,pool是ceph中的概念,因為ceph有S3的兼容接口,看到你這有講S3的概念,就直接問了
哦,好像 ceph 會集成到 openstack 里。