智付云架构

智付云系统架构及配置

1.系统架构说明

本文档描述的智付云系统架构脱胎于现有的apoll系统,并大幅优化了系统的并发性,可维护性能,组件之间的通讯全部使用异步
通讯,使得更有利于适应现在的互联网的运行环境和开发模式。

1.1系统架构图

appoll框架图_ver5.png

1.2系统架构说明

智付云系统前置接入用ng来做LB处理,利用libev作为socket的异步通讯机制,使用flask web框架来作为微服务架构基础构建。整体架构分为3部分,第一部分为python动态加载框架用来加载C、python或者其他语言编写的模块,第二块是redis-server服务端组件,第三块为流程转发组件。动态组件之间的消息传递由redis服务实现。在一个交易流程中以xml消息总线维持之间的通讯。如需多次交互,由redis服务暂存xml消息总线的内容。

1.3Python动态加载框架

此框架由python语言编写,加载Ctype库,从而实现动态加载C或者其他语言库的能力。在动态库的编写上力求接口同化,都以int funcname(char * resq,char* resp)的方式命名,其中resq为接收的xml消息总线的内容,resp为返回的消息总线的内容及该服务处理后的结果。
Python的动态加载框架所需的配置文件存放在智付云mpos/json/和mpos/xml里,由*.json,init_op.xml,init_func.xml,user.xml,yl_trans.xml组成,及各用户文件下的trans_next.xml组成。

1.4Redis服务组件

Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都 支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排 序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文 件,并且在此基础上实现了master-slave(主从)同步 Redis服务组件主要的作用为:为各个组件的通讯提供了mem层的服务,为多次交互的实现提供缓存xml数据总线的服务。
Redis服务较之于传统的消息队列,管道通讯来说有巨大的优势。首先redis服务是基于key_value存储系统,这个特性在高并发的时候能够有效的提供支持。其次在最新的redis中提供了cluster的能力,为智付云系统提供了组件集群的能力。最后,redis提供了比消息队列,管道丰富多的数据类型及操作,基本上一个redis服务就可以完全满足现有或者将来的组件通讯及缓存、高并发的所有需求。

1.5服务转发组件

本组件由Python语言编写,通过加载json文件夹里的配置文件来驱动交易流程。
Json文件夹里的配置流程,按功能分可以划分为3块(“协议配置”,“交易处理配置”,“错误处理配置”)。由于json的流程组件都是标准的json格式,可以完全展现在web平台,并有web平台管理与生成。
Json配置文件采用多级模板体系,对于重复的交易流程,以及交易模块,可以分不同的粒度来生成对应的模板,以达到管理的方便与配置的重用。

2.组件配置

2.1组件加载

启动时由mpos_daemon程序加载mpos/xml/daemon.xml,由daemon.xml里的配置来启动对应的程序。
Daemon.xml实例:

<srvid>0004</srvid><srvnum>10</srvnum><paramdata>mpos_control.py</paramdata><paramindex><num>00</num></paramindex>

<srvid>0010</srvid><srvnum>5</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_dbfunc</01></paramindex>
<srvid>0011</srvid><srvnum>10</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_pyfunc</01></paramindex>
<srvid>0012</srvid><srvnum>5</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_pypos</01></paramindex>
<srvid>0013</srvid><srvnum>5</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_pyesb</01></paramindex>
<srvid>0014</srvid><srvnum>5</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_public</01></paramindex>

<srvid>0090</srvid><srvnum>01</srvnum><paramdata>mpos_nac</paramdata><paramindex><num>02</num><01>00</01><02>server_term.ini</02></paramindex>
<srvid>0091</srvid><srvnum>01</srvnum><paramdata>mpos_nac</paramdata><paramindex><num>02</num><01>01</01><02>server_term.ini</02></paramindex>
<srvid>0092</srvid><srvnum>01</srvnum><paramdata>mpos_nac</paramdata><paramindex><num>02</num><01>02</01><02>server_term.ini</02></paramindex>
<srvid>0093</srvid><srvnum>01</srvnum><paramdata>mpos_nac</paramdata><paramindex><num>02</num><01>03</01><02>server_term.ini</02></paramindex>
Srvid:服务的id号,不重复就可以
Srvnum:启动服务的个数,此例子中启动2个mpos_public服务
Paramdata:需要加载的python动态框架,此处为mpos_service框架
Num:标示需要加载的参数个数
01:参数01的内容为mpos_public,就代表此流程需要加载mpos_public服务
整个流程为:python mpos_service mpos_public

