后台开发之架构

实习

  • 近期在某家公司实习,顺应公司部门文化有一场为期十天左右的实习生小组赛,内容就像黑客马拉松一样。
  • 正好在此小组负起后台开发的职责,技术选择是 Python 的 Web框架 Flask, 至于本文想要记录的并不是 flask这个框架怎么用,好不好用,而是其他一些一直以来比较零散的知识
  • 也许这是在学校中开发所注意不到的,那就是实际情况总是与理想状态相去甚远。

架构图

  • 这个结构是我为本小组提供的后台拓扑,包含三个部分:
    1. Nginx反向代理
    2. Flask逻辑服务器
    3. MySQL数据库服务器
  • 看起来非常朴实,实际上门门道道,细节问题着实费了我两天时间,特别是在逻辑服务器哪里,因为粗心大意,愣是搭了一个晚上还是失败。
  • 本次是将终端和前端的接口进行了统一,实际上这样对一个快速开发项目来说的确是必须的,不必考虑太多细节,追求的是效率,毕竟Flask框架追求的的确就是开发效率,至于并发情况实际上在众多框架中,并不能算太好。

    开始记录

Nginx反向代理

  • 这个的应用应该是非常广泛的,而且十分有效
  • 立竿见影的效果就是屏蔽了后端的大部分细节,提高了容错性,以及其本身就是一个超高性能的静态资源服务器,更是能够帮助很多应用提升性能。
  • 在Linux下安装 Nginx 就像明面上说的那么简单,但是一般主流的发行版中的 Nginx 的版本都比较低,所以可能有时候用起来不如传闻中的那么神速。
    • 于此你可以选择接受它
    • 或者去官方网下载最新的稳定版本,按照步骤一步一步的安装。
  • 在这里就不再详述,这要是详细记录,又是一个独立的话题了。
  • Nginx在Ubuntu下,默认配置文件放在了
    • /etc/nginx/nginx.conf 以及 /etc/nginx/site-available/default
    • 一般情况下,我们修改后者就行,改前记得先备份
  • Nginx有自己的URL一套匹配规则,通俗的来说就是将一个完整的链接解析成服务器知道的绝对路径

例如: http://www.domain.com/index.html 解析成服务器上的 /var/www/html/index.html 路径

  • 这个匹配规则,也是可以单独写成一个话题,那么直接看本次配置的时候遇到的一个问题:除了几种特殊情况意外,其他路径都返回 index.html 这个文件,该如何做到?

    server {
        listen  8080;
        server_name xxx.xxx.xxx.xxx; #公网地址
    
        location /api/ {
            include      uwsgi_params;
            uwsgi_pass   127.0.0.1:7001;  # 指向uwsgi 所应用的内部地址,所有请    求将转发给uwsgi 处理
            uwsgi_param UWSGI_CHDIR  /data/mini/www; # 指向网站根目录
            uwsgi_param UWSGI_SCRIPT manage:app; # 指定启动程序
        }
        location ~ ^/(js|css)/{
            root /data/mini/source/Mini_Production/build;
        }
    
        location ~ ^/(img|lib)/{
            root /data/mini/source/Mini_Production/static;
        }    
        location / {
            root /data/mini/source/Mini_Production/;
            index index.html;
            try_files $uri /index.html;
        }
    }
    

    重点在于最后一个location解决了我的问题

    至于其他的规则,网上详细叙述的太多了,这里只是记录一个特殊情况。

  • 说完这个就该来说说,反向代理的简单配置了

    • 说是简单配置,因为将会使用Nginx自带的功能,而不引入第三方模块的功能
    • 由于服务的需求,我们的应用需要保存session,所以Nginx反向代理的时候必须要保证的一个地方就是:同一个IP必须反向代理到同一个服务器上,否则会导致某些意想不到的错误。
    • 而Nginx自带的两个策略之一ip_hash正为此而生。
  • 反向代理的配置分为两个块,upstreamserver

    • 直接写在/etc/nginx/nginx.conf里就行

      upstream yuehuo{
      ip_hash;
          server  xxx.xxx.xxx.xx1:8080;
          server  xxx.xxx.xxx.xx2:8080;
          ...
      }
      
      server {
          listen 9090;
          location / {
              proxy_pass http://yuehuo;
              proxy_set_header X-Real-IP $remote_addr;
          }
      }
      

      有几台在upstream写几个server,以分号结尾,这样一来就实现了一个最简单的反向代理,对于晓得项目而言其实是已经足够了。

      不用怀疑什么,Nginx是一个很成熟的开源项目,不像某些开源产品只是一副外壳而已。

  • 实际上,着一些就构成了我们小组后台结构的第一部分,也就是理论上暴露给外部的访问点,就是这台独自承载着Nginx反向代理功能的服务器。
  • 任何有效访问只需要访问这台服务器,就能得到他们想要的东西,而不需要去记下多台服务器的域名或者IP。
  • 而我也可以随时替换后端的服务器,并且服务的请求者什么也不会察觉。
  • 甚至还可以配置一个backup,在所有服务器都崩溃的时候,它能挽救一下局面。所谓的最初级的容灾。

Flask逻辑服务器

  • 在这个部分,实际上是用的是Nginx + Supervisor + uwsgi + Flask 进行的搭建
  • 看起来很复杂,很多组建,实际上也就这样:
    • Nginx用来引导所有请求至正确的位置:动态请求转发给 Flask,静态资源自己处理就行。
    • uwsgi则是Flask运行的一个平台,不在叙述,可以将两者看成一体,实际上从上面的Nginx配置文件中也能发现一些端倪。
    • 其实这样已经能够很好完整的服务各方了,那为什么还有一个Supervisor呢?就是为了让这个服务更加规范和自动化,这相当于一个自动化可配置的 uwsgi 启动器(写过ruby on rails的应该也很熟悉这个东西)。
  • Nginx的配置就如上所示,在etc/nginx/sites-available/default里写入上一个模块介绍的内容
  • 安装完Supervisoruwsgi之后,将uwsgi的配置文件存放好,比如放在flask程序的结构包里,总之记好路径,在之后的Supervisor中会用到,之后写好它的配置文件之后,启动它的服务就行。
  • 讲的有点泛泛但是却没什么难点,细心一些就能配置对,当然是找到网上的配置教程。
  • 从前端上传的图片,被存放在了Flask逻辑服务器上,但是这导致一个缺点,就是如何同步多台逻辑服务器之间的图片资源?
    • 虽然图片是静态资源的一种,但是它具有动态更新的可能性,所以解决方案可以有两种:多台服务器同步或者单独存放在一个独立的图片服务器上。
    • 前者的缺点很明显,这是当前技术领域最难的分布式同步问题。后者相对容易而且能带来很不错的性能,可以选择专业的图片存储服务商,或者自己搭建。
    • 当前架构还算小规模,暂时使用scp定时拷贝的策略来同步。

总体来说,Flask 服务器被配置成 Nginx -> supervisor(uwsgi -> flask) 的模式

数据库分离 之 数据库服务器与读写分离

  • 这个部分是耗时比较久的
  • 首先问题描述:
    • 如果数据库内嵌在flask Server中,万一某台Flask Server宕机,同时也会损失一台数据库服务。
    • 多台flask Server中内嵌的数据库之间的数据该如何做同步?
    • Flask Server服务器是否过于臃肿
  • 综上问题,我们相出将数据库从Flask Server 提出来,以分布式的方式实现
  • 首先需要对Flask Server做一下数据库系统的透明处理,意思就是让无论多少台Flask Server都只感觉在操作同一个数据库。
    • 首先想到数据库中间件MySQL-Proxy,做一个读写分离
    • 配置主从数据库,以此实现容灾以及性能提升。
  • 首先将数据库放在单独的服务器中,配置成 一主多从 的的模式,即一写多读,其中主服务器可读可写
    • 这个可以实现的是,当主数据库被写的时候,可以同步更新到从服务器上。
    • 这么做的目的就是将读的性能大大提升。
  • 其次对前方屏蔽数据库系统的细节,即增加中间件的存在
    • 将之前实现的数据库系统,接在MySQL-Proxy的后方,让其帮助我们实现读写分离的功能。

也可以实现多主多从的效果,远离和一主多从十分类似,就是将两台 主数据库,互相配置成主从。并在主服务器的MySQL配置的[mysqld]选项中增加log_slave_update标志

  • 两篇好教程:
    1. MySQL数据库主从配置
    2. MySQL-Proxy配置读写分离
  • 实现上的细节

    1. slave出现:Connecting to master 且错误码为 2003的时候,你需要看看你的MySQL配置中是否有bind-address = 127.0.0.1这个选项,注释掉它
    2. 配置完以后,你会发现状态变了,这样才算成功。
  • 万年不变的MySQL中文问题

    • 在所有方法都查证之后,四方编码都为 UTF-8时,却发现还是不行
    • 这时候,可以尝试将整个数据库Drop,之后重建。这样会使刚才的配置生效在这个数据库上
    • 其实数据库可以不必DROP,只需要将表删了重建即可。

反思

  • 一个最大的问题就是,如果数据已经多到一个DB存不下,不得不用多数据库来存储的时候,该怎么办?
  • 这个是我们这个架构最致命的缺点。
    • 如果有更好的解决方案,我再记录