rabbitmq
线程Q:实现一个进程下不同线程间的数据交互
进程Q:不同进程的数据交互
rabbitmq:消息中间件 -消息队列,用于生产消费模型不同语言多个程序的交互
场景:异步
 开发语言:erlang 爱立信公司
	
	特点:队列可独立,EX交换机放到队列
一、常用命令
	添加用户:rabbitmqctl add_user admin admin
添加权限:rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
修改用户角色rabbitmqctl set_user_tags admin administrator
	启用插件:配置linux 端口 15672 网页管理  5672 AMQP端口
	rabbitmq-plugins enable rabbitmq_management
	重启
	rabbitmq-server restart
	开机
	service rabbitmq-server start 	/etc/init.d/rabbitmq-server start|stop|restart|reload
	关机
	rabbitmqctl stop
	安装:
		https://blog.csdn.net/huxu981598436/article/details/55050528 下载rpm点击安装,额外的依赖环境
	web管理
		http://ip:15672 
	查看队列数
		rabbitmqctl list_queues
客户端连接的时候需要配置认证参数
		credentials = pika.PlainCredentials('alex', 'alex3714')
		 
		 
		connection = pika.BlockingConnection(pika.ConnectionParameters(
			'10.211.55.5',5672,'/',credentials))
		channel = connection.channel()
	# 超时时间
		conn.add_timeout(5, lambda: channel.stop_consuming())
		
二、不同模式
    a.一个生产者,多个消费者
		在这种模式下,RabbitMQ会默认把p发的消息依次分发给各个消费者(c),跟负载均衡差不多
			
	安全性:消费者拿消息时队列还没移除消息,如果该消费者没完成,队列会重新分配这个消费给下一个消费者
			# 注:这时查队列数没有
		消费者完成给队列答复
			channel.basic_consume(callback,
						  queue='task_queue',		# 消费者用queue,生产者用routing_key
						  no_ack=False				# 得确认,True时不需确认队列就移除
						  )
			def callback(ch, method, properties, body): 		# ch:通道实例,method:发布的参数,props:发布的自定义属性
				ch.basic_ack(delivery_tag=method.delivery_tag)  # 答复服务器确认
	持久化:
		不设置持久的情况下,重启服务器队列不存在
		队列持久化:
			channel.queue_declare(queue='hello', durable=True)	# 队列名已存在时需重命名
		生产消息持久化:
			channel.basic_publish(exchange='',
				  routing_key='task_queue',		#发到哪个queue上
				  body=message,				
				  properties=pika.BasicProperties(
					  delivery_mode=2,  		# make message persistent
				  )
			)
	消息公平分发:
		如果Rabbit只管按顺序把消息发到各个消费者身上,不考虑消费者负载的话,很可能出现,
		一个机器配置不高的消费者那里堆积了很多消息处理不完,同时配置高的消费者却一直很轻松。
		为解决此问题,可以在各个消费者端,配置perfetch=1,即消费者当前消息还没处理完的时候就不接收新消息。
		channel.basic_qos(prefetch_count=1)
		channel.basic_consume(callback,
				  queue='task_queue')
