|
| 1 | +# Protecting the Broker in AMQ 7 |
| 2 | + |
| 3 | +This worksheet covers some broker protection features in the AMQ 7 broker. By the end of this worksheet you should know: |
| 4 | + |
| 5 | +1. How to configure the behavior of the broker when an address becomes "full" |
| 6 | +2. How to use resource-limit-settings to restrict how many queues and/or connections a specific user can create |
| 7 | +3. How to configure an acceptor to limit the number of allowed TCP connections |
| 8 | + |
| 9 | +## Address-full policies |
| 10 | + |
| 11 | +As messages accumulate in the broker there is a chance that the JVM could run out of heap space. There are several options to deal with this possibility which can be configured in a matching `<address-setting>`: |
| 12 | + |
| 13 | +* **max-size-bytes** How large an address can be before it's considered "full". The size of an address is calculated by summing the sizes of all the queues bound to the address. This includes durable, non-durable, statically created, and dynamically (i.e. auto) created queues. Note, this is done on a per-address basis so even though the `match` of the `<address-setting>` may be `#` the `max-size-bytes` applies to each individual address and not every matching address as a whole. |
| 14 | +* **address-full-policy** What to do when the address becomes full. Options are: |
| 15 | + * **PAGE** messages will be stored on disk |
| 16 | + * **DROP** messages will be silently dropped (i.e. deleted) |
| 17 | + * **FAIL** messages will be dropped and the relevant producer will receive an error |
| 18 | + * **BLOCK** relevant producer will be blocked from sending additional messages; only clients which support flow-control (i.e. core JMS, AMQP) will block |
| 19 | + |
| 20 | +Aside from the per-address limits there are also global limits: |
| 21 | + |
| 22 | +* **global-max-size** When all the message data in the broker reaches this limit then all addresses will apply their respective `<address-full-policy>`. Measured in bytes; supports byte notation (e.g. Mb, GB, kb, etc.). Defaults to Xmx/2. |
| 23 | +* **max-disk-usage** When disk utilization reaches this percentage (for any reason) then all clients supporting flow control will be blocked and those that don't will be disconnected. |
| 24 | + |
| 25 | +These are the parts of the default configuration relevant to this discussion: |
| 26 | + |
| 27 | +```xml |
| 28 | +<max-disk-usage>90</max-disk-usage> |
| 29 | + |
| 30 | +<global-max-size>100Mb</global-max-size> |
| 31 | + |
| 32 | +<address-setting match="#"> |
| 33 | + <dead-letter-address>DLQ</dead-letter-address> |
| 34 | + <expiry-address>ExpiryQueue</expiry-address> |
| 35 | + <redelivery-delay>0</redelivery-delay> |
| 36 | + <!-- with -1 only the global-max-size is in use for limiting --> |
| 37 | + <max-size-bytes>-1</max-size-bytes> |
| 38 | + <message-counter-history-day-limit>10</message-counter-history-day-limit> |
| 39 | + <address-full-policy>PAGE</address-full-policy> |
| 40 | + <auto-create-queues>true</auto-create-queues> |
| 41 | + <auto-create-addresses>true</auto-create-addresses> |
| 42 | + <auto-create-jms-queues>true</auto-create-jms-queues> |
| 43 | + <auto-create-jms-topics>true</auto-create-jms-topics> |
| 44 | +</address-setting> |
| 45 | +``` |
| 46 | + |
| 47 | +### Hands on |
| 48 | + |
| 49 | +Create a fresh broker instance: |
| 50 | + |
| 51 | +``` |
| 52 | +$ <AMQ_HOME>/bin/artemis create --user <myuser> --password <mypassword> --role admin --require-login <AMQ_HOME>/instances/myprotectedbroker |
| 53 | +``` |
| 54 | + |
| 55 | +Open `AMQ_INSTANCE/etc/broker.xml` and change `<max-disk-usage>` as the default value (i.e. 90) can inadvertently trigger blocking if your disk is close to full: |
| 56 | + |
| 57 | +```xml |
| 58 | +<max-disk-usage>100</max-disk-usage> |
| 59 | +``` |
| 60 | + |
| 61 | +Then change the `<address-full-policy>` for `<address-setting match="#">` to: |
| 62 | + |
| 63 | +```xml |
| 64 | +<address-full-policy>BLOCK</address-full-policy> |
| 65 | +``` |
| 66 | + |
| 67 | +And also change the `<max-size-bytes>` to: |
| 68 | + |
| 69 | +```xml |
| 70 | +<max-size-bytes>1MB</max-size-bytes> |
| 71 | +``` |
| 72 | + |
| 73 | +Now send messages to the broker: |
| 74 | + |
| 75 | +``` |
| 76 | +$ <AMQ_INSTANCE>/bin/artemis producer --user <myuser> --password <mypassword> --message-size 1050 |
| 77 | +``` |
| 78 | + |
| 79 | +The producer should block with this: |
| 80 | + |
| 81 | +``` |
| 82 | +AMQ212054: Destination address=TEST is blocked. If the system is configured to block make sure you consume messages on this configuration. |
| 83 | +``` |
| 84 | + |
| 85 | +Kill the producer (e.g. using Ctrl-C), and consume all the messages: |
| 86 | + |
| 87 | +``` |
| 88 | +$ <AMQ_INSTANCE>/bin/artemis consumer --user <myuser> --password <mypassword> --receive-timeout 1000 --break-on-null |
| 89 | +``` |
| 90 | + |
| 91 | +Check the AMQ 7 log and you'll see where the broker blocked and unblocked during the exercise. |
| 92 | + |
| 93 | +## Resource limits |
| 94 | + |
| 95 | +Broker administrators may be concerned with how many connections or queues a particular user is allowed to create. This can be configured via `<resource-limit-settings>`, e.g.: |
| 96 | + |
| 97 | +```xml |
| 98 | +<resource-limit-settings> |
| 99 | + <resource-limit-setting match="myuser"> |
| 100 | + <max-queues>100</max-queues> |
| 101 | + <max-connections>10</max-connections> |
| 102 | + </resource-limit-setting> |
| 103 | +</resource-limit-settings> |
| 104 | +``` |
| 105 | + |
| 106 | +The `match` attribute on `<resource-limit-setting>` matches a username. Wildcards are not supported on this match. The `<max-queues>` is how many queues the user is allowed to create either manually or via auto-creation. The `<max-connections>` is how many connections the user is allowed to create. |
| 107 | + |
| 108 | +## Hands on |
| 109 | + |
| 110 | +Add this to your `AMQ_INSTANCE/etc/broker.xml`: |
| 111 | + |
| 112 | +```xml |
| 113 | +<resource-limit-settings> |
| 114 | + <resource-limit-setting match="myuser"> |
| 115 | + <max-queues>0</max-queues> |
| 116 | + </resource-limit-setting> |
| 117 | +</resource-limit-settings> |
| 118 | +``` |
| 119 | + |
| 120 | +Restart the broker (because `<resource-limit-settings>` is not automatically picked up when changed at runtime) and try to send messages: |
| 121 | + |
| 122 | +``` |
| 123 | +$ <AMQ_INSTANCE>/bin/artemis producer --user <myuser> --password <mypassword> |
| 124 | +``` |
| 125 | + |
| 126 | +This should fail since the producer will attempt to create the queue "TEST" by default: |
| 127 | + |
| 128 | +``` |
| 129 | +javax.jms.JMSException: AMQ119111: Too many queues created by user '<myuser>'. Queues allowed: 0. |
| 130 | +``` |
| 131 | + |
| 132 | +Now change the `<resource-limit-settings>`: |
| 133 | + |
| 134 | +```xml |
| 135 | +<resource-limit-settings> |
| 136 | + <resource-limit-setting match="myuser"> |
| 137 | + <max-queues>10</max-queues> |
| 138 | + <max-connections>3</max-connections> |
| 139 | + </resource-limit-setting> |
| 140 | +</resource-limit-settings> |
| 141 | +``` |
| 142 | + |
| 143 | +Run a consumer with 2 threads. Note, even though there's just 2 threads the core JMS client will actually create 3 core sessions - 1 for the JMS connection and 1 each for the JMS sessions (1 per thread) for a total of 3. |
| 144 | + |
| 145 | +``` |
| 146 | +$ <AMQ_INSTANCE>/bin/artemis consumer --user <myuser> --password <mypassword> --threads 2 --receive-timeout 100 --break-on-null |
| 147 | +``` |
| 148 | + |
| 149 | +This will run without problems. However, a consumer with 3 threads (i.e. 4 core sessions) will fail: |
| 150 | + |
| 151 | +``` |
| 152 | +$ <AMQ_INSTANCE>/bin/artemis consumer --user <myuser> --password <mypassword> --threads 3 --receive-timeout 100 --break-on-null |
| 153 | +``` |
| 154 | + |
| 155 | +Here's the output: |
| 156 | + |
| 157 | +``` |
| 158 | +javax.jms.JMSException: AMQ119110: Too many sessions for user '<myuser>'. Sessions allowed: 3. |
| 159 | +``` |
| 160 | + |
| 161 | +## Limiting Connections on an Acceptor |
| 162 | + |
| 163 | +Instead of limiting connections on a user-by-user basis you can also apply a global limit on connections to an acceptor using the `connectionsAllowed` URL property. |
| 164 | + |
| 165 | +### Hands on |
| 166 | + |
| 167 | +Stop your broker instance, remove the `<resource-limit-settings>`, and change the `artemis` acceptor to this (adding `;connectionsAllowed=1` to the end): |
| 168 | + |
| 169 | +```xml |
| 170 | +<acceptor name="artemis">tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;connectionsAllowed=1</acceptor> |
| 171 | +``` |
| 172 | + |
| 173 | +Restart the broker and connect a consumer: |
| 174 | + |
| 175 | +``` |
| 176 | +$ <AMQ_INSTANCE>/bin/artemis consumer --user myuser --password mypass |
| 177 | +``` |
| 178 | + |
| 179 | +This will succeed. Note, even though the consumer is creating multiple core sessions it's only using a single TCP connection. |
| 180 | + |
| 181 | +Connect another consumer: |
| 182 | + |
| 183 | +``` |
| 184 | +$ <AMQ_INSTANCE>/bin/artemis consumer --user myuser --password mypass |
| 185 | +``` |
| 186 | + |
| 187 | +This will fail after a bit and ask you for a working URL. The broker will report something like this: |
| 188 | + |
| 189 | +``` |
| 190 | +AMQ222206: Connection limit of 1 reached. Refusing connection from /127.0.0.1:57918 |
| 191 | +``` |
| 192 | + |
| 193 | +The broker here is simply closing the TCP connection which may result in different behavior in the different supported clients. |
0 commit comments