second_服务器项目搭建_基于flask-JSONRPC提供的RPC_APICloud


 

2. 服务端项目搭建

进入服务器,安装各种包

sudo apt-get update  # 更新相关源
sudo apt-get install git  # 按照git

# mysql的安装
sudo apt-get install mysql-server
sudo apt-get install libmysqlclient-dev

 

进入数据库, 刚安装完成的数据库,root是空密码,所以要修改root的密码

mysql -uroot -p

# 修改密码
use mysql;
update user set authentication_string=password('新密码') where user='root' and Host ='localhost';
update user set plugin="mysql_native_password"; 

# 刷新权限
flush privileges;

 

创建数据库命令如下:

create database mogu charset=utf8mb4;

 

redis安装

sudo apt-get install redis-server

注意,redis安装完成以后,要观察redis是否启动了,如果没有启动,则参考之前redis笔记,启动redis。

# 查看是否启动redis-server,一般是没启动
# 原因是现在的云服务器基本都默认给redis配置一个IPV6的绑定地址。而我们大部分人的服务器是没使用IPV6的
ps -aux | grep redis

# 如果没有启动,则运行以下命令,再查看是否启动成功,再不行,则查看下面 注意事项
redis-server /etc/redis/redis.conf

 

注意事项:给服务器开通redis的6379,你也可以把全部端口打开(腾讯云可以)

 

安装虚拟环境

默认情况下ubuntu18.04版本中已经内置了Python3.6.7了。但是没有内置pip。所以先安装pip。

sudo apt install python3-pip
pip3 install virtualenv
pip3 install virtualenvwrapper

安装完成了以后,接下来需要配置系统环境变量

mkdir $HOME/.virtualenvs

执行命令,打开并编辑 ~/.bashrc

vim  ~/.bashrc

文件末尾添加以下几行代码(注意最后一句命令,和我们本地的有所不同),:wq 保存退出。

export WORKON_HOME=$HOME/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh

可以用一下操作查看到你的虚拟环境实际地址

 whereis virtualenvwrapper   # 查看地址
/home/ubuntu/.local/bin/virtualenvwrapper.sh

刷新配置文件

source ~/.bashrc

虚拟环境中安装基本模块

创建虚拟环境

mkvirtualenv mogu -p python3

安装依赖模块

pip install flask==1.0.3
pip install pymysql==0.9.3
pip install redis==3.2.1
pip install flask-sqlalchemy==2.4.0
pip install flask-mysqldb==0.2.0
pip install flask-session==0.3.1
pip install flask-script==2.0.6
pip install flask-migrate==2.5.2

我们把项目搭建在/home/mogu目录下

cd /home
mkdir moguapp
cd moguapp
# 创建flask项目启动文件manage.py
vim manage.py

manage.py文件代码:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
  return 'hello,moluo~'

if __name__ == '__main__':
  app.run(host="0.0.0.0")

在终端下使用python manage.py运行测试服务器

python manage.py

前面我们已经在云服务器控制台的网络安全组中添加5000端口,所以可以直接通过浏览器进行访问。

到码云上面,创建一个git仓库,把服务器代码上传到码云

git config --global user.name "mooluo"
git config --global user.email "649641514@qq.com"
git init
git add .
git commit -m "创建manage.py启动文件"
git remote add origin https://gitee.com/mooluo/moguapp.git
git push -u origin master

 

3. 服务端项目初始化

apt install tree

目录结构

moguapp/
├─ logs/                   # 日志文件存储目录
├─ apps/                   # 项目主要逻辑业务代码保存目录
│  ├─ __init__.py          # 项目初始化文件
│  ├─ modules/             # 保存项目中所有api模块的存储目录
│  │  ├─ common/           # 公共api接口目录
│  │  │  ├─ __init__.py    # 公共api接口的初始化文件
│  │  │  ├─ models.py      # 公共api接口的模型文件
│  │  │  └─ api.py         # 公共api接口代码文件
│  │  └─ __init__.py
|  ├─ utils/               # 项目自定义封装工具包目录
|  ├─ libs/                # 项目第三方工具包目录
|  ├─ settings/            # 项目配置存储目录
│  │  ├─ dev.py            # 开发阶段的配置文件
│  │  ├─ prop.py           # 生产阶段的配置文件
│  │  └─ __init__.py
│  └─ statics/             # 保存项目中所有的静态资源文件[img/css/js]
└── manage.py              # 项目的终端管理脚本文件

