.Net CoreRabbitMQ基本使用

队列模式

https://www.rabbitmq.com/getstarted.html

e17efa51583a6e8b20a94cfec747ded3.png

42da86fbbf3d8487ee88c6f00b2f5533.png

对以上几种模式进行简要分类,可以分成如下三类(RPC暂不考虑)

  • 简单队列模式,单发单收,一对一模式

  • Worker模式,单发多收(一个消息一个接收者,多个消息多个接收者),一对多模式

  • 发布订阅模式,包括发布订阅、路由、通配符模式,这三种只是交换机类型不同

简单队列模式

队列作为中间件,在生产者和消费者中间承担消息传递的通道

1fb04b9f33cc7d3299d83a33893b97fb.png

创建两个控制台项目RabbitMQDemo.Basic.Producer和RabbitMQDemo.Basic.Consumer并安装Nuget包以支持对RabbitMQ操作

install-package rabbitmq.client

生产者代码

  • 通过IConnectionFactory,IConnection和IModel来创建连接和信道。IConnection实例对象负责与RabbitMQ 服务端的连接,信道是在这连接基础上创建虚拟连接,通过复用来减少性能开销且便于管理。

  • 通过QueueDeclare方法创建消息队列,设置消息队列本身的一些属性信息。

  • 发送消息时调用BasicPublish来发送消息,通过exchange和routingkey参数满足大部分匹配消息队列的场景。

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var queueName = "helloworld";channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);while (true){Console.WriteLine("消息内容(exit退出):");var message = Console.ReadLine();if (message.Trim().ToLower() == "exit"){break;}var body = Encoding.UTF8.GetBytes(message);channel.BasicPublish(exchange: "", routingKey: queueName, basicProperties: null, body: body);Console.WriteLine("消息内容发送完毕:" + message);}}
}

消费者代码

  • 消费端同样先创建连接和信道

  • 同样在消费端也会进行队列声明,以确保当生产者并未创建或是手动没有创建情况下不会出现队列不存在的异常。

  • 定义一个EventingBasicConsumer对象的消费者,然后定义接收事件,输出从消息队列中接收的数据,

  • 最后调用BasicConsume方法来启动消费者监听

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var queueName = "helloworld";channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);var consumer = new EventingBasicConsumer(channel);consumer.Received += (model, ea) =>{var message = ea.Body;Console.WriteLine("接收到信息为:" + Encoding.UTF8.GetString(message.ToArray()));((EventingBasicConsumer)model).Model.BasicAck(ea.DeliveryTag, false);};channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);Console.ReadKey();}
}

执行过程

运行代码,可以在管理页面中看到队列声明好了

89bece532dda5d200b42df9fc54d851d.png

生产者发送消息,经RabbitMQ,消费者端接收消息

76ad59668f800aee89e1749302347d7b.png

Worker模式

简单队列模式下,当多开消费者时,便演化到了Worker模式,这种情况下不再考虑基础的怎么用,而是要如何协调多个消费者的工作。

34a5e486dc25a169ce56fcae524e7e30.png

与简单队列模式类似再建立三个控制台项目RabbitMQDemo.Worker.Producer和RabbitMQDemo.Worker.ConsumerA和RabbitMQDemo.Worker.ConsumerB并安装Nuget,抄袭第一部分代码,更改个队列名字,然后直接跑起来,其实是一样的消费模式。

基本使用

当发送多条消息,两个消费者都能够展示消息,并且,其中的消息总是只会被一个消费者所拥有,默认分配方式是轮询。

968edb54436ec58ea1551b33dd63bb3c.png

消费能力

现在,思考下如何能够各消费者的消费能力,来消费消息,这更侧重于消费端了。

将ConsumerA、B在消费时各增加Sleep 1秒和10秒,以区分消费能力的不同。当再次发送消息时,因消费端出现着处理消息耗时的不同,展示数据的时间也不同,但是消息的分配却没有变化。

70119cc1b97f4466509212f8d5bf6bab.png

需要进一步均衡的分配任务,按照消费能力高的分配多,消费能力低的分配少。

