• python入门三十二天——协程 异步IO数据库队列缓存


    异步IO

    在IO编程一节中,我们已经知道,CPU的速度远远快于磁盘、网络等IO。在一个线程中,CPU执行代码的速度极快,然而,一旦遇到IO操作,如读写文件、发送网络数据时,就需要等待IO操作完成,才能继续进行下一步操作。这种情况称为同步IO。

    在IO操作的过程中,当前线程被挂起,而其他需要CPU执行的代码就无法被当前线程执行了。

    因为一个IO操作就阻塞了当前线程,导致其他代码无法执行,所以我们必须使用多线程或者多进程来并发执行代码,为多个用户服务。每个用户都会分配一个线程,如果遇到IO导致线程被挂起,其他用户的线程不受影响。

    多线程和多进程的模型虽然解决了并发问题,但是系统不能无上限地增加线程。由于系统切换线程的开销也很大,所以,一旦线程数量过多,CPU的时间就花在线程切换上了,真正运行代码的时间就少了,结果导致性能严重下降。

    由于我们要解决的问题是CPU高速执行能力和IO设备的龟速严重不匹配,多线程和多进程只是解决这一问题的一种方法。

    另一种解决IO问题的方法是异步IO。当代码需要执行一个耗时的IO操作时,它只发出IO指令,并不等待IO结果,然后就去执行其他代码了。一段时间后,当IO返回结果时,再通知CPU进行处理。

    可以想象如果按普通顺序写出的代码实际上是没法完成异步IO的:

    do_some_code()
    f = open('/path/to/file', 'r')
    r = f.read() # <== 线程停在此处等待IO操作结果
    # IO操作完成后线程才能继续执行:
    do_some_code(r)
    

    所以,同步IO模型的代码是无法实现异步IO模型的。

    异步IO模型需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程:

    loop = get_event_loop()
    while True:
        event = loop.get_event()
        process_event(event)
    

    消息模型其实早在应用在桌面应用程序中了。一个GUI程序的主线程就负责不停地读取消息并处理消息。所有的键盘、鼠标等消息都被发送到GUI程序的消息队列中,然后由GUI程序的主线程处理。

    由于GUI线程处理键盘、鼠标等消息的速度非常快,所以用户感觉不到延迟。某些时候,GUI线程在一个消息处理的过程中遇到问题导致一次消息处理时间过长,此时,用户会感觉到整个GUI程序停止响应了,敲键盘、点鼠标都没有反应。这种情况说明在消息模型中,处理一个消息必须非常迅速,否则,主线程将无法及时处理消息队列中的其他消息,导致程序看上去停止响应

    消息模型是如何解决同步IO必须等待IO操作这一问题的呢?当遇到IO操作时,代码只负责发出IO请求,不等待IO结果,然后直接结束本轮消息处理,进入下一轮消息处理过程。当IO操作完成后,将收到一条“IO完成”的消息,处理该消息时就可以直接获取IO操作结果。

    在“发出IO请求”到收到“IO完成”的这段时间里,同步IO模型下,主线程只能挂起,但异步IO模型下,主线程并没有休息,而是在消息循环中继续处理其他消息。这样,在异步IO模型下,一个线程就可以同时处理多个IO请求,并且没有切换线程的操作。对于大多数IO密集型的应用程序,使用异步IO将大大提升系统的多任务处理能力。



    协程

    在学习异步IO模型前,我们先来了解协程。

    协程,又称微线程,纤程。英文名Coroutine。

    协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用。

    子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。

    所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。

    子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。

    协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

    注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断。比如子程序A、B:

    def A():
        print('1')
        print('2')
        print('3')
    
    def B():
        print('x')
        print('y')
        print('z')
    

      假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:

    1
    2
    x
    y
    3
    z
    

    但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。

    看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那和多线程比,协程有何优势?

    最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

    第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

    因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。


    协程的好处:

    • 无需线程上下文切换的开销
    • 无需原子操作锁定及同步的开销
      •   "原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
    • 方便切换控制流,简化编程模型
    • 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

    缺点:

    • 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
    • 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

    使用yield实现协程操作例子

    import time
    import queue
    def consumer(name):
        print("--->starting eating baozi...")
        while True:
            new_baozi = yield
            print("[%s] is eating baozi %s" % (name,new_baozi))
            #time.sleep(1)
     
    def producer():
     
        r = con.__next__()
        r = con2.__next__()
        n = 0
        while n < 5:
            n +=1
            con.send(n)
            con2.send(n)
            print("33[32;1m[producer]33[0m is making baozi %s" %n )
     
     
    if __name__ == '__main__':
        con = consumer("c1")
        con2 = consumer("c2")
        p = producer()
    

      

    我们先给协程一个标准定义,即符合什么条件就能称之为协程:

    1. 必须在只有一个单线程里实现并发
    2. 修改共享数据不需加锁
    3. 用户程序里自己保存多个控制流的上下文栈
    4. 一个协程遇到IO操作自动切换到其它协程

    基于上面这4点定义,我们刚才用yield实现的程并不能算是合格的线程,因为它有一点功能没实现,哪一点呢?


    Greenlet

    greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    '''
    Administrator 
    2018/8/30 
    '''
    from greenlet import greenlet
    
    
    def test1():
        print(12)
        gr2.switch()
        print(34)
        gr2.switch()
    
    
    def test2():
        print(56)
        gr1.switch()
        print(78)
    
    
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()
    

      感觉确实用着比generator还简单了呢,但好像还没有解决一个问题,就是遇到IO操作,自动切换,对不对?

     


    Gevent 

    Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    '''
    Administrator 
    2018/8/30 
    '''
    
    import gevent
    import time
    
    
    def func1():
        print('33[31;1m running in f1...%s33[0m'%time.ctime())
        gevent.sleep(2)#一个线程中 自己切换  模拟堵塞
        print('33[31;1mrunning and sinning in f1 %s33[0m'%time.ctime())
    
    
    def func2():
        print('33[32;1m jumping in f2.....%s33[0m'%time.ctime())
        gevent.sleep(1)
        print('33[32;1m flying in sky in f2.......%s33[0m'%time.ctime())
    
    
    gevent.joinall([
        gevent.spawn(func1),
        gevent.spawn(func2),
        # gevent.spawn(func3),
    ])
    
    
    # from greenlet import greenlet
    #
    #
    # def test1():
    #     print(12)
    #     gr2.switch()
    #     print(34)
    #     gr2.switch()
    #
    #
    # def test2():
    #     print(56)
    #     gr1.switch()
    #     print(78)
    #
    #
    # gr1 = greenlet(test1)
    # gr2 = greenlet(test2)
    # gr1.switch()
    

      结果:

    "D:Program Files (x86)python36python.exe" F:/python从入门到放弃/8.30/gevent_greenlet.py
     running in f1...Thu Aug 30 11:46:15 2018
     jumping in f2.....Thu Aug 30 11:46:15 2018
     flying in sky in f2.......Thu Aug 30 11:46:16 2018
    running and sinning in f1 Thu Aug 30 11:46:17 2018
    
    Process finished with exit code 0
    

      

     同步与异步的性能区别 

     1 import gevent
     2  
     3 def task(pid):
     4     """
     5     Some non-deterministic task
     6     """
     7     gevent.sleep(0.5)
     8     print('Task %s done' % pid)
     9  
    10 def synchronous():
    11     for i in range(1,10):
    12         task(i)
    13  
    14 def asynchronous():
    15     threads = [gevent.spawn(task, i) for i in range(10)]
    16     gevent.joinall(threads)
    17  
    18 print('Synchronous:')
    19 synchronous()
    20  
    21 print('Asynchronous:')
    22 asynchronous()

    上面程序的重要部分是将task函数封装到Greenlet内部线程的gevent.spawn。 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,后者阻塞当前流程,并执行所有给定的greenlet。执行流程只会在 所有greenlet执行完后才会继续向下走。  

    遇到IO阻塞时会自动切换任务 

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    '''
    Administrator 
    2018/8/30 
    '''
    from gevent import monkey
    monkey.patch_all()#在windows系统上这个相当于打一个补丁,用来提升gevent对于IO堵塞的监听敏感度。
    import gevent
    from urllib.request import urlopen
    
    
    def f(url):
        print('GET: %s' % url)
        resp = urlopen(url)
        data = resp.read()
        # with open("xiaohua.html","wb") as f:
        #     f.write(data)
    
        print('%d bytes received from %s.' % (len(data), url))
    
    # f('http://www.xiaohuar.com/')
    gevent.joinall([
        gevent.spawn(f, 'https://www.python.org/'),
        gevent.spawn(f, 'https://www.yahoo.com/'),
        gevent.spawn(f, 'https://github.com/'),
    ])
    

      

    通过gevent实现单线程下的多socket并发

    server side 

    import sys
    import socket
    import time
    import gevent
     
    from gevent import socket,monkey
    monkey.patch_all()
     
     
    def server(port):
        s = socket.socket()
        s.bind(('0.0.0.0', port))
        s.listen(500)
        while True:
            cli, addr = s.accept()
            gevent.spawn(handle_request, cli)
     
     
     
    def handle_request(conn):
        try:
            while True:
                data = conn.recv(1024)
                print("recv:", data)
                conn.send(data)
                if not data:
                    conn.shutdown(socket.SHUT_WR)
     
        except Exception as  ex:
            print(ex)
        finally:
            conn.close()
    if __name__ == '__main__':
        server(8001)
    

      client side 

    import socket
     
    HOST = 'localhost'    # The remote host
    PORT = 8001           # The same port as used by the server
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    while True:
        msg = bytes(input(">>:"),encoding="utf8")
        s.sendall(msg)
        data = s.recv(1024)
        #print(data)
     
        print('Received', repr(data))
    s.close()
    

      

    import socket
    import threading
    
    def sock_conn():
    
        client = socket.socket()
    
        client.connect(("localhost",8001))
        count = 0
        while True:
            #msg = input(">>:").strip()
            #if len(msg) == 0:continue
            client.send( ("hello %s" %count).encode("utf-8"))
    
            data = client.recv(1024)
    
            print("[%s]recv from server:" % threading.get_ident(),data.decode()) #结果
            count +=1
        client.close()
    
    
    for i in range(100):
        t = threading.Thread(target=sock_conn)
        t.start()
    
    并发100个sock连接
    

     




    论事件驱动与异步IO

    通常,我们写服务器处理模型的程序时,有以下几种模型:
    (1)每收到一个请求,创建一个新的进程,来处理该请求;
    (2)每收到一个请求,创建一个新的线程,来处理该请求;
    (3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求
    上面的几种方式,各有千秋,
    第(1)中方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
    第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
    第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。
    综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式
     

    看图说话讲事件驱动模型

    在UI编程中,常常要对鼠标点击进行相应,首先如何获得鼠标点击呢?
    方式一:创建一个线程,该线程一直循环检测是否有鼠标点击,那么这个方式有以下几个缺点
    1. CPU资源浪费,可能鼠标点击的频率非常小,但是扫描线程还是会一直循环检测,这会造成很多的CPU资源浪费;如果扫描鼠标点击的接口是阻塞的呢?
    2. 如果是堵塞的,又会出现下面这样的问题,如果我们不但要扫描鼠标点击,还要扫描键盘是否按下,由于扫描鼠标时被堵塞了,那么可能永远不会去扫描键盘;
    3. 如果一个循环需要扫描的设备非常多,这又会引来响应时间的问题;
    所以,该方式是非常不好的。

    方式二:就是事件驱动模型
    目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:
    1. 有一个事件(消息)队列;
    2. 鼠标按下时,往这个队列中增加一个点击事件(消息);
    3. 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;
    4. 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;

     

    事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是(单线程)同步以及多线程编程。

    让我们用例子来比较和对比一下单线程、多线程以及事件驱动编程模型。下图展示了随着时间的推移,这三种模式下程序所做的工作。这个程序有3个任务需要完成,每个任务都在等待I/O操作时阻塞自身。阻塞在I/O操作上所花费的时间已经用灰色框标示出来了。

    在单线程同步模型中,任务按照顺序执行。如果某个任务因为I/O而阻塞,其他所有的任务都必须等待,直到它完成之后它们才能依次执行。这种明确的执行顺序和串行化处理的行为是很容易推断得出的。如果任务之间并没有互相依赖的关系,但仍然需要互相等待的话这就使得程序不必要的降低了运行速度。

    在多线程版本中,这3个任务分别在独立的线程中执行。这些线程由操作系统来管理,在多处理器系统上可以并行处理,或者在单处理器系统上交错执行。这使得当某个线程阻塞在某个资源的同时其他线程得以继续执行。与完成类似功能的同步程序相比,这种方式更有效率,但程序员必须写代码来保护共享资源,防止其被多个线程同时访问。多线程程序更加难以推断,因为这类程序不得不通过线程同步机制如锁、可重入函数、线程局部存储或者其他机制来处理线程安全问题,如果实现不当就会导致出现微妙且令人痛不欲生的bug。

    在事件驱动版本的程序中,3个任务交错执行,但仍然在一个单独的线程控制中。当处理I/O或者其他昂贵的操作时,注册一个回调到事件循环中,然后当I/O操作完成时继续执行。回调描述了该如何处理某个事件。事件循环轮询所有的事件,当事件到来时将它们分配给等待处理事件的回调函数。这种方式让程序尽可能的得以执行而不需要用到额外的线程。事件驱动型程序比多线程程序更容易推断出行为,因为程序员不需要关心线程安全问题。

    当我们面对如下的环境时,事件驱动模型通常是一个好的选择:

    1. 程序中有许多任务,而且…
    2. 任务之间高度独立(因此它们不需要互相通信,或者等待彼此)而且…
    3. 在等待事件到来时,某些任务会阻塞。

    当应用程序需要在任务间共享可变的数据时,这也是一个不错的选择,因为这里不需要采用同步处理。

    网络应用程序通常都有上述这些特点,这使得它们能够很好的契合事件驱动编程模型。

    此处要提出一个问题,就是,上面的事件驱动模型中,只要一遇到IO就注册一个事件,然后主程序就可以继续干其它的事情了,只到io处理完毕后,继续恢复之前中断的任务,这本质上是怎么实现的呢?哈哈,下面我们就来一起揭开这神秘的面纱。。。。

    SelectPollEpoll异步IO 

    http://www.cnblogs.com/alex3714/p/4372426.html 

    番外篇 http://www.cnblogs.com/alex3714/articles/5876749.html 

    select 多并发socket 例子

     1 #_*_coding:utf-8_*_
     2 __author__ = 'Alex Li'
     3 
     4 import select
     5 import socket
     6 import sys
     7 import queue
     8 
     9 
    10 server = socket.socket()
    11 server.setblocking(0)
    12 
    13 server_addr = ('localhost',10000)
    14 
    15 print('starting up on %s port %s' % server_addr)
    16 server.bind(server_addr)
    17 
    18 server.listen(5)
    19 
    20 
    21 inputs = [server, ] #自己也要监测呀,因为server本身也是个fd
    22 outputs = []
    23 
    24 message_queues = {}
    25 
    26 while True:
    27     print("waiting for next event...")
    28 
    29     readable, writeable, exeptional = select.select(inputs,outputs,inputs) #如果没有任何fd就绪,那程序就会一直阻塞在这里
    30 
    31     for s in readable: #每个s就是一个socket
    32 
    33         if s is server: #别忘记,上面我们server自己也当做一个fd放在了inputs列表里,传给了select,如果这个s是server,代表server这个fd就绪了,
    34             #就是有活动了, 什么情况下它才有活动? 当然 是有新连接进来的时候 呀
    35             #新连接进来了,接受这个连接
    36             conn, client_addr = s.accept()
    37             print("new connection from",client_addr)
    38             conn.setblocking(0)
    39             inputs.append(conn) #为了不阻塞整个程序,我们不会立刻在这里开始接收客户端发来的数据, 把它放到inputs里, 下一次loop时,这个新连接
    40             #就会被交给select去监听,如果这个连接的客户端发来了数据 ,那这个连接的fd在server端就会变成就续的,select就会把这个连接返回,返回到
    41             #readable 列表里,然后你就可以loop readable列表,取出这个连接,开始接收数据了, 下面就是这么干 的
    42 
    43             message_queues[conn] = queue.Queue() #接收到客户端的数据后,不立刻返回 ,暂存在队列里,以后发送
    44 
    45         else: #s不是server的话,那就只能是一个 与客户端建立的连接的fd了
    46             #客户端的数据过来了,在这接收
    47             data = s.recv(1024)
    48             if data:
    49                 print("收到来自[%s]的数据:" % s.getpeername()[0], data)
    50                 message_queues[s].put(data) #收到的数据先放到queue里,一会返回给客户端
    51                 if s not  in outputs:
    52                     outputs.append(s) #为了不影响处理与其它客户端的连接 , 这里不立刻返回数据给客户端
    53 
    54 
    55             else:#如果收不到data代表什么呢? 代表客户端断开了呀
    56                 print("客户端断开了",s)
    57 
    58                 if s in outputs:
    59                     outputs.remove(s) #清理已断开的连接
    60 
    61                 inputs.remove(s) #清理已断开的连接
    62 
    63                 del message_queues[s] ##清理已断开的连接
    64 
    65 
    66     for s in writeable:
    67         try :
    68             next_msg = message_queues[s].get_nowait()
    69 
    70         except queue.Empty:
    71             print("client [%s]" %s.getpeername()[0], "queue is empty..")
    72             outputs.remove(s)
    73 
    74         else:
    75             print("sending msg to [%s]"%s.getpeername()[0], next_msg)
    76             s.send(next_msg.upper())
    77 
    78 
    79     for s in exeptional:
    80         print("handling exception for ",s.getpeername())
    81         inputs.remove(s)
    82         if s in outputs:
    83             outputs.remove(s)
    84         s.close()
    85 
    86         del message_queues[s]
    87 
    88 select socket server
    select socket server
     1 #_*_coding:utf-8_*_
     2 __author__ = 'Alex Li'
     3 
     4 
     5 import socket
     6 import sys
     7 
     8 messages = [ b'This is the message. ',
     9              b'It will be sent ',
    10              b'in parts.',
    11              ]
    12 server_address = ('localhost', 10000)
    13 
    14 # Create a TCP/IP socket
    15 socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
    16           socket.socket(socket.AF_INET, socket.SOCK_STREAM),
    17           ]
    18 
    19 # Connect the socket to the port where the server is listening
    20 print('connecting to %s port %s' % server_address)
    21 for s in socks:
    22     s.connect(server_address)
    23 
    24 for message in messages:
    25 
    26     # Send messages on both sockets
    27     for s in socks:
    28         print('%s: sending "%s"' % (s.getsockname(), message) )
    29         s.send(message)
    30 
    31     # Read responses on both sockets
    32     for s in socks:
    33         data = s.recv(1024)
    34         print( '%s: received "%s"' % (s.getsockname(), data) )
    35         if not data:
    36             print(sys.stderr, 'closing socket', s.getsockname() )
    select socket client

    selectors模块

    This module allows high-level and efficient I/O multiplexing, built upon the select module primitives. Users are encouraged to use this module instead, unless they want precise control over the OS-level primitives used.

    import selectors
    import socket
     
    sel = selectors.DefaultSelector()
     
    def accept(sock, mask):
        conn, addr = sock.accept()  # Should be ready
        print('accepted', conn, 'from', addr)
        conn.setblocking(False)
        sel.register(conn, selectors.EVENT_READ, read)
     
    def read(conn, mask):
        data = conn.recv(1000)  # Should be ready
        if data:
            print('echoing', repr(data), 'to', conn)
            conn.send(data)  # Hope it won't block
        else:
            print('closing', conn)
            sel.unregister(conn)
            conn.close()
     
    sock = socket.socket()
    sock.bind(('localhost', 10000))
    sock.listen(100)
    sock.setblocking(False)
    sel.register(sock, selectors.EVENT_READ, accept)
     
    while True:
        events = sel.select()
        for key, mask in events:
            callback = key.data
            callback(key.fileobj, mask)
    

      

    数据库操作与Paramiko模块 

    http://www.cnblogs.com/wupeiqi/articles/5095821.html 

    RabbitMQ队列  

    安装 http://www.rabbitmq.com/install-standalone-mac.html

    安装python rabbitMQ module 

    pip install pika
    or
    easy_install pika
    or
    源码
      
    https://pypi.python.org/pypi/pika
    

      实现最简单的队列通信

    send端

    #!/usr/bin/env python
    import pika
     
    connection = pika.BlockingConnection(pika.ConnectionParameters(
                   'localhost'))
    channel = connection.channel()
     
    #声明queue
    channel.queue_declare(queue='hello')
     
    #n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
    channel.basic_publish(exchange='',
                          routing_key='hello',
                          body='Hello World!')
    print(" [x] Sent 'Hello World!'")
    connection.close()
    

      receive端

    #_*_coding:utf-8_*_
    __author__ = 'Alex Li'
    import pika
     
    connection = pika.BlockingConnection(pika.ConnectionParameters(
                   'localhost'))
    channel = connection.channel()
     
     
    #You may ask why we declare the queue again ‒ we have already declared it in our previous code.
    # We could avoid that if we were sure that the queue already exists. For example if send.py program
    #was run before. But we're not yet sure which program to run first. In such cases it's a good
    # practice to repeat declaring the queue in both programs.
    channel.queue_declare(queue='hello')
     
    def callback(ch, method, properties, body):
        print(" [x] Received %r" % body)
     
    channel.basic_consume(callback,
                          queue='hello',
                          no_ack=True)
     
    print(' [*] Waiting for messages. To exit press CTRL+C')
    channel.start_consuming()
    

      

    远程连接rabbitmq server的话,需要配置权限 噢 

    首先在rabbitmq server上创建一个用户

    sudo rabbitmqctl  add_user alex alex3714 
    

      同时还要配置权限,允许从外面访问

    sudo rabbitmqctl set_permissions -p / alex ".*" ".*" ".*"
    

      

    set_permissions [-p vhost] {user} {conf} {write} {read}

    vhost

    The name of the virtual host to which to grant the user access, defaulting to /.

    user

    The name of the user to grant access to the specified virtual host.

    conf

    A regular expression matching resource names for which the user is granted configure permissions.

    write

    A regular expression matching resource names for which the user is granted write permissions.

    read

    A regular expression matching resource names for which the user is granted read permissions.

    客户端连接的时候需要配置认证参数

    http://www.cnblogs.com/alex3714/articles/5248247.html   不摘录了,详细的以后再看

  • 相关阅读:
    IOS系统下虚拟键盘遮挡文本框问题的解决
    ubuntu git的安装更新及配置
    js 画布与图片的相互转化(canvas与img)
    js 图片与base64互相转换
    PHP base64数据与图片的互相转换
    js 判断当前操作系统是ios还是android还是电脑端
    ubuntu下nodejs和npm的安装及升级
    vue中使用html2canvas及解决html2canvas截屏图片模糊问题
    vue文件中引入外部js
    php 执行 命令行命令
  • 原文地址:https://www.cnblogs.com/Mengchangxin/p/9558373.html
Copyright © 2020-2023  润新知