Config项目配置

settings/__init__.py 编写基本配置代码

import redis
class Config(object):
    """项目配置信息"""
    # 设置密钥,可以通过 base64.b64encode(os.urandom(48)) 来生成一个指定长度的随机字符串
    SECRET_KEY = "T1vEjTCjkGon5vU8C6Xq3ujNSQgHQje"
    # 配置日志
    LOG_LEVEL = "DEBUG"

    # 调试模式
    DEBUG = True

    # 数据库的配置信息
    SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1:3306/mogu?charset=utf8"
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    SQLALCHEMY_ECHO = True

    # redis配置
    REDIS_HOST = "127.0.0.1"
    REDIS_PORT = 6379

    # session 配置
    SESSION_TYPE = "redis"  # 指定 session 保存到 redis 中
    SESSION_USE_SIGNER = True  # session_id 进行加密签名处理
    SESSION_REDIS = redis.StrictRedis( host=REDIS_HOST, port=REDIS_PORT,db=1 )
    PERMANENT_SESSION_LIFETIME = 24 * 60 * 60 # session 的有效期,单位是秒

settings/dev.py编写开发环境的配置信息,代码:

from . import Config
class DevelopementConfig(Config):
  """开发模式下的配置"""
  pass

settings/prop.py编写生产环境的配置信息,代码:

from . import Config
class ProductionConfig(Config):
    """生产模式下的配置"""
    DEBUG = False
    LOG_LEVEL = "INFO"
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_ECHO = False

配置项目日志

把日志设置封装成一个函数init_log,保存到utils/log.py文件中

import logging
from logging.handlers import RotatingFileHandler

# 把日志相关的配置封装成一个日志初始化函数
def init_log(Config):
    # 设置日志的记录等级
    logging.basicConfig(level=Config.LOG_LEVEL)  # 调试debug级
    # 创建日志记录器,指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限
    file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024 * 1024 * 300, backupCount=10)
    # 创建日志记录的格式 日志等级 输入日志信息的文件名 行数 日志信息
    formatter = logging.Formatter('%(levelname)s %(filename)s:%(lineno)d %(message)s')
    # 为刚创建的日志记录器设置日志记录格式
    file_log_handler.setFormatter(formatter)
    # 为全局的日志工具对象(flaskapp使用的)添加日志记录器
    logging.getLogger().addHandler(file_log_handler)

项目初始化代码抽取

apps/__init__.py文件中,创建flask应用并加载配置

from flask import Flask
from redis import StrictRedis
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy

from apps.settings.dev import DevelopementConfig
from apps.settings.prop import ProductionConfig
from apps.utils.log import init_log

config = {
    "dev": DevelopementConfig,
    "prop": ProductionConfig,
}

# 预设全局变量
redis_store = None
db = SQLAlchemy()

def init_app(config_name):
    """项目的初始化功能"""
    app = Flask(__name__)

    # 设置配置类
    Config = config[config_name]

    # 加载配置
    app.config.from_object(Config)

        # redis的链接初始化
    global redis_store
    redis_store = StrictRedis(host=Config.REDIS_HOST, port=Config.REDIS_PORT,db=0)

    # 开启session功能
    Session(app)
    
    # 配置数据库链接
    db.init_app(app)

    # 启动日志
    init_log(Config)

    return app

配置项目启动文件

manage.py启动文件中,加载app初始化工厂函数,并使用flask-script启动项目

# coding=utf-8
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

from apps import init_app,db

app = init_app("dev")

# 使用终端脚本工具启动和管理flask
manager = Manager(app)

# 启用数据迁移工具
Migrate(app, db)
# 添加数据迁移的命令到终端脚本工具中
manager.add_command('db', MigrateCommand)

@app.route('/')
def index():
  return 'hello,moluo~'

if __name__ == '__main__':
    manager.run()

在虚拟环境重新使用manage.py启动项目

sudo pip install flask_script
sudo pip install flask_migrate
sudo easy_install redis
python manage.py runserver --host=0.0.0.0 --port=5000

 

4. 基于Flask-JSONRPC提供RPC接⼝

JSON-RPC是一个无状态的、轻量级的远程过程调用(RPC)协议。

所谓的RPC,Remote Procedure Call的简写,中文译作远程过程调用或者远程服务调用

