CentOS 7 with Nginx+uWSGI+Flask

Introduction

Flask 是一個使用 Python 編寫的輕量級 Web 應用框架,由於其輕量特性,也被稱為 micro-framework。雖然我們在完成一個 Flask 應用程式後可以通過 python3 api.py 來啟動伺服器,但這只適合用於測試環境中,正式發佈的服務需要一個穩定而持續的伺服器,例如 Apache、Nginx 等。其中 Nginx 是一款面向效能設計的 HTTP 伺服器,相較於 Apache、lighttpd 具有占有記憶體少,穩定性高等優勢,此外,Nginx 的優點還包括:高度模組化的設計,模組編寫簡單,以及設定檔簡潔。

因此在本篇文章,我們將結合先前的 PyTorch 實戰 - 高鐵驗證碼辨識,實作一個辨識驗證碼的 Flask API──通過傳入圖片在辨識完成後回傳驗證碼──,並部署於 CentOS 7 上,

architecture

Preparation

  • CentOS 7
  • Python 3.6

Implementation

在開始實作之前,我們需要在 CentOS 7 上安裝 Python 3.6,並更新 pip3:

1
2
3
4
5
6
# Install Python 3
$ sudo yum -y install epel-release
$ sudo yum -y install python36

# Update pip3
$ sudo pip3 install --upgrade pip

Flask

Flask is a micro web framework written in Python. It is classified as a microframework because it does not require particular tools or libraries. It has no database abstraction layer, form validation, or any other components where pre-existing third-party libraries provide common functions. However, Flask supports extensions that can add application features as if they were implemented in Flask itself. Extensions exist for object-relational mappers, form validation, upload handling, various open authentication technologies and several common framework related tools. Extensions are updated far more frequently than the core Flask program.

Wikipedia── Flask (web framework)

Install Flask

首先我們先透過 pip3 安裝 Flask:

1
$ sudo pip3 install flask

Create Flask Applocation

接著,我們建立一個資料夾 ocr.holey.cc,並在裡面建立一個 api.py,通過將圖片轉為 base64 後對 api.py 發起 GET 請求,在辨識完成後回傳驗證碼,在這邊需要注意的是,由於 base64 中含有字元 +/ 以及 =,這些都不是被 URL 所接受的,因此在傳送之前我們會先建立規則:

Src-Char Dst-Char
+ -
/ _
= <empty>

而這些規則將會在 api.py 中進行反解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import base64
import os
import re
import uuid
from flask import Flask, request
from io import BytesIO
from PIL import Image

from thsrc.captcha_ocr import CaptchaOCR as thsrc_co
from thsrc.image_transformer import ImageTransformer as thsrc_it


IMAGE_DOWNLOAD_DIR = './__tmp__/images/downloads'


app = Flask(__name__)


def base64_to_image(base64_str, download_dir):
if not os.path.isdir(download_dir):
os.makedirs(download_dir)
filename = '{}/{}.png'.format(download_dir, str(uuid.uuid4()))

base64_data = str.encode(base64_str)
missing_padding = 4 - len(base64_data) % 4
if missing_padding:
base64_data += b'=' * missing_padding

with open(filename, 'wb') as f:
f.write(base64.b64decode(base64_data))
return filename


@app.route('/thsrc', methods=['GET'])
def thsrc():
data = request.args.get('base64_str')
if not data:
return '----'

data = str(data).replace('-', '+').replace('_', '/')
src_captcha = base64_to_image(data, IMAGE_DOWNLOAD_DIR)
dst_captcha = src_captcha.replace('.png', '_dst.png')

it = thsrc_it()
it.Transform(src_captcha, dst_captcha)
co = thsrc_co(dst_captcha, './thsrc/checkpoint/cnn_ckpt.t7')
captcha_code = co.recognizing()

if os.path.isfile(src_captcha):
os.remove(src_captcha)
if os.path.isfile(dst_captcha):
os.remove(dst_captcha)

return '{}'.format(captcha_code)


if __name__ == '__main__':
app.debug = True
app.run()

在建立完 api.py 後,我們可以簡單地通過 python3 api.py 來檢視是否有錯誤,並開啟瀏覽器對網頁發起辨識請求:

