RabbitMQ介绍4 - 编程(C#客户端示例)

C#终端的说明文档: http://www.rabbitmq.com/dotnet-api-guide.html

这里介绍使用RabbitMQ的几种典型场景。

1. 简单direct模式( http://www.rabbitmq.com/tutorials/tutorial-one-dotnet.html)。生产者发送消息到一个队列,消费者从队列读取消息。这是最简单的使用场景,下面的代码使用默认exchange,消息自动确认。注意后台接收消息的线程完成前不要关闭连接,这里消费者是通过Console.ReadLine();保证连接不会Dispose。

 

生产者:

public static void test1()
{
    var factory = new ConnectionFactory() { HostName = "localhost" };
    using (var connection = factory.CreateConnection())
    using (var channel = connection.CreateModel())
    {
        channel.QueueDeclare(queue: "hello",
                             durable: false,
                             exclusive: false,
                             autoDelete: false,
                             arguments: null);

        string message = "Hello World!";
        var body = Encoding.UTF8.GetBytes(message);

        channel.BasicPublish(exchange: "",
                             routingKey: "hello",
                             basicProperties: null,
                             body: body);
        Console.WriteLine(" [x] Sent {0}", message);
    }

    Console.WriteLine(" Press [enter] to exit.");
    Console.ReadLine();
}

消费者

public static void test1()
{
    var factory = new ConnectionFactory() { HostName = "localhost" };
    using (var connection = factory.CreateConnection())
    using (var channel = connection.CreateModel())
    {
        channel.QueueDeclare(queue: "hello",
                             durable: false,
                             exclusive: false,
                             autoDelete: false,
                             arguments: null);

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body;
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine(" [x] Received {0}", message);
        };
        channel.BasicConsume(queue: "hello",
                             noAck: true,
                             consumer: consumer);

        Console.WriteLine(" Press [enter] to exit.");
        Console.ReadLine();
    }
}

2. 多个消费者连接队列(http://www.rabbitmq.com/tutorials/tutorial-two-dotnet.html)。使用场景:生产者发送比较耗时的任务,多个消费者从队列获取任务,完成计算。优点:方便系统扩展,添加消费者即可增加系统的负载能力,通过显式消息确认、prefetch消息量的控制,可以实现多个消费者之间的负载均衡。prefetch N的意思是在消费者确认前,只发送N个消息,也就是等待确认的消息最多N个(默认不设,队列的消息会全部发给消费者,然后等待确认),只要客户端的连接保持,便不会重发,如果连接中断,消息还是没有确认,则会重新发送。

生产者代码和前一个例子类似,这里只给出消费者代码

public static void test2()
{
    var factory = new ConnectionFactory() { HostName = "localhost" };
    using (var connection = factory.CreateConnection())
    using (var channel = connection.CreateModel())
    {
        channel.QueueDeclare(queue: "task_queue",
                             durable: true,
                             exclusive: false,
                             autoDelete: false,
                             arguments: null);

        channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);

        Console.WriteLine(" [*] Waiting for messages.");

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body;
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine(" [x] Received {0}", message);

            int dots = message.Split(‘.‘).Length - 1;
            Thread.Sleep(dots * 10000);

            Console.WriteLine(" [x] Done");

            channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
        };
        channel.BasicConsume(queue: "task_queue",
                             noAck: false,
                             consumer: consumer);

        Console.WriteLine(" Press [enter] to exit.");
        Console.ReadLine();
    }
}

3. 订阅发布模式(http://www.rabbitmq.com/tutorials/tutorial-three-dotnet.html)。exchange使用fanout模式。注意下面代码创建了命名队列(通过队列名可以方便标识消费者),如果队列不需要持久化,也可以使用临时队列(queueName = channel.QueueDeclare().QueueName;),RabbitMQ为队列分配一个唯一标识,消费者断开后会自动删除队列。

生产者:

public static void test3(string[] args)
{
    var factory = new ConnectionFactory() { HostName = "localhost" };
    using (var connection = factory.CreateConnection())
    using (var channel = connection.CreateModel())
    {
        channel.ExchangeDeclare("logs", "fanout");

        for (int i = 0; i < args.Length; i++)
        {
            var message = "task_queue_t3";
            var body = Encoding.UTF8.GetBytes(message);

            var properties = channel.CreateBasicProperties();
            properties.SetPersistent(true);

            channel.BasicPublish(exchange: "logs", routingKey: "", basicProperties: properties, body: body);
            Console.WriteLine(" [x] Sent {0}", message);
        }

    }
}

消费者:

public static void test3()
{
    var factory = new ConnectionFactory() { HostName = "localhost" };
    using (var connection = factory.CreateConnection())
    using (var channel = connection.CreateModel())
    {
        channel.ExchangeDeclare("logs", ExchangeType.Fanout);

        string queueName = "task_queue_t3";

        Console.WriteLine(Encoding.UTF8.GetBytes(queueName).Length);

        channel.QueueDeclare(queue: queueName,
             durable: true,
             exclusive: true,
             autoDelete: true,
             arguments: null);
        //queueName = channel.QueueDeclare().QueueName;

        channel.QueueBind(queue: queueName, exchange: "logs", routingKey: "");

        Console.WriteLine(" [*] Waiting for messages.");

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body;
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine(" [x] Received {0}", message);

            int dots = message.Split(‘.‘).Length - 1;
            Thread.Sleep(dots * 1000);

            Console.WriteLine(" [x] Done");

            channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
        };
        channel.BasicConsume(queue: queueName,
                             noAck: false,
                             consumer: consumer);

        Console.WriteLine(" Press [enter] to exit.");
        Console.ReadLine();
    }
}

 