直观的理解就是,通过网络来请求服务,获取接口数据,而不用知晓底层网络协议的细节。

RPC支持的格式很多,比如XML格式,JSON格式等等。最常用的肯定是json-rpc。

 

JSON-RPC协议中的客户端一般是为了向远程服务器请求执行某个方法/函数。客户端向实现了JSON-RPC协议的服务端发送请求,多个输入参数能够通过数组或者对象传递到远程方法,这个远程方法也能返回多个输出数据,具体是什么,当然要看具体的方法实现。

所有的传输都是单个对象,用JSON格式进行序列化。

请求要求包含三个特定属性:

jsonrpc: 用来声明JSON-RPC协议的版本,现在基本固定为“2.0”

method,方法,是等待调用的远程方法名,字符串类型

params,参数,对象类型或者是数组,向远程方法传递的多个参数值

id,任意类型值,用于和最后的响应进行匹配,也就是这里设定多少,后面响应里这个值也设定为相同的
响应的接收者必须能够给出所有请求以正确的响应。这个值一般不能为Null,且为数字时不能有小数。

响应也有三个属性:

result,结果,是方法的返回值,调用方法出现错误时,必须不包含该成员。

error,错误,当出现错误时,返回一个特定的错误编码,如果没有错误产生,必须不包含该成员。

id,就是请求带的那个id值,必须与请求对象中的id成员的值相同。请求对象中的id时发生错误(如:转换错误或无效的请求),它必须为Null

当然,有一些场景下,是不用返回值的,比如只对客户端进行通知,由于不用对请求的id进行匹配,所以这个id就是不必要的,置空或者直接不要了。

在flask中要实现提供json-rpc接口,开发中一般使用Flask JSON-RPC模块来实现。

git地址:https://github.com/cenobites/flask-jsonrpc

文档:http://wiki.geekdream.com/Specification/json-rpc_2.0.html

安装Flask-JSONRPC模块

pip install Flask-JSONRPC==0.3.1

快速实现一个测试的RPC接口。

例如,我们直接在manage.py启动文件中直接实现。

from apps import init_app,db
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from flask_jsonrpc import JSONRPC

app = init_app("dev")

# 使用终端脚本工具启动和管理flask
manager = Manager(app)

# 启用数据迁移工具
Migrate(app, db)
# 添加数据迁移的命令到终端脚本工具中
manager.add_command('db', MigrateCommand)

@app.route('/')
def index():
  return 'hello,moluo~'

# 初始化jsonrpc模块
jsonrpc = JSONRPC(app, '/api')

# 实现rpc接口
@jsonrpc.method('Common.index')
def index():
    return u'Welcome to Flask JSON-RPC'


if __name__ == '__main__':
    manager.run()

客户端需要发起post请求,访问地址为:http://服务器地址:端口/api

注意

默认情况下,/api接口只能通过post请求访问。如果要使用jsonrpc提供的界面调试工具,则访问地址为:

http://服务器地址端口/api/browse/

访问数据格式应是:

{
    "jsonrpc": "2.0",
    "method": "Common.index",
    "params": {},
    "id": "1"
}

 

通过postman访问效果:

对RPC接口代码进行模块化分离

把jsonrpc模块的初始化代码抽离到app对象初始化函数init__app中。

apps/__init__py,代码:

from flask import Flask
from redis import StrictRedis
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from flask_jsonrpc import JSONRPC

from apps.utils.log import init_log
from apps.settings.dev import DevelopementConfig
from apps.settings.prop import ProductionConfig

config = {
    "dev": DevelopementConfig,
    "prop": ProductionConfig,
}

# 预设全局变量
redis_store = None
db = SQLAlchemy()
# 创建jsonrpc实例对象
jsonrpc = JSONRPC(app=None, service_url='/api', enable_web_browsable_api=True)

def init_app(config_name):
    """项目的初始化功能"""
    app = Flask(__name__)

    # 设置配置类
    Config = config[config_name]

    # 加载配置
    app.config.from_object(Config)

    # redis的链接初始化
    global redis_store
    redis_store = StrictRedis(host=Config.REDIS_HOST, port=Config.REDIS_PORT,db=0)

    # 开启session功能
    Session(app)
    
    # 配置数据库链接
    db.init_app(app)

    # 启动日志
    init_log(Config)

    # jsonrpc注册到app应用对象中
    jsonrpc.init_app(app)

    return app

