又大又粗又猛免费视频久久_国产理论在线播放_久久男人av资源网站免费软件_99国产精品无码

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

在日常生活中,我們經(jīng)常會(huì)遇到搜索照片的情況,如果是要尋找已經(jīng)過去很久的照片,并且記憶中僅剩下零散記憶,常用的檢索照片的方法是定位到大致的時(shí)間,然后一張一張的去查看。但這種做法效率低下,經(jīng)常還會(huì)漏掉目標(biāo)圖片,所以在這種時(shí)候,我們很需要一款可以搜索圖片的軟件,即通過簡(jiǎn)單的文字描述就能實(shí)現(xiàn)圖片的快速檢索。

近幾年微信小程序的發(fā)展速度飛快,從張小龍在 2017 微信公開課 Pro 上發(fā)布小程序正式上線到目前為止,小程序已經(jīng)覆蓋了超過 200 個(gè)細(xì)分行業(yè),服務(wù)超過 1000 億人次用戶,年交易增長超過 600%,創(chuàng)造超過 5000 億的商業(yè)價(jià)值。

本實(shí)例將會(huì)通過微信小程序,在 Serverless 架構(gòu)上實(shí)現(xiàn)一款基于人工智能的相冊(cè)小工具,在保證基礎(chǔ)相冊(cè)功能(新建相冊(cè)、刪除相冊(cè)、上傳圖片、查看圖片、刪除圖片)的基礎(chǔ)上,增加搜索功能,即用戶上傳圖片之后,基于 Image Caption 技術(shù)自動(dòng)對(duì)圖片進(jìn)行描述,實(shí)現(xiàn) Image to Text 的過程,當(dāng)用戶進(jìn)行搜索時(shí),通過文本間的相似度返回給用戶最貼近的圖片。

基礎(chǔ)設(shè)計(jì)

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

該項(xiàng)目設(shè)計(jì)主要包括登錄功能、相冊(cè)新建、圖片上傳、相關(guān)預(yù)覽以及搜索功能,整體如圖所示。

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

其中注冊(cè)功能的主要作用是,通過獲取用戶的唯一 Id(微信中的 OpenId),將用戶信息存儲(chǔ)到數(shù)據(jù)庫中,之后的所有操作都需要根據(jù)該 Id 作為區(qū)分。相冊(cè)功能主要包括相冊(cè)添加、修改、刪除以及查看等。圖片功能包括圖片上傳功能、刪除功能、查看功能。搜索功能主要是可以查看指定標(biāo)簽對(duì)應(yīng)的圖片列表,以及指定搜索內(nèi)容對(duì)應(yīng)的列表。當(dāng)然這四個(gè)主要功能和模塊是與前端關(guān)系緊密的部分,除此之外還有后端異步操作的兩個(gè)模塊,分別是圖像壓縮功能和圖像描述功能。

注冊(cè)功能

注冊(cè)功能主要是用戶點(diǎn)擊注冊(cè)賬號(hào)之后執(zhí)行的動(dòng)作。該動(dòng)作需要注意,用戶點(diǎn)擊注冊(cè)賬號(hào)注冊(cè)的時(shí)候要先判斷用戶是否已經(jīng)注冊(cè)過,如果已經(jīng)注冊(cè)過則默認(rèn)登陸,否則進(jìn)行注冊(cè)并登陸。當(dāng)用戶不想注冊(cè)時(shí),可以點(diǎn)擊體驗(yàn)程序,可以對(duì)程序大部分頁面進(jìn)行預(yù)覽。但是不能實(shí)現(xiàn)有關(guān)數(shù)據(jù)庫的增刪改查等功能。

登錄功能頁面如圖所示。

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

相冊(cè)功能

當(dāng)用戶注冊(cè)登錄之后,可以在相冊(cè)管理頁面進(jìn)行相冊(cè)相關(guān)的管理,包括編輯功能、刪除功能以及新建功能,此處在進(jìn)行添加和修改的時(shí)候,需要注意相冊(cè)名稱是否已經(jīng)存在;在進(jìn)行刪除、修改相冊(cè)等操作時(shí)要判斷用戶是否有操作該相冊(cè)的權(quán)限等。

