Zato入门part2
Zato入门part1
前提:从part已经建立了集群、服务框架并成功的调用了服务。现在我们通过HTTP、ZeroMQ和JSON使用外部服务。
除非坚持手工调用,否则服务从来不知道什么确切的URLs来调用。它们总是被外部链接的信息屏蔽。
刚才指出的面向服务的连接叫做CRM,它是一种可以向服务推送请求的服务。当CRM改变了它的地址,服务不需要重新配置,我们仅仅需要做的是在web前端输入心得地址,那么它将会自动被传播到整个集群,这样下一次服务利用这个服务时会自动连接到新的地址。
我们现在没有CRM和方便的支付系统,但在本入门教程中我们可以通过请求之前准备好的服务(客户信息、消费信息)来模拟。
客户信息
{
"firstName": "Sean",
"lastName": "O'Brien"
}
消费信息
{
"DATE": "2013-05-14T10:42:14.401555",
"AMOUNT": ""
}
利用ZeroMQ连接欺诈检测系统。
现在利用web前端,建立两个外部连接。
操作:Connections -> Outgoing -> Plain HTTP
CRM 连接
消费连接
同步调用
代码
my_service.py
# Zato
from zato.server.service import Service class GetClientDetails(Service):
def handle(self): self.logger.info('Request: {}'.format(self.request.payload))
self.logger.info('Request type: {}'.format(type(self.request.payload))) # Fetch connection to CRM
crm = self.outgoing.plain_http.get('CRM') # Fetch connection to Payments
payments = self.outgoing.plain_http.get('Payments') # Grab the customer info ..
response = crm.conn.send(self.cid, self.request.payload)
cust = response.data # .. and last payment's details
response = payments.conn.send(self.cid, self.request.payload)
last_payment = response.data self.logger.info('Customer details: {}'.format(cust))
self.logger.info('Last payment: {}'.format(last_payment)) response = {}
response['first_name'] = cust['firstName']
response['last_name'] = cust['lastName']
response['last_payment_date'] = last_payment['DATE']
response['last_payment_amount'] = last_payment['AMOUNT'] self.logger.info('Response: {}'.format(response)) # And return response to the caller
self.response.payload = response
热部署
cp my_service.py $path/server1/pickup-dir
通过curl请求服务
$ curl localhost:11223/tutorial/first-service -d '{"cust_id":123, "cust_type":"A"}'
{"first_name": "Sean", "last_name": "O'Brien",
"last_payment_date": "2013-05-14T10:42:14.401555",
"last_payment_amount": ""}
$
同时一个server的日志中,会记录以下内容
INFO - Request: {u'cust_id': 123L, u'cust_type': u'A'}
INFO - Request type: <type 'dict'> INFO - Customer details: {u'lastName': u"O'Brien", u'firstName': u'Sean'}
INFO - Last payment: {u'DATE': u'2013-05-14T10:42:14.401555', u'AMOUNT': u''} INFO - Response: {'last_payment_amount': u'', 'first_name': u'Sean',
'last_name': u"O'Brien", 'last_payment_date': u'2013-05-14T10:42:14.401555'}
提示
- self.request.input.payload是在命令行中-d后面的内容
- payload是python中的字典对象
异步发送信息
到目前为止,我们有一个服务通过HTTP接受json,两个服务通过json调用服务,输出json到一个文档。
part1提到,商务人员对消费者的类型(A,B,C)做出判断需要近处观察——消费者的任何操作需要过侦查系统的检查,即使是最后毫无害处的付款行为。
我们没有机会接触到这样的系统,但是我们可以通过几行代码模拟该行为。这只需要执行一个ZeroMQ的PULL socket在一个无限循环中,并把任何输入的信息记录下来。我们将要把我们的服务做成一个异步推送数据给server,而server是如何处理处理并不是服务所关心的。
# stdlib
import logging # ZeroMQ
import zmq logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s') address = 'tcp://127.0.0.1:35101' context = zmq.Context()
socket = context.socket(zmq.PULL)
socket.bind(address) logging.info('Fraud detection app running on {}'.format(address)) while True:
msg = socket.recv_json()
logging.info(msg)
该系统可以完美地执行,毕竟这是发送信息的整个点,我们只是发送信息,然后就不用管了。在这种特殊的情况下,接受者只是记录下所有的请求——这并不是我们关心的。
假设zato已经安装在$install_dir,我们可以执行该命令如下,注意命令中确实是'py',而不是'python'。
$install_dir/bin/py zmq-server1.py
INFO - Fraud detection app running on tcp://127.0.0.1:35101
我们需要建立一个ZeroMQ:Connections -> Outgoing -> ZeroMQ
新建一个
热部署以下服务,观察侦查系统报出的日志:
# stdlib
from datetime import datetime
from json import dumps # Zato
from zato.server.service import Service class GetClientDetails(Service): def should_notify_frauds(self, cust_type):
config_key = 'myapp:fraud-detection:cust-type'
return cust_type in ('A', 'B', 'C') def handle(self): self.logger.info('Request: {}'.format(self.request.payload))
self.logger.info('Request type: {}'.format(type(self.request.payload))) # Fetch connection to CRM
crm = self.outgoing.plain_http.get('CRM') # Fetch connection to Payments
payments = self.outgoing.plain_http.get('Payments') # Grab the customer info ..
response = crm.conn.send(self.cid, self.request.payload)
cust = response.data # .. and last payment's details
response = payments.conn.send(self.cid, self.request.payload)
last_payment = response.data self.logger.info('Customer details: {}'.format(cust))
self.logger.info('Last payment: {}'.format(last_payment)) response = {}
response['first_name'] = cust['firstName']
response['last_name'] = cust['lastName']
response['last_payment_date'] = last_payment['DATE']
response['last_payment_amount'] = last_payment['AMOUNT'] if self.should_notify_frauds(self.request.payload['cust_type']): fraud_request = {}
fraud_request['timestamp'] = datetime.utcnow().isoformat()
fraud_request['request'] = dumps(self.request.payload)
fraud_request['response'] = response
fraud_request = dumps(fraud_request) self.outgoing.zmq.send(fraud_request, 'Fraud detection') else:
self.logger.info('Skipped fraud detection for CID {}'.format(self.cid)) self.logger.info('Response: {}'.format(response)) # And return response to the caller
self.response.payload = response
调用服务
$ curl localhost:11223/tutorial/first-service -d '{"cust_id":123, "cust_type":"A"}'
{"last_payment_amount": "", "first_name": "Sean",
"last_name": "O'Brien", "last_payment_date": "2013-05-14T10:42:14.401555"}
$
zmq显示
INFO - Fraud detection app running on tcp://127.0.0.1:35101
INFO - {u'timestamp': u'2013-05-14T18:16:56.048224',
u'request': u'{"cust_id": 123, "cust_type": "A"}',
u'response': u'{"last_payment_amount": "357", "first_name": "Sean",
i "last_name": "O\'Brien",
"last_payment_date": "2013-05-14T10:42:14.401555"}'}
可以看到,当类型为A,B,C时,有日志输出,当输入其他类型时,直接忽略了——因为根本就没有请求。
Redis
到目前为止,我们已经做得够炫酷的了,但是还有一件非常不和谐的事情在前面的步骤——消费者的类型需要额外的异步信息输送到检测系统,这在服务中是很难编码的。例如当工作人员决定使用更新的类型时,我们还得去改代码,然后重新部署,至少这样不是最好的。
我们可以把这行类型信息存在Redis中,这样Zato就可以从中读取。
操作:Key/value DB -> Remote commands
点击后,出现窗口,可以直接执行Redis命令,下面是执行INFO后的输出:
下面执行3个Redis命令
LPUSH myapp:fraud-detection:cust-type A
LPUSH myapp:fraud-detection:cust-type B
LPUSH myapp:fraud-detection:cust-type C
现在重新更改服务代码,用LRANGE来读取所有的配置值:
# stdlib
from datetime import datetime
from json import dumps, loads # Zato
from zato.server.service import Service class GetClientDetails(Service): def should_notify_frauds(self, cust_type):
config_key = 'myapp:fraud-detection:cust-type'
return cust_type in self.kvdb.conn.lrange(config_key, 0, -1) def handle(self): request = dumps(self.request.payload) self.logger.info('Request: {}'.format(self.request.payload))
self.logger.info('Request type: {}'.format(type(self.request.payload))) # Fetch connection to CRM
crm = self.outgoing.plain_http.get('CRM') # Fetch connection to Payments
payments = self.outgoing.plain_http.get('Payments') # Grab the customer info ..
cust = crm.conn.send(request)
cust = loads(cust.text) # .. and last payment's details
last_payment = payments.conn.send(request)
last_payment = loads(last_payment.text) self.logger.info('Customer details: {}'.format(cust))
self.logger.info('Last payment: {}'.format(last_payment)) # Create response response = {}
response['first_name'] = cust['firstName']
response['last_name'] = cust['lastName']
response['last_payment_date'] = last_payment['DATE']
response['last_payment_amount'] = last_payment['AMOUNT']
response = dumps(response) # Create a request to fraud detection and send it asynchronously
# but only if a customer is of a certain type. if self.should_notify_frauds(self.request.payload['cust_type']): fraud_request = {}
fraud_request['timestamp'] = datetime.utcnow().isoformat()
fraud_request['request'] = request
fraud_request['response'] = response
fraud_request = dumps(fraud_request) self.outgoing.zmq.send(fraud_request, 'Fraud detection') else:
self.logger.info('Skipped fraud detection for CID {}'.format(self.cid)) self.logger.info('Response: {}'.format(response)) # And return response to the caller
self.response.payload = response
这样当新的事物来的时候,我们只需要操作Redis数据库,不需要修改服务代码,不需要重新热部署。
统计
web前端有丰富的展示统计信息页面。
可以两种类型的统计信息
- 由Zato产生的统计信息
- 由load-balancer产生的统计信息
前一个统计信息可以发现一个服务的好坏;后一个可以用来诊断集群中不良的端点。
例如:
可以看到在我们的服务在最慢的10个服务之列,平均相应时间(M)为147.87ms,这并不意味着很差,因为所有服务的平均相应时间(AM)为857ms。同时可以分析到该服务占总服务数量的3%,占用时间占总时间的9%,倒数第二列告知在最后一小时(TU)总共有299次服务请求。最后一列显示其趋势。
对于load-balancer,可以通过web前端页面分析LB的统计信息。注意这一部分需要一个新的认证信息,它是由潜在的HAProxy直接管理,账户和密码在$path/load-balancer/config/repo/zato.config,admin1为账户名,后面对应的为密码。当然里面密码可以改变,需要重启下服务方可生效。
Zato入门part2的更多相关文章
- PHP入门part2
PHP的数据类型 php数据类型分为三大类, 标量类型:整型.浮点型.布尔型和字符串型 复合类型:数组型和对象(object) 特殊类型:空类型和资源型 !资源型以后会讲 整形(int)就是整数的数值 ...
- ReactiveCocoa入门-part2
ReactiveCocoa是一个框架,它能让你在iOS应用中使用函数响应式编程(FRP)技术.在本系列教程的第一部分中,你学到了如何将标准的动作与事件处理逻辑替换为发送事件流的信号.你还学到了如何转换 ...
- maven入门--part2 安装
Maven安装和配置 (1)下载安装文件apache-maven-3.03-bin.tar (2)解压至安装目录,安装完毕 (3)修改.bash_profile,修改maven安装路径,修改构建GC配 ...
- 知识图谱与机器学习|KG入门 -- Part2 建立知识图谱
介绍 在本系列前面两篇文章中我一直在讨论Data Fabric,并给出了一些关于Data Fabric中的机器学习和深度学习的概念.并给出了我对Data Fabric的定义: Data Fabric是 ...
- Hadoop入门学习笔记---part2
在<Hadoop入门学习笔记---part1>中感觉自己虽然总结的比较详细,但是始终感觉有点凌乱.不够系统化,不够简洁.经过自己的推敲和总结,现在在此处概括性的总结一下,认为在准备搭建ha ...
- Spring boot 1.3.5 RELEASE 官方文档中文翻译--Part2:新手入门
Part II. 新手入门 如果你刚刚开始学习Spring boot或"普通"的Spring,这部分非常适合你!在这里,我们回答了最基础的"什么是?".&quo ...
- 【PyTorch深度学习60分钟快速入门 】Part2:Autograd自动化微分
在PyTorch中,集中于所有神经网络的是autograd包.首先,我们简要地看一下此工具包,然后我们将训练第一个神经网络. autograd包为张量的所有操作提供了自动微分.它是一个运行式定义的 ...
- django入门-模型-part2
尊重作者的劳动,转载请注明作者及原文地址 http://www.cnblogs.com/txwsqk/p/6511177.html 完全翻译自官方文档 https://docs.djangoproje ...
- C语言编程入门之--第五章C语言基本运算和表达式-part2
5.1.4 再来一个C库函数getchar吸收回车键 回车键也是一个字符,在使用scanf的时候,输入完毕要按下回车键,这时候回车键也会被输入到stdin流中,会搞乱我们的程序. 注意:stdin是输 ...
随机推荐
- 前端jq设置下拉框的,单选框,复选框的帖子
$(function(){ var sex=$("#sex").val(); var marriageStatus=$("#marriageStatus").v ...
- C#设计模式--工厂模式和抽象工厂模式
话说有三大潮牌公司一直相互PK,有一天举办了一个活动让这三大公司来一个PK,我们来看看哪家公司的上衣做出来好看穿得舒服 现在我们有一个上衣的抽象产品让三大公司来做 //抽象产品 public inte ...
- centos 7 安装mysql5.6rpm格式
1查看是否安装了mysql rpm -qa|grep -i mysql 如果安装了请卸载:rpm -e --nodeps MySQL... 2.没有安装则进行如下操作 下载mysql rpm ta ...
- mongodb高版本与低版本的区别
mongodb高版本与低版本的区别 一.mongodb引擎: Mongodb 3.0支持用户自定义存储引擎,用户可配置使用mmapv1或者wiredTiger存储引擎. 3.2版本以后默认的开启的是w ...
- jquery遍历-filter()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- String,数组,list集合长度的使用
public class Use{ public static void main(String[] args){ int[] arr=new int[]{19,10,20,30,23,13}; // ...
- mysql基础操作学习笔记(一)
1前期准备: SQL语言包涵以下4个部分: (1)数据定义语言(DDL):包括DROP, CREATE, ALTER等语句 (2)数据操纵语言(DML):包括INSERT, UPDATE, DELET ...
- iOS8 dismissViewControllerAnimated:YES在Show Segue不工作
最近使用iOS8 Sdk(xcode6.1.1)使用Show Segue功能,V_A视图push到V_B视图,然后想通过使用按键返回V_A,我记得ios6的时候可以使用[self dismissVie ...
- django中多个app放入同一文件apps
新建一个apps文件夹 需要整理的app文件夹拖到同一个文件夹中,即apps.(弹出对话框,取消勾选Search for references) 在pycharm中,右键apps文件夹--选择mark ...
- 「BZOJ3998」[TJOI2015] 弦论(第K小子串)
https://www.lydsy.com/JudgeOnline/problem.php?id=3998 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input ...