Why binary memory usage grows up on rabbitmq - memory

How to find why binary memory usage grows up (depend on connections count)
i used rabbitmqctl eval '[garbage_collect(P) || P <- processes()].'
but on stats
root#Broker1:~# /usr/sbin/rabbitmqctl status | grep binary
{binary,6709401976},
root#Broker1:~# free -m
total used free shared buffers cached
Mem: 16084 4147 11937 22 161 1550
-/+ buffers/cache: 2435 13649
Swap: 4183 0 4183
root#Broker2:~# /usr/sbin/rabbitmqctl status
Status of node rabbit#Broker2 ...
[{pid,515},
{running_applications,
[{rabbitmq_web_mqtt,"RabbitMQ MQTT-over-WebSockets adapter","3.6.9"},
{rabbitmq_mqtt,"RabbitMQ MQTT Adapter","3.6.9"},
{rabbitmq_web_stomp,"Rabbit WEB-STOMP - WebSockets to Stomp adapter",
"3.6.9"},
{rabbitmq_stomp,"RabbitMQ STOMP plugin","3.6.9"},
{rabbitmq_management,"RabbitMQ Management Console","3.6.9"},
{rabbitmq_web_dispatch,"RabbitMQ Web Dispatcher","3.6.9"},
{rabbitmq_management_agent,"RabbitMQ Management Agent","3.6.9"},
{rabbit,"RabbitMQ","3.6.9"},
{os_mon,"CPO CXC 138 46","2.3"},
{cowboy,"Small, fast, modular HTTP server.","1.0.4"},
{ranch,"Socket acceptor pool for TCP protocols.","1.3.0"},
{ssl,"Erlang/OTP SSL application","5.3.6"},
{public_key,"Public key infrastructure","0.22.1"},
{cowlib,"Support library for manipulating Web protocols.","1.0.2"},
{crypto,"CRYPTO","3.4.1"},
{amqp_client,"RabbitMQ AMQP Client","3.6.9"},
{rabbit_common,
"Modules shared by rabbitmq-server and rabbitmq-erlang-client",
"3.6.9"},
{inets,"INETS CXC 138 49","5.10.3"},
{mnesia,"MNESIA CXC 138 12","4.12.3"},
{compiler,"ERTS CXC 138 10","5.0.2"},
{sockjs,"SockJS","0.3.4"},
{xmerl,"XML parser","1.3.7"},
{syntax_tools,"Syntax tools","1.6.16"},
{asn1,"The Erlang ASN1 compiler version 3.0.2","3.0.2"},
{sasl,"SASL CXC 138 11","2.4.1"},
{stdlib,"ERTS CXC 138 10","2.2"},
{kernel,"ERTS CXC 138 10","3.0.3"}]},
{os,{unix,linux}},
{erlang_version,
"Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:10:10] [async-threads:160] [kernel-poll:true]\n"},
{memory,
[{total,3874499056},
{connection_readers,598416},
{connection_writers,139368},
{connection_channels,774752},
{connection_other,176069624},
{queue_procs,36524016},
{queue_slave_procs,0},
{plugins,80184008},
{other_proc,0},
{mnesia,48042392},
{metrics,7245632},
{mgmt_db,74517952},
{msg_index,1751384},
{other_ets,14801208},
{binary,3379246384},
{code,28040419},
{atom,1000601},
{other_system,33438764}]},
{alarms,[]},
{listeners,
[{clustering,2000,"::"},
{amqp,5672,"::"},
{http,15672,"::"},
{stomp,61613,"::"},
{'http/web-stomp',15674,"::"},
{mqtt,1883,"::"},
{'http/web-mqtt',15675,"::"}]},
{vm_memory_high_watermark,{absolute,"12288MiB"}},
{vm_memory_limit,12884901888},
{disk_free_limit,50000000},
{disk_free,91027542016},
{file_descriptors,
[{total_limit,65435},
{total_used,9214},
{sockets_limit,58889},
{sockets_used,9002}]},
{processes,[{limit,1048576},{used,42726}]},
{run_queue,0},
{uptime,82689},
{kernel,{net_ticktime,60}}
]
total used free shared buffers cached
Mem: 16084 4517 11567 27 160 2487
-/+ buffers/cache: 1869 14215
Swap: 4189 0 4189
my memory is free!!! but after memory grows up connection to server very slow!!
Cluster: rabbit#Broker1 (change)
RabbitMQ 3.6.9, Erlang 17.3
rabbitmq.config
[
{log_levels, [{connection, none},{channel,none}]},
{file_descriptors, [{total_limit, 65000}]},
{kernel, [
{log_levels, [{connection, none},{channel,none}]},
{inet_dist_listen_min, 2000},
{inet_dist_listen_max, 65000}
]},
{rabbit, [
{loopback_users, []},
{vm_memory_high_watermark, {absolute, "12288MiB"}},
{vm_memory_high_watermark_paging_ratio, 0.60},
{tcp_listeners, [5672]},
{log_levels, [{connection, none},{channel,none}]},
{heartbeat, 10000}
]},
{rabbitmq_mqtt, [
{log_levels, [{connection, none},{channel,none}]},
{default_user, <<"guest">>},
{default_pass, <<"guest">>},
{allow_anonymous, false},
{vhost, <<"test">>},
{exchange, <<"amq.topic">>},
{subscription_ttl, 180000},
{prefetch, 10},
{tcp_listeners, [1883]},
{tcp_listen_options, [
binary,
{packet, raw},
{reuseaddr, false},
{backlog, 8192},
{nodelay, true}
]}
]},
{rabbitmq_stomp, [
{default_vhost, <<"test">>},
{exchange, <<"amq.topic">>},
{tcp_listen_options, [
{backlog, 128},
{nodelay, true}
]}
]}
].