1
http://127.0.0.1:5000/thsrc?base64_str=iVBORw0KGgoAAAANSUhEUgAAAHwAAAAvCAYAAAAhBK7nAAA_N0lEQVR4nFW8Z3jcd5nu_5muaRpNUZlRnVHvzSq25RI7jh0nkJyEskAgBA4hWSC7AbKUACFLdiF7Alkgu2woKYRkIdc5u5CYxI57kyxbxZZk9d5GbUaa3mf-L3LN9_rvS7-RpZnf7yn3_bkfSU9PT0qlUrG2tkY0GsVutxOLxYhGo2xsbLB79270ej0rKytMT09jNpvZ2dlBpVIhl8sxGAy4XC4-_elPc_LkSSQSCaFQCKPRSEFBAf39_ej1etxuNyaTiZKSEiYnJ8nKyiIUCrG5uUlOTg5SqZSRkRFMJhP79u3j5MmTKBQKDAYDMpkMr9dLZWUlMzMz1NfXEwqFUCqV6HQ6Tp48yR133IFKpWJ9fR2r1crq6iputxu9Xk8ymaSsrIyLFy9SWlrKzZs3yczMpKuri-7ubvx-P_F4XPy8lpYWlpaWyMnJYXl5merqaqanp4lGo-j1em7evInFYiEQCGCxWKiqqmJtbY1gMEhOTg4mk4loNMqNGzfo6uoiEomwsbGBVqvlxo0bNDc3Mzc3h16vp6amBoC5uTk8Hg96vZ6trS3UajWLi4tIpVJkMhkKhYL29nbGx8cxGAysrKygUqnIysrCbrdz9uxZUqkUDQ0NJBIJlpeXaWhoYHBwkPb2duLxOBKJBKlcLmd5eZny8nJqamrEFy6Xy1Gr1cRiMd577z3uuusu7HY7Dz30EFqtlry8PFpbW8nNzSUej_PWW2-xtbXF2toaAH6_n8HBQTIzMykqKkIikSCRSDh58iRLS0tEIhFmZmaIRqPEYjG2traora3F4_EwPDxMUVERVVVV4o_Ys2cPAHq9ns3NTSYmJnC73WxubtLR0cHVq1dxuVx4vV7OnDnD8PAwfr8fjUaD0-nkvffeQ6FQsL29zYEDB_D7_UilUnJycqioqEAikVBSUoJUKsXpdGKz2ejp6SEajeLz-XC73SgUCmZmZnA4HLjdbmKxGDU1NYRCIcbGxqioqCAnJ4e7776b8fFxWlpa6O7uRqPRoFarycjIQKlUMj8_T05ODiUlJbjdbhKJBHNzc1RUVDAzM4NSqSQajXLo0CEqKyvR6XQcOHCAq1evYjKZWFpaQqVSUV5ejtvtpq-vD71ej1qtJhQKsbq6yrPPPsvGxgadnZ1sb2-TlZXF8vIy0qKiIjIyMtjZ2cHpdDI7O0t-fj6xWAyr1cqNGzewWCz84Q9_IBaL8d___d-Ew2Fu3bpFNBpFpVJRX19PW1sb__Iv_4JOp8NutzM1NUVDQwNra2vI5XJKS0uZnJykoaGByspKFhYWqKysxG63o1QqkUgkTE9PA2A2myksLCQ_P5-ysjIikQjr6-sYjUaKiooIBoNYLBY6Ozu5ceMG4-Pj2Gw2pFIpJSUlaLVaDAYDJpOJyclJioqKcDgc7OzskJuby-zsLEeOHGFqaopAIMB3v_tdzGYzy8vLHDt2TDxMR48exeVykZGRQSqVwmw2YzQaCQQCFBYWUlBQwM7ODi6XC4vFgt_vJxAI8NWvfpXc3FxcLhc2m42TJ08yODiI0-lELpdTVFSE1Wrl2rVrADidTgwGAwBarZaioiLW19eZn59ne3ub5uZmLl68SF5enqgef_M3fyP-RpVKxf79-8nOzqazs5NoNMqbb77JwsICa2trhEIh_H4_wWAQyebmZioajdLd3c3Ro0cZHx8nlUpRWlrK0NAQGRkZuN1uvvKVr9Db20tfXx979-7F7_fT19dHaWkpi4uLtLW10dvbS2VlJevr6xQUFGCxWBgbGyMajVJWVoZSqSQQCKDRaFhaWmJubo7Dhw9z_vx5bDYbwWCQ3NxcFhYWqKurw2g0srGxgd_vZ2dnh7a2NjY2NgiHw4TDYZaXl_nCF77AxYsXGR0dpbW1FafTiVarJZlMEo_HWV9fJxqNYjKZ2NzcBKChoYHR0VHi8TiHDh1CJpOJB6Onp4d4PE5LS4sorxqNhqmpKQCKi4sZHh4mMzMTuVxOQ0MDp0-f5o477iAcDtPa2sovf_lLcnNzUavVyGQyNjc3qaqqIhaLIZFIcLvdPPXUU7z88svk5eXxxS9-kampKfEwZ2ZmsrOzw8LCAhUVFVy-fJnCwkJisRgtLS2cOHECq9VKbm4ua2trZGVl8corr3DnnXfS0dGB3-9neHiYjIwM2traCIVCZGRk4HK5kIZCITweD4cOHWJ5eZloNMry8jK9vb3s3r2bnZ0djhw5wtDQEGtra5jNZi5evMjQ0BDV1dVEo1EcDgeXL1_mwIED7OzskJWVhVarxev1srOzw4MPPkg4HCYWi9HY2IhSqSQcDnPo0CHGx8ex2-2sr6-jUCjIyspCKpUSjUaZnZ3FarUyOTmJUqlkc3OTxcVFPvOZzzA9PU0qleL27dtYrVYOHjzI5OQkjY2NPPnkk9hsNsLhMLm5uUQiEVKpFHv37sVoNDI4OIhUKqWurg6JRMLo6CgOh4P5-Xna2tqwWCxIpVJisRg6nY75-Xni8TjBYJChoSE-85nP8POf_5xjx45RVVXF9773Pe6__35cLhe5ubn88Y9_5OGHH-a---7jb__2b_n0pz_N2toaXV1djI6OcvDgQbxeL8FgkDvuuEN8yU6nE6lUysDAgOjxg4OD3HnnnbhcLhwOB2tra7S2ttLU1MTNmzfJycmhqKiIxx9_HIfDIR6qyspKkskka2tr9Pf34_P5mJ2dReJ0OlPpUn7kyBHOnTtHa2srAwMDyOVydu3aRTKZ5Pbt23i9XgBUKhWvvfYaGxsbJJNJAoEARUVF9PX1YTAYOHHiBENDQ7S3t6PValleXmZzc5OdnR1MJhOlpaX09fUhl8tpbm5mZmYGj8dDZmYmRqORaDRKMBgklUqRn5_PxMQEFRUVjI6OUltbSygUQi6Xi95XVFTE5uYmwWAQq9WKWq1maWmJqqoqvv71r_Pd736XzMxM8vLy6OnpwWq1Eo_HmZubY__-_VRWVrJ__35Onz5NLBajoaGBVCpFIBBgfX2dtrY2RkZGSCaT1NbWsrGxwfnz52lpaaG-vh6fz0cymRS_T2dnJ_Pz86K6pCtFNBqlqKiIX__61zz44IPi31qtlqWlJTEAHzhwgI2NDTE0ejwelEol-fn5ZGRk8Nxzz_HEE08wNjZGU1MTKysrFBUVcfPmTTo6Okgmk7z33nt85CMfwev14nQ6sVgs9PX1IXnnnXdS6f843eS9Xi9yuRyz2UwqlSIWizExMYHRaESlUrG4uIhMJuOb3_wmOzs72O12BgYGKC4uZnl5mbGxMbxeL1tbW2RmZhIMBgmHw5SVlfH5z3-eM2fO8MYbb5BMJqmpqSEWi7G6uopWq0WhUKDRaMjOzuaRRx5hZmZGTPZra2uUl5fzk5_8hI6ODlFq_X4_u3btYnV1la2tLaqqqujo6ECn02E2mzl__jzvvvsuL7zwAumKlpeXx_j4OENDQwBcu3YNqVRKUVERMpmMBx54gNXVVTQaDUajEb1ez_r6Onl5eeTm5rK6uko4HMZisXDx4kUAdu_ezeuvv05bWxtNTU1sbm5SUFDApUuXRMUrLCzEYDDw5ptv8olPfIL_-q__orKykoyMDFpaWtDpdGxtbbGzs4PD4WBxcZHl5WU0Gg3BYJDdu3fj8_lYWFhArVazurrKwYMHWVxcRKvV4vF4qKysxOl0is9CpVLx2c9-li9_-ctIwuFwamJiAp_PR1tbG9euXaOwsJC8vDxOnjxJIpHAarXyta99jd_85jf4_X4ef_xxrl69yv33308gEGBxcZHMzEyWl5fZv38_gUCAM2fOMD8_zz333ENvby-xWIzp6WnKy8uZnJzk0UcfRavVkpOTw_DwMKdPn8ZsNrOxsUE0GkUqlfLggw_i9_sJh8NkZGSwZ88ebt26RSgUQqvV8vrrr6NWq9ne3qa9vZ3h4WE-_elPU11dTUZGBisrK-j1erq7u9m_f79Ylex2O9vb24TDYd544w3a2tpYWloiNzeXsbExysvLuX79OhKJhK6uLtRqNcFgkIyMDM6cOUNeXh733XcfFouFcDhMUVERN27c4ODBg2xsbBAIBHA6ndTU1BAMBrHZbPT392O1WtnY2MDhcPDyyy_z8MMP4_V6KSgoIJlMijdULpej0Whwu934_X6sVivhcJiFhQUaGxuZmZlhdnaWlpYWBgcHKSsrIysri62tLex2O_39_QSDQbxeL3l5eVRXVzM4OPhhC9vc3Extb28TDAYxm81IpVK0Wi0XLlxg165d3L59m3A4TGlpKfF4nGQyiUwmw2w2c_ToUTFYxWIxKioq0Gq1pFIpvvSlLyGVSsWeuLy8zPb2No2Njdxzzz3MzMzQ1NTE7OwsBQUFPPPMM2RnZ6PT6RgYGOD48ePcd999hEIh_vKXv-Dz-TAYDGKvDAaDJBIJAPr6-mhtbWVnZ4dwOIxWq2VlZYXKykoGBweRSCQUFBTw-c9_noKCAjY3N1Gr1ezs7HDhwgUkEgmJRAKv10symWR2dpb6-noikQjz8_NYLBZyc3PZ2dkhPz-fVCrFyZMnqa6uJjs7mwsXLlBcXMzS0hJPPvkkdrtdbATpCTw7O5vx8XGUSiWRSITXXnuN9fV1JBIJdXV1GAwGTp8-TVFRETk5OZjNZpxOJ8lkklAo9GH_lUjIyckRb-7GxgZ2u53r16-LKd9sNqNSqUgmkwBsbW1htVqRy-UfbkwTExOo1Wqys7ORy-VEo1FcLhdf__rX-X__7__R1dWFXC5ndHSU6upqRkZGUKlUKJVKXnrpJZ5--mnMZjOBQIDz589z9913MzMzw09-8hN-9rOfsbS0xPr6Onv27MFsNrO6ukpeXh5qtRqDwUA8Hmd2dpZYLMZDDz2ESqViZmaGlpYWent7WVlZoaenh3379tHf309xcTHBYJCLFy-ya9cu-vv7Rc8sLCwkGAySn59PQUEB169f58iRI3g8HpaXl1lbW2NtbY2amhrUajXLy8skEgnW19dZWFggFAr9D90g3ce3trYAWF9fJxAIcOTIEerr64U4YjKZmJmZoaqqivr6el555RUSiQROp1OsowsLC5SXl4u3d2Fhgc7OTm7evIlCoWB2dpY9e_Zw_fp1srOzGRgYoL6-Hr_fL2Ybl8tFTk4O1dXVXLlyhZaWFpxOJyaTiYyMDKLRqBhC77vvPgKBANPT0-zs7HDs2DFycnKQf_vb3-YPf_gDcrkcr9eL3-_H5XJx4sQJRkdH8fv9FBUVkZ2dzcWLF4lEIthsNtbW1vjpT3-KWq3m8uXLGI1Grly5wne_-13cbjdqtRqVSkVJSQmLi4vEYjFCoRDf-MY3-POf_ywEjqWlJcLhMA8--CB1dXXMzs5is9kYHh6moqKCWCyGRqNBIpFgsVgYGRlh165d6HQ61tbWaGxsxOfz4fF4xMzwwQcfkJWVRU1NDSsrK2RlZRGPx2ltbWVlZYWlpSU0Gg07OztsbGxQXFzMpz71KQoLC-nv70epVLJr1y6ee-45nnzySfx-P5FIhH379nHjxg1UKpVQFFdWVtjY2CCVSrG4uEhVVRVNTU38-c9_JjMzE4lEQnFxMWtra0gkEubn52loaCAWizE4OIhKpUKv17O6usrw8DA2m4319XUSiQThcBi5XI5EIsFkMjE0NITJZGJubo5YLMYnP_lJfv3rX7O8vMzu3btxOp0UFBQAMDo6SnFxMbt27UIul-PxeJiZmUH-_PPPEw6HUSgUTExMUFtby9zcHEajkVQqJXpCTU0NgUCAQ4cOkUqlxKiffgKzs7P56Ec_KtaXP_7xjzzzzDNincvNzaW4uBitVst7772HUqlEo9FQXFzM2NgYR44c4d___d956KGHGBoaYvfu3SwsLPClL32JgYEBsUNLJBKGhoZQKpU0NzczPj5OcXExGo1GfOkFBQVi6IvH4-j1esbGxvjrX_8qBrH027mxscHY2BgnTpzgE5_4BOfPn6epqQm3201rayvJZJK6ujpGRkYIBoP4fD46Ojq4dOkSdXV1VFVVodFoCIfD3Lx5E7_fz-bmJqlUSvwuer2etrY2tre32bdvHxKJhD179vD----zZ88eUqkUiUSCmpoaent7ycnJwe1209_fTzweR6FQ0NDQgFarZXFxkerqaoqKirjnnnsIBAIkk0lSqRQbGxvI5XJkMhkmk4mrV6_S3NzMxMQERUVFFBYWIi8vL2dmZoaZmRlKS0uJRCLU1NSIL3x0dJR9-_YRDAYxGAw88MAD_Md__AfxeBy_309OTg5Go5E__elPQpKVSqVMTU3x7W9_my996UscPHhQyJqnTp3in__5n5HL5aJUOZ1OnE4njz76KFtbWywsLIjpvaKiAqPRiN_vR6vVkp2dzfHjx0mlUvziF79AKpWSTCaprq5mdXWVT3ziE-zbt4_u7m4SiQQ2m42dnR0qKysJBAJUV1ezubnJ0tIS5eXlPPDAAzz99NNkZ2fzgx_8gNXVVd58800OHjxIIpEgGAwik8n43Oc-B0BOTg5KpRKtVotGo2F4eJjS0lJGR0e56667GBkZoaysjJWVFdra2giHw0xNTWGz2XA6nWJwy8jI4LHHHqOsrIyBgQEAjhw5wuLiIr29vbz__vuo1Wqam5uFcFJbW0t3d_eHEqlUKibwhx56iP7-fhKJBKurq-h0OrKysti3bx_JZBKbzcby8vKH1WJoaCiVHv_1ej0TExNsb29TVVVFUVGRUKT0ej0ej4eioiJ2dnZ46KGHePbZZ6mqqmJ9fZ26ujoGBgb42Mc-xptvvolGo0GhUPDEE0_gdDppbW0VRoZUKuWNN94Qb6rD4WB1dZWnnnqKnZ0dMjMzWVlZEYbEiRMnhDSo1-uFuFBdXU1OTg6rq6t4PB4WFxd54okn8Pv9SCQSampqiEQijI2N0d7ezuuvv47BYODq1atotVrSppHD4SAnJwev18vMzAwqlYqCggJmZmZIpVKo1WohejQ2NpJKpejq6uKf_umfeOaZZ9je3iYzMxO_308sFmN8fJzBwUGUSiVer5cHH3yQ1157jc7OTg4fPsza2hqBQICMjAwxDAJUV1czOzuL2Wxma2sLi8WC2Wzm2WefxeVyUVpaSn5-PnNzc8zOzhKPx8nPz8disZBMJpmZmSE3NxeZTEZpaSlyuZwLFy5w4MABAoEAAwMDSP77v_87dfz4ccbHx1GpVMIVOn78OJubmwwNDWEwGHjxxRc5e_YsVVVVYtIMBAL89a9_ZXZ2FofDwa1bt6ivr0cikeD3-8nKymJ-fp4f_ehHAFy6dIl7772XUCjEm2--yeDgII2NjXi9XlKpFMlkEq1WC0A8HmdoaEioUhcvXqSmpobp6WkOHz6MVCoVK9T29jY6nY6VlRU--clPotVqsdvt9PX1CeHmgw8-IBQKUVlZiVwuJxgMCgMlFouhVqtxuVxUVFSIyd7n83Ho0CGhO8zMzGAwGPg__-f_MDExQTQaFdN0KpVCLpdTVVXFCy-8QGFhIe-99x5arRaLxcLHP_5x8VA4HA6uXbtGdXU1VquV3t5e8vPzxZs5OjqKVqslEomgUqnwer2sr68zOTmJ2-3GbreTSCSQSCRcunSJ_fv3s7CwwKOPPso___M_09HRwcDAAHV1dUxNTfHcc8_x7W9_-8MK3N7ezpUrV9DpdBgMBux2O5FIhMzMTBoaGlhfX6e4uJjV1VWKioq4ffs2er1eGBgulwuDwSAEk2AwyMTEBO3t7fT19VFTU8P29jY2m42CggJu3brFq6--yuHDh3n66adpbm4mPz8fiUTCs88-SyqVIjc3F4lEQjAYFFOyw-GgrKyMubk5EomEKFU9PT04HA5sNhsSiYTMzEwqKio4ffo0gOjZkUiE7OxsVlZWKCgoEENfdnY2NpuNmzdv4na7iUQiFBUVkZmZiUKhwOVysb6-jtPppLOzk-vXrzM8PEx-fj79_f0UFhZSXV3N2NgYtbW1OJ1O3G43SqVSOGnf__73SSQSlJWVsbS0xODgIA0NDWxsbPCrX_2KH_3oR0xMTBAMBrl586bQ3YuLi7l69SqxWIzKykoeffRRIpEI__qv_4rD4RCfxdTUFNnZ2Zw4cQKPx8OpU6fIyMhgdHSUqqoqvvGNb4jWK00bJI888gjt7e18-9vfprS0lN27d-P1eikuLiYrK4uNjQ2sVivt7e3Y7XZMJhNKpRKLxYJarWZtbY2Ojg6CwSD_9m__xuzsLP_7f_9vjh49itls5gtf-AKXL1_mv_7rv8Se_J3vfEfssmlnCBBmhk6nw-fzEQgE0Gq1XL58mfr6eoaHh5HL5cLJU6vVHD58mNbWVqRSKT09PdTV1VFSUkJnZyezs7OkUimhVUulUhQKBdXV1ZjNZiFUlJeXU1lZKQyToaEhYrEY5eXlfOQjH0Gj0XDvvfcSj8eZnJwULcrr9eJ2u_m7v_s7fvrTn7K6usqtW7eYnp7GYDCI-Wh2dha9Xo9KpcJsNrO-vs7DDz9MPB4XA15VVRV6vV6YNceOHePAgQOEQiHGx8fx-Xy0t7fjcrlEO2poaMBkMglb12AwCOv03LlzqNVqJBIJIyMjSL_2ta9x991343K5aG5uxufz8elPf5qBgQFsNhsejwe3201TUxM6nY6RkRHm5-cpLy_HbDYzNzeH2WxGqVTyqU99invvvZdvfetbPPXUUwC0trZy_vx5dDodvb29BAIB3G43ZWVlFBYWcuHCBZ577jk0Gg1ms5m2tjbcbjcSiQSARCKBTqcjHA4L-3Fra0t8mAqFgpaWFra2ttje3kYmk7Fv3z6mp6fJzMzkz3_-Mz6fj9LSUkKhEDabjUgkQldXF3Nzc_T09FBbW4vdbmdkZITh4WFWVlZIpVIUFhYyMzOD2-1meXmZoaEhKioqqKqqwu12U1xcjEQiYWtri4yMDI4fP04ymaSlpYXMzEz27NlDe3s7m5ub-P1-otEoi4uLGAwGRkZGiMfjVFRUiPYwOTlJIpFgcXFRWMjp-SU_P18IY0ajkezsbBobGyktLcXr9YqHdnNzk_LycnJzc4V7l658LpcLSWdnZ0qj0Yi3JZVK0djYyNzcHFar9X8QE8FgEJVKxeTkJNvb26ytrXH__fcTDAY5ffo0HR0deL1eRkdH6e3t5e6776atrU1Mji-__DI1NTWcOnWKRx55hJaWFgwGAxkZGZw-fZqenh5BuMTjcbq7uzEYDBQXF5OXl8f58-cpKSmhurpayLlZWVmYTCYOHDiARCLh_PnzlJeXMz4-Tm5uLrdv3yYQCBCPx4WW4HA4MBqNKJVKIQQNDAxQU1NDT08PR48eFa3JarUSCoVIpVI8-eSTZGRk4PP5iEaj3L59m4KCAsLhMA6HA5fLxX_-53-iUChwOp1sb2_zox_9CL_fL-if-vp6bty4wd69e0mlUvh8Pnw-H4WFhfzpT38iEokwNDSEw-FAp9Nx_PhxJiYm0Ov1og2Mjo6SlZXFq6--isFgYGtrC4PBIKgai8VCIpHgG9_4Bq---iqLi4skEgna2tqQm81m-vr6hOOURmTSw47RaOTs2bPirZuensbpdLJv3z4--OADdDodbreb_Px8Ojo6WFhY4NKlS_h8Pux2O5ubm2RnZws78sqVK-Tl5Yk3Q6vV4vf7ycvLY3Jykra2NvR6PRcvXqSuro54PI5arebcuXNYrVZisRjvvPMOtbW1ANxzzz3E43FcLpcYmhYWFjCbzXR3d3P58mWsVit-v18MTACLi4vI5XLi8bj4ME6ePMnPfvYzHA4HFouF5eVlYrEYUqmURCLBiy--yP3338_a2hqdnZ10dHTQ3d3N4cOHuX37Nv_xH__B9vY2TU1NTE5O8otf_ELAH0tLS2RlZTExMSH2_OLiYr73ve8RDoepra0lkUhw8-ZNSkpKmJ-fZ2dnh5_97Gesr6-j0-n41re-RVZWFnfddRcul4va2lq0Wi2BQIC1tTUsFovAs-x2O7_85S-FN6FWqxkbG0N-7do1ioqKmJ6exm63YzAYOH_-PG-88QY6nQ61Ws3GxoZwbbKzs4nH45w_f566ujq2traYnJwkHo8zMDCAVqtFIpGwf_9-fvrTn_LYY4-RSCTw-XyYTCb--Mc_0t7eTnl5uTBefvOb3_Dkk09SVVXF0NAQwWCQyspK4vG44OdKSkpI0znV1dUMDAzw8MMPEwgEaG5uJhaLkZmZidvtJiMjg9LSUjwej9hVH3zwQU6dOsXq6ip-v5_GxkYmJyfZtWsXY2Nj-P1-CgsL-cMf_oDf76enpweAjo4OIfZ0dHSgVCo5ePAgV65cEUzf-Pi4oFgA0UJu3bqFVqslkUiws7ODQqEgOzub9fV18ftGo1EqKysZGRmhs7NTyM_Nzc0EAgFkMhllZWXMzMzw9NNPc-zYMfbs2UN5eTkul4ulpSWMRqNgDaenpwUskV63c3JyCAQC5OXlIVUoFNjtdhYWFvjlL3_JJz_5SZaXl7FarQJBMhgMfOITn6CmpobKykqysrKIRqNCwCguLhZyYdq5SluJCoUCiUSC0WikpqZGrHUzMzMUFhYilUr5wQ9-QGFhIVarlaamJmw2G8lkkoWFBUGt5OfnC_LF6_USj8eZmJjA4_Egk8kIh8Nsbm6yubmJ1-vlqaeeEu6bRqPh7bffxufz0dDQQFdXF5mZmTQ1NbG4uIharaa1tZX6-nrC4bBArN566y3RVysqKjh06BA6nY6-vj4Beubk5LC9vc0TTzyBWq1GKpUyOzvL8PAw9fX1guSxWq2sr68jlUqprq4WWkRlZSWJRILq6mrOnDkjHka3201mZiZqtRqn00lJSQlKpZKpqSnee-89rly5QmlpqeD-qqqqaG5uxmw2c_XqVcETpEHQ9M-Tvvrqq_zsZz9DJpOhVCo5fPgwo6Oj5ObmEovFuHLlCvX19fz6179mcnKS06dPYzAYuOOOO4hEIkIECQQChMNhqqqquHbtGhsbG9TW1nLx4kXUajWbm5u4XC6-8pWvsL29zZUrV1hcXCQYDNLT08M777zDAw88gEajwWq1sr29TWtrK83NzQCMj48jkUiYmpoiKyuLT33qUzgcDtra2jCZTBQWFqJUKjGZTLz77ruYTCYWFhZwOp3cunWLj33sY8TjcdxuNydPnmRxcRGPx8Pm5iZ79-5ldXWVnZ0dlEolx44dIxaL8cc__hGXy0VfX5_QA9IMWzKZpKqqioKCAuLxONXV1QL2sNlsNDc3884777C-vi7WRpVKxdTUFL29vcLk6O_vJzMzk2QyyfHjx_H5fOzs7DAwMEBPTw9jY2NMTU3R09NDTk4Oc3NzDA4OMjIywsc-9jEuX77Mj370I4LBIOfOncNoNNLS0sLIyAiLi4v4_X6cTifZ2dkftqe6ujqUSiWTk5PEYjGGhoZYWlqir6-PSCQiSoXL5cJoNCKXy8nOzmZpaYlEIsFrr71GOBxmdnaW1dVVSktL-bu_-zvsdjtra2sMDQ1hsVh45ZVXKCkpYXBwkKmpKXJycvD7_djtdqqqqjh-_DiRSIRIJMLCwgKlpaVsbGywvb0tvii1Wk1bWxudnZ00NjYSiURwu93Mz89z6dIlDAYD1dXVhMNhotEoCoWCqqoq7HY7p06dYmVl5cOyJpUSCARYWVlBo9EIyjWVSnHz5k3eeust8vLyWFhYYGtrC51Ox9TUFHNzc5SUlNDc3ExdXR0TExMCIEy3tbq6Opqbm0WFW1lZYXV1lYGBATIzM9HpdOTn55Obm8vnPvc5urq6ROvo7e3FbreTnZ1NuvLq9XqUSiV33303NTU1NDQ0oFQquXr1Ki---CItLS08__zzqFQqUqkU169fR6fTYbVacTgcOBwOQcz09vYiTWPB6f4cDAZpbm5m165dzM7OEo1GSSaTJJNJfD4f8Xicffv2CYAgEAiQnZ1NTU0NH__4x_H7_cJo8Xg8mM1m_v7v_57f_va3GAwG1Go1jz32GFqtlsbGRm7evMnbb7_N7OwsW1tbTE1NUVJSQjQaRafToVAoyM3NJTMzk2vXrhEOhzEajczOzrK2tiagxvb2dra2tvj7v_979uzZQ319PbOzs-Tk5AimTSqVcuXKFXbv3i3YealUyuLiIgcOHECn06FUKgUHJ5fLsdlsYh0qLCykpKRE_MxwOMyFCxd4_fXX0Wg0JJNJhoeHeeONN6ivr0cmk5GXl8fU1BRqtRqj0UhbWxtZWVlkZGTw17_-lZ6eHsbHxzlz5owwa8bHx8nLy2NnZ4fCwkIABgcHGR0dJRKJUFpaKiihUChEfn4-ly5dIpVKkZeXRzKZpKCggNOnT3PhwgW8Xi_Z2dkcOHAAaUlJCV6vVxAreXl5uN1u5HI5kUhESI2xWIyNjQ327t3LpUuXhH6eSqVIpVK4XC4GBwcJBoOMjY1RVlbGwsIC2dnZVFdXs7CwACCQ3vRAE4vF-MpXvoLH4yEcDoupOc3FAxQUFFBQUMCuXbv4m7_5GzHx33HHHdTV1QlK809_-pPAnkdGRrjzzjtFr1coFEilUsGwJxIJTCYTWq2WiooK4YnbbDZu3bqF0WhkZWWFWCzG448_LpjxYDDI5cuXSSaTlJeX8_rrr3Pp0iW2t7fxer14vV46OjqErdvY2EgoFBItZGJigmeffZbV1VWKi4tpa2ujpaWFlZUVZDIZX_ziF1ldXSU7OxuPx4PH4-HIkSO0tbWJmUSv14u_c2lpSTD9afQqLdL87ne_w2w2i_BFb28v0sHBQQHWV1dXU1NTg8_nE_ru2toaVVVV3H333WRkZNDd3c3GxgaHDx8mIyODw4cP4_P5CIVCwndOM1fxeByj0YjP5-Nf__VfRQnt6-vj-9__vkh8pHns5uZmZDIZbrdbKE0LCwsEAgEBPKYh_KmpKbq7u8UUmob8b9--zalTp9i1a5eQOQ8fPkw0GhVCSkdHBwUFBaytraHX68nMzPwff0s4HCaVStHS0oLP52NgYICcnBxaWlrQaDTs3btXAAd-v5-DBw-SkZGB1WrlzjvvJBaLCXQ6GAxSXV3Nq6--is1mY2FhgY9-9KPI5XKWlpbw-_2iNVy4cIGbN28K5bKqqorV1VU--OAD6uvrmZ6exmKxMDU1hVQqpbi4mNbWVsLhMF_4whdYXV0l_QJvb2_z3HPPYbFYBCzZ2NiINDc3F61WK_Dg3t5eXC4XR48eRalUEovFuH37NpFIBKVSSUNDA-Xl5QwPD9Pd3c3U1BQqlYquri5UKhW3b99GLpcDoFAoBKOeXhnKy8tpaGjg3XffJRAIUFlZiUQiweVyMTAwQEVFBQCNjY2ipKYfiuzsbOE01dTU0NzczMjICGNjY5w9e5b-_n6am5vp6uri2rVrYtuYmJjAZrPx2c9-ViQ_0uudSqVia2sLuVxOVlYWfr9fqF2nT5_mpz_9KYFAgAsXLuDxeOjv70ej0TA3N8f6-jp33HGHEIS6u7uJRCIoFAqKi4sB6O3txWq18tJLLzEwMCBQMaVSSXFxMbdu3cJgMFBXV0cymWRwcBCz2Yzdbmd6epqysjLUajUvvfQS0WiU_Px8WltbUavVQpxSq9X88Ic_RKlUMjc3h1KppL6-Xsi5ZWVl3L59m7GxMaTFxcXcf__9ZGVlMTQ0RCqVoq2tjcnJSRwOBy0tLdTW1hKNRqmpqeHWrVv4_X5KS0uRyWTs7OwwPz_PyZMn0el0FBQU4HA4kMlkVFdXU1BQgMvloqioiHPnzokp9KMf_SgAgUBAOD6FhYX09fVhtVrp6-sjHA7jdDrZ3Nxka2uLWCxGRkYGyWSSYDAogg_vvvsubrcbmUzGyMgIGxsbKBQKmpub6e3tpby8nPX1dd58802-853vsL6-jkwm48SJEzidTgKBACaTiZ_85CfU19cLrX3Pnj2Ew2Hq6-vZs2cPg4ODyGQytre3aWhowOFwEI_Hyc3NxWq18vTTT3Px4kV6enpQKBT09_cTiUTE-lpQUEBubi5NTU1oNBocDgcqlQqr1crQ0JCYD9xuNysrK6jVam7cuIFcLic_P5_Ozk5GRkbo7u5mZGQEjUaDzWYjGo3S0NCARqMRWkEwGGR9fV2swB0dHfh8PqRVVVW88sor2Gw22tra2NzcZH5-ntzcXM6cOcPNmzcxmUxEIhFWVlYEU76xsUF9fT1lZWUEg0HxhBoMBiYnJ7FYLGxsbHDlyhW--c1vCqR3aWkJh8PBxMSEeFj27dsHgMfjERGcjo4OxsfHMZlM7N69mx__-McAuN1ubty4wcWLFykrK8NmswnyUy6X097ejl6vRy6Xo1AoiMfjgvuyWq18__vfR6fTEQqFOHTokMCOpVIpVqtVzC0A169fJz8_n7GxMXp6eoTxcuPGDQYGBvje977H1atXxYR8_vx5lEolv_rVr8jMzEQmk5FMJjEajQJXGh0dFQ5ecXEx8XhcVLtAIMCXv_xloZatra2h0-mQyWRCUpXJZOTn53Pfffexs7ODx-NhY2ODYDAoqozH40Gn0wkb2Ov1srS0RCgUQjo-Pk4ymeT69evMzc1x33334fP5GBsbY__-_RQVFREKhejr68NisQh0Nr3Drq2tiR0UIJVKUVFRwcjICIFAgLq6On73u9-xsrLCysoKN27c4Pr162RmZmK328nNzWVlZYXr169jMpl45JFHMBgMTExMEAgECAaDFBQUIJFIBIzf1dVFV1cXzz33HP_5n_9Je3u7gDYyMjKQSCS0trbS29uL0WhEJpMJESI_P19InysrKwJ77unpIZVKEYlEWFpaoqamhpdffpmBgQHUajX19fVkZWVRWVlJNBolNzeX48ePY7fbKSgoQKPRCKrkhz_8oTAyZmZmmJiYQKvVMjMzA4DBYBADcn5-PtXV1ezevZt4PM5PfvITZmZmOHLkCHK5nM3NTeRyOXa7XbyQaY_jU5_6FG1tbRiNRkKhEMPDwygUCpaWlhgbG-Phhx8WgcW6ujoikQiS8-fPp-RyOXq9no2NDbq6utizZw8__OEPBX81MDCATqejsLCQ9fV1ZmdnMZlMYo9eXFzkBz_4AXq9HqPRKDTh7u5uATb4_X4-__nP8_vf_56vfvWrhMNhERMeGhqiqalJmBRpU2Bubo7s7Gy6urrIy8sTT3MasHA4HFitVrxer6hCGo2GEydOsLS0hE6nE309jV2l28SePXuEJJqTk8O5c-eor68XJTCZTPIP__APKJVK1Go1Fy5c4M477-T27dtUV1fj8Xh48cUXqaiowOPxYDKZ6O7upqmpieHhYWpqahgZGSGVSvHYY49RXl4OwMbGBi-99BIPPPAACoWCkpISfvvb3wqDo7i4mJycHK5cuUJtbS2xWAyLxSI0fblcTiqVYmxsTLBuaZBkaGiInJwcEokEmZmZ9Pf309bWhsfjEYiaZGxsLGW327l27Rput5u9e_eK-GtaUhwZGaG9vV1YoXl5eaLszs_P8_Wvf52VlRWMRiMajYbBwUGqqqo4c-YMPp9PIMEKhYK8vDycTicf-9jHqK6uBuDs2bOUlJQgl8tJJpN0d3dz8eJFTCYTxcXF9PX18dBDDwnQb3V1laGhIQ4dOoTP52N-fh6ZTCasz7SsmH7L29vbUSqVyGQyXC4XP__5z9HpdCwtLbF3717m5-f57ne_y-c-9znMZrMwQI4dO4bZbGZ8fFxkyWOxmODV19fXRUgvHA6LvPfJkycxm81CzozH4zz__PMEAgFSqRQ7OzssLy8jk8lQqVT89re_5ZlnnuHtt9_G6XRSUVHBxsYGc3NzaLVaHA4HUqmUvLw88WCkbWuZTEZhYSEf__jH-cUvfoFKpRKRpLQKml7VEokE0rQLlk5rpk36O-64Q2A09fX1bGxsUFhYKJi2r33taywtLREMBnniiSdYXl7m1q1b3L59m_3796NSqRgbG2N-fp5bt24J4yMajQqE6cKFCwwPD6PX61lYWBBrytjYGBkZGWg0GgKBABKJhPz8fCH_tra28slPflLksY4dOybo0ra2NhKJBA0NDdy-fRu73c7ly5eJRqNiOHv00UeJxWIEg0FcLhdms5nHH38ci8VCfX09-_bt45vf_CYymQyNRkNVVRXLy8t0dHRQX1_P3r17mZ6eFv0xGAyi1Wqpra1ldHQUm81GLBajtbUVq9XKz3_-czIzM3n22WfR6_VIpVLMZjNvvfUWMpmMr371q_zqV79ic3NT-A7Z2dnce--9gpIZHR2lpqZGfD-VlZW0traSl5dHeXk5v_3tb4X_vri4yI0bNzh69KioVh6P50OeMI339vT0sLOzIxKZyWSShoYGoZcXFRWJcjcwMMCrr75KSUkJ7e3ttLe385e__AWXy8X777_PW2-9xT_8wz8IlMjlcom15urVq8zNzTE8PIxSqUQqlQIwNTXFyy-_TF1dHTqdDr1ez9zcHJOTk_h8PlZXV4XAc-bMGZEWmZmZEVRnT08PPp-Pq1evolAoqKuro7S0VBga165d4-zZs8zOzvL5z38eh8OBz-ejrKxMQJvRaJShoSFmZ2f5xS9-If7PdDJFr9fz2muvkUgkMJvNArcKhULcunWLrKwsAGpra1lfX0er1fLYY4_xwgsv8OUvf5nx8XESiQSBQICWlhZUKhVqtZpHH32UvLw8UaF6enrweDxiI5DJZPT09AghKL27u1wuTp06RX5-PlKplO7ubhHi_Pd__3cikQi1tbXs3bv3wx4-MjKSAoRnm47c3rp1S3zw_39-raqqiq2tLcGd9fX1UVdXx-3bt6mtrWV4eJiSkhLy8vJIJBIsLCxgNBpRKBS43W58Ph_r6-t0dnayvr7O008_jUql4vLly0K6_ctf_sKhQ4eADy9JpCtCOpl69OhRQqEQjz_-OACzs7P_I0jR2dnJ1tYWt2_fZvfu3aysrFBSUoJEIhEsePr_jUajdHZ2irDi3r172dnZYXNzk8997nOC19-_f78IIr700kssLy_jcDhEm0mfLhkbG6OoqEjIuHv37qWvr4-8vDyi0Sj_63_9L8xmMw6Hg3TEK82nSaVSvv_975ORkSGgzKamJhHUTCdUs7OzKSwsZGVlhdnZWXbt2sX29jabm5tEIhFhKQcCAe6--24uXbok6CHJ22-_nSotLaWoqIjV1VWWl5fZt28fSqUSt9vN9PQ09fX1zM_PI5FI0Gq13Lx5E4PBIAJs6QsSWVlZJJNJdu_eLViqeDyOxWKhrq6O8fFxlpaWsNlsLC0tkUqlKC8vR6_XI5PJcDqdBINBPB4P0WgUpVJJW1sbly9fJiMjg0Qiwd69e4VEmxaGsrOzOXr0KC-88AKlpaUsLCzwla98hYmJCe666y4uXLjA4cOHmZ2dpaioiLm5Of7xH_9R2JDpFInT6aSxsRGbzcb777_PsWPHiEajXL9-Hb_fT0ZGBjKZjMbGRm7fvk1DQwPxeJxYLIbb7RYtIm0cpT-LWCyGTCZja2sLv9_P0aNHRYpUIpHQ3d3N7t27RdbMZrPxf__v_8XlclFQUMD09DRerxe1Wk1ZWRkejweXy8XMzAwvvfQSP_zhD6moqODatWtIJBL27dvHpUuXOHr0KNevX8fhcDA2NsZTTz2F3GKxYDAYRH67qKgIlUrFysoKU1NT1NXVEQ6HcblcfPazn-XXv_41zc3NZGVlEQ6HxfUHt9uN1WplYGBABPEzMjJE3vvq1avs2bOH7e1tioqKSCaTKBQKkSjJysrC6_UKY2FnZwez2cw999zDv_3bv2EymcjOzmZ0dJRAIEBxcTFzc3OEQiGmp6c5deqUCEGm18zLly8D8OMf_5jNzU1CoRB5eXns3r2bsrIydnZ2MBgMYvrPz88nGo3idDpRKBScOXOGhoYGIWyEw2HW1tbEG6tQKMRZjbTBVFhYSHFxMYFAgMbGRoaHhykvL2dubg6NRkNeXp7Aq_Pz87HZbNx7771Eo1Hi8bg4e7Jv3z5MJhMTExN0dXXxu9_9jtXVVe666y5isRh6vZ54PM7q6qo44rBv3z7GxsZEBb516xYdHR1sb29jsVh49913kUxMTKS8Xi_l5eX4fD4hOOzfvx-dTgcgskyVlZUiEJCRkSFyy9vb28RiMVE2VCqV4NjSuedQKEQoFBJ9RyKRsLa2JpInXV1dggNLS45NTU3CsEi7Qpubm9x3333cuHFDPHQAmZmZ3LhxA4lEQmlpKRKJBI_HQ1ZWlijpaXo0HWZI781LS0uiEqnVahYWFrjrrrvo7-9HoVCQSCQEap2Zmcn09DQHDhzg2rVrqFQqZDKZSLWMjo6SkZGBwWAQ5VoulxMIBASIkD5G8NxzzyGRSMQXndYbHA4Hzz__PM3NzVRUVKDX68nKyuLChQuYTCZcLhe7d-9mamoKg8GAVCrl5z__OY2NjYyNjQner7a29n-s1c3NzUi3trZ4_vnnUSgUrK2tsby8zJEjR4jFYoyNjTE8PMz09DRGo5H-_n56e3uZmppia2uLgwcPCjvRZrPR0NDA9vY2RqMRq9XK3r17BfC3vr4utPaZmRlRSZxOJ2q1muHhYTweDzU1NahUKsrKykTI7hvf-AZ1dXWUl5cLsGBycpLr16_jdDp56qmnuHnzJp2dncIhS58VSVewtbU18vLySKVS4vCAyWQS0ENzczNlZWX4_X6ys7O5dOkSe_fuZX19ncbGRoE9b25uYjQa6enpEYmPdBVMzyu7du3CarXS2trKrVu3BE-WTuam1bNXXnmFv_71r2xtbZGVlSUooOvXr9Pc3MyhQ4cEtatQKMTBpIMHDxIKhSgtLWV-fp5QKER2djZXrlzB7_ejVqsxm82YzWamp6dpbGykpaWF_v5-pHq9nt___vfielNaSEh_WGnQcHFxke3tbRKJhLjw9M4776DX6wmHw0QiEc6dOyfyWGmbde_eveI-WiQSQavV0tXVJQiW3Nxc3G63SDmmPe5UKoVMJqO9vZ3Lly8LGbG_vx-dTkdeXh6ZmZnk5-fz-uuvCzBgY2ODyclJ1Gq1eHgSiQSNjY3o9Xry8vK4deuWCCimqdSBgQGmp6cpLi7GYrGwe_duEokECoWCgYEBPB4PTqdTnD3p7OzEaDSyd-9eZmdnqaqqEjde0mU17blPTExw-PBhxsbGcLlcAiVO3645f_48RUVFYmUzGAzo9XquXbuG3W4nEAhw8uRJCgoKhKw7NjZGJBLh1VdfxWQyMTY2hlqtxufzodVqufvuu9nY2BC839DQ0Idp1mAwmPL5fOj1ekZGRsTlJbPZLEoqIAJ9Xq9XZK-mpqbE2lZdXY3BYCAQCKBSqTh79ixmsxmXy4XVakWj0Yj8dXZ2NteuXaO8vBytVsv6-jolJSX09PQgkUjo7OxEoVBw48YN3G43DodDGCBpN2pxcZGysjKRQBkeHhaDYxqAHBoawm63EwqFGBkZISMjg4MHD3L27FlsNhtf_vKXefvtt5mYmODOO-8kkUiIASqNbaXfUKfTyf79-3E6neTn5yOXywW0EQqFMJvNlJeX09vby549ewSlkwYyT58-LQSVnZ0d3nrrLX784x9TX18vjgwZjUauXbvGP_7jP2IwGDCbzQwNDVFeXs61a9fIzs6mqKiIiYkJampqRIlPp3l1Oh2RSIRLly6h1-spKSnh5s2b5Ofnk5WVRWFhIZLTp0-n0iuXVCoVq5VKpcJisVBYWMjCwgLz8_M0NzcLr3Vra4u6ujoWFhbEgJWWPEOhEOXl5RQUFHDx4kWKi4spLy_nypUrxGIx2traxAzg8_moqqri3LlzZGZmYjabyc7OZnJyUvSrXbt2kUqlUCqVnD59GrVajVwux2KxoFKpxP6cDsnLZDKWl5fFIaD01YZ0-fP7_ZSVlYnjel1dXbz77rvceeedRKNRpqamhCpnMpmQSqXk5-eLPTt9pC8rK4uqqirOnj1LQ0ODyM2bTCbW19fJyckRcm46ExaPx4lEIrS0tPD----TlZVFQUEB4-Pj6PV6iouLhYXb2NjIrl27BI-2uLgIfHhLBj48UPDiiy8Kty4Wi3H-_HmSySSHDx9GIpEILj99uE-enqiXlpY4evQoHR0dnD59mmQyyfz8vLgDls4gp1czt9vN2toa29vbFBYW4vP5aGlp4TOf-Qwvv_yysDQNBgMFBQVcu3ZNIDgej4dLly6JkyJp2jMdKyooKKC1tZUbN27wzDPP8MILL2AymTCZTPzt3_4tJ06cIBgMinNX4-Pj4jqkUqkUVwvTZsLo6CjNzc2UlpaKnNr169fFgZzLly_T3NwsVsw0JuRyuYjH4zgcDgYHB9mzZw9er1do5-kDAGmUO62SnTlzBoVCgc1mw2azsbi4KECExsZG-vv7OXfunPh90y0yXaFSqZQwqj744AMBg5jNZubn5zGbzeh0OhwOB5FIhKysLILBIIODgyICduPGDZqamrDb7fh8PrxeLxaLBcnq6mpqcHCQiooKPvOZz_Ctb32LY8eOcfbsWfbu3Stus-Tk5LCxsYHH4xF3zNLQYlp10uv19PT0EAqFOHr0KMlkklu3bqFSqYhEIuj1esGUp_-wzMxMAS-mQ4XLy8uMj49z7NgxUTUkEgkXL14U7pfX6-XAgQMMDQ1hs9nQarVIpVKuXr3K4cOHxZ1StVrN7du3SefgpVKp8AV0Op1429MnMtOau1wuZ3V1ld27dws5WC6Xc-XKFXHMIB6Pi-lfq9USDAaJx-NIpVIikQiVlZX4fD7y8vLEgy6Xy0U-PJ3S7e_v58aNG6jValKplAh3SCQSzGazADvTwGmaJtq_fz8dHR2cP3-ewcFBbDYbubm5IvKcvpiRjnnX1tYivX79Ou3t7dy8eZPnn3-eJ554glOnTlFTU8MjjzyCUqkUiQmj0YjdbicrK4u8vDwqKiooKCjAbDYLIjOtKS8tLbGysiJUo7RHOzg4SFNTExMTE9jtdnHAxmQyMTs7y9TUlIjtppWxNEKcznJ5PB6RuKirqyMWi-H1esXliMnJSXFKM03KDA0N0dnZSTgcpq-vD4_Hw2OPPcby8rKIV21tbQnSNB6Pi5Nbvb294k6MzWbjmWeeoampCYvFwtDQEPn5-dxzzz1YLBba29uprKzkzjvvZGdnh9raWuRyuVD-ZDIZRqMRi8WCXC7n97__PQAWi4XW1lZCoRBFRUXU1dVht9spKSnBarXykY98REjQaYMkFArx_vvvixdmbW0NpVKJXC5HqVTi9_spLy_Hbrej0WiYnZ1F-sUvfpGFhQX0ej3Nzc2Mjo4K_vs73_kOSqWS6elpKisr0ev1YuceHR3l_vvvZ3JyEpfLhVarZXBwUFxh9nq9rK2tUVpaSkZGBhaLBa_XS11dHWfPnmXfvn3Mzs4yPz-PRqNhfX2dtB4QCoUEdVlXV8fS0hJarZaSkhKampo4ePAgFotFePhra2scPHiQ4uJixsfHMRqNLCwsoNFoiMVirK2todFoRLojNzeXvLw8fvOb35BIJER7ysvLY2lpSah-OTk5ojSnEyNSqZR_-qd_EoZQWtLt6elhY2ND5MzTWbz5-Xnm5-dJJBK0trayvb3N3NwcKysrDA4OioTJ7OwsoVAInU7H5OQkcrkck8nE5cuXWVpa4vLly0QiESwWi6iE6Vx9PB7njjvuYHt7G4_HI1jEdAo2fcB3fn6e_w868iDz_g7kjAAAAABJRU5ErkJggg