下圖是相冊(cè)功能相關(guān)原型圖。

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

圖片功能

圖片功能主要包括圖片列表以及圖片獲取、圖片刪除以及圖片上傳功能,在圖片獲取與刪除的過程中,要對(duì)用戶是否有該項(xiàng)操作的權(quán)限進(jìn)行判斷,圖片上傳時(shí)也要判斷用戶是否有上傳到指定相冊(cè)的權(quán)限。圖片功能相關(guān)原型圖如所示。

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

圖片功能部分除了用戶側(cè)可見的功能,還有定時(shí)任務(wù),當(dāng)用戶上傳圖片之后,系統(tǒng)會(huì)在后臺(tái)異步進(jìn)行圖像壓縮以及圖像的描述、關(guān)鍵詞提取等。

整體流程如圖所示:

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

搜索功能

搜索功能指的是通過關(guān)鍵詞或者使用者的描述,獲得目標(biāo)數(shù)據(jù)的過程,這一功能原型圖如圖所示:

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

這一部分的難點(diǎn)和重點(diǎn)在于通過用戶的描述,搜索到目標(biāo)數(shù)據(jù)。這個(gè)過程的基本流程如圖所示:

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

項(xiàng)目開發(fā)

初步了解 Serverless Cli

Serverless 架構(gòu)具備按量付費(fèi)、低成本運(yùn)維、高效率開發(fā)等優(yōu)點(diǎn),可以幫助我們快速開發(fā),快速迭代項(xiàng)目。而 Serverless Framework 則是一個(gè)非常高效的工具,其兼容了 AWS,Google Cloud 以及騰訊云等多家廠商的 Serverless 架構(gòu),為開發(fā)者提供一個(gè)多云的開發(fā)者工具,若以騰訊云為例,其擁有 Plugin 和 Components 兩個(gè)部分。

Plugin 和 Components 這兩個(gè)部分可以說是各有千秋,具體操作大家可以參看官方說明,我在這里想列舉幾點(diǎn):

  • Plugin 部署到線上的函數(shù)會(huì)自動(dòng)變更名字,例如函數(shù)是 myFunction,服務(wù)和階段是 myService-Dev,那么函數(shù)部署到線上就是 myService-Dev-myFunction,這樣的函數(shù)名,很可能會(huì)讓函數(shù)間調(diào)用等部分產(chǎn)生很多不可控因素。例如現(xiàn)在的環(huán)境是 Dev,函數(shù)間調(diào)用就要寫函數(shù)名是 myService-Dev-myFunction,如果環(huán)境是 Test,此時(shí)就要寫 myService-Test-myFunction,我始終覺得,更改環(huán)境應(yīng)該只需要更改配置,而不是更深入的代碼邏輯。
  • Plugin 也是有優(yōu)勢(shì)的,例如 Invoke、Remove 以及部署單個(gè)函數(shù)的功能,同時(shí) Plugin 也有全局變量,這更像是一個(gè)開發(fā)者工具,可以開發(fā)、部署、調(diào)用、查看一些信息、指標(biāo),刪除回滾等操作也可以通過 Plugin 完成;
  • Components 可以看作是一個(gè)組件集,這里面包括了很多的 Components,既有基礎(chǔ)的 Components,例如 cos、scf、apigateway 等,也有一些拓展的 Components,例如在 cos 上拓展出來的 website,可以直接部署靜態(tài)網(wǎng)站等,還有一些框架級(jí)的,例如 Koa、Express 等;
  • Components 除了支持的產(chǎn)品多,可以部署框架之外,更有吸引力的是其部署到線上的函數(shù)名字就是我指定的名字,不會(huì)出現(xiàn)額外的東西;
  • 相比 Plugin,Components 在功能上略顯單薄,除了部署和刪除,再?zèng)]有其他功能了。如果有多個(gè)功能要部署,并寫在了一個(gè) Components 的 yaml 上,那么每次部署都要部署所有的功能。換句話說,如果你只修改了一個(gè)函數(shù),并且因?yàn)椴幌胫匦虏渴鸷瘮?shù),而希望注釋掉其它函數(shù),這個(gè)是無法實(shí)現(xiàn)的,在 Components 看來就只有一個(gè)函數(shù),并且它還會(huì)幫你把注釋掉的函數(shù)在線上刪除;
  • Components 更多的定義是組件,所以每個(gè)組件就是一個(gè)東西,在 Components 上是沒有全局變量的。