I believe your implementation is incorrect. I am using RabbitMQ for live notifications on a live site and it works without any problems. I had a concurrent clients of upto 3500, but a 8GB RAM instance worked well enough.
So what you must exactly do?
You have not specified your use case. But as per rabbitmq documentations, the memory grow only when you have lot of undelivered messages. Lot of undelivered messages can cause the disk space/ram space depending on how your clients connect to you.
If you have one broker per user, that is very costly. Instead, have one exchange per user, if your user is going to receive same message on multiple ends, eg, phone and a web at the same time. In that case, use a fanout exchange and make the user subscribe to it. Always enable auto delete flag when messages in exchanges reaches zero. Use non-persistant exchanges; but keep your brokers less.
If you are sure that one user is going to connect on only one device and not more than that, you don't need a fanout exchange, instead, build one queue per user. Still, enable auto delete flags.
Keeping less number of brokers and handling requests with exchanges or queues are efficient.
One more option to use if it suits your use case is message expiry. Let's say you are using RabbitMQ to send notifications to your users. You will however write them to a database and to make a notification, you may send it via RMQ. If the user is not online, may be for about 10 min or more, the expiry is a nice option. Because it is however live messaging, not persistent messaging. He is going to read it from database
NOTE: I can help you in detail if you explain your usecase.
Read this to understand more: https://www.rabbitmq.com/blog/2014/10/30/understanding-memory-use-with-rabbitmq-3-4/
As said here,
All binary use is due to messages in queues.
Best way is to use Non Persistent queues and auto-expiry messages!
Note that "In memory" and "Persistent" messages are not antonyms here:
a non-persistent message can be paged out under memory pressure and a
persistent message can be in memory too.
That explains your issue!

Related

Do I need to start multiple server-side workers for just a handful of ZeroMQ clients?

