前言
最近在學(xué)習(xí)Serverless架構(gòu)相關(guān)的知識(shí),學(xué)習(xí)過程中發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象:無論是教程示例,還是場(chǎng)景實(shí)例,Serverless架構(gòu)中鮮有出現(xiàn)數(shù)據(jù)庫(kù)的身影。各類文章所介紹的Serverless架構(gòu)應(yīng)用場(chǎng)景中,也幾乎都是無需數(shù)據(jù)庫(kù)的業(yè)務(wù)場(chǎng)景。在一些教程文章中,對(duì)于一些需要進(jìn)行數(shù)據(jù)存儲(chǔ)的場(chǎng)景,通常的做法是將數(shù)據(jù)存儲(chǔ)在 JSON 文件中,然后上傳到對(duì)象存儲(chǔ)服務(wù)中,在搜索相關(guān)資料的過程中甚至還發(fā)現(xiàn)了SQLite+對(duì)象存儲(chǔ)這種很硬核的數(shù)據(jù)存儲(chǔ)方式,這些方法顯然只能應(yīng)對(duì)簡(jiǎn)單的數(shù)據(jù)存儲(chǔ)。那么數(shù)據(jù)庫(kù)作為互聯(lián)網(wǎng)時(shí)代的基石,從單體架構(gòu)到微服務(wù)架構(gòu),其都扮演著舉足輕重的角色,為何偏偏在Serverless架構(gòu)中存在感這么低呢?
從用戶的角度來看,Serverless架構(gòu)雖然有著免運(yùn)維、彈性伸縮、按需付費(fèi)等優(yōu)點(diǎn),但同時(shí)由于其本身構(gòu)建復(fù)雜,擴(kuò)展性不強(qiáng),維護(hù)困難等缺點(diǎn),用戶一般只會(huì)用其來實(shí)現(xiàn)一些簡(jiǎn)單的業(yè)務(wù),追求的是低成本、輕量級(jí)、無需維護(hù)長(zhǎng)期運(yùn)行的服務(wù)器。此時(shí)如果引入傳統(tǒng)的數(shù)據(jù)庫(kù),整個(gè)架構(gòu)就會(huì)重新變得厚重,用戶看中的優(yōu)勢(shì)也被完全破壞了,違背了使用Serverless架構(gòu)的初衷。
那么,有什么方案可以解決這個(gè)問題呢?答案就是讓數(shù)據(jù)庫(kù)也Serverless化,讓數(shù)據(jù)庫(kù)也具備免運(yùn)維、彈性伸縮、按需付費(fèi)等特點(diǎn),這就是近兩年比較火熱的Serverless數(shù)據(jù)庫(kù)
。剛好在CSDN看到騰訊云 TDSQL-C Serverless 產(chǎn)品測(cè)評(píng)活動(dòng),可以免費(fèi)體驗(yàn)騰訊云推出的Serverless數(shù)據(jù)庫(kù)產(chǎn)品TDSQL-C Serverless
。本篇博文就帶大家一起,使用騰訊云云函數(shù)+TDSQL-C Serverless實(shí)現(xiàn)一個(gè)“時(shí)光郵局”,體驗(yàn)全棧Serverless的魅力。
一、TDSQL-C Serverless簡(jiǎn)介
TDSQL 是騰訊云自研的新一代云原生關(guān)系型數(shù)據(jù)庫(kù)。融合了傳統(tǒng)數(shù)據(jù)庫(kù)、云計(jì)算與新硬件技術(shù)的優(yōu)勢(shì),100% 兼容 MySQL,為用戶提供極致彈性、高性能、高可用、高可靠、安全的數(shù)據(jù)庫(kù)服務(wù)。實(shí)現(xiàn)超百萬 QPS 的高吞吐、PB 級(jí)海量分布式智能存儲(chǔ)、Serverless 秒級(jí)伸縮,助力企業(yè)加速完成數(shù)字化轉(zhuǎn)型。
TDSQL-C Serverless 服務(wù)是騰訊云自研的新一代云原生關(guān)系型數(shù)據(jù)庫(kù) TDSQL-C MySQL 版的無服務(wù)器架構(gòu)版,是全 Serverless 架構(gòu)的云原生數(shù)據(jù)庫(kù)。TDSQL-C Serverless 服務(wù)支持按實(shí)際計(jì)算和存儲(chǔ)資源使用量收取費(fèi)用,不用不付費(fèi),將騰訊云云原生技術(shù)普惠用戶。其架構(gòu)特點(diǎn)如下:
-
按需啟動(dòng),不需要時(shí)可關(guān)閉。
-
自動(dòng)擴(kuò)展/收縮。
-
縮放對(duì)應(yīng)用程序無影響。
TDSQL-C Serverless 服務(wù)優(yōu)勢(shì):
自動(dòng)駕駛(Autopilot):數(shù)據(jù)庫(kù)根據(jù)業(yè)務(wù)負(fù)載自動(dòng)啟動(dòng)停止,無感擴(kuò)縮容,擴(kuò)縮容過程不會(huì)斷開連接。
按使用計(jì)費(fèi)(Utility Pricing):按實(shí)際使用的計(jì)算和存儲(chǔ)量計(jì)費(fèi),不用不付費(fèi),按秒計(jì)量,按小時(shí)結(jié)算。
二、云函數(shù)+TDSQL-C Serverless實(shí)現(xiàn)“時(shí)光郵局”
1.購(gòu)買TDSQL-C Serverless實(shí)例
TDSQL-C Serverless購(gòu)買地址:https://buy.cloud.tencent.com/cynosdb,關(guān)鍵配置說明:
-
實(shí)例形態(tài)選擇
Serverless
-
網(wǎng)絡(luò)選擇:后續(xù)創(chuàng)建云函數(shù)時(shí),需要選擇與這里一致的VPC及子網(wǎng)
-
算力配置:
彈性伸縮
的關(guān)鍵配置,與購(gòu)買傳統(tǒng)云數(shù)據(jù)庫(kù)需要挑選固定規(guī)格不同的是,TDSQL-C Serverless只需要配置最小CCU和最大CCU即可。CCU(TDSQL-C Compute Unit)為 Serverless 的計(jì)算計(jì)費(fèi)單位,1CCU約等于1C2G的計(jì)算資源。根據(jù)配置的CCU范圍,TDSQL-C Serverless可以在這個(gè)區(qū)間內(nèi)實(shí)現(xiàn)自動(dòng)的彈性伸縮 -
自動(dòng)暫停:
按需付費(fèi)
的關(guān)鍵配置,數(shù)據(jù)庫(kù)在設(shè)定時(shí)間內(nèi)無連接將自動(dòng)進(jìn)入暫停狀態(tài),暫停后計(jì)算將不再計(jì)費(fèi)。當(dāng)有連接訪問時(shí),系統(tǒng)會(huì)秒級(jí)自動(dòng)啟動(dòng)處于暫停狀態(tài)的數(shù)據(jù)庫(kù),用戶不需設(shè)置重連機(jī)制。 -
其他購(gòu)買配置與傳統(tǒng)數(shù)據(jù)庫(kù)大同小異,根據(jù)自身需求配置即可
2.建庫(kù)建表
通過DMC數(shù)據(jù)庫(kù)管理工具可以快速的完成建庫(kù)建表等操作,建表語句:
CREATE TABLE `future_email` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` date DEFAULT NULL,
`email` varchar(50) DEFAULT '',
`letter` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4
3.創(chuàng)建云函數(shù)
-
關(guān)鍵配置:?jiǎn)⒂盟接芯W(wǎng)絡(luò),保證可以內(nèi)網(wǎng)訪問TDSQL-C Serverless
-
創(chuàng)建API網(wǎng)關(guān)觸發(fā)器、定時(shí)觸發(fā)器
-
云函數(shù)代碼
''' 原作者:乂乂又又 原文鏈接:https://cloud.tencent.com/developer/article/1618588 修改說明:原文采用JSON文件+COS實(shí)現(xiàn)數(shù)據(jù)存儲(chǔ),本文修改為使用TDSQL-C Serverless作為數(shù)據(jù)存儲(chǔ)方案 ''' # -*- coding: utf-8 -*- import json import datetime import random from email.mime.text import MIMEText from email.header import Header from email.utils import formataddr import smtplib import pymysql # 配置TDSQL-C Serverless連接信息 host = '172.16.0.3' port = 3306 user = 'root' password = 'xxxxxxx' database = 'test' #配置發(fā)件郵箱 mail_host = "smtp.163.com" mail_user = "xxxx@163.com" mail_pass = "xxxxxxxxxxxx" mail_port = 465 #smtp郵箱實(shí)例 smtpObj = smtplib.SMTP_SSL(mail_host, mail_port) #獲取所有信件 def getletters(): db = pymysql.connect(host=host,port=port,user=user,password=password,database=database) cursor = db.cursor() # SQL 查詢語句 sql = "SELECT * FROM future_email WHERE date = %s" try: cursor.execute(sql, (today())) results = cursor.fetchall() data_list = [] for row in results: data_list.append(list(row)) return data_list except: print("Error: unable to fetch data") db.close() #添加信件 def addletter(date, email, letter): db = pymysql.connect(host=host, port=port, user=user, password=password, database=database) cursor = db.cursor() # SQL 插入語句 sql = "INSERT INTO future_email (date, email, letter) VALUES (%s, %s, %s)" try: cursor.execute(sql, (date, email, letter)) db.commit() print("Data inserted successfully.") except pymysql.Error as e: print(f"MySQL Error {e.args[0]}: {e.args[1]}") db.rollback() return False db.close() return True #刪除信件 def delletter(id): db = pymysql.connect(host=host, port=port, user=user, password=password, database=database) cursor = db.cursor() # SQL 刪除語句 sql = "DELETE FROM future_email WHERE id = %s" try: cursor.execute(sql, (id)) db.commit() print("Data deleted successfully.") except pymysql.Error as e: print(f"MySQL Error {e.args[0]}: {e.args[1]}") db.rollback() db.close() # 獲取今日日期 def today(): return datetime.datetime.now().strftime("%Y-%m-%d") # 根據(jù)時(shí)間生成uuid def randomKey(): return ''.join(random.sample('zyxwvutsrqponmlkjihgfedcba0123456789', 6)) # api網(wǎng)關(guān)回復(fù)消息格式化 def apiReply(reply, html=False, code=200): htmlStr = r'''<!DOCTYPE html> <html lang="zh-cn"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <title>給未來的自己寫封信</title> <style> html, body { padding: 0px; margin: 0px; height: 100vh; } .main { display: flex; flex-direction: column; justify-content: center; align-items: center; } .main_phone { display: flex; flex-direction: column; justify-content: start; align-items: center; } </style> </head> <body id='body'> <div class="main" style="width: 80vw;"> <div style="height: 5vh;"></div> <div id='letter_top'> <p style="text-align: center;">開始寫信</p> <wired-textarea id="letter" style="height: 320px;width: 300px;" placeholder="此刻平靜地寫下一封信,給未來的自己一份溫暖..." elevation="6" rows="14"></wired-textarea> </div> <div style="display: flex;align-items: center;justify-content: center;"> <div id='letter_left'> <p style="text-align: center;">開始寫信</p> <wired-textarea id="letter" style="height: 320px;width: 300px;" placeholder="此刻平靜地寫下一封信,給未來的自己一份溫暖..." elevation="6" rows="14"></wired-textarea> </div> <div style="width: 16px;"></div> <div> <p style="text-align: center;">送信日期</p> <wired-calendar id="calendar"></wired-calendar> </div> </div> <wired-divider style="margin: 16px 0;"></wired-divider> <p id="hitokoto"></p> <div> <wired-input id="email" placeholder="收件郵箱"></wired-input> <wired-button οnclick="send()">投遞</wired-button> </div> <div style="height: 5vh;"></div> </div> <script> let datex = ''; let myEmail = document.getElementById('email'); let myLetter = document.getElementById('letter'); let myCalendar = document.getElementById('calendar'); let width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth let height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight let pc = width >= height let today = new Date(); let info = today.toString().split(' '); let selected = `${info[1]} ${today.getDate()}, ${today.getFullYear()}`; document.getElementById('body').classList.add(pc ? 'main' : 'main_phone'); if(pc){ document.getElementById('letter_top').remove(); document.getElementById('letter_left').style.display = 'block'; myLetter = document.getElementById('letter'); } else { document.getElementById('letter_top').style.display = 'block'; document.getElementById('letter_left').remove(); myLetter = document.getElementById('letter'); } myCalendar.setAttribute("selected", selected); myCalendar.addEventListener('selected', () => { let selectedObject = myCalendar.value; let date = new Date(new Date().setDate(selectedObject.date.getDate())); datex = date.toISOString().substr(0, 10); }); function send() { console.log(datex, myEmail.value, myLetter.value) if (datex.length < 1 || myEmail.value.length < 1 || myLetter.value.length < 1) { alert('信件內(nèi)容、送信日期或投遞郵箱不能為空'); return; } fetch(window.location.href, { method: 'POST', body: JSON.stringify({ date: datex, email: myEmail.value, letter: myLetter.value }) }).then(res => res.json()) .catch(error => console.error('Error:', error)) .then(response => alert(response.ok ? '添加成功:)' : '添加失敗:(')); } </script> <script src="https://v1.hitokoto.cn/?encode=js&select=%23hitokoto" defer></script> <script src="https://unpkg.com/wired-elements@2.0.5/lib/wired-elements-bundled.js"></script> </body> </html>''' return { "isBase64Encoded": False, "statusCode": code, "headers": {'Content-Type': 'text/html' if html else 'application/json', "Access-Control-Allow-Origin": "*"}, "body": htmlStr if html else json.dumps(reply, ensure_ascii=False) } #登陸郵箱 def loginEmail(): try: smtpObj.login(mail_user, mail_pass) return True except smtplib.SMTPException as e: print(e) return False #發(fā)送郵件 def sendEmail(letter): message = MIMEText(letter[3], 'plain', 'utf-8') message['From'] = formataddr(('時(shí)間郵局', mail_user)) message['To'] = letter[2] message['Subject'] = '一封來自很久以前的信' try: smtpObj.sendmail(mail_user, letter[2], message.as_string()) print("send email success") return True except smtplib.SMTPException as e: print(f"Send EMail Error {e.args[0]}: {e.args[1]}") return False #每天定時(shí)檢查需要發(fā)送的信件 def check_send_letters(): loginEmail() letters = getletters() for letter in letters : if letter[1] == datetime.date.today(): status = sendEmail(letter) if(status): delletter(letter[0]) def main_handler(event, context): if 'Time' in event.keys(): # 來自定時(shí)觸發(fā)器 check_send_letters() return if 'httpMethod' in event.keys(): # 來自api網(wǎng)關(guān)觸發(fā)器 if event['httpMethod'] == 'GET': return apiReply('', html=True) # 返回網(wǎng)頁 if event['httpMethod'] == 'POST': # 添加信件 body = json.loads(event['body']) flag = addletter(body['date'], body['email'], body['letter']) return apiReply({ 'ok': True if flag else False, 'message': '添加成功' if flag else '添加失敗' }) return apiReply('', html=True)
4.查看效果
-
頁面效果
-
TDSQL-C Serverless內(nèi)存儲(chǔ)的數(shù)據(jù)
-
郵件效果
5.TDSQL-C Serverless狀態(tài)以及賬單
-
當(dāng)發(fā)生請(qǐng)求時(shí)的資源使用情況,可以清晰的看到TDSQL-C Serverless的自動(dòng)啟動(dòng)過程
-
根據(jù)購(gòu)買時(shí)配置的
自動(dòng)暫停
時(shí)間,10分鐘后TDSQL-C Serverless已自動(dòng)暫停 -
賬單
總結(jié)
TDSQL-C Serverless是一款完全符合Serverless特征的關(guān)系型數(shù)據(jù)庫(kù)產(chǎn)品,無需運(yùn)維
,彈性伸縮
,按需付費(fèi)
。有了它,數(shù)據(jù)庫(kù)將不再是Serverless架構(gòu)的“短板”,Serverless架構(gòu)的落地場(chǎng)景也將不再局限于簡(jiǎn)單業(yè)務(wù)的處理,當(dāng)遇到數(shù)據(jù)存儲(chǔ)需求時(shí),也不用再退而求其次的去使用JSON文件+對(duì)象存儲(chǔ)的方案。
單從架構(gòu)優(yōu)勢(shì)上來說,TDSQL-C Serverless的出現(xiàn),打破了Serverless架構(gòu)落地的最后一關(guān),極大的豐富了Serverless架構(gòu)的應(yīng)用落地場(chǎng)景,用戶可以體驗(yàn)到從前端、到后端、再到數(shù)據(jù)存儲(chǔ)落地的全棧Serverless。文章來源:http://www.zghlxwxcb.cn/news/detail-734042.html
TDSQL-C Serverless繼承了Serverless架構(gòu)的優(yōu)點(diǎn)的同時(shí),不可避免的也會(huì)存在Serverless的一些缺點(diǎn),最直觀的一個(gè)缺點(diǎn)就是冷啟動(dòng)時(shí)間
過長(zhǎng),云函數(shù)當(dāng)前已經(jīng)可以做到毫秒級(jí)的冷啟動(dòng),但TDSQL-C Serverless的冷啟動(dòng)時(shí)長(zhǎng)卻還在秒級(jí)。希望TDSQL-C Serverless在后續(xù)的版本可以持續(xù)優(yōu)化這個(gè)耗時(shí),將騰訊云云原生技術(shù)普惠用戶。文章來源地址http://www.zghlxwxcb.cn/news/detail-734042.html
到了這里,關(guān)于【騰訊云 TDSQL-C Serverless 產(chǎn)品體驗(yàn)】云函數(shù)+TDSQL-C Serverless:體驗(yàn)全棧Serverless的魅力的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!