這時候應該 Nginx 會發出 414 Request-URI Too Large 警告,其原因為 GET 標頭過大,超過了 large_client_header_buffer,解決方法是在 /etc/nginx/nginx.confhttp 區段中加入以下參數並重新啟動 Nginx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo vi /etc/nginx/nginx.conf

...
http {
...
# Fix 414 Request-URI Too Large
client_header_buffer_size 512k;
large_client_header_buffers 4 512k;
client_max_body_size 100m;
...
}
...

$ sudo systemctl restart nginx

在測試沒問題後,就可以開始安裝 uWSGI 並開始部署。

uWSGI

uWSGI is a software application that “aims at developing a full stack for building hosting services”. It is named after the Web Server Gateway Interface (WSGI), which was the first plugin supported by the project.

uwsgi (all lowercase) is the native binary protocol that uWSGI uses to communicate with other servers.

uWSGI is often used for serving Python web applications in conjunction with web servers such as Cherokee and Nginx, which offer direct support for uWSGI’s native uwsgi protocol. For example, data may flow like this: HTTP client ↔ Nginx ↔ uWSGI ↔ Python app.

Wikipedia── uWSGI

Install uWSGI

在安裝 uWSGI 之前,我們需要先安裝相依函式庫再安裝 uWSGI:

1
2
$ sudo yum -y install gcc python3-devel
$ sudo pip3 install uwsgi