綜上所述,對(duì)比 Plugin 和 Components 各有優(yōu)劣,我很期待產(chǎn)品策略能夠?qū)⒍吆喜⒒蛘吖δ軐?duì)齊。在本文,我選擇了 Components 來做這個(gè)項(xiàng)目。

造輪子:全局變量組件

使用 Components 做項(xiàng)目,我遇到的第一個(gè)難題是配置文件怎么辦?我有很多的配置,我難道要在每個(gè)函數(shù)中寫一遍?

于是,我做了一個(gè)新的: serverless-global 。這是一個(gè) Components 功能,用來滿足全局變量的需求。

復(fù)制代碼

Conf: component: "serverless-global" inputs: mysql_host: gz-cdb-mytest.sql.tencentcdb.com mysql_user: mytest mysql_password: mytest mysql_port: 62580 mysql_db: mytest mini_program_app_id: mytest mini_program_app_secret: mytest

在使用的時(shí)候,只需要使用${}就可以引用,例如:

復(fù)制代碼

Album_Login: component: "@serverless/tencent-scf" inputs: name: Album_Login codeUri: ./album/login handler: index.main_handler runtime: Python3.6 region: ap-shanghai environment: variables: mysql_host: ${Conf.mysql_host} mysql_port: ${Conf.mysql_port} mysql_user: ${Conf.mysql_user} mysql_password: ${Conf.mysql_password} mysql_db: ${Conf.mysql_db}

利用這個(gè)功能就可以很輕松將配置信息統(tǒng)一提取到了一個(gè)地方。需要說明的是,為什么我要把一些配置信息放在環(huán)境變量,而不是統(tǒng)一放在一個(gè)配置文件中,因?yàn)榄h(huán)境變量在 SCF 中會(huì)真的打到環(huán)境中,也就是說,你可以直接取到,我個(gè)人覺得比每次創(chuàng)建實(shí)例讀取一次配置文件性能要好一些,雖然性能優(yōu)勢(shì)有限,但是,我還是覺得這樣做是比較優(yōu)雅的。最主要的是,相比寫到代碼中和配置到單獨(dú)的配置文件中,這樣做可以將代碼分享給別人,并更好的保護(hù)敏感信息。

數(shù)據(jù)庫設(shè)計(jì)

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

數(shù)據(jù)庫部分主要對(duì)相關(guān)的表和表之間的關(guān)系進(jìn)行建立。首先需要?jiǎng)?chuàng)建項(xiàng)目所必須的表:

復(fù)制代碼

CREATE DATABASE `album`;CREATE TABLE `album`.`tags` ( `tid` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(255) NOT NULL , `remark` TEXT NULL , PRIMARY KEY (`tid`)) ENGINE = InnoDB;CREATE TABLE `album`.`category` ( `cid` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(255) NOT NULL , `sorted` INT NOT NULL DEFAULT '1' , `user` INT NOT NULL , `remark` TEXT NULL , `publish` DATE NOT NULL , `area` VARCHAR(255) NULL , PRIMARY KEY (`cid`)) ENGINE = InnoDB;CREATE TABLE `album`.`users` ( `uid` INT NOT NULL AUTO_INCREMENT , `nickname` TEXT NOT NULL , `wechat` VARCHAR(255) NOT NULL , `remark` TEXT NULL , PRIMARY KEY (`uid`)) ENGINE = InnoDB;CREATE TABLE `album`.`photo` ( `pid` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(255) NOT NULL , `small` VARCHAR(255) NOT NULL , `large` VARCHAR(255) NOT NULL , `category` INT NOT NULL , `tags` VARCHAR(255) NULL , `remark` TEXT NULL , `creattime` DATE NOT NULL , `creatarea` VARCHAR(255) NOT NULL , `user` INT NOT NULL , PRIMARY KEY (`pid`)) ENGINE = InnoDB;CREATE TABLE `album`.`photo_tags` ( `ptid` INT NOT NULL AUTO_INCREMENT , `tag` INT NOT NULL , `photo` INT NOT NULL , `remark` INT NULL , PRIMARY KEY (`ptid`)) ENGINE = InnoDB;