d3a38e2427417e19cd1c0df983daeb44.png

当消费能力不同时,可以将消费的任务均衡分配,这样来使得整体消费端的能力充分发挥。

负载能力

RabbitMQ提供了一个属性可以设置各消费端的能力,以此可以根据能力分配消息。

在消费端代码中更改下Qos(quality-of-service),即消费端最多接收未被ack的消息个数,举个例子:

  • 如果输入1,当消费端接收到一个消息,但是没有应答(活还在干别再分配任务了),则消费端不会收到下一个消息,消息只会在队列中阻塞。

  • 如果输入3,那么可以最多有3个消息不应答(可以同时干三个活),如果到达了3个,则当有分配给这个消费端的消息时只会在阻塞到队列中,消费端不会接收到消息。
    对ConsumerA、B分别设置值prefetchCount为10和1。

ConsumerA: channel.BasicQos(0, prefetchCount:10, false);
ConsumerB: channel.BasicQos(0, prefetchCount:1, false);

当再次发送消息时,会因为因为A、B两端的消费能力不同而出现消息聚集侧重于一端
生产者发送一堆消息,两个消费者自身的消费能力不同,设置的能够消费的容量不同,这样分配得到的消息数量也不同。

f1bb3ca4575d88461971d67354060352.png

生产者代码

此处并未做任何大的改变,只是将队列名更改为当前模式的名字以示区分。

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var queueName = "worker";channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);while (true){Console.WriteLine("消息内容(exit退出):");var message = Console.ReadLine();if (message.Trim().ToLower() == "exit"){break;}var body = Encoding.UTF8.GetBytes(message);channel.BasicPublish(exchange: "", routingKey: queueName, basicProperties: null, body: body);Console.WriteLine("消息内容发送完毕:" + message);}}
}

消费者代码

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var queueName = "worker";channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);channel.BasicQos(0, 1, false);var consumer = new EventingBasicConsumer(channel);consumer.Received += (model, ea) =>{Thread.Sleep(10000);var message = ea.Body;Console.WriteLine("接收到信息为:" + Encoding.UTF8.GetString(message.ToArray()) + DateTime.Now.ToString("hh:mm:ss"));((EventingBasicConsumer)model).Model.BasicAck(ea.DeliveryTag, false);};channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);Console.ReadKey();}
}

Exchange模式

发布订阅,路由和通配符这三种可以算为一种模式,区别仅仅是交互机类型不同。

  • 发布订阅模式:使用fanout类型交换机

  • 路由模式:使用direct类型交换机

  • 通配符模式:使用topic类型交换机

03f6ec665f34a43588f7ba7c5fd190dd.png

生产者将消息及RoutingKey发送到指定交换机,消费者创建各自的消息队列并绑定到交换机,交换机根据路由规则匹配生产者发送的RoutingKey转发消息到相应队列中,其本身不存储消息。

Exchange类型

简要介绍这几种交换机类型,本身只是对路由规则的匹配方式不同。

  • fanout: 把所有发送到该交换机的消息路由到所有与该交换机绑定的队列中。

  • direct: 把消息路由到那些BindingKey和RoutingKey完全匹配的队列中。

  • topic: 把消息路由到那些BindingKey和RoutingKey相匹配的队列中。

  • header: 不依赖RoutingKey的匹配规则,而是根据消息内容中的headers属性匹配(性能差,不实用,使用少)。

注意:BindingKey为交换机和队列绑定时指定的RoutingKey,发送消息时也会给定一个RoutingKey,两者会按照交换机类型的不同而匹配

发布订阅模式(fanout)

fanout模式下会把所有发送到该交换机的消息路由到所有与该交换机绑定的队列中。

f9f99aeaea5481bc946d3e81f12416fc.png

当生产者发送消息到指定交换机,该交换机会将消息路由到绑定的Queue1和Queue2,两个队列分别转发给其下绑定的消费者,从单个队列视角来看,便是Worker模式了。