Python动态加载框架启动时,从Mpos/xml/init_op.xml加载对应的模块,从Mpos/xml/init_func.xml加载对应的服务。
Init_op.xml实例:

<svrname>mpos_dbfunc</svrname><libname>mpos_dbfunc</libname><lib>mpos_dbfunc</lib><type>py</type>

<svrname>mpos_pyfunc</svrname><libname>mpos_pyfunc</libname><lib>mpos_pyfunc</lib><type>py</type>
<svrname>mpos_pypos</svrname><libname>mpos_pypos</libname><lib>mpos_pypos</lib><type>py</type>

<svrname>mpos_pyesb</svrname><libname>mpos_pyesb</libname><lib>mpos_pyesb</lib><type>py</type>

<svrname>mpos_public</svrname><libname>mpos_public</libname><lib>libmpos_public.so</lib><type>c</type>
<svrname>mpos_socket</svrname><libname>mpos_socket</libname><lib>libmpos_socket.so</lib><type>c</type>

这里的实例为智付云组件服务需要加载的组件
svrname:服务的名称
Libname:需要加载到框架里服务的名称,此处为mpos_public服务
Lib:需要加载的服务的文件名,此处为libmpos_public.so
Type:加载库的类型,此处为c库

init_func.xml实例:

<libname>mpos_public</libname><func>get_term_to_local</func>
Libname:为需要加载的服务名,此处为mpos_public
Func:需要加载的函数名称。此处为get_term_to_local

3编程示例

下面以查余额为一个示例流程

3.1交易流程

交易中需要加载的的配置文件有:Mpos/xml/yl_trans.xml, Mpos/xml/userid/trans_next.xml,Mpos/json/

(1) Mpos/xml/yl_trans.xml为交易终端送上来的交易码转换为智付云系统内部的交易码:

<uniontrans>signin</uniontrans><localtrans>20000002</localtrans><tradename>签到</tradename><readme>signin</readme>
Uniontrans:为交易终端上送的交易码
Localtrans:为智付云系统内部交易码

(2) Mpos/xml/userid/trans_next.xml为内部交易时的流程转换:
此处没有

<begintrans>100000005</begintrans><nextflag>00</nextflag><F2>00000</F2><nexttrans>10000005</nexttrans><tag></tag><readme>余额查询</readme>
Begintrans:为当前交易码
Nextflag:为下步流程选择项,如begintrans为10000005时,nextflag为00时,nexttrans的值为10000005。
Nexttrans:为下批处理流程的交易码

