项目作者: edisonlz

项目描述 :
Python服务端开发框架-极易上手,超出你的想象!
高级语言: Python
项目地址: git://github.com/edisonlz/fastor.git
创建时间: 2019-12-23T09:18:29Z
项目社区:https://github.com/edisonlz/fastor

开源协议:Apache License 2.0

关键词:
api-tornado django-tornado python

下载


欢迎使用Python 服务端开发框架 Fastor

Fastor是一款专为Python 打造的API与后端管理系统,通过精心的设计与技术实现,集成了大部分稳定开发组件,memcache , redis,tornado,django,mysql 等。特点概述:

  • 功能丰富 : 支持大部分服务器组件,支持API Doc。
  • 得心应手 : 简单的实例,非常容易上手。
  • 代码自动生成 : 根据定义的model模型,自动生成增删改查代码。
  • 性能优先 : API使用Tornado开发,性能极高。
  • 稳定服务 : django和tornado经过深度优化,例如:数据库连接自动重连,缓存过期防止雪崩策略等等。
  • 支付功能 : 支持微信,支付宝支付功能。
  • API安全 : 在nginx层使用lua插件,对api签名并校验。

Fastor = faster + 人 , 意为(人效更高)


开始使用

Fastor 分为后端管理系统和API系统

1)后端管理系统

后台系统

2)API系统

Api系统

3)系统架构图

系统架构图d&


环境配置

部署
  • 下载或者clone fastor 代码
  • 支持python2.7,目前不支持python3.0。

框架结构

APP 目录结构
  • api: API 接口代码
  • app: ORM Model与后端Views代码
  • background: 分布式异步处理Async代码 & 定时任务
  • base: 基类和一些帮助函数
  • base/site-packages: 这里优先使用的是代码中的 site-packages 的python第三方库

一 后端管理系统

1)配置 base/settings.py
  • 创建数据库fastor_db
  • 配置数据库连接参数
  • 配置redis连接参数
  • 配置memached连接参数
  • 其他配置等
    1. 示例
    2. 'default': {
    3. 'ENGINE': 'django.db.backends.mysql',
    4. 'NAME': 'fastor_db', # Or path to database file if using sqlite3.
    5. 'USER': 'root', # Not used with sqlite3.
    6. 'PASSWORD': '123456', # Not used with sqlite3.
    7. 'HOST': '127.0.0.1', # Set to sempty string for localhost. Not used with sqlite3.
    8. 'PORT': '3306', # Set to empty string for default. Not used with sqlite3.
    9. 'CHARSET': 'utf8',
    10. 'OPTIONS': {
    11. 'init_command': 'SET storage_engine=INNODB; SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;set autocommit=1;',
    12. },
    13. },
    配置 app/settings.py
  • 配置应用(app) 如第一次使用默认配置即可
2)配置成功后,同步数据库并运行演示程序
  1. cd app/
  2. python manager.py syncdb #同步数据库并创建管理员
  3. python runserver #启动服务
  4. open http://127.0.0.1:8000/ 并使用刚创建的管理员账号密码登录
3)创建自己的 models
1. 在目录 iclass/models/中创建model文件,例如 user.py
2. 在 iclass/models/init中导入 user 中的model对象
3. 示例
  1. class BaseUser(models.Model):
  2. """
  3. 用户
  4. """
  5. user_id = models.CharField(max_length=32, verbose_name=u'用户ID', default='',primary_key=True)
  6. username = models.CharField( max_length=11, verbose_name=u'用户名', default='')
  7. nickname = models.CharField(max_length=20, verbose_name=u'昵称', default='')
  8. password = models.CharField(max_length=100, verbose_name=u'密码', default='')
  9. image_url = models.CharField( max_length=255, verbose_name=u'用户头像', default='')
  10. sex = models.IntegerField( verbose_name=u'性别', default='1')
  11. email = models.CharField( max_length=50, verbose_name=u'邮箱', default='' )
  12. status = models.IntegerField( verbose_name=u'状态', default='0' ) # 0-关闭,1-开启
  13. register_from = models.IntegerField(verbose_name=u'注册设备', default='0', choices=RegisterFromChoices)
  14. last_login_time = models.DateTimeField(auto_now_add=True, verbose_name=u'最后登录时间')
  15. create_time = models.DateTimeField(auto_now_add=True, verbose_name=u'创建时间')
  16. class Meta:
  17. app_label = "iclass"
  18. db_table = "user"
  19. verbose_name = u"客户"