生产者代码

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var exchangeName = "publishsubscribe_exchange";channel.ExchangeDeclare(exchange: exchangeName, type: "fanout");while (true){Console.WriteLine("消息内容(exit退出):");var message = Console.ReadLine();if (message.Trim().ToLower() == "exit"){break;}var body = Encoding.UTF8.GetBytes(message);channel.BasicPublish(exchange: exchangeName, routingKey: "", basicProperties: null, body: body);Console.WriteLine("消息内容发送完毕:" + message);}}
}

如上生产者端在worker模式的基础上,改动了几处

  • 消息队列声明变成了交换机声明,其类型为fanout。

  • 发送消息时由指定相应的队列名改成了空,而指定了交换机名称。

  • routingKey留空,fanout无需关心routingKey。

消费者代码

此处设置两个queue,分别为publishsubscribe_exchange_worker_1和publishsubscribe_exchange_worker_2

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var exchangeName = "publishsubscribe_exchange";channel.ExchangeDeclare(exchange: exchangeName, type: "fanout");var queueName = exchangeName + "_worker_1";channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: "");channel.BasicQos(0, 10, false);var consumer = new EventingBasicConsumer(channel);consumer.Received += (model, ea) =>{Thread.Sleep(1000);var message = ea.Body;Console.WriteLine("接收到信息为:" + Encoding.UTF8.GetString(message.ToArray()) + DateTime.Now.ToString("hh:mm:ss"));((EventingBasicConsumer)model).Model.BasicAck(ea.DeliveryTag, false);};channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);Console.ReadKey();}
}

与Worker的消费者端相比,代码上也做了些调整,其余是保持一致的。

  • 增加了交换机名称并声明了交换机且类型为fanout,这和生产者端保持一致了

  • 将队列名和交换机名进行了绑定

  • routingKey留空,fanout无需关心routingKey。

执行过程

当启动生产者端和消费者端时,交换机和两个队列都声明完毕

4ba746342dc4483debf4c776822a98fa.png

同时,点入交换机中,可以看到该交换机下绑定了两个队列

595fc71b09b585b9c53ddcc5860f4afe.png

这样一来,当有消息到达交换机,交换机可以根据消息名来路由到相应的队列。因此处设置的routekey是空的,两个队列绑定时用的routekey也是空的,因此两个队列都符合路由规则,则消息会同时存在于两个队列中。

5a0999c2fd4b3d769872f3beb11b8a3b.png

路由模式(direct)

direct模式下会把消息路由到那些BindingKey和RoutingKey完全匹配的队列中。

e8f689fd7239bdbebc9fcad0455637a9.png

当生产者发送了一个消息且发送的RoutingKey为Warning时,交换机会根据该RoutingKey匹配并转发消息到Queue1和Queue2,两个队列都满足了路由规则,当RoutingKey为Info是,仅Queue2满足,则将消息转发给Queue2。

生产者代码

生产者端在worker模式的基础上,只需改动几处

  • 交换机类型从fanout变更为direct

  • 生产者发送消息时指定RoutingKey,原先是留空

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var exchangeName = "routing_exchange";channel.ExchangeDeclare(exchange: exchangeName, type: "direct");while (true){Console.WriteLine("消息RoutingKey(warning or info):");var routingKey = Console.ReadLine();Console.WriteLine("消息内容(exit退出):");var message = Console.ReadLine();if (message.Trim().ToLower() == "exit"){break;}var body = Encoding.UTF8.GetBytes(message);channel.BasicPublish(exchange: exchangeName, routingKey: routingKey, basicProperties: null, body: body);Console.WriteLine("消息内容发送完毕:" + message);}}
}

消费者代码

接收者端在发布订阅模式的基础上增加了交换机和队列时绑定的key,用于交换机路由规则时选择队列。

be1ca82fa4011dcca448321c1c4025ba.png