(3) Mpos/json/*.json为交易流程处理驱动配置:

首先在bankuser.json中增加用户类型

{
    "bankuser": [
          {
            "usertype": "01",
            "describe": "term",
            "next_step": "term_menu.json"
        }
        
    ]
}
Usertype:为用户类型,此处为01
Describe:为描述字段,此处为term,标示为支付终端
Next_step:为“01”的用户菜单模板

(4) 下面为term_menu.json的菜单内容:

{
    "term_menu": [
      
       {
            "trans_id": "10000005",
            "menu":
                {
                    "next_step": "term_query.json",
                    "next_step_err": "term_err_info.json",
                    "readme":"query"
                }

       },
       {
          "trans_id": "20000002",
        "menu": {
            "next_step": "term_signin.json",
            "next_step_err": "term_err_info.json",
            "readme": "term_signin"
        }

       }
    ]
}
Trans_id:为本地交易码
Menu:为交易处理流程
Next_step:为本交易流程的模板json
Next_step_err:为本次交易流程错误流程处理模板json
Readme:为交易内容说明

(5) 通用支付流程:

{
    "term_signin": [
       {
            "service": "mpos_pypos",
            "command": [
                { "op": "trans_cups" }
            ]
        },
       {
            "service": "mpos_dbfunc",
            "command": [
                { "op": "update_signin" }
            ]
        },{
            "next_step":"term_end.json"
        }
    ]
}

Trans_id:为智付云系统内部交易码
next_step:为交易模板
trans_tag:动态模板标签,根据此标签定义不同的模板功能

(6) 流程处理解释:

当从yl_trans.xml中获得智付云本地交易码后执行term_signin处理流程,

       {
            "service": "mpos_pypos",
            "command": [
                { "op": "trans_cups" }
            ]
        }
通过调用mpos_pypos服务将签到报文通过trans_cups函数发往收单系统,并接受返回报文 。
service:为当前流程需要的哪个服务模块处理
Command:为处理流程
op:服务模块的名称
      { "op": "trans_cups" } :与收单系统通信

做完这个模板交易后,执行交易:

{
            "service": "mpos_dbfunc",
            "command": [
                { "op": "update_signin" }
            ]
        }
调用mpos_dbfunc服务,将签到结果记录到数据库中。
"op": "update_signin":跟新终端的签到信息

根本结束后,最后执行模板 "next_step":"term_end.json":

{
       "next_step":"term_end.json"
}
{
    "term_end": [
       {
            "service": "mpos_pyfunc",
            "command": [
                { "op": "set_resp" },
                { "op": "encrypt_message" },
                { "op": "packhttp_pad_trans"},
                { "op": "packhttp_pad_head"},
                { "op": "log_list" },
                { "op": "gen_FPOSR_buf"},
                { "op": "EnableGatewayIndex"}
            ]
        },
        {
            "service": "mpos_nac",
            "command": [
                { "op": "None" }
            ]
        }
    ]
}
最后整个流程走完,重新打包发给交易终端
"op": "set_resp":设置交易结果
"op": "encrypt_message":加密返回报文
"op": "packhttp_pad_trans":打包交易报文
"op": "packhttp_pad_head":打包交易报文头
    "op": "log_list":记录交易日志
    "op": "gen_FPOSR_buf":生成返回报文
    "op": "EnableGatewayIndex":发往网关

3.2代码示例

智付云系统中python动态加载框架所加载的服务必须是以int funcname(char resq,charresp)的方式,否则Python框架无法加载函数。
例如:

def set_resp(resq, resp):
    #新增返回信息
    err_code = public.xml_getvalue(resq.value, "F61")
    tran_type = public.xml_getvalue(resq.value, "FTRANS")
    if(len(err_code)==0 and (tran_type=="common" or tran_type=="esign" or tran_type=="activate" or tran_type=="query_detail" or tran_type=="signin")):
        resp.value = public.xml_chgvalue(resp.value, "F61", "000000")
        resp.value = public.xml_chgvalue(resp.value, "F60", "交易成功")
    print err_code
    if(err_code == "000000" or err_code == "00" ):
        resp.value = public.xml_chgvalue(resp.value, "F61","000000")
        resp.value = public.xml_chgvalue(resp.value, "F60","交易成功")
    if (err_code!="00" and len(err_code)==2):
       resq.value = public.xml_chgvalue(resq.value, "FERR", err_code)
       get_err_name(resq,resp)
set_resp为函数名称
resq:传入的xml数据总线
resp:返回的xml数据总线
从交易报文中取得F61和FTRANS标签的值,通过判断结果和交易类型来判断是否交易成功
请注意,每次调用函数返回的resp,Python动态加载框架都会加入xml数据总线中
在mpos_pyfunc中写好此函数后,需要加入init_func.xml中:
<libname>mpos_pyfunc</libname><func>set_resp</func>

3.3交易组包流程

交易组包的模块在mpos_pyfunc服务中,其功能完成了xml到json的互转,http报文的打包与解包,utf-8与GBK的互转。mpos_pyfunc服务启动时,从Mpos/cfg_json/http_mapping.json中加载。

 "signin_unpack":
    [
        {"out_code":"term_sign",                "sys_code":"FSIGN",               "flag":"T","father":""},
        {"out_code":"company",                  "sys_code":"FCOM",                "flag":"T","father":""},
        {"out_code":"user_id",                  "sys_code":"FUID",                "flag":"T","father":""},
        {"out_code":"app_ver",                  "sys_code":"FVER",                "flag":"T","father":""},
        {"out_code":"Longitude",                "sys_code":"FLON",                "flag":"T","father":""},
        {"out_code":"Latitude",                 "sys_code":"FLAT",                "flag":"T","father":""},
        {"out_code":"key",                     "sys_code":"Frsakey",                "flag":"T","father":""},
        {"out_code":"key_index",               "sys_code":"Fkey_index",                "flag":"T","father":""},
        {"out_code":"message",                  "sys_code":"FPOS",                "flag":"T","father":""},
        {"out_code":"message_type",             "sys_code":"Ftype",               "flag":"T","father":""}
    ],
    "signin_pack":
    [
        {"out_code":"Code",                     "sys_code":"F61",               "flag":"T","father":""},
        {"out_code":"Msg",                      "sys_code":"F60",               "flag":"T","father":""},
        {"out_code":"message",                  "sys_code":"FPOS",              "flag":"T","father":""}
    ]
Out_code:外部标签
Sys_code:本地标签


signin_pack为打包配置,signin_uppack为解包配置

4 unixodbc及freetds 配置

4.1 配置freetds并用tsql测试连接

vim /home/ehmuser/Mpos_local/etc/freetds.conf

添加如下内容:

[Sybase]
        host = IP 地址
        port = 端口号
        tds version = 5.0
        client charset = UTF-8

具体的IP和端口号替换成个人所需即可

测试连接:
/usr/local/freetds/bin/tsql -S Sybase  -U 用户名 -P 密码
如果出现如下内容,或者类似内容,表示连接成功,此时可以执行一些SQL语句试试
locale is "en_US.utf8"
locale charset is "UTF-8"
using default charset "UTF-8"
1> 

4.2 配置unixodbc以及测试isql

创建驱动的模板文件tds.txt 内容如下:

[NHJSTDS]
Description             = NHJSDB
Driver64                = /home/ehmuser/Mpos_local/lib/libtdsodbc.so
Driver                  = /home/ehmuser/Mpos_local/lib/libtdsodbc.so
FileUsage               = 1

然后使用odbcinst安装驱动:
odbcinst -i -d -f tds.txt
执行完成之后,可以检查一下/home/ehmuser/Mpos_local/etc/odbcinst.ini,如果可以看到TDS的内容,说明配置没问题

4.3 创建数据源的模板文件

[ehmdb]
Driver =  NHJSTDS
Descrption = Sybase Server
Trace = No
Server =  147.91.1.117
Database = master
Port = 4400

odbcinst -i -s -f unixodbc.txt
执行完成以后,会在用户的home目录下生成.odbc.ini的文件,vim ~/.odbc.ini检查一下文件内容即可。也可以用odbcinst -q -s检查可用的数据源
用isql测试连接: isql -v ehmdb 用户名 密码
如果出现如下内容,说明连接成功了。

+---------------------------------------+

| Connected!                            |

|                                       |

| sql-statement                         |

| help [tablename]                      |

| quit                                  |

|                                       |

+---------------------------------------+

SQL> 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,673评论 18 139
  • 刚才出去扔垃圾,在门口碰到一位很久没见的大哥,我跟他打招呼,问他这么热怎么没开车,他说村里停车位紧张,他回来收个房...
    鼹鼠的土豆阅读 828评论 2 21
  • 愿我们, 友谊地久天长, 只可惜, 相见不如怀念。 难忘, 那常青树下的冬装, 与一把微笑的铁锹。 铭记, 是在桌...
    長宫阅读 170评论 0 0
  • 青春,人生之歌最美的一曲,洋溢着美妙的旋律,夹杂着苍白的忧伤,青春100字作文. 青春,生命舞曲最靓的一段,旋转...
    追求_b8eb阅读 153评论 0 0
  • http://whutzkj.space/2017/08/05/vue%E7%BB%84%E4%BB%B6%E4%...
    凯旋阅读 87评论 0 0