前言
最近在学习Serverless架构相关的知识,学习过程中发现一个有趣的现象:无论是教程示例,还是场景实例,Serverless架构中鲜有出现数据库的身影。各类文章所介绍的Serverless架构应用场景中,也几乎都是无需数据库的业务场景。在一些教程文章中,对于一些需要进行数据存储的场景,通常的做法是将数据存储在 JSON 文件中,然后上传到对象存储服务中,在搜索相关资料的过程中甚至还发现了SQLite+对象存储这种很硬核的数据存储方式,这些方法显然只能应对简单的数据存储。那么数据库作为互联网时代的基石,从单体架构到微服务架构,其都扮演着举足轻重的角色,为何偏偏在Serverless架构中存在感这么低呢?
从用户的角度来看,Serverless架构虽然有着免运维、弹性伸缩、按需付费等优点,但同时由于其本身构建复杂,扩展性不强,维护困难等缺点,用户一般只会用其来实现一些简单的业务,追求的是低成本、轻量级、无需维护长期运行的服务器。此时如果引入传统的数据库,整个架构就会重新变得厚重,用户看中的优势也被完全破坏了,违背了使用Serverless架构的初衷。
那么,有什么方案可以解决这个问题呢?答案就是让数据库也Serverless化,让数据库也具备免运维、弹性伸缩、按需付费等特点,这就是近两年比较火热的Serverless数据库
。刚好在CSDN看到腾讯云 TDSQL-C Serverless 产品测评活动,可以免费体验腾讯云推出的Serverless数据库产品TDSQL-C Serverless
。本篇博文就带大家一起,使用腾讯云云函数+TDSQL-C Serverless实现一个“时光邮局”,体验全栈Serverless的魅力。
一、TDSQL-C Serverless简介
TDSQL 是腾讯云自研的新一代云原生关系型数据库。融合了传统数据库、云计算与新硬件技术的优势,100% 兼容 MySQL,为用户提供极致弹性、高性能、高可用、高可靠、安全的数据库服务。实现超百万 QPS 的高吞吐、PB 级海量分布式智能存储、Serverless 秒级伸缩,助力企业加速完成数字化转型。
TDSQL-C Serverless 服务是腾讯云自研的新一代云原生关系型数据库 TDSQL-C MySQL 版的无服务器架构版,是全 Serverless 架构的云原生数据库。TDSQL-C Serverless 服务支持按实际计算和存储资源使用量收取费用,不用不付费,将腾讯云云原生技术普惠用户。其架构特点如下:
-
按需启动,不需要时可关闭。
-
自动扩展/收缩。
-
缩放对应用程序无影响。
TDSQL-C Serverless 服务优势:
自动驾驶(Autopilot):数据库根据业务负载自动启动停止,无感扩缩容,扩缩容过程不会断开连接。
按使用计费(Utility Pricing):按实际使用的计算和存储量计费,不用不付费,按秒计量,按小时结算。
二、云函数+TDSQL-C Serverless实现“时光邮局”
1.购买TDSQL-C Serverless实例
TDSQL-C Serverless购买地址:https://buy.cloud.tencent.com/cynosdb,关键配置说明:
-
实例形态选择
Serverless
-
网络选择:后续创建云函数时,需要选择与这里一致的VPC及子网
-
算力配置:
弹性伸缩
的关键配置,与购买传统云数据库需要挑选固定规格不同的是,TDSQL-C Serverless只需要配置最小CCU和最大CCU即可。CCU(TDSQL-C Compute Unit)为 Serverless 的计算计费单位,1CCU约等于1C2G的计算资源。根据配置的CCU范围,TDSQL-C Serverless可以在这个区间内实现自动的弹性伸缩 -
自动暂停:
按需付费
的关键配置,数据库在设定时间内无连接将自动进入暂停状态,暂停后计算将不再计费。当有连接访问时,系统会秒级自动启动处于暂停状态的数据库,用户不需设置重连机制。 -
其他购买配置与传统数据库大同小异,根据自身需求配置即可
2.建库建表
通过DMC数据库管理工具可以快速的完成建库建表等操作,建表语句:
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.创建云函数
-
关键配置:启用私有网络,保证可以内网访问TDSQL-C Serverless
-
创建API网关触发器、定时触发器
-
云函数代码
''' 原作者:乂乂又又 原文链接:https://cloud.tencent.com/developer/article/1618588 修改说明:原文采用JSON文件+COS实现数据存储,本文修改为使用TDSQL-C Serverless作为数据存储方案 ''' # -*- 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' #配置发件邮箱 mail_host = "smtp.163.com" mail_user = "xxxx@163.com" mail_pass = "xxxxxxxxxxxx" mail_port = 465 #smtp邮箱实例 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") # 根据时间生成uuid def randomKey(): return ''.join(random.sample('zyxwvutsrqponmlkjihgfedcba0123456789', 6)) # api网关回复消息格式化 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('信件内容、送信日期或投递邮箱不能为空'); 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 #发送邮件 def sendEmail(letter): message = MIMEText(letter[3], 'plain', 'utf-8') message['From'] = formataddr(('时间邮局', 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 #每天定时检查需要发送的信件 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(): # 来自定时触发器 check_send_letters() return if 'httpMethod' in event.keys(): # 来自api网关触发器 if event['httpMethod'] == 'GET': return apiReply('', html=True) # 返回网页 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内存储的数据
-
邮件效果
5.TDSQL-C Serverless状态以及账单
-
当发生请求时的资源使用情况,可以清晰的看到TDSQL-C Serverless的自动启动过程
-
根据购买时配置的
自动暂停
时间,10分钟后TDSQL-C Serverless已自动暂停 -
账单
总结
TDSQL-C Serverless是一款完全符合Serverless特征的关系型数据库产品,无需运维
,弹性伸缩
,按需付费
。有了它,数据库将不再是Serverless架构的“短板”,Serverless架构的落地场景也将不再局限于简单业务的处理,当遇到数据存储需求时,也不用再退而求其次的去使用JSON文件+对象存储的方案。
单从架构优势上来说,TDSQL-C Serverless的出现,打破了Serverless架构落地的最后一关,极大的丰富了Serverless架构的应用落地场景,用户可以体验到从前端、到后端、再到数据存储落地的全栈Serverless。文章来源:https://www.toymoban.com/news/detail-734042.html
TDSQL-C Serverless继承了Serverless架构的优点的同时,不可避免的也会存在Serverless的一些缺点,最直观的一个缺点就是冷启动时间
过长,云函数当前已经可以做到毫秒级的冷启动,但TDSQL-C Serverless的冷启动时长却还在秒级。希望TDSQL-C Serverless在后续的版本可以持续优化这个耗时,将腾讯云云原生技术普惠用户。文章来源地址https://www.toymoban.com/news/detail-734042.html
到了这里,关于【腾讯云 TDSQL-C Serverless 产品体验】云函数+TDSQL-C Serverless:体验全栈Serverless的魅力的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!