創(chuàng)建之后,逐步添加表之間的關(guān)系以及部分限制條件:

復(fù)制代碼

ALTER TABLE `photo_tags` ADD CONSTRAINT `photo_tags_tags_alter` FOREIGN KEY (`tag`) REFERENCES `tags`(`tid`) ON DELETE CASCADE ON UPDATE RESTRICT; ALTER TABLE `photo_tags` ADD CONSTRAINT `photo_tags_photo_alter` FOREIGN KEY (`photo`) REFERENCES `photo`(`pid`) ON DELETE CASCADE ON UPDATE RESTRICT;ALTER TABLE `photo` ADD CONSTRAINT `photo_category_alter` FOREIGN KEY (`category`) REFERENCES `category`(`cid`) ON DELETE CASCADE ON UPDATE RESTRICT;ALTER TABLE `photo` ADD CONSTRAINT `photo_user_alter` FOREIGN KEY (`user`) REFERENCES `users`(`uid`) ON DELETE CASCADE ON UPDATE RESTRICT;ALTER TABLE `category` ADD CONSTRAINT `category_user_alter` FOREIGN KEY (`user`) REFERENCES `users`(`uid`) ON DELETE CASCADE ON UPDATE RESTRICT;ALTER TABLE `tags` ADD unique(`name`);

函數(shù)功能開發(fā)

接下來,開始寫第一個(gè)函數(shù)——注冊(cè)登錄函數(shù)。因?yàn)檫@是一個(gè)小程序,所以注冊(cè)登錄實(shí)際上就是拿著用戶的 openId 去數(shù)據(jù)庫查查有沒有信息,有信息的話,就執(zhí)行登錄,沒有信息的話就 insert 一下。那么問題來了,如何連接數(shù)據(jù)庫?之所以有這樣的問題,是源自兩個(gè)因素:

  • 我們平時(shí)做項(xiàng)目的時(shí)候,并不是每次請(qǐng)求都連接一次數(shù)據(jù)庫,很多時(shí)候,數(shù)據(jù)庫的連接是可以保持下來的,但是在 Serverless 架構(gòu)下可以實(shí)現(xiàn)嗎?或者我們需要去哪里連接數(shù)據(jù)庫呢?
  • 傳統(tǒng)項(xiàng)目,我們做數(shù)據(jù)庫連接只用一個(gè)方法就可以搞定,但是函數(shù)中,每個(gè)函數(shù)都是單獨(dú)存在的,是否每個(gè)函數(shù)都要連接一下數(shù)據(jù)庫?

初始化資源探索

針對(duì)問題 1,我們來做一個(gè)實(shí)驗(yàn),先在騰訊云云函數(shù)創(chuàng)建一個(gè) test:

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

創(chuàng)建之后,瘋狂點(diǎn)擊測(cè)試按鈕,多次記錄運(yùn)行日志:

第一次

復(fù)制代碼

START RequestId: 4facbf59-3787-11ea-8026-52540029942f Event RequestId: 4facbf59-3787-11ea-8026-52540029942f 11111111 222222222 END RequestId: 4facbf59-3787-11ea-8026-52540029942f Report RequestId: 4facbf59-3787-11ea-8026-52540029942f Duration:1ms Memory:128MB MaxMemoryUsed:27.3164MB

第二次

復(fù)制代碼

START RequestId: 7aaf7921-3787-11ea-aba7-525400e4521d Event RequestId: 7aaf7921-3787-11ea-aba7-525400e4521d 222222222 END RequestId: 7aaf7921-3787-11ea-aba7-525400e4521d Report RequestId: 7aaf7921-3787-11ea-aba7-525400e4521d Duration:1ms Memory:128MB MaxMemoryUsed:27.1953MB

第三次

復(fù)制代碼

START RequestId: 742be57a-3787-11ea-b5c5-52540047de0f Event RequestId: 742be57a-3787-11ea-b5c5-52540047de0f 222222222 END RequestId: 742be57a-3787-11ea-b5c5-52540047de0f Report RequestId: 742be57a-3787-11ea-b5c5-52540047de0f Duration:1ms Memory:128MB MaxMemoryUsed:27.1953MB