如下为Queue2下的消费者,为Queue2设置了info和warning两个RoutingKey用于交换机和队列绑定。

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var exchangeName = "routing_exchange";channel.ExchangeDeclare(exchange: exchangeName, type: "direct");var queueName = exchangeName + "_worker_1";channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);var routingKey1 = "warning";channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: routingKey1);var routingKey2 = "info";channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: routingKey2);channel.BasicQos(0, 10, false);var consumer = new EventingBasicConsumer(channel);consumer.Received += (model, ea) =>{Thread.Sleep(1000);var message = ea.Body;Console.WriteLine("接收到信息为:" + Encoding.UTF8.GetString(message.ToArray()) + DateTime.Now.ToString("hh:mm:ss"));((EventingBasicConsumer)model).Model.BasicAck(ea.DeliveryTag, false);};channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);Console.ReadKey();}
}

执行过程

运行代码,交换机、队列及两者的绑定先完成,可以在管理页面中看到声明的信息。

8ba86c06505b540cac181165340cc908.png

当生产者发送消息且RoutingKey为warning,两个队列都满足路由条件接收到消息,两个消费者都展示了消息。

当发送消息且RoutingKey为info,queue2队列满足路由条件接收了消息,一个消费者展示了消息。

1eb834b606dfd67babd5f8635035a925.png

通配符模式(topic)

topic模式会把消息路由到那些BindingKey和RoutingKey相匹配的队列中。

33e64d2221f3ac851a19e2b48355a2b5.png

topic类型与direct类型相似,但匹配规则上有所不同,direct需要完全匹配,topic可以设置通配符以达到局部匹配即可满足。

和direct不同的是,topic设定的RoutingKey(不论是BindingKey还是RoutingKey)都需要为带"."的字符串。比如a.b、c.d.e、fff.gggg.hhhh等,最多为 255 个字节。

在交换机和队列绑定时,给定的RoutingKey可以依照如下来设置。

  • #:匹配0~N个单词

  • *:匹配1个单词
    举例说明下,比如两个RoutingKey分别为#.created和index.,当生产者发送消息时给定的RoutingKey为a.created、aa.created或是b.created等都满足#.created的规则,index.a、index.b或index.c等都满足index.的规则。

生产者代码

在路由模式的基础上更改交换机类型为topic

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var exchangeName = "topics_exchange";channel.ExchangeDeclare(exchange: exchangeName, type: "topic");while (true){Console.WriteLine("消息RoutingKey:");var routingKey = Console.ReadLine();Console.WriteLine("消息内容(exit退出):");var message = Console.ReadLine();if (message.Trim().ToLower() == "exit"){break;}var body = Encoding.UTF8.GetBytes(message);channel.BasicPublish(exchange: exchangeName, routingKey: routingKey, basicProperties: null, body: body);Console.WriteLine("消息内容发送完毕:" + message);}}
}

消费者代码

接收者端在路由模式的基础上更改了交换机和队列绑定的key,可以方便满足多种情况下的需要。

2bba2aeb60522f5e83e41e3c8b0490d5.png

如下为Queue2下的消费者,为Queue2设置了index.*和#.created.#两个RoutingKey用于交换机和队列绑定。

var connFactory = new ConnectionFactory
{HostName = "xxx.xxx.xxx.xxx",Port = 5672,UserName = "rabbitmqdemo",Password = "rabbitmqdemo@test",VirtualHost = "rabbitmqdemo"};
using (var conn = connFactory.CreateConnection())
{using (var channel = conn.CreateModel()){var exchangeName = "topics_exchange";channel.ExchangeDeclare(exchange: exchangeName, type: "topic");var queueName = exchangeName + "_worker_2";channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);var routingKey1 = "index.*";channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: routingKey1);var routingKey2 = "#.created.#";channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: routingKey2);channel.BasicQos(0, 1, false);var consumer = new EventingBasicConsumer(channel);consumer.Received += (model, ea) =>{Thread.Sleep(10000);var message = ea.Body;Console.WriteLine("接收到信息为:" + Encoding.UTF8.GetString(message.ToArray()) + DateTime.Now.ToString("hh:mm:ss"));((EventingBasicConsumer)model).Model.BasicAck(ea.DeliveryTag, false);};channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);Console.ReadKey();}
}

执行过程

运行代码,交换机、队列及绑定关系,相应RoutingKey都在管理页面中展示

1188845f4cb1d8940b75bcf89edbf4e5.png