三、消息发布\订阅
	前面都基本都是1对1的消息发送和接收,即消息只能发送到指定的queue里,
	但如果想让消息被所有的Queue收到,类似广播的效果,这时候就要用到exchange了
	Exchange在定义的时候是有类型的,以决定到底是哪些Queue符合条件,可以接收消息
		- fanout: 所有bind到此exchange的queue都可以接收消息	# 广播
		- direct: 通过routingKey和exchange决定的那个queue可以接收消息	# 组播
		- topic:所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息
		- headers: 通过headers 来决定把消息发给哪些queue		
	1、广播fanout	
		生产者声明:
			connection = pika.BlockingConnection(pika.ConnectionParameters(
				host='localhost'))
			channel = connection.channel()
			channel.exchange_declare(exchange='logs',	#定义Exchange类型
				exchange_type='fanout')
			message = ' '.join(sys.argv[1:]) or "info: Hello World!"		#sys.argv[]是一个列表,0表示py自身
			channel.basic_publish(exchange='logs',		#发布广播了
				routing_key='',		# 此时不用声明队列名
				body=message)
			connection.close()
		消费者:
			# 声明类型
			channel.exchange_declare(exchange='logs',	# 防备启动了没有声明,和声明queue一样
				exchange_type='fanout')
				
			#不指定queue名字,rabbit会分配一个新随机名字,exclusive=True排他,生成的queue重启会删除
			result = channel.queue_declare(exclusive=True) 
			queue_name = result.method.queue
			# 绑定队列
			channel.queue_bind(exchange='logs',
				queue=queue_name)
			# 监听消息
			channel.basic_consume(callback,
				queue=queue_name,
				no_ack=True)
			
		场景:实时直播,每几秒切片,把内容分发到子服务器
	2、组播		
		RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,
		exchange根据 关键字 判定应该将数据发送至指定队列。	
			
		生产者:	
			severity = sys.argv[1] if len(sys.argv) > 1 else 'info' # 严重程度,级别  xxx.py info发布
			message = ' '.join(sys.argv[2:]) or 'Hello World!'
			channel.basic_publish(exchange='direct_logs',
								  routing_key=severity,	# 这里
								  body=message)
		消费者:
			severities = sys.argv[1:]		# python xxx.py info warning error这样传参
			if not severities:
				sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
				sys.exit(1)
			for severity in severities:
				channel.queue_bind(exchange='direct_logs',
								   queue=queue_name,
								   routing_key=severity)	# 绑定该exchange下所有的routing_key
			
	3、更细致的消息过滤		
			
		生产者和消费者的代码和组播一样,变了exchange类型。在于运行文件时输入的参数不同
		
		表达式符号说明:#表示可以匹配0个或多个单词,*表示只能匹配一个单词
			#注:使用RoutingKey为#,Exchange Type为topic的时候相当于使用fanout
		#			# 监听所有:
		kern.* 		# 接收kern.xxxx,但不接收kern.xxxx.xxxx
		*表示一个单词
四、RPC(Remote procedure call)
	双向队列,远程过程调用,短连接
	发布消息的时候加上返回队列、以及消息的唯一标识符(用于异步并发发布)
	# 下面是等待消息返回,可以写成并发多个,不用等待接收而能干其他事情,这样类似异步,io多路复用
	client端:
		import pika
		import uuid
		class FibonacciRpcClient(object):   # 斐波那契数列
			def __init__(self):
				self.connection = pika.BlockingConnection(pika.ConnectionParameters(
					host='localhost'))
self.channel = self.connection.channel()
				result = self.channel.queue_declare(exclusive=True)
				self.callback_queue = result.method.queue           # 随机生成一个队列用于返回
				self.channel.basic_consume(self.on_response, no_ack=True,   # 准备接结果
										   queue=self.callback_queue)
			def on_response(self, ch, method, props, body):
				if self.corr_id == props.correlation_id:
					self.response = body
			def call(self, n):
				self.response = None                # 返回的结果
				self.corr_id = str(uuid.uuid4())    # 唯一标识符
				self.channel.basic_publish(exchange='',
										   routing_key='rpc_queue',         # 得事前有这个队列
										   properties=pika.BasicProperties(
											   reply_to=self.callback_queue,    # 要回复的已创建好的随机队列
											   correlation_id=self.corr_id,      # 唯一标识符
										   ),
										   body=str(n))
				count = 0
				while self.response is None:	#这里能改为死循环,不断监听
					self.connection.process_data_events()   # 循环,如果没值,不断执行这个方法,直到有值后执行self.on_response
					count +=1
					print("check...",count)
				return int(self.response)
fibonacci_rpc = FibonacciRpcClient()
		print(" [x] Requesting fib(30)")
		response = fibonacci_rpc.call(30)   # call可以异步并发
		print(" [.] Got %r" % response)
	server端:
		# _*_coding:utf-8_*_
		__author__ = 'Alex Li'
		import pika
		import time
		connection = pika.BlockingConnection(pika.ConnectionParameters(
			host='localhost'))
		channel = connection.channel()
		channel.queue_declare(queue='rpc_queue')
		def fib(n):
			if n == 0:
				return 0
			elif n == 1:
				return 1
			else:
				return fib(n - 1) + fib(n - 2)
		def on_request(ch, method, props, body):
			n = int(body)
			print(" [.] fib(%s)" % n)
			response = fib(n)
			ch.basic_publish(exchange='',
							 routing_key=props.reply_to,
							 properties=pika.BasicProperties(correlation_id= \
																 props.correlation_id),
							 body=str(response))
			ch.basic_ack(delivery_tag=method.delivery_tag)
		channel.basic_qos(prefetch_count=1)
		channel.basic_consume(on_request, queue='rpc_queue')
		print(" [x] Awaiting RPC requests")
		channel.start_consuming()
 
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号