在 《函數計算本地運行與調試 - Fun Local 基本用法》 中,咱們介紹了利用 Fun Local 本地運行、調試函數的方法。但若是僅僅這樣簡單的介紹,並不能展示 Fun Local 對函數計算開發的巨大效率的提高。php
這一次,咱們拿一個簡單的場景來舉例子——開發一個簡單的爬蟲函數(代碼參考函數計算控制檯模板),介紹如何以正確姿式,從零開始,開發一個自動伸縮、按調用次數收費的 serverless 爬蟲應用。html
咱們將這個完整的應用拆分紅多步,而且在每一步完成後,咱們都會進行相應的運行驗證。java
首先,咱們建立一個名爲 image-crawler 的目錄做爲項目的根。而後在該目錄下建立一個名爲 template.yml 的文件,內容爲:node
ROSTemplateFormatVersion: '2015-09-01' Transform: 'Aliyun::Serverless-2018-04-03' Resources: localdemo: Type: 'Aliyun::Serverless::Service' Properties: Description: 'local invoke demo' image-crawler: Type: 'Aliyun::Serverless::Function' Properties: Handler: index.handler CodeUri: code/ Description: 'Hello world with python2.7!' Runtime: python2.7
若是不瞭解 Fun 定義的 Serverless Application Model,能夠參考 這裏。python
操做完成後,咱們的項目目錄結構以下:git
. └── template.yml
在根目錄下建立一個名爲 code 的目錄,並在該目錄下建立一個名爲 index.py 的文件,內容爲一個簡單的 helloworld 函數:github
def handler(event, context): return 'hello world!'
在項目根目錄下執行:正則表達式
fun local invoke image-crawler
函數運行成功:sql
操做完成後,咱們的項目目錄結構以下:json
. ├── code │ └── index.py └── template.yml
咱們簡單修改第 2 步的代碼,將 event 打印到 log 中。
import logging logger = logging.getLogger() def handler(event, context): logger.info("event: " + event) return 'hello world!'
經過觸發事件的方式運行函數,獲得以下結果:
能夠看到,咱們的函數已經能正確接收到觸發事件了。
Fun Local 更多幫助信息,參考。
接下來,咱們添加獲取網頁內容的代碼。
import logging import json import urllib logger = logging.getLogger() def handler(event, context): logger.info("event: " + event) evt = json.loads(event) url = evt['url'] html = get_html(url) logger.info("html content length: " + str(len(html))) return 'Done!' def get_html(url): page = urllib.urlopen(url) html = page.read() return html
代碼邏輯比較簡單,咱們這裏直接使用了 urllib 庫,讀取網頁內容。
運行函數,獲得如下輸出:
咱們打算經過正則解析網頁中包含的 jpg 圖片,所以這一步會比較繁瑣,由於涉及到正則表達式的微調。爲了能快速的解決問題,咱們決定利用 fun local 提供的 local debugging 解決問題。local debugging 方法參考: 《函數計算本地運行與調試 - Fun Local 基本用法》。
首先,咱們在下面這一行下個斷點:
logger.info("html content length: " + str(len(html)))
而後以 debug 的方式啓動,vscode 調試器鏈接後,函數會繼續運行到咱們斷點的這一行:
咱們能夠直接在 Locals 一欄看到本地變量,其中包含了 html
這個變量,也就是咱們獲取到的 html 源碼。咱們能夠將它的值複製出來,分析下,而後設計正則表達式。
咱們能夠先寫一個簡單的,好比能夠是 http:\/\/[^\s,"]*\.jpg
。
怎麼快速校驗這段代碼的正確性呢?咱們能夠利用調試器提供的 Watch(監視)
功能。
建立一個 Watch 變量,將下面的值輸入進去:
re.findall(re.compile(r'http:\/\/[^\s,"]*\.jpg'), html)
回車後,便可看到代碼的執行效果:
這裏通常不太容易一次寫對,能夠反覆修改正則測試,直到正確爲止。
咱們獲得的正確的圖片解析的邏輯添加到代碼中:
reg = r'http:\/\/[^\s,"]*\.jpg' imgre = re.compile(reg) def get_img(html): return re.findall(imgre, html)
而後在 handler 方法中調用便可:
def handler(event, context): logger.info("event: " + event) evt = json.loads(event) url = evt['url'] html = get_html(url) img_list = get_img(html) logger.info(img_list) return 'Done!'
編寫完成後,能夠繼續本地執行,驗證下結果:
echo '{"url": "https://image.baidu.com/search/index?tn=baiduimage&word=%E5%A3%81%E7%BA%B8"}' \ | fun local invoke image-crawler
能夠看到,img_list 已經輸出到控制檯了:
解析到的圖片,咱們選擇使用 oss 存儲。
首先,咱們須要經過環境變量配置 OSS Endpoint 以及 OSS Bucket。
在 template 中配置環境變量(需提早建立好 oss bucket):
EnvironmentVariables: OSSEndpoint: oss-cn-hangzhou.aliyuncs.com BucketName: fun-local-test
而後就能夠直接在函數中獲取到這兩個環境變量了:
endpoint = os.environ['OSSEndpoint'] bucket_name = os.environ['BucketName']
另外,fun local 運行函數時,還會提供一個額外的變量用來標識這是一個本地運行的函數。經過這個標識,咱們能夠用來作一些本地化的操做,好比咱們能夠在線上運行時鏈接 RDS,在本地運行時鏈接 Mysql。
這裏,咱們用該標識以不一樣的的方式建立 oss client,緣由是線上運行時,經過 credentials 獲取到的是扮演角色的臨時 ak,有有效期限制,而本地運行時,沒有該限制。oss 提供了這兩種方式的構造方法,咱們直接使用便可:
creds = context.credentials if (local): auth = oss2.Auth(creds.access_key_id, creds.access_key_secret) else: auth = oss2.StsAuth(creds.access_key_id, creds.access_key_secret, creds.security_token) bucket = oss2.Bucket(auth, endpoint, bucket_name)
接着咱們遍歷全部圖片,將全部的圖片上傳到 oss:
count = 0 for item in img_list: count += 1 logging.info(item) # Get each picture pic = urllib.urlopen(item) # Store all the pictures in oss bucket, keyed by timestamp in microsecond unit bucket.put_object(str(datetime.datetime.now().microsecond) + '.png', pic)
再在本地運行一下函數:
echo '{"url": "https://image.baidu.com/search/index?tn=baiduimage&word=%E5%A3%81%E7%BA%B8"}' \ | fun local invoke image-crawler
能夠從日誌看到,圖片被一張一張的解析出來,並被上傳到 oss 上了。
登錄 oss 控制檯,能夠看到這些圖片。
本地開發完成後,咱們還須要將其發佈到線上,讓其成爲一個可被調用的服務。以往,你可能以爲比較麻煩,好比要登錄控制檯,建立服務、建立函數、配置環境變量,建立角色等,如今有了 fun 後,這一切都不須要了。
不過,本地與線上仍是有些區別的,那就是要受權函數計算可以訪問 OSS,怎麼作呢?很簡單,在咱們的 template 中加入一行配置便可(Polices 文檔,能夠參考):
Policies: AliyunOSSFullAccess
添加後的 template.yml 內容以下:
ROSTemplateFormatVersion: '2015-09-01' Transform: 'Aliyun::Serverless-2018-04-03' Resources: localdemo: Type: 'Aliyun::Serverless::Service' Properties: Description: 'local invoke demo' Policies: AliyunOSSFullAccess image-crawler: Type: 'Aliyun::Serverless::Function' Properties: Handler: index.handler CodeUri: code/ Description: 'Hello world with python2.7!' Runtime: python2.7 EnvironmentVariables: OSSEndpoint: oss-cn-hangzhou.aliyuncs.com BucketName: fun-local-test
而後,使用 fun deploy
後,能夠看到部署成功的日誌。
登錄控制檯,能夠看到,咱們的服務、函數、代碼、環境變量等都已經就緒了。
在觸發事件中,寫入咱們用來測試的 json,而後執行:
能夠發現,會得到與本地一致的效果:
fcli 幫助文檔 參考。
在終端執行如下命令,能夠獲取函數列表:
fcli function list --service-name localdemo
能夠看到咱們的 image-crawler 已經建立成功了。
{ "Functions": [ "image-crawler", "java8", "nodejs6", "nodejs8", "php72", "python27", "python3" ], "NextToken": null }
使用如下命令則能夠調用函數運行:
fcli function invoke --service-name localdemo \ --function-name image-crawler \ --event-str '{"url": "https://image.baidu.com/search/index?tn=baiduimage&word=%E5%A3%81%E7%BA%B8"}'
運行成功後,會獲得與控制檯與 fun local 一致的結果。
至此,咱們的開發就算告一段落。
本文利用 fun local 提供的本地運行、調試的能力,作到了在本地開發函數,而且經過反覆的執行函數獲得反饋以便於快速迭代代碼。
在本地開發完成後,不須要對代碼進行任何修改,經過 fun deploy 命令,一鍵部署到雲端,達到預期的效果。
本文介紹的方法,並非開發函數計算的惟一方式。本文的目的,是可以向開發者傳達一種信號——開發函數計算時,只要身姿正確,就會很是享受,開發流程也會十分順暢。祝您使用愉快。
本文爲雲棲社區原創內容,未經容許不得轉載。