第四次

復(fù)制代碼

START RequestId: 6faf934b-3787-11ea-8026-52540029942f Event RequestId: 6faf934b-3787-11ea-8026-52540029942f 222222222 END RequestId: 6faf934b-3787-11ea-8026-52540029942f Report RequestId: 6faf934b-3787-11ea-8026-52540029942f Duration:1ms Memory:128MB MaxMemoryUsed:27.1953MB

大家仔細(xì)觀察,發(fā)現(xiàn)了什么?我在函數(shù)外側(cè)寫的print("11111111")實(shí)際上只出現(xiàn)了一次,也就是說只運(yùn)行了一次,而函數(shù)內(nèi)的print("222222222")則是出現(xiàn)了多次,確切來說是每次都會(huì)出現(xiàn),函數(shù)在創(chuàng)建的時(shí)候,會(huì)讓我們寫一個(gè)執(zhí)行方法,例如index.main_handler,就是說默認(rèn)的入口文件就是index.py下的main_handler方法。通過剛才的小實(shí)驗(yàn),是不是可以認(rèn)為,云函數(shù)實(shí)際上是隨著機(jī)器或者容器啟動(dòng)同時(shí)啟動(dòng)了一個(gè)進(jìn)程(這個(gè)時(shí)候會(huì)走一次外圍的一些代碼邏輯),然后當(dāng)函數(shù)執(zhí)行的時(shí)候,會(huì)走我們指定的方法,當(dāng)函數(shù)執(zhí)行完,這個(gè)容器并不會(huì)被馬上銷毀,而是進(jìn)入銷毀的倒計(jì)時(shí),這個(gè)時(shí)候如果有請(qǐng)求來了,那么很可能復(fù)用這個(gè)容器,此時(shí)就沒有容器啟動(dòng)的說法,會(huì)直接執(zhí)行我們的方法。

按照這個(gè)邏輯,是不是我們的函數(shù),如果要在我們的方法之外,初始化數(shù)據(jù)庫就可以保證盡可能少的數(shù)據(jù)庫連接建立,而滿足更多的請(qǐng)求呢?換句話說,是不是和容器復(fù)用類似,我們就可以復(fù)用數(shù)據(jù)庫的連接了?

所以,我們可以嘗試這樣寫整個(gè)代碼(login 為例)

復(fù)制代碼

# -*- coding: utf8 -*- import osimport pymysqlimport json connection = pymysql.connect(host=os.environ.get('mysql_host'), user="root", password=os.environ.get('mysql_password'), port=int(62580), db="mini_album", charset='utf8', cursorclass=pymysql.cursors.DictCursor, autocommit=1) def getUserInfor(connection, wecaht): try: connection.ping(reconnect=True) cursor = connection.cursor() search_stmt = ( "SELECT * FROM `users` WHERE `wechat`=%s" ) data = (wecaht) cursor.execute(search_stmt, data) cursor.close() result = cursor.fetchall() return len(result) except Exception as e: print("getUserInfor", e) try: cursor.close() except: pass return False def addUseerInfor(connection, wecaht, nickname, remark): try: connection.ping(reconnect=True) cursor = connection.cursor() insert_stmt = ( "INSERT INTO users(wechat,nickname,remark) " "VALUES (%s,%s,%s)" ) data = (wecaht, nickname, remark) cursor.execute(insert_stmt, data) cursor.close() connection.close() return True except Exception as e: print(e) try: cursor.close() except: pass return False def main_handler(event, context): print(event) body = json.loads(event['body']) wecaht = body['wechat'] nickname = body['nickname'] remark = str(body['remark']) if getUserInfor(connection, wecaht) == 0: if addUseerInfor(connection, wecaht, nickname, remark): result = True else: result = False else: result = True return { "result": result }

公共組件的編寫

  • 如果這個(gè)函數(shù)要作為小程序的一個(gè)接口,那么就要接 APIGW,那么如何才能實(shí)現(xiàn)本地測(cè)試呢?每次都發(fā)到線上配置 APIGW 觸發(fā)器才能測(cè)試,這顯然是不現(xiàn)實(shí)的。
  • 這個(gè)函數(shù)需要數(shù)據(jù)庫的連接,需要獲取用戶的信息等,難道別的函數(shù)不需要嗎?如果需要,那么是否每個(gè)函數(shù)都要重復(fù)寫這部分代碼?或者說,代碼的復(fù)用應(yīng)該如何處理呢?是否可以提取公共組件呢?

