使用 Serverless 創建一個簡單的短網址服務

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文首發於 ","attrs":{}},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/FBN5ViQDwBSSG87CdXRD6w","title":null},"content":[{"type":"text","text":"ServerLife 公衆號","attrs":{}}]},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"短網址服務,是一個能夠將冗長的網址轉換縮短爲更簡短、方便的短網址的工具。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過短網址服務縮短的網址很短很容易使用和傳播。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如我們經常會在營銷短信中會看到短網址。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文將介紹使用 Serverless 創建一個簡單的短網址服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"使用的技術","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用的產品與服務:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Serverless Framework:一個免費開源的 Serverless 框架。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Tencent SCF:騰訊云云函數服務。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Lambda Store:全球第一個 Serverless Redis 服務。","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"語言及框架:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Python 3.6","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Flask:一個微型的 Python 開發的 Web 框架。","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"項目初始化","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過 npm 全局安裝 Serverless 命令行工具:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"npm install -g serverless","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用模板初始化項目:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"serverless init flask-starter --name url-shortener","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"業務設計與實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個簡單的短網址服務,主要有以下幾個接口,目前沒有前端頁面:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、將長網址轉換爲短網址","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、訪問短網址時將其重定向到原始的長網址","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、將短網址還原爲原始的長網址","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據目前存儲到 redis 中,這裏用到了 Lambda Store 服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在生成短網址時,會生成一個6位的隨機標識符(如果標識符已存在,會重新生成,最多嘗試20次,如果還是失敗,則返回錯誤信息),然後以 key 爲短網址標識符,以 value 爲原始的長網址,將其存儲到 redis 中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"訪問短網址時,首先以標識符爲 key 從 redis 獲取相應的 原始的長網址,如果獲取成功,執行重定向操作,否則返回404。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還原短網址時,也是以標識符爲 key 從 redis 獲取相應的 原始的長網址,如果獲取成功,則返回相應的原始長網址,否則返回錯誤信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"已將代碼上傳到 GitHub: ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/donhui/url-shortener","title":null},"content":[{"type":"text","text":"https://github.com/donhui/url-shortener","attrs":{}}]},{"type":"text","text":", 核心代碼如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"import random\nimport string\nfrom flask import Flask, jsonify, request, abort, redirect, render_template\nimport redis\nfrom settings import redis_settings\napp = Flask(__name__)\n\n\ndef generate_identifier(n=6):\n identifier = \"\"\n for i in range(n):\n identifier += random.choice(string.ascii_letters)\n return identifier\n\n\ndef get_redis_instance():\n return redis.Redis(\n host=redis_settings.get('host'),\n port=redis_settings.get('port'),\n password=redis_settings.get('password'))\n\n\nredis_instance = get_redis_instance()\n\n\[email protected](\"/\")\ndef index():\n return render_template(\"index.html\")\n\n\[email protected](\"/generate/
/\")\ndef generate(address):\n identifier = ''\n i = 0\n while True:\n if i == 20:\n break\n identifier = generate_identifier()\n if redis_instance.exists(identifier):\n i = i + 1\n continue\n else:\n break\n\n if identifier == '':\n return jsonify({\"status\": \"fail\", \"error_msg\": \"generate shortened_url fail, please try again.\"})\n\n if not (address.startswith(\"http://\") or address.startswith(\"https://\")):\n address = \"http://\" + address\n\n redis_instance.set(identifier, address)\n\n shortened_url = request.host_url + identifier\n return jsonify({\"identifier\": identifier, \"shortened_url\": shortened_url})\n\n\[email protected](\"//\")\ndef fetch_original(identifier):\n try:\n origin_url = redis_instance.get(identifier)\n except:\n abort(404)\n return redirect(origin_url)\n\n\[email protected](\"/restore//\")\ndef restore(identifier):\n try:\n original_url = redis_instance.get(identifier)\n except:\n pass\n if original_url is None:\n return jsonify({\"status\": \"fail\", \"error_msg\": \"get original_url fail, please check the identifier.\"})\n return jsonify({\"identifier\": identifier, \"original_url\": str(original_url)})\n\n\nif __name__ == \"__main__\":\n app.run(debug=True)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開發完成後,代碼目錄結構截圖如下:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/82/82130e84fbc47c9c3952219bc30de6d9.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"部署到騰訊雲 SCF","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 serverless deploy 命令即可一鍵快速將服務部署到騰訊雲 SCF。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f2/f295495a1f4638597160beb55cbd0ba2.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"訪問短網址相關服務","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"短網址顧名思義網址比較短,一般都會有一個短的域名。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"理論上 SCF 支持自定義域名,當然最好是個短域名。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面的  Demo 出於演示的目的,權且使用騰訊雲自帶的 API 網關地址。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先進入首頁:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/58/58f40ebc4e4385f3b665ab422206de63.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生成一個短網址:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cc/cc141a2e5849c130986a5c47df299adc.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生成這個短網址後,使用瀏覽器訪問它,它會跳轉到原始的網址。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還原短網址:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/81/81a7a3157af9a1ce37eeb0674d4bb1ae.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文使用 Serverless 創建了一個簡單的短網址服務。用到了 Serverless Framework 及其組件 tencent-flask,還用到了 Lambda Store 服務,創建了一個完完全全的 Serverless 服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然從功能的角度來講,還有很多可以優化的地方,比如:友好的前端頁面、更友好的提示、更好的標識符生成算法等等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"參考","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://registry.serverless.com/package/flask-starter","title":null},"content":[{"type":"text","text":"https://registry.serverless.com/package/flask-starter","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/serverless-components/tencent-flask","title":null},"content":[{"type":"text","text":"https://github.com/serverless-components/tencent-flas","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/LordGhostX/fauna-url-shortener","title":null},"content":[{"type":"text","text":"https://github.com/LordGhostX/fauna-url-shortener","attrs":{}}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章