阅读 124

Serverless-实现一个短网址服务(一)

要点

将一个长url生成一个短链接是很常见的需求,本文尝试通过serverless的方式来提供这个功能,主要有两部分内容:

  1. 一个简单的短链接生成方案
  2. 腾讯云的scf函数如何使用第三方依赖库

如何生成短链接

这里使用了一个很简单的方案,提交url时,先通过mysql的自增id获取一个整数,表结构大致如下:

CREATE TABLE `short_url` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `hash` varchar(45) DEFAULT NULL,
  `name` varchar(45) NOT NULL,
  `url` varchar(1024) NOT NULL,
  `created_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
)
复制代码

这样insert数据的时候,id会自增,获取到这个自增的整数值,然后使用hashids这个库将这个id转换为一个短字符串:

hashids.org/python/

将这个短字符串作为短网址域名的路径就可以使用了。

腾讯云scf如何使用第三方库

由于使用到了hashids这个第三方库,就需要将这个库也打包一起上传到腾讯云,这里需要注意的要点就是安装hashids库的命令:

pip install hashids -t 云函数代码所在目录
复制代码

注意后面的 -t 参数,将依赖库安装在云函数的代码所在的目录,这样在使用scf命令打包上传时,依赖库也会被一起上传

直接贴代码

# -*- coding: utf8 -*-
from os import getenv
from hashids import Hashids
import json
import pymysql
from pymysql.err import OperationalError

mysql_conn = None


def init_db():
    global mysql_conn
    if not mysql_conn:
        mysql_conn = pymysql.connect(
            host=getenv('DB_BING_HOST'),
            port=int(getenv('DB_BING_PORT')),
            user=getenv('DB_BING_USER'),
            password=getenv('DB_BING_PASSWORD'),
            db=getenv('DB_BING_DATABASE'),
            charset='utf8mb4',
            autocommit=True
        )


def close_db():
    global mysql_conn
    if mysql_conn:
        mysql_conn.close()

def __get_cursor():
    try:
        return mysql_conn.cursor()
    except OperationalError:
        mysql_conn.ping(reconnect=True)
        return mysql_conn.cursor()


def save2db(name, url):
    sql_template = """INSERT INTO `bing`.`short_url` (`name`,  `url`) VALUES  (%s,%s)"""

    with __get_cursor() as cursor:
        cursor.execute(sql_template, (name, url))
        auto_id = cursor.lastrowid
        return auto_id


def set_hash(id, hash):
    sql_template = """UPDATE `bing`.`short_url` SET `hash` = %s WHERE `id` = %s;"""
    with __get_cursor() as cursor:
        cursor.execute(sql_template, (hash, id))


def main_handler(event, context):
    # 读取请求参数
    if "requestContext" not in event.keys():
        return {"errorCode":410,"errorMsg":"event is not come from api gateway"}
    request = event["requestContext"]
    query = json.loads(event['body'])
    name = query.get('name')
    url = query.get('url')

    #初始化数据库连接
    init_db()
    #插入记录,返回自增id
    auto_id = save2db(name, url)
    #生成短字符串
    hashid = Hashids(salt="bangbangbang")
    hash = hashid.encode(auto_id)
    print(auto_id, hash)
    #将短字符串保存到记录中
    set_hash(int(auto_id), hash)
    #关闭数据库连接
    close_db()
    return  {"code":200,"data": hash} 
复制代码

测试

在本地创建一个测试数据文件,event.json:

{
    "requestContext": {},
    "body": "{\"name\": \"测试\",\"url\": \"http://www.qq.com\"}"
}
复制代码

本地测试验证:

cat event.json |scf native invoke
复制代码

将返回的短字符串拼接到你想使用的域名后面,就可以获得一个自己的短网址生成器了:

http://short.url/zv
复制代码

下一篇将通过云函数的方式来实现短链接的跳转功能

需要注意的问题

腾讯云提供了serverless_db_sdk,但是经尝试,这个sdk里面获取到的cousor对象无法获取lastrowid,也就是取不到每次插入记录后的自增id,所以这里自行使用pymysql来操作数据库,所以务必记得在最后要手动关闭链接

生成短网址后实现短链接跳转的函数可参考下一篇 Serverless-实现一个短网址服务(二)