• 系统架构图:

    9.1 系统联调与测试 - 图1


    • 系统在线部分的主要服务:
      • werobot服务
      • 主要逻辑服务
      • 句子相关模型服务
      • redis服务(会话管理)
      • neo4j服务(图数据查询)

    • 说明: 系统联调与测试是对系统在线部分服务的联调与测试, 不包含离线部分的内容.

    • 启动系统在线部分的服务:
      • 以挂起的方式启动werobot服务
      • 使用supervisor启动主要逻辑服务及其redis服务
      • 以挂起的方式启动子相关模型服务
      • 启动和查看neo4j服务(默认已启动)

    • 以挂起的方式启动werobot服务:
      1. nohup python /data/wr.py &

    • 运行位置: /dara/目录下.

    • 通过端口查看挂起的服务进程:
      1. # 通过yum安装lsof命令
      2. sudo yum install lsof -y
      3. # 查看80端口的进程
      4. lsof -i:80

    • 使用supervisor启动主要逻辑服务及其redis服务
      • supervisor配置文件简要分析:
    1. ...
    2. ; 使用gunicorn启动基于Flask框架的主要逻辑服务
    3. [program:main_server]
    4. command=gunicorn -w 1 -b 0.0.0.0:5000 app:app ; the program (relative uses PATH, can take args)
    5. stopsignal=QUIT ; signal used to kill process (default TERM)
    6. stopasgroup=false ; send stop signal to the UNIX process group (default false)
    7. killasgroup=false ; SIGKILL the UNIX process group (def false)
    8. stdout_logfile=./log/main_server_out ; stdout log path, NONE for none; default AUTO
    9. stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
    10. stderr_logfile=./log/main_server_error ; stderr log path, NONE for none; default AUTO
    11. stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
    12. ; 启动redis服务,作为会话管理数据库
    13. [program:redis]
    14. command=redis-server

    • 文件位置: /data/doctor_online/main_serve/supervisor.conf

    • 启动服务:
    1. # 使用supervisord命令, 读取指定目录下的文件
    2. supervisord -c /data/doctor_online/main_serve/supervisor.conf

    • 查看启动的服务状态
    1. supervisorctl status all
    • 输出效果:
    1. main_server RUNNING pid 31609, uptime 0:32:20
    2. redis RUNNING pid 31613, uptime 0:32:18

    • 以挂起的方式启动句子相关模型服务:
      • 编写启动脚本start.sh
    1. # 脚本中是使用gunicorn启动服务的命令
    2. gunicorn -w 1 -b 0.0.0.0:5001 app:app

    • 代码位置: /data/doctor_online/bert_serve/start.sh

    • 以挂起的方式启动服务
    1. nohup sh /data/doctor_online/bert_serve/start.sh &

    • 运行位置: /data/doctor_online/bert_serve/目录下.

    • 通过端口查看挂起的服务进程
    1. # 通过yum安装lsof命令
    2. sudo yum install lsof -y
    3. # 查看5001端口的进程
    4. lsof -i:5001

    • 启动和查看neo4j服务(图数据查询):
    1. # neo4j服务在之前应该一直处在启动状态
    2. neo4j start
    3. # 查看服务启动状态:
    4. neo4j status

    • 进行测试:
      • 第一步: 明确测试说明.
      • 第二步: 添加打印测试内容.
      • 第三步: 重新启动主要逻辑服务.
      • 第四步: 进行数据流测试并查看打印日志.

    • 第一步: 测试说明
      • 因为主要逻辑服务是所有在线服务的中心服务(该服务将接收或发送请求给其他服务), 因此我们的测试打印信息都在主要逻辑服务中进行.

    • 第二步: 添加打印测试内容
      1. class Handler(object):
      2. """主要逻辑服务的处理类"""
      3. def __init__(self, uid, text, r, reply):
      4. """
      5. :param uid: 用户唯一标示uid
      6. :param text: 该用户本次输入的文本
      7. :param r: redis数据库的连接对象
      8. :param reply: 规则对话模版加载到内存的对象(字典)
      9. """
      10. self.uid = uid
      11. self.text = text
      12. self.r = r
      13. self.reply = reply
      14. def non_first_sentence(self, previous):
      15. """
      16. description: 非首句处理函数
      17. :param previous: 该用户当前句(输入文本)的上一句文本
      18. :return: 根据逻辑图返回非首句情况下的输出语句
      19. """
      20. # 尝试请求模型服务, 若失败则打印错误结果
      21. ###################################################
      22. # 能够打印该信息, 说明已经进入非首句处理函数.
      23. print("准备请求句子相关模型服务!")
      24. ###################################################
      25. try:
      26. data = {"text1": previous, "text2": self.text}
      27. result = requests.post(model_serve_url, data=data)
      28. if not result.text: return unit_chat(self.text)
      29. ###################################################
      30. # 能够打印该信息, 说明句子相关模型服务请求成功.
      31. print("句子相关模型服务请求成功, 返回结果为:", result.text)
      32. ###################################################
      33. except Exception as e:
      34. print("模型服务异常:", e)
      35. return unit_chat(self.text)
      36. ###################################################
      37. # 能够打印该信息, 说明开始准备请求neo4j查询服务.
      38. print("请求模型服务后, 准备请求neo4j查询服务!")
      39. ###################################################
      40. # 继续查询图数据库, 并获得结果
      41. s = query_neo4j(self.text)
      42. ###################################################
      43. # 能够打印该信息, 说明neo4j查询成功.
      44. print("neo4j查询服务请求成功, 返回结果:", s)
      45. ###################################################
      46. # 判断结果为空列表, 则直接使用UnitAPI返回
      47. if not s: return unit_chat(self.text)
      48. # 若结果不为空, 获取上一次已回复的疾病old_disease
      49. old_disease = self.r.hget(str(self.uid), "previous_d")
      50. if old_disease:
      51. # new_disease是本次需要存储的疾病, 是已经存储的疾病与本次查询到疾病的并集
      52. new_disease = list(set(s) | set(eval(old_disease)))
      53. # res是需要返回的疾病, 是本次查询到的疾病与已经存储的疾病的差集
      54. res = list(set(s) - set(eval(old_disease)))
      55. else:
      56. # 如果old_disease为空, 则它们相同都是本次查询结果s
      57. res = new_disease = list(set(s))
      58. # 存储new_disease覆盖之前的old_disease
      59. self.r.hset(str(self.uid), "previous_d", str(new_disease))
      60. # 设置过期时间
      61. self.r.expire(str(self.uid), ex_time)
      62. # 将列表转化为字符串, 添加到规则对话模版中返回
      63. res = ",".join(s)
      64. ###################################################
      65. # 能够打印该信息, 说明neo4j查询后有结果并将使用规则对话模版.
      66. print("使用规则对话模版进行返回对话的生成!")
      67. ###################################################
      68. print("###################################################")
      69. return self.reply["2"] %res
      70. def first_sentence(self):
      71. """首句处理函数"""
      72. # 直接查询图数据库, 并获得结果
      73. ###################################################
      74. # 能够打印该信息, 说明进入了首句处理函数并马上进行neo4j查询
      75. print("该用户近期首次发言, 不必请求模型服务, 准备请求neo4j查询服务!")
      76. ###################################################
      77. s = query_neo4j(self.text)
      78. ###################################################
      79. # 能够打印该信息, 说明已经完成neo4j查询.
      80. print("neo4j查询服务请求成功, 返回结果为:", s)
      81. ###################################################
      82. # 判断结果为空列表, 则直接使用UnitAPI返回
      83. if not s: return unit_chat(self.text)
      84. # 将s存储为"上一次返回的疾病"
      85. self.r.hset(str(self.uid), "previous_d", str(s))
      86. # 设置过期时间
      87. self.r.expire(str(self.uid), ex_time)
      88. # 将列表转化为字符串, 添加到规则对话模版中返回
      89. res = ",".join(s)
      90. ###################################################
      91. # 能够打印该信息, 说明neo4j查询后有结果并将使用规则对话模版.
      92. print("使用规则对话生成模版进行返回对话的生成!")
      93. ###################################################
      94. print("###################################################")
      95. return self.reply["2"] %res
      96. # 设定主要逻辑服务的路由和请求方法
      97. @app.route('/v1/main_serve/', methods=["POST"])
      98. def main_serve():
      99. ###################################################
      100. # 能够打印该信息, 说明werobot服务发送请求成功.
      101. print("已经进入主要逻辑服务, werobot服务运行正常!")
      102. ###################################################
      103. # 接收来自werobot服务的字段
      104. uid = request.form['uid']
      105. text = request.form['text']
      106. # 从redis连接池中获得一个活跃连接
      107. r = redis.StrictRedis(connection_pool=pool)
      108. # 根据该uid获取他的上一句话(可能不存在)
      109. previous = r.hget(str(uid), "previous")
      110. # 将当前输入的文本设置成上一句
      111. r.hset(str(uid), "previous", text)
      112. ###################################################
      113. # 能够打印该信息, 说明redis能够正常读取和写入数据.
      114. print("已经完成初次会话管理, redis运行正常!")
      115. ###################################################
      116. # 读取规则对话模版内容到内存
      117. reply = json.load(open(reply_path, "r"))
      118. # 实例化主要逻辑处理对象
      119. handler = Handler(uid, text, r, reply)
      120. # 如果previous存在, 说明不是第一句话
      121. if previous:
      122. # 调用non_first_sentence方法
      123. return handler.non_first_sentence(previous)
      124. else:
      125. # 否则调用first_sentence()方法
      126. return handler.first_sentence()

    • 代码位置: /data/doctor_online/main_serve/app.py

    • 第三步: 重新启动主要逻辑服务
      1. supervisorctl restart all

    • 运行位置: /data/doctor_online/main_serve

    • 第四步: 进行数据流测试并查看打印日志
      • 测试请求1: 用户关注公众号后首次发送一些症状描述.
      • 测试请求2: 首次发送后用户继续发送一些症状描述.

    • 测试请求1:
      • 用户关注公众号后首次发送一些症状描述.

    • 对应数据流:
      • werobot服务—>请求主要逻辑服务—>主要逻辑服务中请求redis服务—>请求neo4j查询服务—>使用规则对话模版/UnitAPI.

    • 对应操作:
      • 关注公众号(使用新用户), 发送”我最近有些腹痛”.

    • 查看主要逻辑服务日志:
    1. cat /data/doctor_online/main_serve/log/main_server_out

    • 日志打印结果:
    1. ## 打印如下结果说明测试成功!
    2. 已经进入主要逻辑服务, werobot服务运行正常!
    3. 已经完成初次会话管理, redis运行正常!
    4. 该用户近期首次发言, 不必请求模型服务, 准备请求neo4j查询服务!
    5. neo4j查询服务请求成功, 返回结果为: ['癫痫', '小儿糖尿病', '肾上腺危象', '异位急性阑尾炎', '急性胆囊炎']
    6. 使用规则对话模版进行返回生成的对话!

    • 测试请求2:
      • 首次发送后用户继续发送一些症状描述.

    • 对应数据流:
      • werobot服务—>请求主要逻辑服务—>主要逻辑服务中请求redis服务—>请求句子相关模型服务—>请求neo4j查询服务—>使用规则对话模版/UnitAPI.

    • 对应操作:
      • 发送”我最近有些腹痛”后, 继续发送”并且左腹部有一些红点”.

    • 查看主要逻辑服务日志:
    1. cat /data/doctor_online/main_serve/log/main_server_out

    • 日志打印结果:
    1. ## 打印如下结果说明测试成功!(使用UnitAPI返回结果)
    2. 已经进入主要逻辑服务, werobot服务运行正常!
    3. 已经完成初次会话管理, redis运行正常!
    4. 准备请求句子相关模型服务!
    5. 句子相关模型服务请求成功, 返回结果为: 1
    6. 请求模型服务后, 准备请求neo4j查询服务!
    7. neo4j查询服务请求成功, 返回结果: []

    • 注意: 打印日志若不能即时出现, 重新启动主要逻辑服务即可.

    • 本章总结:
      • 学习了掌握如何启动系统在线部分的服务:
        • 以挂起的方式启动werobot服务
        • 使用supervisor启动主要逻辑服务及其redis服务
        • 以挂起的方式启动子相关模型服务
        • 启动和查看neo4j服务(默认已启动)

    • 学习了如何进行测试:
      • 第一步: 明确测试说明.
      • 第二步: 添加打印测试内容.
      • 第三步: 重新启动主要逻辑服务.
      • 第四步: 进行数据流测试并查看打印日志.

    • 第一步: 测试说明
      • 因为主要逻辑服务是所有在线服务的中心服务(该服务将接收或发送请求给其他服务), 因此我们的测试打印信息都在主要逻辑服务中进行.

    • 第二步: 添加打印测试内容

    • 第三步: 重新启动主要逻辑服务

    • 第四步: 进行数据流测试并查看打印日志
      • 测试请求1: 用户关注公众号后首次发送一些症状描述.
      • 测试请求2: 首次发送后用户继续发送一些症状描述.