modules/common/api.py,代码:

from apps import jsonrpc
# 实现rpc接口
@jsonrpc.method('Common.index')
def index():
    return u'Welcome to Flask JSON-RPC'

manage.py启动文件中加载api接口

from apps import init_app,db
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

app = init_app("dev")

# 使用终端脚本工具启动和管理flask
manager = Manager(app)

# 启用数据迁移工具
Migrate(app, db)
# 添加数据迁移的命令到终端脚本工具中
manager.add_command('db', MigrateCommand)

# api接口列表
from apps.modules.common import api

@app.route('/')
def index():
  return 'hello,moluo~'

if __name__ == '__main__':
    manager.run()

重新启动项目,使用postman进行还是原来的结果,则表示调整成功,后面的开发中,我们只需要不断增加对应的接口即可。

使用jsonrpc接受客户端请求的参数

服务器提供rpc接口方法:

from apps import jsonrpc

@jsonrpc.method("模块名.方法名(username=String, password=String)")
def 方法名(username,password):
    return u"账号:%s,密码:%s" % (username,password)

客户端发送请求:

{
    "jsonrpc": "2.0",
    "id": 188,
    "method": "Common.add",
    "params": {
        "username":"xiaohui",
        "age":33,
        "sex":false,
        "lve":["吃饭","睡觉",3,4,{"title":"aaaa"}],
        "son":{
            "username":"xiaohuihui",
            "age":12
        }
    }
}

实现jsonrpc接口的版本迭代

基于flask_jsonrpc.site.JSONRPCSite 来实现

apps/__init__代码:

from flask import Flask
from redis import StrictRedis
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from flask_jsonrpc import JSONRPC
from flask_jsonrpc.site import JSONRPCSite
from apps.settings.dev import DevelopementConfig
from apps.settings.prop import ProductionConfig
from apps.utils.log import init_log

config = {
  "dev": DevelopementConfig,
  "prop": ProductionConfig,
}

# 预设全局变量
redis_store = None
db = SQLAlchemy()
# jsonrpc实例化
jsonrpc = JSONRPC(service_url="/api/v1", site=JSONRPCSite(), enable_web_browsable_api=True)
jsonrpc_v2 = JSONRPC(service_url="/api/v2", site=JSONRPCSite(), enable_web_browsable_api=True)


def init_app(config_name):
  
  。。。。

  # 启动日志
  init_log(Config)

  # 把jsonrpc注入到app实例对象中
  jsonrpc.init_app(app)
  jsonrpc2.init_app(app)

  return app

模块目录modules/common/api.py,代码:

from apps import jsonrpc,jsonrpc2

@jsonrpc.method("Common.add(username=String,age=Number,sex=Boolean,lve=Array,son=Object)")
def add_v1(username,age,sex,lve,son):
  return "username=%s,age=%s,sex=%s,lve=%s,son=%s" % (username,age,sex,lve,son)

@jsonrpc2.method("Common.add(username=String,age=Number,sex=Boolean,lve=Array,son=Object)")
def add_v2(username,age,sex,lve,son):
  return u"账号=%s,年龄=%s,性别=%s,爱好=%s,后代=%s" % (username,age,sex,lve,son)

5. APICloud的基本入门

.
|-- config.xml         # app核心配置文件
|-- css/               # 公共css存储目录
|   |-- api.css
|   `-- style.css
|-- feature            # 启动画面图片
|-- html/              # 页面/窗口的文件存储目录
|-- icon               # 手机应用图标
|-- image/             # 静态图片存储目录
|-- index.html
|-- launch             # 启动画面图片
|-- res                # 除了图片以外的其他文件
|-- script/            # javascript文件存储目录
|   `-- api.js
`-- wgt                # app模块目录

欢迎页面

欢迎页面是用户打开APP看到的第一个页面,我们要实现欢迎页面,首先要弄清楚APICloud开发过程中的几个关于页面的概念:

window       APP的窗口
Frame        窗口里面的框架页面
FrameGroup   框架页面的组合
api.openWin({
    name: 'page1',        # window名字
    url: './page1.html',  # 页面地址,可以为本地文件路径,支持相对路径和绝对路径
    pageParam: {          # 页面参数,新页面中可以通过 api.pageParam 获取
        name: 'test'
    }
});

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM