在?《函數(shù)計算本地運行與調(diào)試 - Fun Local 基本用法》?中,我們介紹了利用 Fun Local 本地運行、調(diào)試函數(shù)的方法。但如果僅僅這樣簡單的介紹,并不能展現(xiàn) Fun Local 對函數(shù)計算開發(fā)的巨大效率的提升。
這一次,我們拿一個簡單的場景來舉例子——開發(fā)一個簡單的爬蟲函數(shù)(代碼參考函數(shù)計算控制臺模板),介紹如何以正確姿勢,從零開始,開發(fā)一個自動伸縮、按調(diào)用次數(shù)收費的 serverless 爬蟲應(yīng)用。
開發(fā)步驟
我們將這個完整的應(yīng)用拆分成多步,并且在每一步完成后,我們都會進(jìn)行相應(yīng)的運行驗證。
1. 創(chuàng)建 Fun 項目
首先,我們創(chuàng)建一個名為 image-crawler 的目錄作為項目的根。然后在該目錄下創(chuàng)建一個名為 template.yml 的文件,內(nèi)容為:
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,可以參考?這里。
操作完成后,我們的項目目錄結(jié)構(gòu)如下:
. └──?template.yml
2. 編寫 helloworld 函數(shù)代碼
在根目錄下創(chuàng)建一個名為 code 的目錄,并在該目錄下創(chuàng)建一個名為 index.py 的文件,內(nèi)容為一個簡單的 helloworld 函數(shù):
def?handler(event,?context): ????return?'hello?world!'
在項目根目錄下執(zhí)行:
fun?local?invoke?image-crawler
函數(shù)運行成功:
操作完成后,我們的項目目錄結(jié)構(gòu)如下:
. ├──?code│???└──?index.py└──?template.yml
3. 事件觸發(fā)函數(shù)運行
我們簡單修改第 2 步的代碼,將 event 打印到 log 中。
import?logging
logger?=?logging.getLogger()def?handler(event,?context):
????logger.info("event:?"?+?event)????return?'hello?world!'通過觸發(fā)事件的方式運行函數(shù),得到如下結(jié)果:
可以看到,我們的函數(shù)已經(jīng)能正確接收到觸發(fā)事件了。
Fun Local 更多幫助信息,參考。
4. 獲取網(wǎng)頁源碼內(nèi)容
接下來,我們添加獲取網(wǎng)頁內(nèi)容的代碼。
import?loggingimport?jsonimport?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 庫,讀取網(wǎng)頁內(nèi)容。
運行函數(shù),得到以下輸出:

5. 解析網(wǎng)頁中的圖片
我們打算通過正則解析網(wǎng)頁中包含的 jpg 圖片,因此這一步會比較繁瑣,因為涉及到正則表達(dá)式的微調(diào)。為了能快速的解決問題,我們決定利用 fun local 提供的 local debugging 解決問題。local debugging 方法參考:?《函數(shù)計算本地運行與調(diào)試 - Fun Local 基本用法》。
首先,我們在下面這一行下個斷點:
logger.info("html?content?length:?"?+?str(len(html)))然后以 debug 的方式啟動,vscode 調(diào)試器連接后,函數(shù)會繼續(xù)運行到我們斷點的這一行:

我們可以直接在 Locals 一欄看到本地變量,其中包含了?html?這個變量,也就是我們獲取到的 html 源碼。我們可以將它的值復(fù)制出來,分析下,然后設(shè)計正則表達(dá)式。
我們可以先寫一個簡單的,比如可以是?http:\/\/[^\s,"]*\.jpg。
怎么快速校驗這段代碼的正確性呢?我們可以利用調(diào)試器提供的?Watch(監(jiān)視)?功能。
創(chuàng)建一個 Watch 變量,將下面的值輸入進(jìn)去:
re.findall(re.compile(r'http:\/\/[^\s,"]*\.jpg'),?html)
回車后,即可看到代碼的執(zhí)行效果:

這里一般不太容易一次寫對,可以反復(fù)修改正則測試,直到正確為止。
我們得到的正確的圖片解析的邏輯添加到代碼中:
reg?=?r'http:\/\/[^\s,"]*\.jpg'imgre?=?re.compile(reg)def?get_img(html): ????return?re.findall(imgre,?html)
然后在 handler 方法中調(diào)用即可:
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!'編寫完成后,可以繼續(xù)本地執(zhí)行,驗證下結(jié)果:
echo?'{"url":?"https://image.baidu.com/search/index?tn=baiduimage&word=%E5%A3%81%E7%BA%B8"}'?\
????|?fun?local?invoke?image-crawler可以看到,img_list 已經(jīng)輸出到控制臺了:

6. 將圖片上傳到 oss
解析到的圖片,我們選擇使用?oss?存儲。
首先,我們需要通過環(huán)境變量配置 OSS Endpoint 以及 OSS Bucket。
在 template 中配置環(huán)境變量(需提前創(chuàng)建好 oss bucket):
EnvironmentVariables:????OSSEndpoint:?oss-cn-hangzhou.aliyuncs.com ????BucketName:?fun-local-test
然后就可以直接在函數(shù)中獲取到這兩個環(huán)境變量了:
endpoint?=?os.environ['OSSEndpoint'] bucket_name?=?os.environ['BucketName']
另外,fun local 運行函數(shù)時,還會提供一個額外的變量用來標(biāo)識這是一個本地運行的函數(shù)。通過這個標(biāo)識,我們可以用來做一些本地化的操作,比如我們可以在線上運行時連接 RDS,在本地運行時連接 Mysql。
這里,我們用該標(biāo)識以不同的的方式創(chuàng)建 oss client,原因是線上運行時,通過 credentials 獲取到的是扮演角色的臨時 ak,有有效期限制,而本地運行時,沒有該限制。oss 提供了這兩種方式的構(gòu)造方法,我們直接使用即可:
creds?=?context.credentialsif?(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?=?0for?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)
再在本地運行一下函數(shù):
echo?'{"url":?"https://image.baidu.com/search/index?tn=baiduimage&word=%E5%A3%81%E7%BA%B8"}'?\
????|?fun?local?invoke?image-crawler可以從日志看到,圖片被一張一張的解析出來,并被上傳到 oss 上了。
登陸 oss 控制臺,可以看到這些圖片。
部署
本地開發(fā)完成后,我們還需要將其發(fā)布到線上,讓其成為一個可被調(diào)用的服務(wù)。以往,你可能覺得比較麻煩,比如要登陸控制臺,創(chuàng)建服務(wù)、創(chuàng)建函數(shù)、配置環(huán)境變量,創(chuàng)建角色等,現(xiàn)在有了 fun 后,這一切都不需要了。
不過,本地與線上還是有些區(qū)別的,那就是要授權(quán)函數(shù)計算能夠訪問 OSS,怎么做呢?很簡單,在我們的 template 中加入一行配置即可(Polices 文檔,可以參考):
Policies:?AliyunOSSFullAccess
添加后的 template.yml 內(nèi)容如下:
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?后,可以看到部署成功的日志。

驗證
通過控制臺驗證
登陸控制臺,可以看到,我們的服務(wù)、函數(shù)、代碼、環(huán)境變量等都已經(jīng)就緒了。

在觸發(fā)事件中,寫入我們用來測試的 json,然后執(zhí)行:
可以發(fā)現(xiàn),會獲得與本地一致的效果:
通過 fcli 驗證
fcli 幫助文檔?參考。
在終端執(zhí)行以下命令,可以獲取函數(shù)列表:
fcli?function?list?--service-name?localdemo
可以看到我們的 image-crawler 已經(jīng)創(chuàng)建成功了。
{??"Functions":?[????"image-crawler",????"java8",????"nodejs6",????"nodejs8",????"php72",????"python27",????"python3"
??],??"NextToken":?null}使用以下命令則可以調(diào)用函數(shù)運行:
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 一致的結(jié)果。
小結(jié)
至此,我們的開發(fā)就算告一段落。
本文利用 fun local 提供的本地運行、調(diào)試的能力,做到了在本地開發(fā)函數(shù),并且通過反復(fù)的執(zhí)行函數(shù)得到反饋以便于快速迭代代碼。
在本地開發(fā)完成后,不需要對代碼進(jìn)行任何修改,通過 fun deploy 命令,一鍵部署到云端,達(dá)到預(yù)期的效果。
本文介紹的方法,并不是開發(fā)函數(shù)計算的唯一方式。本文的目的,是能夠向開發(fā)者傳達(dá)一種信號——開發(fā)函數(shù)計算時,只要身姿正確,就會非常享受,開發(fā)流程也會十分順暢。祝您使用愉快。
電子發(fā)燒友App


























評論