所以,我將這個(gè)函數(shù)進(jìn)行了規(guī)范化和完整化:

復(fù)制代碼

# -*- coding: utf8 -*- import json try: import returnCommon from mysqlCommon import mysqlCommonexcept: import common.testCommon common.testCommon.setEnv() import common.returnCommon as returnCommon from common.mysqlCommon import mysqlCommon mysql = mysqlCommon() def main_handler(event, context): try: print(event) body = json.loads(event['body']) wecaht = body['wechat'] nickname = body['nickname'] remark = str(body['remark']) if not wecaht: return returnCommon.return_msg(True, "請(qǐng)使用微信小程序登陸本頁面。") if not mysql.getUserInfor(wecaht): if not nickname: return returnCommon.return_msg(True, "參數(shù)異常,請(qǐng)重試。") if mysql.addUserInfor(wecaht, nickname, remark): return returnCommon.return_msg(False, "注冊(cè)成功") return returnCommon.return_msg(True, "注冊(cè)失敗,請(qǐng)重試。") return returnCommon.return_msg(False, "登錄成功") except Exception as e: print(e) return returnCommon.return_msg(True, "用戶信息異常,請(qǐng)聯(lián)系管理員處理") def test(): event = { "requestContext": { "serviceId": "service-f94sy04v", "path": "/test/{path}", "httpMethod": "POST", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "identity": { "secretId": "abdcdxxxxxxxsdfs" }, "sourceIp": "14.17.22.34", "stage": "release" }, "headers": { "Accept-Language": "en-US,en,cn", "Accept": "text/html,application/xml,application/json", "Host": "service-3ei3tii4-251000691.ap-guangzhou.apigateway.myqloud.com", "User-Agent": "User Agent String" }, "body": json.dumps({ "wechat": "12345", "nickname": "test", "remark": "", }), "pathParameters": { "path": "value" }, "queryStringParameters": { "foo": "bar" }, "headerParameters": { "Refer": "10.0.2.14" }, "stageVariables": { "stage": "release" }, "path": "/test/value", "queryString": { "foo": "bar", "bob": "alice" }, "httpMethod": "POST" } print(main_handler(event, None)) if __name__ == "__main__": test()

數(shù)據(jù)庫等一些公共組件,統(tǒng)一放在common目錄下,例如mysqlCommon.py(部分):

復(fù)制代碼

# -*- coding: utf8 -*- import osimport randomimport pymysqlimport datetime try: import cosClientexcept: import common.cosClient as cosClient class mysqlCommon: def __init__(self): self.getConnection({ "host": os.environ.get('mysql_host'), "user": os.environ.get('mysql_user'), "port": int(os.environ.get('mysql_port')), "db": os.environ.get('mysql_db'), "password": os.environ.get('mysql_password') }) def getConnection(self, conf): self.connection = pymysql.connect(host=conf['host'], user=conf['user'], password=conf['password'], port=int(conf['port']), db=conf['db'], charset='utf8', cursorclass=pymysql.cursors.DictCursor, autocommit=1) def doAction(self, stmt, data): try: self.connection.ping(reconnect=True) cursor = self.connection.cursor() cursor.execute(stmt, data) result = cursor cursor.close() return result except Exception as e: print(e) try: cursor.close() except: pass return False def addUserInfor(self, wecaht, nickname, remark): insert_stmt = ( "INSERT INTO users(wechat, nickname, remark) " "VALUES (%s,%s,%s)" ) data = (wecaht, nickname, remark) result = self.doAction(insert_stmt, data) return False if result == False else True

這樣做的好處是:

  • 將數(shù)據(jù)庫提取出一個(gè)公共組件,便于維護(hù)
  • 在 login 函數(shù)中,根據(jù)不同的時(shí)期(本地開發(fā)和線上)可以導(dǎo)入不同的模塊

便于開發(fā)與測(cè)試的方法