4. 路由(http://www.rabbitmq.com/tutorials/tutorial-four-dotnet.html)。exchange使用Direct模式,通过binding key 精确匹配routing key选择消息路由。可以有多个binding key。

生产者:

channel.ExchangeDeclare("direct_logs", "direct");

channel.BasicPublish(exchange: "direct_logs", routingKey: "info", basicProperties: properties, body: body);

 

消费者:

channel.ExchangeDeclare("direct_logs", "direct");

string queueName = channel.QueueDeclare().QueueName;

channel.QueueBind(queueName, "direct_logs", "info");
channel.QueueBind(queueName, "direct_logs", "error");

5. Topic模式(http://www.rabbitmq.com/tutorials/tutorial-five-dotnet.html)。exchange使用Topic模式,可以实现模糊匹配。

生产者:

channel.ExchangeDeclare("topic_logs", ExchangeType.Topic);

channel.BasicPublish(exchange: "topic_logs", routingKey: "lazy.green.cat", basicProperties: properties, body: body);

消费者:

channel.ExchangeDeclare("topic_logs", "topic");

string queueName = "Q2";
channel.QueueDeclare(queue: queueName,
     durable: true,
     exclusive: true,
     autoDelete: true,
     arguments: null);

channel.QueueBind(queueName, "topic_logs", "*.*.rabbit");
channel.QueueBind(queueName, "topic_logs", "lazy.#");

6. 远程过程调用RPC(http://www.rabbitmq.com/tutorials/tutorial-six-dotnet.html)。实现客户和服务之间的通信。一次通信过程如下:

  • 客户端发送请求消息。客户端发送请求时通过reply_to指定回复的地址(回复的queue,由于RPC的特殊性,我们使用默认的exchange来做路由,这时候queue名字便是路由键)和correlation_id(标记发送的消息,回复的时候通过这个ID来确认回复的是哪个请求)。在reply_to的queue上等待回复。
  • 服务器收到请求,处理后发送回复到默认exchange,用请求消息的reply_to做路由键,这样回复便发到了reply_to指定的queue。设置的回复消息的correlation_id=请求消息的correction_id。
  • 客户端在reply_to的队列上收到回复。
  • 通过ack和prefetch N控制同步。

RPCServer端代码:

public static void RPCServer()
        {
            var factory = new ConnectionFactory() { HostName = "localhost" };
            using (var connection = factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                channel.QueueDeclare(queue: "rpc_queue",
                                     durable: false,
                                     exclusive: false,
                                     autoDelete: false,
                                     arguments: null);
                channel.BasicQos(0, 1, false);
                var consumer = new QueueingBasicConsumer(channel);
                channel.BasicConsume(queue: "rpc_queue",
                                     noAck: false,
                                     consumer: consumer);
                Console.WriteLine(" [x] Awaiting RPC requests");

                while (true)
                {
                    string response = null;
                    var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                    var body = ea.Body;
                    var props = ea.BasicProperties;
                    var replyProps = channel.CreateBasicProperties();
                    replyProps.CorrelationId = props.CorrelationId;

                    try
                    {
                        var message = Encoding.UTF8.GetString(body);
                        int n = int.Parse(message);
                        Console.WriteLine(" [.] fib({0})", message);
                        response = "abc"; //do time consuming work here
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(" [.] " + e.Message);
                        response = "";
                    }
                    finally
                    {
                        var responseBytes = Encoding.UTF8.GetBytes(response);
                        channel.BasicPublish(exchange: "",
                                             routingKey: props.ReplyTo,
                                             basicProperties: replyProps,
                                             body: responseBytes);
                        channel.BasicAck(deliveryTag: ea.DeliveryTag,
                                         multiple: false);
                    }
                }
            }
        }

 

客户端代码:

class RPCClient
    {
        private IConnection connection;
        private IModel channel;
        private string replyQueueName;
        private QueueingBasicConsumer consumer;

        public RPCClient()
        {
            var factory = new ConnectionFactory() { HostName = "localhost" };
            connection = factory.CreateConnection();
            channel = connection.CreateModel();
            replyQueueName = channel.QueueDeclare().QueueName;
            consumer = new QueueingBasicConsumer(channel);
            channel.BasicConsume(queue: replyQueueName,
                                 noAck: true,
                                 consumer: consumer);
        }

        public string Call(string message)
        {
            var corrId = Guid.NewGuid().ToString();
            var props = channel.CreateBasicProperties();
            props.ReplyTo = replyQueueName;
            props.CorrelationId = corrId;

            var messageBytes = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish(exchange: "",
                                 routingKey: "rpc_queue",
                                 basicProperties: props,
                                 body: messageBytes);

            while (true)
            {
                var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                if (ea.BasicProperties.CorrelationId == corrId)
                {
                    return Encoding.UTF8.GetString(ea.Body);
                }
            }
        }

        public void Close()
        {
            connection.Close();
        }
    }

 

//测试

public static void RPCTest()
{
    var rpcClient = new RPCClient();

    Console.WriteLine(" [x] Requesting fib(30)");
    var response = rpcClient.Call("30");
    Console.WriteLine(" [.] Got ‘{0}‘", response);

    rpcClient.Close();
}

时间: 2024-12-30 11:11:15

RabbitMQ介绍4 - 编程(C#客户端示例)的相关文章

Rabbitmq 简单介绍,安装和go客户端使用

Rabbitmq 简单介绍,安装和go客户端使用 1,消息队列介绍 1.1 什么是消息队列? 消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户.消息队列提供了异步的通信协议,每一个贮列中的纪录包含详细说明的数据,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列互交.消息会保存在队列中,直到接收者取回它.消息队列,一般我们会简称他为MQ(Message

Linux程序设计学习笔记----网络通信编程API及其示例应用

转载请注明出处, http://blog.csdn.net/suool/article/details/38702855. BSD Socket 网络通信编程 BSD TCP 通信编程流程 图为面向连接的Socket通信的双方执行函数流程.使用TCP协议的通信双方实现数据通信的基本流程如下 建立连接的步骤 1.首先服务器端需要以下工作: (1)调用socket()函数,建立Socket对象,指定通信协议. (2)调用bind()函数,将创建的Socket对象与当前主机的某一个IP地址和TCP端口

Python Socket 编程——聊天室示例程序

原文:Python Socket 编程--聊天室示例程序 上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的理解. 聊天室程序需求 我们要实现的是简单的聊天室的例子,就是允许多个人同时一起聊天,每个人发送的消息所有人都能接收到,类似于 QQ 群的功能,而不是点对点的 QQ 好友之间的聊天.如下图: 图来自:http://www.ibm.com/de

【windows socket编程+服务器客户端】

Windows Socket编程与服务器客户端示例 Winsock是 Windows下套接字标准. Socket套接字基于计算机网络,提供同一系统上不同进程或由局域网连接在一起的不同机器上的进程间通讯功能.如下图: 套接字通过IP地址,Port端口号标识,通过这个标识可以在整个局域网定位一个套接字,通过套接字进程便可以相互传输数据.如:进程A与进程B之间欲通过套接字通信,首先进程A创建一个有IP地址,端口号唯一标识的套接字,进程B同样创建一个有IP地址,端口号唯一标识的套接字,进程A,B便可以通

Linux网络编程:客户端/服务器的简单实现

一. Socket的基本知识 1. socket功能 Socket层次 Socket实质上提供了进程通信的端点,进程通信之前,双方必须首先各自创建一个端点,否则是没有办法建立联系并相互通信的. 每一个Socket都一个半相关描述: {协议, 本地地址, 本地端口} 完整的Socket的描述: {协议, 本地地址, 本地端口, 远程地址, 远程端口} 2. Socket工作流程 面向连接(TCP)的Socket工作流程 UDP的socket工作流程 l 服务器端 首先,服务器应用程序用系统调用so

C编程规范, 示例代码。

/*************************************************************** *Copyright (c) 2014,TianYuan *All rights reserved. * *文件名称: standard.h *文件标识: 编程规范示例代码 * *当前版本:V1.0 *作者:wuyq *完成日期:20140709 * *修改记录1: //修改历史记录,包括修改日期.版本号.修改人及修改内容等 *修改日期 版本号 修改人 修改内容 *

C# WebSocket 服务端示例代码 + HTML5客户端示例代码

WebSocket服务端 C#示例代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.Threading; using System.Text.RegularExpressions; using System.Security.Cryptography; na

RabbitMQ介绍及安装部署

本节内容: RabbitMQ介绍 RabbitMQ运行原理 RabbitMQ重要术语 三种ExchangeType RabbitMQ集群种类 集群基本概念 镜像模式部署集群 一.RabbitMQ介绍 消息系统通过将消息的发送和接收分离来实现应用程序的异步和解偶.或许你正在考虑进行数据投递,非阻塞操作或推送通知.或许你想要实现发布/订阅,异步处理,或者工作队列.所有这些都属于消息系统的模式.RabbitMQ是一个消息代理,一个消息系统的媒介.它可以为你的应用提供一个通用的消息发送和接收平台,并且保

网络编程 实现 客户端与服务器端的简单通信

六,代码演示实现客户端与服务器端的简单通信 代码中所使用的 IP号码,必须是本机自己的IP号码 (自行查询:cmd---ipconfig/all ) 1.[客户端向服务器端 发送一个整型数据,服务器端进行接收] (1)先写服务器端 import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java