当生产者发送消息且RoutingKey为#.created,两个队列都满足路由条件接收到消息,两个消费者都展示了消息。

当生产者发送消息且RoutingKey为#.created.#,queue2队列满足了路由条件接收了消息,一个消费者展示了消息。

8b635e0dba085c80b3a9b3b897cf3217.png

总结

对于在生产者和消费者间解耦,完成异步协作,消息队列可太方便了,暂不深入考虑三者间如何可靠传输,仅看消息队列提供的多种交换机模式,很大程度上满足实际使用中需要用到的很多功能。

2022-07-29,望技术有成后能回来看见自己的脚步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283105.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【微信小程序】:实现轮播图3秒滚动

wxml模板&#xff1a;&#xff08;数据一维数组&#xff09; <scroll-view scroll-y"true"><swiper autoplay"auto" interval"3000" duration"500"><block wx:for"{{home_pics}}" wx:for-index"index…

Linux包系列的知识(附:Ubuntu16.04升级到18.04的案例)

Linux基础&#xff1a;https://www.cnblogs.com/dunitian/p/4822808.html#linux 之前看到朋友还动不动 apt-get update upgrade&#xff0c;就很纳闷&#xff0c;后来发现原来他只是知道这个更新命令却不知其意&#xff0c;所以每次安装个包就把所有apt-get的常用清除更新命令打…

java获取tomcat目录结构_Tomcat目录结构详解

Tomcat目录结构图如下&#xff1a;bin目录存放一些可执行的二进制文件&#xff0c;.sh结尾的为linux下执行命令&#xff0c;.bat结尾的为windows下执行命令。catalina.sh&#xff1a;真正启动tomcat文件&#xff0c;可以在里面设置jvm参数。startup.sh&#xff1a;启动tomcat(需…

智慧农业物联网云平台方案

2019独角兽企业重金招聘Python工程师标准>>> 多比智慧农业物联网云平台解决方案结合了最先进的物联网、云计算、传感器、自动控制等, 在浏览器或手机客户端实时显示大棚、大田、温室等温度、湿度、PH值、光强度、CO2&#xff0c;或作为自动控制的参变量参与到自动控…

Linux下汇编语言学习笔记65 ---

这是17年暑假学习Linux汇编语言的笔记记录&#xff0c;参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译《汇编语言基于Linux环境》的书&#xff0c;喜欢看原版书的同学可以看《Assembly Language Step-By-Setp:Programming with Linux 3rd Edition》&#xff0c;非常感谢该…

python:继承日志模块生成自定义日志

1 继承日志模块生成自定义日志 from __future__ import absolute_importimport os import sys import time import datetime import logging import logging.handlers import tempfileDATE_FORMAT %Y-%m-%d %H:%M:%Sdef create_logfile():if SYAPI_LOG_TEST in os.environ:val…

使用JDBC获取Oracle连接时报错

The Network Adapter could not establish the connection 网络适配器不能创建连接 作为初学者的来说&#xff0c;这个问题让我找了好多次&#xff0c;每次重新开启电脑时就可以正常获取连接&#xff0c;过了一会儿&#xff0c;自己不知道做了什么就会又报错&#xff0c;…

.Net CoreRabbitMQ消息转发可靠机制(上)

前言生产者发送消息到了队列&#xff0c;队列推送数据给了消费者&#xff0c;这里存在一些问题需要思考下生产者如何确保消息一定投递到了队列中RabbitMQ 丢失了消息(下文暂不涉及这块)队列如何确保消费者收到了消息呢生产者可靠发送执行流程当生产者将消息发送出去后&#xff…

堆栈的理解

Java把内存分成两种&#xff0c;一种叫做栈内存&#xff0c;一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时&#xff0c;java就在栈中为这个变量分配内存空间&#xff0c;当超过变量的作用域后&am…

MySQL安装时出现的问题

mysql正常安装结束之后需要连接你所安装的数据库的时候出现下面的错误: Client does not support authentication protocol requested by server;consider upgrading mysql client 解决方法:启动:mysql 8.0 command line client 之后输入下面的代码即可。use mysql;ALTER USER…

一个java文件中可包含多个main方法