Create uwsgi.ini File

建立一個 uwsgi.ini 檔案在專案目錄下:

1
2
3
4
5
6
7
8
9
10
11
12
13
[uwsgi]
chdir = /home/wwwroot/ocr.holey.cc
socket = 127.0.0.1:8400
chmod-socket = 666
master = 1
processes = 2
callable = app
wsgi-file = api.py
daemonize = /home/wwwroot/ocr.holey.cc/uwsgi.log
stats = 127.0.0.1:8401
buffer-size = 32768
vacuum = true
die-on-term = true

Setup Website

Create Nginx conf File

建立一個 ocr.holey.cc.conf 檔案在 /etc/nginx/conf.d/ 下:

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
listen [::]:80;

server_name ocr.holey.cc;
root /home/wwwroot/ocr-thsrc;

location / {
include uwsgi_params;
uwsgi_pass 0.0.0.0:5000;
}
}

在建立完成後重新讀取組態檔:

1
$ sudo nginx -s reload

Create Service

接著為了我們操作方便,我們將建立一個 ocr.holey.cc.service 來管理網站:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sudo vi /etc/systemd/system/ocr.holey.cc.service
[Unit]
Description=Captcha OCR

[Service]
WorkingDirectory=/home/wwwroot/ocr.holey.cc
ExecStart=/usr/local/bin/uwsgi --ini /home/wwwroot/ocr.holey.cc/uwsgi.ini
Restart=always
# Restart service after 10 seconds if the uwsgi service crashes:
RestartSec=10
SyslogIdentifier=ocr-python
Type=forking
User=wwwroot

在建立完成後重新讀取組態檔:

1
$ sudo systemctl daemon-reload

如此一來我們便可以透過 systemctl 簡單地啟動、暫停、重啟網站:

1
2
3
4
5
6
7
8
# Start website
$ sudo systemctl start ocr.holey.cc.service

# Stop website
$ sudo systemctl stop ocr.holey.cc.service

# Restart website
$ sudo systemctl restart ocr.holey.cc.service

References