前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【腾讯云 TDSQL-C Serverless 产品体验】云函数+TDSQL-C Serverless:体验全栈Serverless的魅力

【腾讯云 TDSQL-C Serverless 产品体验】云函数+TDSQL-C Serverless:体验全栈Serverless的魅力

原创
作者头像
用户10819167
修改2023-11-15 10:39:10
2050
修改2023-11-15 10:39:10
举报
文章被收录于专栏:TDSQL-CTDSQL-C

前言

最近在学习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数据库管理工具可以快速的完成建库建表等操作,建表语句:

代码语言:javascript
复制
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网关触发器、定时触发器
  • 云函数代码
代码语言:javascript
复制
'''
原作者:乂乂又又
原文链接: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 onclick="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已自动暂停
  • 账单
fig:
fig:

总结

TDSQL-C Serverless是一款完全符合Serverless特征的关系型数据库产品,无需运维,弹性伸缩,按需付费。有了它,数据库将不再是Serverless架构的“短板”,Serverless架构的落地场景也将不再局限于简单业务的处理,当遇到数据存储需求时,也不用再退而求其次的去使用JSON文件+对象存储的方案。

单从架构优势上来说,TDSQL-C Serverless的出现,打破了Serverless架构落地的最后一关,极大的丰富了Serverless架构的应用落地场景,用户可以体验到从前端、到后端、再到数据存储落地的全栈Serverless。

TDSQL-C Serverless继承了Serverless架构的优点的同时,不可避免的也会存在Serverless的一些缺点,最直观的一个缺点就是冷启动时间过长,云函数当前已经可以做到毫秒级的冷启动,但TDSQL-C Serverless的冷启动时长却还在秒级。希望TDSQL-C Serverless在后续的版本可以持续优化这个耗时,将腾讯云云原生技术普惠用户。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、TDSQL-C Serverless简介
  • 二、云函数+TDSQL-C Serverless实现“时光邮局”
    • 1.购买TDSQL-C Serverless实例
      • 2.建库建表
        • 3.创建云函数
          • 4.查看效果
            • 5.TDSQL-C Serverless状态以及账单
            • 总结
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档