有奖捉虫:云通信与企业服务文档专题,速来> HOT

操作场景

您可以在云函数中,通过编写代码,快速连接您本地的数据库或者腾讯云云数据库,本文介绍如何使用已有 SDK 在云函数代码中连接 MySQL 云数据库,并实现对数据库的插入、查询等操作。同时还支持已有 SDK 连接 TDSQL-CTDSQL MySQL版 数据库,您可按需进行相应操作。
说明
您也可以通过 Serverless Cloud Framework 组件完成数据库与函数的部署,详情请参见 Serverless 组件

前提条件

网络环境打通:
对于自建数据库(非腾讯云云数据库),您需要开启公网访问才可以进行连接,否则可能因网络不通导致连接失败。
对于腾讯云云数据库 ,需要保证函数和数据库在同一个 VPC 下。

操作步骤

您可执行以下步骤,开始在云函数代码中连接与管理您的腾讯云云数据库。

创建私有网络 VPC

说明
自建数据库可跳过此步骤。
按照以下步骤创建 VPC 和子网,详情可参见 快速搭建私有网络
2. 在顶部选择私有网络所属的地域后,单击**+新建**。
3. 在“新建 VPC ”弹窗中,根据如下信息填入私有网络信息和初始子网名称以及地域。如下图所示:


4. 单击确定即可创建私有网络。

创建数据库实例

说明
自建数据库可跳过此步骤。
以下步骤将以 云原生数据库 TDSQL-C 为例,指导您如何快速创建 MySQL 数据库。
说明
其它类型数据库请参考相关产品文档:
1. 登录 TDSQL-C 购买页,选择所要部署的地域、可用区以及数据库规格等信息等,单击立即购买
2. 购买成功后,返回集群列表,待集群状态显示为“运行中”,即可正常使用。如下图所示:


3. 单击集群 ID,进入集群详情页面。可以对您的数据库集群进行配置修改、账号管理、安全组设置等操作,详情请参见 管理 TDSQL-C 集群。如下图所示:



创建云函数

1. 登录 Serverless 控制台,单击左侧导航栏中的函数服务
2. 编写您的业务代码,按照正常连接数据库的方式,通过已有 SDK 或者云函数封装的 SCF DB SDK for MySQL 连接数据库,此处以 Node.js 函数为例,更多语言请参考下方 函数代码示例
说明
使用已有 SDK,需要先安装依赖包,详情请参见 依赖安装
exports.main_handler = async (event, context, callback) => {
var mysql = require('mysql2');
var connection = mysql.createConnection({
host : process.env.HOST,
user : process.env.USER,
password : process.env.PASSWORD
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
connection.end();
}
3. 进入该函数的“函数配置”页面,参考以下信息进行配置。
1. 新增环境变量,并参考以下表格填写。如下图所示:


key
value
HOST
数据库地址
USER
数据库用户名
PASSWORD
数据库密码
2. 开启私有网络,并选择和数据库相同的私有网络和子网。如下图所示:


3. 完成配置后保存,调用您的函数,即可连接与管理您的数据库。

函数代码示例

您可以参考以下代码示例创建云函数并为函数配置与示例代码对应的 环境变量
Python
Node.js
PHP
Java
Python 可使用云函数环境已经内置的 pymysql 依赖包进行数据库连接。示例代码如下:
# -*- coding: utf8 -*-
from os import getenv
import pymysql
from pymysql.err import OperationalError
mysql_conn = None
def __get_cursor():
try:
return mysql_conn.cursor()
except OperationalError:
mysql_conn.ping(reconnect=True)
return mysql_conn.cursor()
def main_handler(event, context):
global mysql_conn
if not mysql_conn:
mysql_conn = pymysql.connect(
host = getenv('DB_HOST', '<YOUR DB HOST>'),
user = getenv('DB_USER','<YOUR DB USER>'),
password = getenv('DB_PASSWORD','<YOUR DB PASSWORD>'),
db = getenv('DB_DATABASE','<YOUR DB DATABASE>'),
port = int(getenv('DB_PORT','<YOUR DB PORT>')),
charset = 'utf8mb4',
autocommit = True
)

with __get_cursor() as cursor:
cursor.execute('select * from employee')
myresult = cursor.fetchall()
print(myresult)
for x in myresult:
print(x)

Node.js 支持使用连接池进行连接,连接池具备自动重连功能,可有效避免因云函数底层或者数据库释放连接造成的连接不可用情况。示例代码如下:
说明
使用连接池前需先安装 mysql2 依赖包,详情请参见 依赖安装
'use strict';
const DB_HOST = process.env[`DB_HOST`]
const DB_PORT = process.env[`DB_PORT`]
const DB_DATABASE = process.env[`DB_DATABASE`]
const DB_USER = process.env[`DB_USER`]
const DB_PASSWORD = process.env[`DB_PASSWORD`]
const promisePool = require('mysql2').createPool({
host : DB_HOST,
user : DB_USER,
port : DB_PORT,
password : DB_PASSWORD,
database : DB_DATABASE,
connectionLimit : 1
}).promise();

exports.main_handler = async (event, context, callback) => {
let result = await promisePool.query('select * from employee');
console.log(result);
}

PHP 可使用 pdo_mysqlmysqli 依赖包进行数据连接。示例代码如下:
<?php
function handler($event, $context) {
try{
$pdo = new PDO('mysql:host= getenv("DB_HOST");dbname= getenv("DB_DATABASE"),getenv("DB_USER"),getenv("DB_PASSWORD")');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
echo '数据库连接失败: '.$e->getMessage();
exit;
}
}

<?php
function main_handler($event, $context) {
$host = "";
$username = "";
$password = "";

// 创建连接
$conn = mysqli_connect($servername, $username, $password);

// 检测连接
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
echo "连接成功";

mysqli_close($conn);
echo "断开连接";
}
?>
<dependencies>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>scf-java-events</artifactId>
<version>0.0.2</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
</dependencies>

使用 Hikari 连接池进行连接,示例代码如下:
package example;

import com.qcloud.scf.runtime.Context;
import com.qcloud.services.scf.runtime.events.APIGatewayProxyRequestEvent;
import com.qcloud.services.scf.runtime.events.APIGatewayProxyResponseEvent;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
public class Http {
private DataSource dataSource;
public Http() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://" + System.getenv("DB_HOST") + ":"+ System.getenv("DB_PORT") + "/" + System.getenv("DB_DATABASE"));
config.setUsername(System.getenv("DB_USER"));
config.setPassword(System.getenv("DB_PASSWORD"));
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setMaximumPoolSize(1);
dataSource = new HikariDataSource(config);
}

public String mainHandler(APIGatewayProxyRequestEvent requestEvent, Context context) {
System.out.println("start main handler");
System.out.println("requestEvent: " + requestEvent);
System.out.println("context: " + context);

try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement("SELECT * FROM employee")) {
ResultSet rs = ps.executeQuery();
while (rs.next()) {
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("first_name"));
System.out.println(rs.getString("last_name"));
System.out.println(rs.getString("address"));
System.out.println(rs.getString("city"));
}
} catch (SQLException e) {
e.printStackTrace();
}

APIGatewayProxyResponseEvent apiGatewayProxyResponseEvent = new APIGatewayProxyResponseEvent();
apiGatewayProxyResponseEvent.setBody("API GW Test Success");
apiGatewayProxyResponseEvent.setIsBase64Encoded(false);
apiGatewayProxyResponseEvent.setStatusCode(200);

Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "text");
headers.put("Access-Control-Allow-Origin", "*");
apiGatewayProxyResponseEvent.setHeaders(headers);

return apiGatewayProxyResponseEvent.toString();
}
}


SCF DB SDK for MySQL

为方便使用,云函数团队将 Node.js 和 Python 连接池相关代码封装为 SCF DB SDK for MySQL,通过该 SDK,您可以在云函数代码中连接 MySQLTDSQL-CTDSQL MySQL版 数据库,并实现对数据库的插入、查询等操作。
SCF DB SDK for MySQL 具备以下特点:
自动从环境变量初始化数据库客户端。
SDK 会在全局维护一个数据库长连接,并处理连接中断后的重连。
云函数团队会持续关注 issue,确保获得连接即可用,不需要关注数据库连接。
示例代码如下:
Node.js SDK
Python SDK
'use strict';
const database = require('scf-nodejs-serverlessdb-sdk').database;
exports.main_handler = async (event, context, callback) => {
let pool = await database('TESTDB2').pool()
pool.query('select * from coffee',(err,results)=>{
console.log('db2 callback query result:',results)
})
// no need to release pool

console.log('db2 query result:',result)
}

from serverless_db_sdk import database
def main_handler(event, context):
print('Start Serverlsess DB SDK function')
connection = database().connection(autocommit=False)
cursor = connection.cursor()
cursor.execute('SELECT * FROM name')
myresult = cursor.fetchall()
for x in myresult:
print(x)

说明
1. Python3.6、Python2.7、Node.js12.16、Node.js10.15已经内置了 SCF DB SDK for MySQL,无需额外安装。
2. 其他 Node.js 版本可参考 依赖安装 安装 scf-nodejs-serverlessdb-sdk 进行使用。
3. Node.js SDK 具体使用方法请参见 SCF DB SDK for MySQL

常见问题

在云函数 SCF 的运行机制下如何更加高效的管理数据库连接?

SCF 的每个请求实际运行在一个容器上,容器在有连续请求的场景下可在一段时间内复用。数据库的连接创建建议在初始化容器的时候进行,即对应函数代码的全局部分。数据库连接建立后,在容器存在的时间段内均可以进行复用,在容器释放时会断开。避免在入口函数内部频繁进行数据库的连接和关闭,影响性能。为保证数据库的连接可用,可在入口函数内部进行连接检查。
建议使用数据库连接池进行容器数据库连接管理,最小连接数设置为1即可。

在函数高并发场景下如何进行数据库连接管理?

函数高并发场景下,并发数可能超过数据库最大连接数,可以参考如下方案进行处理:
增大数据库最大连接数。
设置函数最大独占并发配额,限制函数的并发数小于数据库最大连接数。
云数据库 MySQL 提供 数据库代理功能,通过代理集群中转访问数据库的主从节点,进行读写分离,将读请求转发至只读实例,降低主库的负载。