由于云函數(shù)的測(cè)試非常不友好,所以為了在編寫代碼時(shí)可以更快地模擬線上環(huán)境,我選擇通過增加test()方法來模擬觸發(fā)器情況,進(jìn)行簡(jiǎn)單的測(cè)試。

復(fù)制代碼

try: import cosClientexcept: import common.cosClient as cosClient

這樣會(huì)更加便利,同時(shí)模擬網(wǎng)關(guān),做一個(gè)測(cè)試方法:

復(fù)制代碼

def test(): event = { "requestContext": { "serviceId": "service-f94sy04v", "path": "/test/{path}", "httpMethod": "POST", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "identity": { "secretId": "abdcdxxxxxxxsdfs" }, "sourceIp": "14.17.22.34", "stage": "release" }, "headers": { "Accept-Language": "en-US,en,cn", "Accept": "text/html,application/xml,application/json", "Host": "service-3ei3tii4-251000691.ap-guangzhou.apigateway.myqloud.com", "User-Agent": "User Agent String" }, "body": json.dumps({ "wechat": "12345", "nickname": "test", "remark": "", }), "pathParameters": { "path": "value" }, "queryStringParameters": { "foo": "bar" }, "headerParameters": { "Refer": "10.0.2.14" }, "stageVariables": { "stage": "release" }, "path": "/test/value", "queryString": { "foo": "bar", "bob": "alice" }, "httpMethod": "POST" } print(main_handler(event, None))

增加本地測(cè)試時(shí),指定test()方法:

復(fù)制代碼

if __name__ == "__main__": test()

這樣,線上觸發(fā)時(shí)會(huì)默認(rèn)執(zhí)行main_handler, 而本地執(zhí)行,則會(huì)通過test走入main_handler,我們可以邊開發(fā),邊測(cè)試,全部弄好之后再部署到線上。

線上獲取配置信息是通過獲取環(huán)境變量,本地又該如何執(zhí)行?我們需要先進(jìn)行這個(gè)操作:

復(fù)制代碼

# -*- coding: utf8 -*- import yamlimport os def setEnv(): file = open("/Users/dfounderliu/Documents/code/AIAlbum/serverless.yaml", 'r', encoding="utf-8") file_data = file.read() file.close() data = yaml.load(file_data) for eveKey, eveValue in data['Conf']['inputs'].items(): print(eveKey, eveValue) os.environ[eveKey] = str(eveValue)

這樣,我們這個(gè)文件就既可以線上直接用,也可以本地直接用了!

那么,Yaml 怎么寫?

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

然后,我們可以在部署函數(shù)的時(shí)候?qū)⒐步M件引入項(xiàng)目中。

本地形式:

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

線上形式:

基于 Serverless Framework 的人工智能小程序開發(fā)(人工智能小程序代碼)

項(xiàng)目部署

  • 在使用之前,需要先有一個(gè)騰訊云的賬號(hào),并且開通了 COS、COS、APIGW 以及 CDB 等相關(guān)產(chǎn)品權(quán)限;
  • 將項(xiàng)目 clone 到本地,配置自己的密鑰信息、數(shù)據(jù)庫信息。配置文件在cloudFunction目錄下的serverless.yaml中:

復(fù)制代碼

# 函數(shù)們的整體配置信息Conf: component: "serverless-global" inputs: region: ap-shanghai runtime: Python3.6 handler: index.main_handler include_common: ./common mysql_host: gz-c************************.com mysql_user: root mysql_password: S************************! mysql_port: 6************************0 mysql_db: album mini_program_app_id: asdsa************************dddd mini_program_app_secret: fd340c4************************8744ee tencent_secret_id: AKID1y************************l1q0kK tencent_secret_key: cCoJ************************FZj5Oa tencent_appid: 1256773370 cos_bucket: 'album-1256773370' domain: album.0duzahn.com

由于我目前使用的是 Serverless Components,沒有全局變量等,所以在此處增加了全局變量組件,在這里設(shè)置好全局變量,在之后的 Components 中可以直接引用,例如:

復(fù)制代碼