java中的main方法是java应用程序的入口&#xff0c;java程序在运行时&#xff0c;首先调用执行main方法。但并不是说java中只能有一个main方法&#xff0c;不同类中都可以包含main方法。当JVM进行编译时&#xff0c;会提示选择其中一个main方法作为编译的入口。 转载于:https:/…

linux java性能监控工具_Linux实时监控工具Nmon使用

官网&#xff1a;http://nmon.sourceforge.net/pmwiki.php?nMain.H网络解压&#xff1a;#chmod ux nmon_x86_64_sles11#chmod 777 nmon_x86_64_sles11版本不同&#xff0c;对应文件也不同启动&#xff1a;# ./nmon_x86_64_sles11按C显示CPU信息&#xff0c;再按一次C关闭按M显…

【SRM-05 B】无题?

Description 有一个拥有n个城市的国家。这个国家由n-1条边连接起来。有一天国家发生叛乱。叛军已占领了一些城市。如果叛军占领的城市中&#xff0c;存在两个城市之间有边直接相连&#xff0c;则称这种情况是坏的。现在并不知道叛军占领了那些城市&#xff0c;问有多少种情况是…

MapReduce 2 中一些基础数据类型

1. LongWritable, IntWritable, Text 均是 Hadoop 中实现的用于封装 Java 数据类型的类&#xff0c;这些类实现了WritableComparable接口&#xff0c;都能够被串行化从而便于在分布式环境中进行数据交换&#xff0c;以及进行大小比较。你可以将它们分别视为long,int,String 的替…

分享一些 Java 后端的个人干货

学习 Java 也有了不少时间&#xff0c;入 Java 后台的坑也有了一段时日。这段时间里&#xff0c;听过许多前辈的经验与分享&#xff0c;也看过许多大佬的文章和作品。找了个时间整理和总结了一下我个人到目前为止一路以来的听到看到或者自己感悟到的干货。 这篇文章可能更多的是…

.NET MAUI实战 Routing

1.详情本章继续分享.NET MAUI中的路由&#xff0c;这个概念依旧是在Prism里存在过的概念。如果使用过Prism框架的小伙伴使用该机制上手速度是非常快的。接下来一起来看看什么是路由。.NET 多平台应用 UI (.NET MAUI) Shell 包含基于 URI 的导航体验&#xff0c;该体验使用路由导…

分享Web应用运行的细节问题:预编译提高网站性能、跟踪用户习惯和解决线程同步...

在这个文章里&#xff0c;我将分享一下在iOpenWorks.com这个网站试运行中碰到的若干问题和解决方案&#xff0c;这些问题包含了&#xff1a;&#xff08;1&#xff09;如何通过ASP.NET MVC预编译提高性能&#xff1b;&#xff08;2&#xff09;如何知道网站在运行中&#xff0c…

mondrain配置mysql_mondrian 4.7 源码部署(示例代码)

mondrian是一个开源的数据分析工程, 网上有关mondrian3.X的源码部署比较多, 有关4.X的部署较少. 目前官方推荐使用的时mondrian3.7的修订版, 可以再github上下载到最近更新维护的mondrian-master, 下载下来后基本上只需要按部就班的使用maven build一下就可以正常使用了, 如有问…

腾讯云DevOps技术揭秘:新时代运维重器Tencent Hub最佳实践

随着云计算和容器技术的发展以及微服务架构的兴起&#xff0c;服务能够实现细粒度的部署&#xff0c;维护和伸缩。在使开发人员能快速开发的同时&#xff0c;这些技术也给系统和应用的运维带来了更大的挑战。DevOps理念也应运而生&#xff0c;强调研发和运维的流程及工具的自动…

关于内存的划分和传引用传参数的区别

1.堆 2.栈 空间较小 3.全局 4.代码段 要搞懂每个区域放什么东西 堆 复杂类型栈 变量静 代复杂类型对象没有指针指的时候&#xff0c;堆中的内容垃圾回收1函数传对象的时候&#xff0c;其实是首先在堆里面开一个内存放对象属性内容&#xff0c;在栈里面开一个内存放一个变量&…