快速掌握RabbitMQ(二)——四种Exchange介绍及代码演示
在上一篇的最后,编写了一个C#驱动RabbitMQ的简单栗子,了解了C#驱动RabbitMQ的基本用法。本章介绍RabbitMQ的四种Exchange及各种Exchange的使用场景。
1 direct类型
1 direct路由规则
上一篇最后一个栗子使用的Exchange就是direct类型的,direct类型的exchange路由规则很简单:
exchange在和queue进行binding时会设置routingkey(为了避免和下边的routingKey混淆,很多时候把这里的routingKey叫做BindingKey)
channel.QueueBind(queue:"Q1", exchange:"myexchange", routingKey:"orange");
将消息发送到Broker时会设置对应的routingkey:
channel.BasicPublish(exchange: "myexchange",routingKey: "orange", basicProperties: null, body: body);
只有RoutingKey和BindingKey完全相同时,exchange才会把消息路由到绑定的queue中去。
2 代码示例
我们知道了direact类型的交换机只有routingKey和bindingKey相同的时候才会进行消息路由,根据这一特点我们可以通过routingKey将消息路由到不同的queue中。如在进行日志处理时,需求是所有的日志都保存到文本文件,出现错误日志时则还需要短信通知以便及时处理。我们可以创建两个队列:只接收错误日志的log_error队列和接收所有日志信息的log_all队列。消费者C1处理log_error队列中消息,将这些消息通过短信通知管理员,消费者C2处理log_all队列的信息,将这些信息记录到文本文件。
生产者用于发送日志消息,代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "myexchange",
type: ExchangeType.Direct,
durable: true,
autoDelete: false,
arguments: null);
//声明两个队列,log_all保存所有日志,log_error保存error类型日志
channel.QueueDeclare(queue: "log_all",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.QueueDeclare(queue: "log_error",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
//绑定所有日志类型到log_all队列
string[] logtypes = new string[] { "debug", "info", "warn", "error" };
foreach (string item in logtypes)
{
channel.QueueBind(queue: "log_all",
exchange: "myexchange",
routingKey: item);
}
//绑定错误日志到log_all队列
channel.QueueBind(queue: "log_error",
exchange: "myexchange",
routingKey: "error");
//准备100条测试日志信息
List<LogMsg> msgList = new List<LogMsg>();
for (int i = ; i < ; i++)
{
if (i%==)
{
msgList.Add(new LogMsg() { LogType = "info", Msg = Encoding.UTF8.GetBytes($"info第{i}条信息") });
}
if (i % == )
{
msgList.Add(new LogMsg() { LogType = "debug", Msg = Encoding.UTF8.GetBytes($"debug第{i}条信息") });
}
if (i % == )
{
msgList.Add(new LogMsg() { LogType = "warn", Msg = Encoding.UTF8.GetBytes($"warn第{i}条信息") });
}
if (i % == )
{
msgList.Add(new LogMsg() { LogType = "error", Msg = Encoding.UTF8.GetBytes($"error第{i}条信息") });
}
}
Console.WriteLine("生产者发送100条日志信息");
//发送日志信息
foreach (var item in msgList)
{
channel.BasicPublish(exchange: "myexchange",
routingKey: item.LogType,
basicProperties: null,
body: item.Msg);
}
}
}
Console.ReadKey();
}
}
消费者C1用于处理log_error队列中的消息,错误消息进行短信通知,代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "myexchange",
type: ExchangeType.Direct,
durable: true,
autoDelete: false,
arguments: null);
//声明队列queue
channel.QueueDeclare(queue: "log_all",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null); //绑定
channel.QueueBind(queue: "log_error",
exchange: "myexchange",
routingKey: "error"); //定义消费者
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
//只是为了演示,并没有存入文本文件
Console.WriteLine($"接收成功!【{message}】,发送短信通知");
};
Console.WriteLine("消费者C1【接收错误日志,发送短信通知】准备就绪....");
//处理消息
channel.BasicConsume(queue: "log_error",
autoAck: true,
consumer: consumer);
Console.ReadLine();
}
}
}
消费者C2用于处理log_all队列中的消息,所有消息记录到文本文件中,代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "myexchange",
type: ExchangeType.Direct,
durable: true,
autoDelete: false,
arguments: null);
//声明队列queue
channel.QueueDeclare(queue: "log_all",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null); //绑定
string[] logtypes = new string[] { "debug", "info", "warn", "error" };
foreach (string item in logtypes)
{
channel.QueueBind(queue: "log_all",
exchange: "myexchange",
routingKey: item);
}
//定义消费者
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
//只是为了演示,并没有存入文本文件
Console.WriteLine($"接收成功!【{message}】,存入文本文件");
};
Console.WriteLine("消费者C2【接收所有日志信息,存入文本文件】准备就绪....");
//处理消息
channel.BasicConsume(queue: "log_all",
autoAck: true,
consumer: consumer);
Console.ReadLine();
}
}
}
运行这三个项目,执行结果如下:
2 fanout类型
1 fanout路由规则
fanout类型的exchange路由规则是最简单的,交换机会把消息广播到与该Exchange绑定的所有queue中,即所有和该exchange绑定的队列都会收到消息。fanout类型exchange和队列绑定时不需要指定routingKey,即使指定了routingKey也会被忽略掉。路由结构如下:
fanout类型交换机主要用于发布/订阅的一些场景,如用户注册了我们的网站后,我们通过短信和邮件两种方式通知用户
2 代码示例
这里通过代码简单演示将消息同时使用短信和邮件两种方式通知用户的流程。首先声明一个fanout类型的exchange,然后声明两个队列 SMSqueue和EMAILqueue,这两个队列都和这个exchange绑定。消费者1处理EMAILqueue的消息,通过邮件方式发送通知;消费者2处理SMSqueue的消息通过短信方式发送通知。
生产者发送信息,代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//第一步:创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "myfanoutexchange",
type: ExchangeType.Fanout,
durable: true,
autoDelete: false,
arguments: null);
//声明SMSqueue队列,用于短信通知
channel.QueueDeclare(queue: "SMSqueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
//声明队列,Email队列,用于邮件通知
channel.QueueDeclare(queue: "EMAILqueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null); //绑定exchange和queue
channel.QueueBind(queue: "SMSqueue", exchange: "myfanoutexchange", routingKey: string.Empty,arguments:null);
channel.QueueBind(queue: "EMAILqueue", exchange: "myfanoutexchange", routingKey: string.Empty, arguments: null);
Console.WriteLine("生产者准备就绪...."); string message = "";
//第六步:发送消息
//在控制台输入消息,按enter键发送消息
while (!message.Equals("quit", StringComparison.CurrentCultureIgnoreCase))
{
message = Console.ReadLine();
var body = Encoding.UTF8.GetBytes(message);
//基本发布
channel.BasicPublish(exchange: "myfanoutexchange",
routingKey: string.Empty,
basicProperties: null,
body: body);
Console.WriteLine($"消息【{message}】已发送到队列");
}
}
}
Console.ReadKey();
}
消费者1将EMAILqueue的消息通过邮件方式发送通知,代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "myfanoutexchange",
type: ExchangeType.Fanout,
durable: true,
autoDelete: false,
arguments: null);
//声明队列queue
channel.QueueDeclare(queue: "EMAILqueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null); //绑定exchange和queue
channel.QueueBind(queue: "EMAILqueue", exchange: "myfanoutexchange", routingKey: string.Empty, arguments: null);
//定义消费者
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
//只是为了演示,并没有存入文本文件
Console.WriteLine($"接收成功!【{message}】,邮件通知");
};
Console.WriteLine("邮件通知服务准备就绪...");
//处理消息
channel.BasicConsume(queue: "EMAILqueue",
autoAck: true,
consumer: consumer);
Console.ReadLine();
}
}
}
消费者2将SMSqueue的消息通过短信方式发送通知,代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "myfanoutexchange",
type: ExchangeType.Fanout,
durable: true,
autoDelete: false,
arguments: null);
//声明队列queue
channel.QueueDeclare(queue: "SMSqueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null); //绑定exchange和queue
channel.QueueBind(queue: "SMSqueue", exchange: "myfanoutexchange", routingKey: string.Empty,arguments:null);
//定义消费者
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
//只是为了演示,并没有存入文本文件
Console.WriteLine($"接收成功!【{message}】,短信通知");
};
Console.WriteLine("短信通知服务准备就绪...");
//处理消息
channel.BasicConsume(queue: "myfanoutqueue",
autoAck: true,
consumer: consumer);
Console.ReadLine();
}
}
}
启动这三个应用程序,执行结果如下:
3 topic类型
1 topic路由规则
topic类型exchange的路由规则也是基于routingKey和bindingKey的,其路由过程和direct类型基本一致,两者的区别在于direct类型的exchange要求routingKey和bindingKey必须相同才进行将消息路由到绑定的queue中,而topic类型的bindingKey是一个匹配规则,只要routingKey符合bindingKey的规则就可以将消息路由到绑定的queue中去,结构如下图所示。注意routingKey和bindingKey的结构都是一系列由点号连接单词的字符串,例如【aaa.bbb.ccc】。
bindingKey的两个特殊符号:*表示一个单词,#表示0或多个单词(注意是单词,而不是字符)。如下图,usa.news和usa.weather都和usa.#匹配,而usa.news和europe.news都和#.news匹配。
2 代码实现
这里使用代码实现上图中的例子,为了方便我们只定义两个队列:接收美国相关信息的usaQueue(bindingKey是usa.#)和接收新闻消息的newsQueue(bindingKey是#.news)。然后定义两个消费者,消费者1处理useaQueue的消息,消费者2处理newsQueue的消息。
生产者代码:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "mytopicExchange",
type: ExchangeType.Topic,
durable: true,
autoDelete: false,
arguments: null);
//声明队列usaQueue
channel.QueueDeclare(queue: "usaQueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
//声明队列newsQueue
channel.QueueDeclare(queue: "newsQueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
Console.WriteLine("生产者准备就绪....");
//绑定usaQueue队列到交互机,routingKey为usa.#
channel.QueueBind(queue: "usaQueue", exchange: "mytopicExchange", routingKey: "usa.#", arguments: null);
//绑定newsQueue队列到交互机,routingKey为#.news
channel.QueueBind(queue: "newsQueue", exchange: "mytopicExchange", routingKey: "#.news", arguments: null); ////--------------------开始发送消息
//1.发送美国新闻消息
string message1 = "美国新闻消息:内容balabala";
var body1 = Encoding.UTF8.GetBytes(message1);
channel.BasicPublish(exchange: "mytopicExchange",
routingKey: "usa.news",
basicProperties: null,
body: body1);
Console.WriteLine($"消息【{message1}】已发送到队列"); //2.发送美国天气消息
string message2 = "美国天气消息:内容balabala";
var body2 = Encoding.UTF8.GetBytes(message2);
channel.BasicPublish(exchange: "mytopicExchange",
routingKey: "usa.weather",
basicProperties: null,
body: body2);
Console.WriteLine($"消息【{message2}】已发送到队列");
//3.发送欧洲新闻消息
string message3 = "欧洲新闻消息:内容balabala";
var body3 = Encoding.UTF8.GetBytes(message3);
channel.BasicPublish(exchange: "mytopicExchange",
routingKey: "europe.news",
basicProperties: null,
body: body3);
Console.WriteLine($"消息【{message3}】已发送到队列"); //4.发送欧洲天气消息
string message4 = "欧洲天气消息:内容balabala";
var body4 = Encoding.UTF8.GetBytes(message4);
//基本发布
channel.BasicPublish(exchange: "mytopicExchange",
routingKey: "europe.weather",
basicProperties: null,
body: body4);
Console.WriteLine($"消息【{message4}】已发送到队列");
}
}
Console.ReadKey();
}
消费者1代码,只处理usaQueue中的消息:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "mytopicExchange",
type: ExchangeType.Topic,
durable: true,
autoDelete: false,
arguments: null);
//声明队列queue
channel.QueueDeclare(queue: "usaQueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
Console.WriteLine("usaQueue消费者准备就绪....");
//绑定usaQueue队列到交互机
channel.QueueBind(queue: "usaQueue", exchange: "mytopicExchange", routingKey: "usa.#", arguments: null);
//定义消费者
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"接收成功!【{message}】");
};
//处理消息
channel.BasicConsume(queue: "usaQueue",
autoAck: true,
consumer: consumer);
Console.ReadLine();
}
}
}
消费者2代码,只处理newsQueue中的消息:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "mytopicExchange",
type: ExchangeType.Topic,
durable: true,
autoDelete: false,
arguments: null);
//声明队列queue
channel.QueueDeclare(queue: "newsQueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
Console.WriteLine("newsQueue消费者准备就绪....");
//绑定usaQueue队列到交互机
channel.QueueBind(queue: "newsQueue", exchange: "mytopicExchange", routingKey: "#.news", arguments: null);
//定义消费者
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"接收成功!【{message}】");
};
//处理消息
channel.BasicConsume(queue: "newsQueue",
autoAck: true,
consumer: consumer);
Console.ReadLine();
}
}
}
生成者发送的四条消息中,消息1的routingKey为usa.news,同时符合usaQueue的bindingKey(usa.#)和newsQueue的bindingKey(#.news),所以消息1同时路由到两个队列中;消息2的routingKey为usa.weather只符合usa.#,发送到usaQueue;消息的rouKey为europe.news,只符合#.news,发送到newsQueue中;消息4的routingKey为europe.weahter,和两个队列的bindingKey都不符合,所以被丢弃。执行这三个Console应用程序,结果如下:
一点补充:topic类型交换机十分灵活,可以轻松实现direct和fanout类型交换机的功能。如果绑定队列时所有的bindingKey都是#,则交换机和fanout类型交换机表现一致;如果所有的bindingKey都不包含*和#,则交换机和direct类型交换机表现一致。
4 header类型
1 header路由规则
header类型路由规则和上边的几种exchange都不一样,header类型exchange不是通过routingKey进行路由的,而是通过Headers。exchange在和queue进行binding时可以设置arguments:
channel.QueueBind(queue: "Allqueue",
exchange: "myheaderExchange",
routingKey: string.Empty,
arguments: new Dictionary<string, object> {
{ "x-match","all"},
{ "user","jack"},
{ "pass",""}
});
将消息发送到exchange时可以设置消息的Header:
var props1 = channel.CreateBasicProperties();
props1.Headers = new Dictionary<string, object>() {
{ "user","jack"},
{ "pass",""}
};
var body1 = Encoding.UTF8.GetBytes(msg1);
//发送消息
channel.BasicPublish(exchange: "myheaderExchange",
routingKey: string.Empty,
basicProperties: props1,
body: body1);
user和pass是普通的键值对,我们也可以设置其他的键值对。x-match是一个特殊的属性,当x-match为all时,aguments和basicProrperties.Headers的所有键值对都相等时才会路由到queue(AND关系);当x-match为any时,aguments和basicProrperties.Headers的键值对只要有一个相同就可以路由到queue(OR关系)。
2.代码示例
看一个简单的栗子,创建两个队列Allqueue和Anyqueue,其中Allqueue和exchange绑定时的x-match为all,Anyqueue和exchange绑定时的x-match为any;然后发送两条消息,发送第一条消息时basicProperties.Headers中的user和pass都和绑定队列时的agruments的user和pass相等,发送第二条消息是两者的pass不相等,代码如下:
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
//rabbitmq-server所在设备ip,这里就是本机
HostName = "127.0.0.1",
UserName = "wyy",//用户名
Password = ""//密码
};
//创建连接connection
using (var connection = factory.CreateConnection())
{
//创建通道channel
using (var channel = connection.CreateModel())
{
//声明交换机exchang
channel.ExchangeDeclare(exchange: "myheaderExchange",
type: ExchangeType.Headers,
durable: true,
autoDelete: false,
arguments: null);
//声明Allqueue队列
channel.QueueDeclare(queue: "Allqueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
//声明Anyqueue队列
channel.QueueDeclare(queue: "Anyqueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
Console.WriteLine("生产者准备就绪...."); //////发送消息消息1,user和pass都相同
//绑定exchange和Allqueue
channel.QueueBind(queue: "Allqueue",
exchange: "myheaderExchange",
routingKey: string.Empty,
arguments: new Dictionary<string, object> {
{ "x-match","all"},
{ "user","jack"},
{ "pass",""}});
string msg1 = "user和pass都相同时发送的消息";
var props1 = channel.CreateBasicProperties();
props1.Headers = new Dictionary<string, object>() {
{ "user","jack"},
{ "pass",""}
};
var body1 = Encoding.UTF8.GetBytes(msg1);
//基本发布
channel.BasicPublish(exchange: "myheaderExchange",
routingKey: string.Empty,
basicProperties: props1,
body: body1);
Console.WriteLine($"消息【{msg1}】已发送到队列"); //////发送消息消息2,user和pass不完全相同
//绑定exchange和Anyqueue
channel.QueueBind(queue: "Anyqueue",
exchange: "myheaderExchange",
routingKey: string.Empty,
arguments: new Dictionary<string, object> {
{ "x-match","any"},
{ "user","jack"},
{ "pass",""},}); string msg2 = "user和pass不完全相同时发送的消息";
var props2 = channel.CreateBasicProperties();
props2.Headers = new Dictionary<string, object>() {
{ "user","jack"},
{ "pass",""}//这里的pass和BindQueue方法的中argumens中的pass不相同
};
var body2 = Encoding.UTF8.GetBytes(msg2);
//基本发布
channel.BasicPublish(exchange: "myheaderExchange",
routingKey: string.Empty,
basicProperties: props2,
body: body2);
Console.WriteLine($"消息【{msg2}】已发送到队列"); }
}
Console.ReadKey();
}
}
执行程序,打开WebUI管理界面,结果如下,我们看到只有user和pass都相等时消息才会路由到Allqueue;user和pass只要有一个相等就会路由到Anyqueue
5 小结
RabbitMQ的交换机(exchange)的作用是路由消息,我们可以根据应用场景的不同选择合适的交换机。如果需要精准路由到队列,或者对消息进行单一维度分类(只对日志的严重程度这一维度进行分类)可以使用direct类型交换机;如果需要广播消息,可以使用fanout类型的交换机;如果对消息进行多维度分类(如例子中按照地区和消息内容类型两个维度进行分类)使用topic类型的交换机;如果消息归类的逻辑包含了较多的AND/OR逻辑判断可以使用header类型交换机(开发中很少用到Header类型,官网上关于Header类型的介绍也不多)。
【参考文章】
1. https://www.cnblogs.com/zhangweizhong/p/5713874.html
2.https://blog.csdn.net/ctwy291314/article/details/83147194
快速掌握RabbitMQ(二)——四种Exchange介绍及代码演示的更多相关文章
- RabbitMQ的四种ExChange
在message到达Exchange后,Exchange会根据route规则进入对应的Queue中,message可能进入一个Queue也可能进入对应多个Queue,至于进入哪个Queue或者是说哪个 ...
- RabbitMQ 四种Exchange
AMQP协议中的核心思想就是生产者和消费者隔离,生产者从不直接将消息发送给队列.生产者通常不知道是否一个消息会被发送到队列中,只是将消息发送到一个交换机.先由Exchange来接收,然后Exchang ...
- Netty之WebSocket和四种IO介绍
Netty简介 一.什么是netty? 高性能 事件驱动 异步非堵塞 基于NIO的客户端,服务器端编程框架 稳定性和伸缩性 二.Netty的使用场景 高性能领域 多线程并发领域 异步通信领域 ...
- mysql进阶(六)模糊查询的四种用法介绍
mysql中模糊查询的四种用法介绍 这篇文章主要介绍了mysql中模糊查询的四种用法,需要的朋友可以参考下. 下面介绍mysql中模糊查询的四种用法: 1 %: 表示任意0个或多个字符.可匹配任意类型 ...
- 快速理解VirtualBox的四种网络连接方式
VirtualBox中有4中网络连接方式: NAT Bridged Adapter Internal Host-only Adapter VMWare中有三种,其实他跟VMWare 的网络连接方式都是 ...
- 单元测试_JUnit常用单元测试注解介绍及代码演示
JUnit常用单元测试注解介绍及代码演示 by:授客 QQ:1033553122 1. 测试环境 1 2. 基础概念 1 3. 常用Annotation 1 4. 运行环境配置 3 maven配置 ...
- RabbitMQ ——四种ExChange及完整示例
RabbitMQ常用的Exchange Type有fanout.direct.topic.headers这四种,下面分别进行介绍. 这四种类的exchange分别有以下一些属性,分别是: name:名 ...
- mysql中模糊查询的四种用法介绍
下面介绍mysql中模糊查询的四种用法: 1,%:表示任意0个或多个字符.可匹配任意类型和长度的字符,有些情况下若是中文,请使用两个百分号(%%)表示. 比如 SELECT * FROM [user] ...
- php 冒泡 快速 选择 插入算法 四种基本算法
php四种基础算法:冒泡,选择,插入和快速排序法 来源:PHP100中文网 | 时间:2013-10-29 15:24:57 | 阅读数:120854 [导读] 许多人都说 算法是程序的核心,一个程序 ...
随机推荐
- java在线聊天项目1.1版 ——开启多个客户端,分别实现注册和登录功能,使用客户端与服务端信息request机制,重构线程,将单独的登录和注册线程合并
实现效果图: eclipse项目中初步整合之前的各个客户端和服务端的窗口与工具类,效果如下图: 已将注册服务器线程RegServer功能放到LoginServer中,使用客户端与服务端的request ...
- ASIHTTPRequest简单学习
ASIHTTPRequest框架是优秀的第三方Objective-C的HTTP框架,支持Mac OS X和iOS下的HTTP开发. 一.ASIHTTPRequest框架的安装和配置 (1)首先要在项目 ...
- XCode和Cocoa在开发中使用第三方dylib示例
XCode和Cocoa在开发中使用第三方dylib示例 www.educity.cn 发布者:yukowang 来源:网络转载 发布日期:2014年06月13日 XCode和Co ...
- perl学习之五:列表和数组
列表及其形式 数组 数组的赋值 数组的读取 数组片段 数组函数 二维数组简介 总结 1.列表形式: 1.(item1,item2,...) 2.qw(item1 item2 item3 ...) 3. ...
- Python9-装饰器进阶-day12
wapers方法 def wahaha(): ''' sljfldsjflds :return: ''' print('娃哈哈') print(wahaha.__name__) #查看字符串格式的函数 ...
- gnu make规则记录
1. $(shell CMD) 名称: 执行 shell 命令函数 功能: 在新的 shell 中执行 CMD 命令 返回值: CMD 在 shell 中执行的结果 例如:PLATFORM=$(she ...
- tomcat启动后 404 页面无法访问
如果修改端口后还不能访问,先关闭tomcat, 在bin目录下命令 ./shutdown.sh 找到80进程 netstat -an | grep 80 杀死80进程 ps -ef | grep h ...
- Java-使用哈希码比较对象的值
在一些特殊的情况下使用 package com.tj; import java.io.File; public class MyHash { public static void main(Strin ...
- Python第三方库之openpyxl(7)
Python第三方库之openpyxl(7) 散点图 散点或xy图表类似于一些折线图.主要的区别在于,一个系列的值被绘制在另一个值上.当值未排序时,这是有用的. from openpyxl impor ...
- Java程序员---技能树
计算机基础: 比如网络相关的知识. 其中就包含了 TCP 协议,它和 UDP 的差异.需要理解 TCP 三次握手的含义,拆.粘包等问题. 当然上层最常见的 HTTP 也需要了解,甚至是熟悉. 这块推荐 ...