# 創(chuàng)建存儲(chǔ)桶CosBucket: component: '@serverless/tencent-website' inputs: code: src: ./cos region: ${Conf.region} bucketName: ${Conf.cos_bucket}

  • 安裝必備工具,例如必須要安裝 Serverless Framework(可以參考: https://cloud.tencent.com/document/product/1154/39005), 同樣由于本項(xiàng)目后臺(tái)開發(fā)語言是 Python,您也需要一些 Python 的開發(fā)工具以及包管理工具,以及小程序云開發(fā)的 IDE;
  • 在部分文件夾下安裝相對(duì)應(yīng)的依賴:cloudFunction/album/prdiction需要安裝 Pillow, opencv,tensorflow,jiebacloudFunction/album/getPhotoSearch需要安裝 gensim,jieba 以及 collectionscloudFunction/album/compression需要安裝 Pillow
    注意,在安裝的時(shí)候一定要用 CentOS 操作系統(tǒng),并且 Python 要 3.6 版本,如果沒相對(duì)應(yīng)系統(tǒng),可以在這里打包對(duì)應(yīng)的依賴: http://serverless.0duzhan.com/app/scf_python_package_download/
  • 將項(xiàng)目部署到云端,只需要通過指令serverless –debug即可:

復(fù)制代碼

DEBUG ─ Resolving the template's static variables. DEBUG ─ Collecting components from the template. DEBUG ─ Downloading any NPM components found in the template. DEBUG ─ Analyzing the template's components dependencies. DEBUG ─ Creating the template's components graph. DEBUG ─ Syncing template state. DEBUG ─ Executing the template's components graph. DEBUG ─ Starting API-Gateway deployment with name APIService in the ap-shanghai region ... ... DEBUG ─ Updating configure... DEBUG ─ Created function Album_Get_Photo_Search successful DEBUG ─ Setting tags for function Album_Get_Photo_Search DEBUG ─ Creating trigger for function Album_Get_Photo_Search DEBUG ─ Deployed function Album_Get_Photo_Search successful DEBUG ─ Uploaded package successful /Users/dfounderliu/Documents/code/AIAlbum/.serverless/Album_Prediction.zip DEBUG ─ Creating function Album_Prediction DEBUG ─ Updating code... DEBUG ─ Updating configure... DEBUG ─ Created function Album_Prediction successful DEBUG ─ Setting tags for function Album_Prediction DEBUG ─ Creating trigger for function Album_Prediction DEBUG ─ Trigger timer: timer not changed DEBUG ─ Deployed function Album_Prediction successful Conf: region: ap-shanghai ... ... - path: /photo/delete method: ANY apiId: api-g9u6r9wq - path: /album/delete method: ANY apiId: api-b4c4xrq8 - path: /album/add method: ANY apiId: api-ml6q5koy 156s ? APIService ? done

這個(gè)過程只用了 156s 就部署了所有函數(shù),然后打開小程序的 id 帶入miniProgram目錄,并且填寫自己的appid在文件project.config.json的第 17 行,同時(shí)也要配置自己項(xiàng)目的基礎(chǔ)目錄,就是 API 網(wǎng)關(guān)給我們返回的地址,寫在app.js的第 10 行,此時(shí)項(xiàng)目就可以運(yùn)行起來了。

總結(jié)

本文中的例子是通過 Serverless 架構(gòu)使用 Python 語言開發(fā)了一個(gè)微信小程序,這里面涉及到了數(shù)據(jù)庫的增刪改查,公共組件的提取,如何定義 Components 的全局變量,如何本地調(diào)試和線上觸發(fā)二者兼得,以及在什么地方初始化數(shù)據(jù)庫"性價(jià)比較高"。希望通過這樣一個(gè)簡(jiǎn)單的例子,可以讓 Serverless 在更多的領(lǐng)域都有實(shí)際的應(yīng)用價(jià)值,可以給更多人靈感和啟發(fā):Serverless?萬物都可以 Serverless 么?讓我們一起來嘗試更多 Serverless 架構(gòu)的應(yīng)用領(lǐng)域吧。

關(guān)注我并轉(zhuǎn)發(fā)此篇文章,私信我“領(lǐng)取資料”,即可免費(fèi)獲得InfoQ價(jià)值4999元迷你書!

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
在線咨詢
分享本頁
返回頂部