I am using Chumak in erlang, opening a ROUTER socket.
I have a handful (4 or so) clients that use the Python zmq library to send REQ requests to this server.
Things work fine most of the time, but sometimes a client will have disconnect issues (reconnecting automatically is in the client code, and it works). I've found that when an error occurs in one client connection, it seems to move on to others as well, and I get a lot of
** {{noproc,{gen_server,call,[<0.31596.16>,incomming_queue_out]}},
on the server.
On the server side, I'm just opening one chumak socket and looping:
{ok, Sock} = chumak:socket( router ),
{ok, _} = chumak:bind( Sock, tcp, "0.0.0.0", ?PORT ),
spawn_link( fun() -> loop( Sock ) end ),
...
loop( CmdSock ) ->
{ok, [Identity, <<>>, Data]} = chumak:recv_multipart( Sock ),
...
The ZeroMQ docs seem to imply that one listening socket is enough unless I have many clients.
Do I misunderstand them?
No, there is no need to increase number of Socket instances
Abstractions are great to reduce a need to understand all the details under the hood for a typical user. That ease of life stops whenever such user has to go into performance tuning or debugging incidents.
Let's step in this way:
- unless some mastodon beast sized data payloads are to get moved through, there is quite enough to have a single ROUTER-AccessPoint into a Socket-instance, for say tens, hundreds, thousands of REQ-AccessPoints on the client side(s).
- yet, such numbers will increase the performance envelope requirements for the ROUTER-side Context-instance, so as to remain capable of handling all the Scalable Formal Communication Archetype ( pre-scribed ) handling, so as to all happen in due time and fair fashion.
This means, one can soon realise benefits from spawning Context-instances with more than its initial default solo-thread + in all my high-performance setups I advocate for using zmq.AFFINITY mappings, so as to squeeze indeed a max performance on highest-priority Socket-instances, whereas leaving non-critical resources sharing a common sub-set of the Context-instance's IO-thread-pool.
Next comes RAM
Yes, the toys occupy memory.
Check all the .{RCV|SND}BUF, .MAXMSGSIZE, .{SND|RCV}HWM, .BACKLOG, .CONFLATE
Next comes LINK-MANAGEMENT
Do not hesitate to optimise .IMMEDIATE, .{RCV|SND}BUF, .RECONNECT_IVL, .RECONNECT_IVL_MAX, .TCP_KEEPALIVE, .TCP_KEEPALIVE_CNT, .TCP_KEEPALIVE_INTVL, .TCP_KEEPALIVE_IDLE
Always set .LINGER right upon instantiations, as drop-outs cease to be lethal.
Next may come a few defensive and performance helper tools:
.PROBE_ROUTER, .TCP_ACCEPT_FILTER, .TOS, .HANDSHAKE_IVL
Next step?
If no memory-related troubles remain in the game and once mentioning reconnections, my suspect would be to rather go and setup .IMMEDIATE + possibly let ROUTER benefit from explicit PROBE_ROUTER signalling.

How do I prevent message flooding with open_port?

I'm trying to read a 100gb file through stdin one line at a time using
Port = open_port({fd, 0, 1}, [in, binary, {line, 4096}]),
but this floods my system with messages until I run out of ram. Is there a away to make it like {active, once} with ports? There is also io:get_line() but I was wondering if this could work.
No, there is not flow control over ports so if you can't process fast enough you should use another method of processing. You can set binary mode on STDIN using
ok = io:setopts(standard_io, [binary]),
and then you can read it using file:read_line(standard_io) if you are using version 17 or newer (there was performance impacting bug).

ESP8266 with NodeMCU firmware: receiving empty messages with secured MQTT subscription

I'm trying to securely connect ESP8266 to MQTT Azure Protocol Gateway broker on cloud or local computer (tried both) like this (not important - connection works correctly):
m = mqtt.Client("{deviceId}", "3600", "{iotHub}/{deviceId}", "{SASToken}")
...
m:on("message", function(conn, top, data)
print(data) -- EMPTY string here!
end)
...
m:connect({IP}, 8883, 1, function(conn)
m:subscribe("devices/{deviceId}/messages/devicebound/#", 1, function(conn)
...
end)
end)
ESP connects to server and handshake is completed successfully. When I publish some data, I can read it on server properly, it is OK. I subscribe to topic without problems. When I send data in cloud-to-device message from server to ESP to subscribed topic, 'on message' event is called but data attribute passed to the function is EMPTY string.
I'm using latest NodeMCU master build based on 1.4.0 SDK (tried both integer and float version). I can't turn on debugging, because i don't have NodeMCU developer yet.
I tried following:
dev version - not help
free memory up to 32kB - not help
captured packets with WireShark: packets contain encrypted data with some lenght, so it is not empty and packet size is less than 2kB buffer size
Can someone please advise me where could be a problem or how to debug it for more info? I would approciate any ideas. Thank you.
EDIT:
I've tried debug mode, and there is nothing interesting on output:
enter mqtt_socket_received.
MQTT_DATA: type: 3, qos: 0, msg_id: 0, pending_id: 0
enter deliver_publish.
string
userdata: 3fff3e88
devices/ESP/messages/devicebound
<- here should be printed data
On
leave deliver_publish.
receive, queue size: 0
leave mqtt_socket_received.
enter mqtt_socket_timer.
timer, queue size: 0
keep_alive_tick: 71
leave mqtt_socket_timer.
enter mqtt_socket_received.
MQTT_DATA: type: 7, qos: 1, msg_id: 8813, pending_id: 0
receive, queue size: 0
leave mqtt_socket_received.
This may be stupid but sometimes lua expects specific names in some functions.
Change this
function(conn, top, data)
for this
function(conn, topic, data)
I have worked previously with mqtt in lua with password protection, and i had to drop it beacuse i wasnt recieving the message content either, however i think mine was due to the amount of messages i was recieving/delivering. I also changed broker from mosquitto to mosca.
I hope you find a fix soon and please share it we might be able to use it to :)

