rabbitmq是否可以防止错误的客户端创建太多的临时队列?

时间:2013-05-24 21:38:53

标签: erlang rabbitmq

如果客户端配置为接收消息创建临时队列,是否可以配置只允许创建一个队列? 如果有错误的客户端在服务器上创建了太多队列,它对服务器来说会是一个大问题吗?

如何配置以防止客户浪费资源?

-module(amqp_example).

-include("amqp_client.hrl").

-compile([export_all]).

test() ->
    %% Start a network connection
    {ok, Connection} = amqp_connection:start(#amqp_params_network{}),
    %% Open a channel on the connection
    {ok, Channel} = amqp_connection:open_channel(Connection),

    %% Declare a queue
    #'queue.declare_ok'{queue = Q}
        = amqp_channel:call(Channel, #'queue.declare'{}),
    ok = create_temp_queue(Channel,10),

    %% Publish a message
    Payload = <<"foobar">>,
    Publish = #'basic.publish'{exchange = <<>>, routing_key = Q},
    amqp_channel:cast(Channel, Publish, #amqp_msg{payload = Payload}),

    %% Get the message back from the queue
    Get = #'basic.get'{queue = Q},
    {#'basic.get_ok'{delivery_tag = Tag}, _Content}
        = amqp_channel:call(Channel, Get),

    %% Do something with the message payload
    %% (some work here)

    %% Ack the message
    amqp_channel:cast(Channel, #'basic.ack'{delivery_tag = Tag}),

    %% Close the channel
    amqp_channel:close(Channel),
    %% Close the connection
    amqp_connection:close(Connection),

    ok.

create_temp_queue(Channel,Loop)->
    [     %% Declare a queue
          #'queue.declare_ok'{queue = _Q}
          = amqp_channel:call(Channel, #'queue.declare'{})
          ||
        _X <- lists:seq(1,Loop)].


(emacs@yus-iMac.local)58> amqp_example:test().
** exception error: no match of right hand side value 
                    [{'queue.declare_ok',
                         <<"amq.gen-AqAaMLydgMf43y_XoYSdq5">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-A75g--nsvheNbwYMr34M-E">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-wmkOrALHBIj6Ot6ZuZZOQJ">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-wX2NmwMHBeDaKLvoZgJhEh">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-gcvScDp-RFMVwxWpyWjI-9">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-Q4CS7jNu3cde0RNdVdO3PJ">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-QKNrG8IJPVvfAlLukq38x_">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-wqJ2V1HQDaJjOzRDhv8gT4">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-AYeZiuNYsFOUMVw6xKcZh4">>,0,0},
                     {'queue.declare_ok',
                         <<"amq.gen-AJDqT2h2fq9cZOsVbNESi0">>,0,0}]
     in function  amqp_example:test/0 (src/amqp_example.erl, line 16)


yus-iMac:~ yuchen$ sudo rabbitmqctl list_queues
Password:
Listing queues ...
amq.gen-Q4CS7jNu3cde0RNdVdO3PJ  0
amq.gen-QKNrG8IJPVvfAlLukq38x_  0
amq.gen-AqAaMLydgMf43y_XoYSdq5  0
amq.gen-AJDqT2h2fq9cZOsVbNESi0  0
amq.gen-wqJ2V1HQDaJjOzRDhv8gT4  0
amq.gen-AYeZiuNYsFOUMVw6xKcZh4  0
amq.gen-wzvWzxXo2MJVZsyrwfzM8A  0
amq.gen-A75g--nsvheNbwYMr34M-E  0
amq.gen-gcvScDp-RFMVwxWpyWjI-9  0
amq.gen-wX2NmwMHBeDaKLvoZgJhEh  0
 amq.gen-wmkOrALHBIj6Ot6ZuZZOQJ 0
 ...done.

2 个答案:

答案 0 :(得分:0)

客户端应创建一个具有特定名称的队列,并确保将其设置为autodelete。这样,客户端将不会重新创建预先存在的队列。每个客户端都会创建自己的特定队列,因为每个客户端都会相应地命名其队列。

答案 1 :(得分:0)

好的。现在我们假设你有一个Erlang Rabbitmq client的正确设置。使用用户指南 here

1。通用RABBITMQ连接器

-module(rabbit_utils).
-compile(export_all).
-define(RABBIT_SERVER,"localhost").
-define(RABBIT_PORT,9001).

-record(pipe,{connection,channel}).

create_pipe()->    
    try amqp_connection:start(#amqp_params_network{host = ?RABBIT_SERVER,
                                                   port = RABBIT_PORT}) of
        {ok, Connection} -> 
            try amqp_connection:open_channel(Connection) of
                {ok,Channel} -> 
                    #pipe{
                        connection = Connection,
                        channel = Channel
                     };
                _ -> amqp_connection:close(Connection),error
            catch
                _:_ -> amqp_connection:close(Connection),error
            end;
        _ -> error
    catch
        _:_ ->  error
    end.

close_pipe(Channel,Connection)->
    try amqp_channel:close(Channel) of _ -> ok catch _:_ -> ok end,
    try amqp_connection:close(Connection) of _ -> ok catch _:_ -> ok end,
    ok.

我们会继续注意到,我会继续创建管道并关闭管道。这是因为,在erlang rabbitmq client的最新版本中,连接和通道变量是Erlang进程。每当某些内容未按计划在此库中进行时,这些进程将在运行时死亡。出于这个原因,我在try ... catch ... end代码块中捕获了大多数代码。如果您正在使用gen_servers,那么,将它们设为trap_exit = true,然后将它们链接到RabbitMQ客户端进程(连接和通道)。因此,每当连接中断时,Connection进程就会死亡,将其捕获到gen_server中,然后尝试另一个连接,如果失败,也许你可以拥有许多RABBITMQ个服务器{{1 }} 至。概念很深入,但让我们继续。


2。 Exchange管理器
这确保fail over服务器上存在交换。交换必须是RABBITMQ。我假设我们需要持久的交流和队列。持久交换和队列的一个优点是您只需创建一次,即使重新启动RABBITMQ服务,它也会使用这些交换和队列进行引导。

ensure_exchange_exists(Exchange)->
    case create_pipe() of
        error -> error;
        #pipe{connection = Connection,channel = Channel} ->             
            RandomQueue = list_to_binary(guid()),            
            #'queue.declare_ok'{} = amqp_channel:call(Channel,#'queue.declare'{queue = RandomQueue}),
            Binding = #'queue.bind'{queue = RandomQueue,exchange = Exchange,
                                    routing_key = <<"testing">>},
            try amqp_channel:call(Channel, Binding) of 
                #'queue.bind_ok'{} ->                         
                        Delete = #'queue.delete'{queue = RandomQueue},
                        #'queue.delete_ok'{} = amqp_channel:call(Channel, Delete),
                        close_pipe(Channel,Connection),
                        ok;
                _ ->                     
                    close_pipe(Channel,Connection),
                    create_exchange(Exchange,Server)
            catch 
                _R:_T ->                     
                    close_pipe(Channel,Connection),
                    create_exchange(Exchange,Server)
            end;
        _ -> error
    end.

create_exchange(Exchange)->
    case create_pipe() of
        error -> error;
        #pipe{connection = Connection,channel = Channel} -> 
            Exc = #'exchange.declare'{exchange = Exchange,durable = true},
            try amqp_channel:call(Channel,Exc) of
                #'exchange.declare_ok'{} -> ok;
                _ -> close_pipe(Channel,Connection),error
            catch
                _:_ -> close_pipe(Channel,Connection),error
            end
    end.

guid()->
    random:seed(now()),
    MD5 = erlang:md5(term_to_binary([random:uniform(7677771995517),{self(),time(),node(), now(), make_ref()}])),
    MD5List = lists:nthtail(2, binary_to_list(MD5)),
    F = fun(N) -> f("~2.16.0B", [N]) end,
    L = [F(N) || N <- MD5List],
    lists:flatten(L).

f(S)-> f(S,[]).
f(S,Args) -> lists:flatten(io_lib:format(S, Args)).


3。队列管理器
对于大型系统,我们发现使用一个交换机(或几个交换机)更容易,但拥有与系统需要交互的队列一样多的队列。想象一下,在集群中,每个系统都有一个队列,其他系统在需要从该系统询问某些内容时发送消息。此外,当系统从另一个系统获取请求时,它会通过将答案发送到该系统的队列来回复该请求。通过良好的消息格式协议,它可以实现系统识别,区分来自回复,时间戳,系统ping,远程过程调用的请求,e.t.c最终会在群集中显示消息路由器。在最近的一个项目中,我们提出了一个远程过程协议,使用binary作为消息格式。系统可以通过AMQP接口调用方法并将参数传递给远程系统。我不能在这里详细介绍,但只是知道,我们现在依赖它JSON Genuis。{
无论如何,我们首先需要确定一个队列存在,然后,如果存在,检查它是否绑定到我们的交换。我们走吧 !!

ensure_queue(Queue,Exchange)->
#pipe{connection = Connection,channel = Channel} = create_pipe(), Get = #'basic.get'{queue = Queue}, Sure = try amqp_channel:call(Channel, Get) of #'basic.get_empty'{} -> queue_exists;
{#'basic.get_ok'{}, _Content} -> queue_exists; _ -> queue_missing catch _EE:_EE2 -> queue_missing end, close_pipe(Channel,Connection), case Sure of queue_exists -> ensure_bound(Queue,Exchange); queue_missing -> create_queue_and_ensure_bound(Queue,Exchange) end.
ensure_bound(Queue,Exchange)->
#pipe{connection = Connection,channel = Channel} = create_pipe(), B1 = #'queue.bind'{queue = Queue,exchange = Exchange,routing_key = Queue}, try amqp_channel:call(Channel, B1) of #'queue.bind_ok'{} -> close_pipe(Channel,Connection), ok; _OtherErr -> error catch _ET:_EY -> error end.

create_queue_and_ensure_bound(Queue,Exchange)-> #pipe{connection = Connection,channel = Channel} = create_pipe(), TheQ = #'queue.declare'{queue = Queue,durable = true}, try amqp_channel:call(Channel,TheQ) of #'queue.declare_ok'{} -> Binding = #'queue.bind'{queue = Queue,exchange = Exchange,routing_key = Queue}, try amqp_channel:call(Channel, Binding) of #'queue.bind_ok'{} -> close_pipe(Channel,Connection), error; _Other2 -> close_pipe(Channel,Connection), error catch _ER11:_ER22 -> close_pipe(Channel,Connection), error end; _Other -> close_pipe(Channel,Connection), error catch _ER1:_ER2 -> close_pipe(Channel2,Connection2), error end.


通过将容错添加到具体的erlang应用程序中,利用上面的一些代码,您将拥有一个非常紧凑的集群。 RABBITMQ的可用性非常高,特别是如果您在了解自己在做什么的同时使用它。大多数问题都是由网络引起的。如果丢失与Rabbitmq服务器的连接,您的应用程序将做什么?冗余来自这里。您可以拥有所有群集节点都知道的各种100%服务器,并且您在所有RABBITMQ服务器上安装了所有相同的交换和队列,这样如果一台服务器丢失,其他服务器可用于继续服务。