4).执行自动生成view/templates代码 python manage.py gencode iclass.models.base_user BaseUser
  1. - 这里将会在viewstemplates自动生成增删改查的代码
  2. - iclass/urls.py 编写url规则
  3. - view 导入views/__init__.py
  4. - 设置入口导航 iclass/templates/cms_index/left_side_menu.html
  1. """
  2. 自动生成view.templates 说明:
  3. 图像上传: field_name 中包括 image 字符串的会自动检测为图像控件
  4. 时间: field_type 等于 DatetimeField 会自动生成时间控件
  5. poistion 字段:如果包含position 字段,1,数据不分页 2.可以生成拖动保存位置
  6. choices: 如果 module 里面包括choices,自动生成select控件
  7. """
5) 后台系统服务部署
  1. - 部署nginx nginx配置文件路径, app/conf/nginx.conf
  2. - 执行 bash app/app.sh 执行进程的数量,端口号均在这里配置。
6) 为了区分开发环境和线上环境支持本地my_settings.py,如果配置了my_settings.py,将覆盖原有配置,默认加到了.gitignore
  1. - 配置 app/my_settings.py
  1. #示例配置
  2. def load_settings(settings):
  3. settings.update({
  4. 'DEBUG': True,
  5. 'CACHES': {
  6. 'default': {
  7. 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
  8. }
  9. },
  10. 'DATABASES': {
  11. 'default': {
  12. 'ENGINE': 'django.db.backends.mysql',
  13. 'NAME': 'fastor_db', # Or path to database file if using sqlite3.
  14. 'USER': 'api', # Not used with sqlite3.
  15. 'PASSWORD': 'Win123456', # Not used with sqlite3.
  16. 'HOST': '58ec9db06f05c.sh.cdb.myqcloud.com',
  17. 'PORT': '3612',
  18. 'CHARSET': 'utf8',
  19. 'OPTIONS': {
  20. 'init_command': 'SET storage_engine=INNODB; SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;set autocommit=1;',
  21. },
  22. },
  23. },
  24. 'AUTHENTICATION_BACKENDS': (
  25. "django.contrib.auth.backends.ModelBackend",
  26. "app.user.backends.LDAPBackend",
  27. ),
  28. "memcache_settings": {
  29. "func_cache": ["127.0.0.1:11211"],
  30. },
  31. 'redis_settings': {
  32. "REDIS_BACKEND": {"servers": '127.0.0.1', "port": 6379, "db": 11},
  33. "MQUEUE_BACKEND": {"servers": '127.0.0.1', "port": 6379, "db": 12},
  34. "MASTER_REDIS": {"servers": '127.0.0.1', "port": 6379, "db": 9},
  35. "SLAVE_REDIS": {"servers": '127.0.0.1', "port": 6379, "db": 9},
  36. },
  37. "memcache_proxy_settings": {
  38. },
  39. })
7) 关于上传图片地址的配置
  1. base/settings.py
  2. #上传到本地地址
  3. 'SAVE_IMAGE_PATH':"/tmp",
  4. #这个本地地址chrome访问无权限,如果存储在本地,可以启动nginx作为代理服务器访问本地图片
  5. "IMAGE_URL_HOST":"file:///private/tmp",
  6. #参考配置
  7. server {
  8. listen 80;
  9. server_name image.fastor.com;
  10. charset utf-8;
  11. location / {
  12. alias /data/images/;
  13. expires 15m;
  14. }
  15. }
  16. "IMAGE_URL_HOST":"http://image.fastor.com",
  17. 备注:建议上传到腾讯云或者阿里云的对象存储中。
8) DBrouter配置
  1. base/settings.py
  2. #默认配置
  3. 'DATABASE_ROUTERS': ['app.db_router.MainRouter'],
  4. #语法: { 'app_label': '数据库连接' }
  5. 'DATABASE_MAPPING': {},
  6. 'DATABASE_READ_MAPPING': {},
  7. #读写分离配置
  8. 'DATABASE_MAPPING': {"iclass":"default"},
  9. DATABASE_READ_MAPPING': {"iclass":"slave"},
9) UMeditor配置
  1. 配置地址:app/statics/umeditor/umeditor.conf
  2. #默认配置,如有需要可自行配置
  3. ,imageUrl:"/editor/upload_img" //图片上传地址
  4. ,imageFieldName: "files[]" //图片数据的key,若此处修改,需要在后台对应文件修改对应参数
10) 右侧菜单选中规则
  1. 右侧菜单:app/iclass/templates/cms_index/left_side_menu.html
  2. data-menu-id="100"
  3. #view 对应的增删改查 template 中的 menu-sel 须和 data-menu-id值相等
  4. <input type="hidden" id="menu-sel" value="100">

二 API管理系统

1)运行api管理系统
  1. cd api/
  2. python main.py --doc --debug --logging=debug
  3. API系统默认用户名:admin 密码:123456
  4. 密码可以在api/settings.py 中配置 ["api"]["password"] = "123456"
2)创建api文件,在api/handler/user_api.py
  1. 示例代码user_api.py
  2. @handler_define
  3. class GetUserInfo(CachedPlusHandler):
  4. def get_cache_expire(self):
  5. return 60 * 1
  6. def get_cache_key(self):
  7. return {
  8. 'user_id': self.arg('user_id', ''),
  9. }
  10. @api_define("GetUserInfo", r'/user/info/detail',
  11. [
  12. Param('user_id', True, str, "", "201702071511512892383865", u'用户id'),
  13. ],
  14. description="""读取用户基本信息""",
  15. return_desc=""""""
  16. )
  17. def get(self):
  18. user_id = self.arg('user_id')
  19. user = BaseUser.objects.filter(user_id=user_id).first()
  20. if not user:
  21. response = {
  22. "code": 0,
  23. "status": "fail",
  24. "msg": "用户不存在",
  25. }
  26. return self.write(result)
  27. response = {
  28. "status": "success",
  29. "code": 200,
  30. "user":user.to_json()
  31. }
  32. return self.write(response)
2)注册api,在api/document/doc_insall_handlers.py 注册新增api
  1. 示例代码
  2. INSTALL_HANDLERS = [
  3. "api.handler.common",
  4. "api.handler.user_api",
  5. ]
  6. INSTALL_HANDLERS_NAME = {
  7. "api.handler.common": "通用接口",
  8. "api.handler.user_api":"用户接口",
  9. }
3)重新运行API
  1. python main.py --doc --debug --logging=debug
4)API 缓存配置
  1. from api.view.base import BaseHandler , CachedPlusHandler
  2. #1.接口缓存
  3. @handler_define
  4. class GetUserInfo(CachedPlusHandler):
  5. #这个方法返回接口缓存时间带娃秒
  6. def get_cache_expire(self):
  7. return 60 * 1
  8. #这个方法返回缓存key值,一般返回请求参数即可
  9. def get_cache_key(self):
  10. return {
  11. 'user_id': self.arg('user_id', ''),
  12. }
  13. #2.方法缓存
  14. from wi_cache import function_cache
  15. #cache_keys.方法参数,如多个参数用逗号分隔。例如:"user_id,course_id"
  16. #prefix:缓存key前缀#expire_time:缓存时间单位秒
  17. @classmethod
  18. @function_cache(cache_keys="user_id", prefix="func#get_user", expire_time=60*5)
  19. def get_user(cls, user_id):
  20. user = cls.objects.filter(user_id=user_id).first()
  21. return user
  1. #https://pypi.org/project/hash_ring/
  2. #默认memcached分布式算法使用求余数,
  3. #如果是大型应用,可以将memcached 修改为一致性hash算法
  4. #一致性hash算法,主要解决key分布不均匀问题。
  5. #备注:不建议使用memcached代理服务,对性能有损耗,出问题不好查找,max_open_files连接符开到2万没问题的,内存要够。
  6. #修改算法路径 /fastor/base/site-packages/wi_cache/__init__.py #148行
  7. # 将 func_cache = memcache.Client(memcache_settings["func_cache"])
  8. # 替换为 func_cache = MemcacheRing(memcache_settings["func_cache"])
  9. #示例代码
  10. from hash_ring import MemcacheRing
  11. mc = MemcacheRing(['127.0.0.1:11222','127.0.0.1:11111'])
  12. mc.set('hello', 'world')
  13. print mc.get('hello')
5)API 异步处理
  1. #示例客户端代码
  2. @handler_define
  3. class AsyncDemo(BaseHandler):
  4. @api_define("AsyncDemo", r'/api/async/demo', [
  5. Param('user_id', True, str, "" , "123456" , u'用户ID'),
  6. Param('course_name', True, str, "","Ed老师的python课程" , u'课程名称'),
  7. ], description="[示例]处理异步事件", return_desc="""""")
  8. def get(self):
  9. user_id = self.arg("user_id")
  10. course_name = self.arg("course_name")
  11. data = {
  12. "user_id":user_id,
  13. "course_name":course_name,
  14. }
  15. dispatch_client = Client()
  16. dispatch_client.dispatch("demo.async.send", data)
  17. response = {
  18. "code": 200,
  19. "status": "success",
  20. }
  21. return self.write(response)
  1. #示例服务端代码 background/demo.py
  2. def do_sync_worker(data):
  3. print "**Recieve data: ", data
  4. logging.error(data)
  5. if __name__ == "__main__":
  6. worker = Worker("demo.async.send",support_brpop=False)
  7. try:
  8. worker.register(do_sync_worker)
  9. worker.start()
  10. except KeyboardInterrupt:
  11. worker.stop()
  12. print "exited cleanly"
  13. sys.exit(1)
  14. except Exception as e:
  15. logging.error(e)
  1. #示例服务端代码启动配置 background/supervisord.conf
  2. #启动:supervisord -c background/supervisord.conf
  3. #重启:supervisorctl -c background/supervisord.conf restart all
  4. #重启demo服务:supervisorctl -c background/supervisord.conf restart demo:*
  5. [program:demo]
  6. process_name = demo-%(process_num)s
  7. command=/data/python2.7/bin/python /data/python/fastor/background/demo.py
  8. process_name=%(program_name)s_%(process_num)02d
  9. stdout_logfile = /data/logs/demo.log
  10. numprocs=2 #这里需要配置你并发处理任务的进程数量
  11. autostart=true
6)API 增加了微信支付宝支付接口
  • 文档详见doc/pay文档
  • 需要根据当前业务,实现业务逻辑
  • 代码依赖第三发库,PIL,OpenSSL,qrcode 需要部署安装
  1. #代码示例
  2. @handler_define
  3. class WeixinAppPayHandler(BaseHandler):
  4. @api_define("WeixinAppPayHandler", r'/api/wx/app/pay',
  5. [
  6. Param('order_id', True, str, "", "201907261548295499512023", u'订单id'),
  7. Param('good_name', True, str, "", "xxx", u'good_name'),
  8. ],
  9. description="微信APP支付")
  10. def get(self):
  11. application_id = self.arg_int('application_id', 1)
  12. order_id = self.arg('order_id')
  13. good_name = self.arg('good_name','')
  14. order = OrderInfo.objects.filter(order_id=order_id).first() #订单逻辑需要根据当前业务实现
  15. pay = WePayDoPay(
  16. out_trade_no=order.order_id,
  17. subject=good_name,
  18. total_fee= int(order.amount * 100),
  19. body=good_name,
  20. ip = self.user_ip,
  21. payment_type = "NATIVE",
  22. application_id=application_id
  23. )
  24. params = pay.get_pay_params()
  25. results = {
  26. "params": params,
  27. "code":200,
  28. 'status': "success",
  29. "msg":"成功",
  30. }
  31. return self.write(results)
8) API系统高级方法
  1. #获取参数,必传
  2. order_id = self.arg('order_id')
  3. #获取参数,带默认值,非必传
  4. order_id = self.arg('order_id','')
  5. #获取int型参数
  6. user_type = self.arg_int('user_type')
  7. #获取bool型参数
  8. is_admin = self.arg_bool('is_admin')
  9. #(用户)客户端ip地址
  10. user_ip = self.user_ip
9) API系统服务部署
  1. - 部署nginx nginx配置文件路径, api/conf/nginx/nginx.conf
  2. - 执行 bash api/tornado.sh 执行进程的数量,端口号均在这里配置。
  1. 日志切割
  2. #Delete old more than 7 days log files
  3. 0 0 * * * /data/python/fastor/api/bin/logrotate-nginx.sh > /dev/null 2>&1
  4. 5 0 * * * /data/python/fastor/api/bin/logrotate.sh > /dev/null 2>&1
  5. 22 2 * * * find /data/logs/ -mtime +7 -type f -name "*log*" -exec rm -rf {} \;
10) API安全数字签名,此功能适合高手
  1. 1API接口安全规则
  2. Method GET | POST
  3. 增加参数:
  4. _s_: signature 签名
  5. _t_: 当前时间戳,校队系统时间在初始化接口返回,接口参数有效期30分钟
  6. _t_ = timestamp = timestamp + (客户端当前时间 - 客户端从初始化获得timestamp的时间)
  7. 这样做到原因是需要客户端校对服务器时间,因为客户端时间有可能不准确。
  8. 签名方式:
  9. token_string = req_method + ":" + path + ":" + sorted(query) + ":" + timestamp + ":" + secret
  10. 其中:sorted(query) 是按照key的自然顺序排列,然后以key=value的形式累加
  11. 例如: GET /test?b=2&a=1
  12. token_string = GET + ":" + /test + ":" + a=1b=2 + ":" + 1457665234 + ":" + secret_xxxxxx
  13. signature = ngx.md5(token_string)
  14. *methodpost的情况下,需要将请求发到body中,不支持url参数post*
  15. 接口返回状态码为 410 请重新更新服务器时间。
  16. 接口返回 403 为签名错误,访问被禁止。
  1. 2Nginx 部署,详见 deploy.secure.api.sh
  2. - 这里建议使用 nginx-1.0.4 版本,稳定/性能高。
  3. - deploy.secure.api.sh部署起来过于繁琐,需要花费一定的时间和经历。
  4. - 部署完毕后建议制作镜像。
  5. - 如果下载不到历史版本,可以从百度云盘下载,链接: https://pan.baidu.com/s/19-5fSBn5wM-xwGLZL-K34Q 提取码: jhbt
  1. 3nginx配置示例
  2. #lua配置地址:api/conf/nginx/lua
  3. #签名密钥配置:api/conf/nginx/lua/check_pid_signature.lua
  4. local secret = "82406d2ff6c40894a26a3ad34eafff2f" #32位字符串
  5. location / {
  6. add_header Access-Control-Allow-Origin *;
  7. add_header Access-Control-Allow-Headers X-Requested-With;
  8. add_header Access-Control-Allow-Methsods GET,POST,OPTIONS;
  9. #引入效验文件,如上传图片,初始化接口可不加载该配置
  10. access_by_lua_file conf/lua/check_pid_signature.lua;
  11. proxy_pass http://make_app_api;
  12. proxy_connect_timeout 3;
  13. proxy_send_timeout 3;
  14. proxy_read_timeout 3;
  15. proxy_redirect default;
  16. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  17. proxy_set_header X-Real-IP $remote_addr;
  18. proxy_set_header Host $http_host;
  19. proxy_set_header Range $http_range;
  20. }

未来版本支持

  • tensorflow 基础版本基于大数据的用户分类
  • 图像识别 opencv 拍照识别答题卡
作者: 向Ed老师曾经的战友们致敬!