Erlang: what is this process identifier in the form of {from, Pid, Ref}?

I am now facing a problem. when i check the erl_crash.dump, i found some stuff as below:
=proc:<0.19275.17>
State: Scheduled
Spawned as: proc_lib:init_p/5
Spawned by: <0.18723.17>
Started: Wed May 8 13:30:40 2013
Message queue length: 1
Number of heap fragments: 0
Heap fragment data: 0
Link list: [<0.20051.17>, <0.9976.18>, ..., **{from,<6524.13.0>,#Ref<6524.0.1.37040>}, {from,<6474.13.0>,#Ref<6474.0.1.36682>}, {from,<6470.13.0>,#Ref<6470.0.1.34219>}**, ...]
there is something like {from, Pid, Ref} in the link list of the proc <0.19275.17>.
i have no idea what these weird formed processes identifier are. i guess maybe it is related to "process monitoring". am i right? and i still want to know how can i generate such a process identifier and how can i make use of them ?
Thank you in advance :)
This means that the process was monitored by other processes. From documentation:
If process monitoring is used, this field also tells in which
direction the monitoring is in effect, i.e., a link being "to" a
process tells you that the "current" process was monitoring the other
and a link "from" a process tells you that the other process was
monitoring the current one.
You can find more infromation here
I think {from,<6524.13.0>,#Ref<6524.0.1.37040>} is that you register a global name, so global name server is monitoring this process.
more info: http://www.erlang.org/doc/man/global.html

Yaws websocket send message to all connected users

I am using yaws (Erlang framework) for socket communication. I can send message back to the user from server using websocket_send however i need to specify the PID of the user, that means that i can send message back to that user. However, i would like to send message to all connected users. Is there any way to do it?
Every time a websocket connection is established a new gen_server process is created for that connection. Hence each of these servers corresponds to one websocket connection. Thus websocket_send requires the PID of the gen_server.
For sending message to all the connected clients you need to maintain the PIDs of all the gen_servers. This can be done by having your own gen_server or using ets.
Similar to sending the Pid to gen_server
you can send the Pid in websocket callback init function
init(Args) ->
gen_server:cast(?YOURSERVER,{connection_open, self()}),
{ok, []}.
During termination
terminate(Reason, State) ->
gen_server:cast(?YOURSERVER,{connection_close, self()}).
Your gen_server handle_cast may look like this
handle_cast({connection_open, Pid}, Pids) ->
{noreply, [Pid | Pids]};
handle_cast({connection_close, Pid}, Pids) ->
{noreply, lists:delete(Pid, Pids)};
handle_cast({send_to_all, Msg}, Pids) ->
[yaws_api:websocket_send(Pid, Msg) || Pid <- Pids, is_process_alive(Pid)],
{noreply, Pids}.
Got it worked !!! Using GProc :)
Gproc is a process dictionary for Erlang, which provides a number of useful features beyond what the built-in dictionary has:
Use any term as a process alias
Register a process under several aliases
Non-unique properties can be registered simultaneously by many processes
QLC and match specification interface for efficient queries on the dictionary
Await registration, let's you wait until a process registers itself
Atomically give away registered names and properties to another process
Counters, and aggregated counters, which automatically maintain the total of all counters with a given name
Global registry, with all the above functions applied to a network of nodes
That will need a comprehensive approach which involves in-memory storage. Forexample, Each user may have a process holding the socket connection and so, you save say, in mnesia, or ets table e.t.c. a record like: #connected_user{pid = Pid,username = Username,other_params = []}. Later after advancing your perception of this problem, you will move onto session management, how to handle offline messages, and most importantly presence. Anyways, when a message comes in, having the destination username, then you will make a lookup from our table and get the corresponding Pid, and then send it this message, which in turn, it will then send it through its live Web Socket.

Resources