forked from igniterealtime/openfire-hazelcast-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
readme.html
304 lines (295 loc) · 15.1 KB
/
readme.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="en">
<head>
<title>Hazelcast Clustering Plugin Readme</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 11pt;
font-weight : bold;
}
H3 {
font-size : 10pt;
font-style : italic;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new, monospaced, sans-serif;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new, monospaced, sans-serif;
font-size : 100%;
}
#datatable TH {
color : #fff;
background-color : #2A448C;
text-align : left;
}
#datatable TD {
background-color : #FAF6EF;
}
</style>
</head>
<body>
<h1>Hazelcast Clustering Plugin Readme</h1>
<h2>Overview</h2>
<p>
The Hazelcast plugin adds support for running multiple redundant Openfire
servers together in a cluster. By running Openfire as a cluster, you can
distribute the connection load among several servers, while also providing
failover in the event that one of your servers fails. This plugin is a
drop-in replacement for the original Openfire clustering plugin, using the
open source <a href="http://www.hazelcast.org">Hazelcast In-Memory Data Grid</a> data distribution
framework in lieu of an expensive proprietary third-party product.
</p>
<p>
The current version of the plugin uses Hazelcast release 3.12.5
</p>
<h3>Clustering vs. Federation</h3>
<p>XMPP is designed to scale in ways that are similar to email. Each Openfire
installation supports a single XMPP domain, and a server-to-server (S2S)
protocol as described in the specification is provided to link multiple XMPP
domains together. This is known as federation. It represents a powerful way to
"scale out" XMPP, as it allows an XMPP user to communicate securely with any
user in any other such federated domain. These federations may be public or
private as appropriate. Federated domains may exchange XMPP stanzas
across the Internet (WAN) and may even discover one another using DNS-based
service lookup and address resolution.
</p>
<p>By contrast, clustering is a technique used to "scale up" a single XMPP domain.
The server members within a cluster all share an identical configuration. Each
member will allow any user within the domain to connect, authenticate, and exchange
stanzas. Clustered servers all share a single database, and are also required to
be resident within the same LAN-based (low latency) network infrastructure. This
type of deployment is suitable to provide runtime redundancy and will support a
larger number of users and connections (within a single domain) than a single
server would be able to provide.
</p>
<p>For very large Openfire deployments, a combination of federation and clustering will
provide the best results. Whereas a single clustered XMPP domain will be able to
support tens or even hundreds of thousands of users, a federated deployment will be
needed to reach true Internet scale of millions of concurrent XMPP connections.
</p>
<h2>Installation</h2>
<p>
To create an Openfire cluster, you should have at least two Openfire servers,
and each server must have the Hazelcast plugin installed. To install Hazelcast,
simply drop the hazelcast.jar into $OPENFIRE_HOME/plugins along with any other
plugins you may have installed. You may also use the Plugins page from the
admin console to install the plugin. Note that all servers in a given cluster
must be configured to share a single external database (not the Embedded DB).
</p>
<p>
By default during the Openfire startup/initialization process, the servers
will discover each other by exchanging UDP (multicast) packets via a configurable
IP address and port. However, be advised that many other initialization options
are available and may be used if your network does not support multicast
communication (see <a href="#config">Configuration</a> below).
</p>
<p>After the Hazelcast plugin has been deployed to each of the servers, use the
radio button controls located on the Clustering page in the admin console to
activate/enable the cluster. You only need to enable clustering once; the change
will be propagated to the other servers automatically. After refreshing the
Clustering page you will be able to see all the servers that have successfully
joined the cluster.
</p>
<p>
Note that Hazelcast and the earlier clustering plugins (clustering.jar and enterprise.jar)
are mutually exclusive. You will need to remove any existing older clustering plugin(s)
before installing Hazelcast into your Openfire server(s).
</p>
<p>
With your cluster up and running, you will now want some form of load balancer to
distribute the connection load among the members of your Openfire cluster. There
are several commercial and open source alternatives for this. For example,
if you are using the HTTP/BOSH Openfire connector to connect to Openfire,
the Apache web server (httpd) plus the corresponding proxy balancer module
(<a href="http://httpd.apache.org/docs/current/mod/mod_proxy_balancer.html">mod_proxy_balancer</a>)
could provide a workable solution. Some other popular options include the
<a href="http://www.f5.com/products/big-ip/big-ip-local-traffic-manager/overview/">F5 LTM</a>
(commercial) and <a href="http://haproxy.1wt.eu/">HAProxy</a> (open source), among
<a href="http://en.wikipedia.org/wiki/Load_balancing_%28computing%29">many more</a>.
<p>
A simple round-robin DNS configuration can help distribute XMPP connections across multiple
Openfire servers in a cluster. While popular as a lightweight and low-cost way to provide
basic scalability, note that this approach is not considered adequate for true load balancing
nor does it provide high availability (HA) from a client perspective. If you are evaluating
these options, you can <a href="http://en.wikipedia.org/wiki/Round-robin_DNS">read more here</a>.
</p>
<h2>Upgrading the Hazelcast Plugin</h2>
<p>
The process of upgrading the Hazelcast plugin requires a few additional steps when
compared with a traditional plugin due to the cross-server dependencies within a running
cluster. Practically speaking, all the members of the cluster need to be running the
same version of the plugin to prevent various errors and data synchronization issues.
</p>
<h3>Option 1: Offline</h3>
<p><strong>NOTE:</strong> This upgrade procedure is neat and tidy, but will incur a brief service outage.
</p>
<ol>
<li>Shut down Openfire on all servers in the cluster.</li>
<li>For the first server in the cluster, perform the following steps:
<ol type="a">
<li>Remove the existing <code>plugins/hazelcast.jar</code></li>
<li>Remove (recursively) the <code>plugins/hazelcast</code> directory</li>
<li>Copy the updated <code>hazelcast.jar</code> into the <code>plugins</code> directory</li>
<li>Restart Openfire to unpack and install the updated plugin</li>
</ol>
</li>
<li>Repeat these steps for the remaining servers in the cluster.</li>
</ol>
<h3>Option 2: Online</h3>
<p><strong>NOTE:</strong> Using this approach you should be able to continue servicing
XMPP connections during the upgrade.
</p>
<ol>
<li>Shut down Openfire on all servers <strong>except one</strong>.</li>
<li>Using the Plugins page from the online server, remove the existing Hazelcast plugin.</li>
<li>Upload the new Hazelcast plugin and confirm it is installed (refresh the page if necessary)</li>
<li>Use the "Offline" steps above to upgrade and restart the remaining servers.</li>
</ol>
<h3>Option 3: Split-Brain</h3>
<p><strong>NOTE:</strong> Use this approach if you only have access to the Openfire console.
Note however that users may not be able to communicate with each other during the upgrade
(if they are connected to different servers).
</p>
<ol>
<li>From the Clustering page in the Openfire admin console, disable clustering. This will disable
clustering for all members of the cluster.</li>
<li>For each server, update the Hazelcast plugin using the Plugins page.</li>
<li>After upgrading the plugin on all servers, use the Clustering page to enable clustering.
This will activate clustering for all members of the cluster.</li>
</ol>
<a name="config"></a>
<h2>Configuration</h2>
<p>There are several configuration options built into the Hazelcast plugin
as Openfire system properties:
</p>
<ol>
<li><em>hazelcast.startup.retry.count</em> (1): Number of times to retry
initialization if the cluster fails to start on the first attempt.</li>
<li><em>hazelcast.startup.retry.seconds</em> (10): Number of seconds to wait
between subsequent attempts to start the cluster.</li>
<li><em>hazelcast.max.execution.seconds</em> (30): Maximum time to wait
when running a synchronous task across members of the cluster.</li>
<li><em>hazelcast.config.xml.filename</em> (hazelcast-cache-config.xml): Name
of the Hazelcast configuration file. By overriding this value you can easily
install a custom cache configuration file in the Hazelcast plugin /classes/
directory, in the directory named via the <em>hazelcast.config.xml.directory</em>
property (described below), or in the classpath of your own custom plugin.</li>
<li><em>hazelcast.config.xml.directory</em> ({OPENFIRE_HOME}/conf): Directory
that will be added to the plugin's classpath. This allows a custom Hazelcast
configuration file to be located outside the Openfire home directory.</li>
<li><em>hazelcast.config.jmx.enabled</em> (false): Enables JMX support for
the Hazelcast cluster if JMX has been enabled via the Openfire admin console.
Refer to the <a href="http://docs.hazelcast.org/docs/3.12.5/manual/html-single/index.html#monitoring-with-jmx">
Hazelcast JMX docs</a> for additional information.</li>
</ol>
<em>Note:</em> The default <code>hazelcast-cache-config.xml</code> file included with the plugin will include a file
<code>conf/hazelcast-local-config.xml</code> that will be preserved between plugin updates. It is recommended that
local changes are kept in this file.
<p>The Hazelcast plugin uses the <a href="http://docs.hazelcast.org/docs/3.12.5/manual/html-single/index.html#configuring-declaratively">
XML configuration builder</a> to initialize the cluster from the XML file <code>conf/hazelcast-local-config.xml</code>.
By default the cluster members will attempt to discover each other via UDP multicast at the
following location:
</p>
<ul>
<li>IP Address: 224.2.2.3</li>
<li>Port: 54327</li>
</ul>
These values can be overridden in the <code>conf/hazelcast-local-config.xml</code>
file via the multicast-group and multicast-port elements. Many other initialization and
discovery options exist, as documented in the Hazelcast configuration docs noted above. For
example, to set up a two-node cluster using well-known DNS name/port values, try the
following alternative:
<pre>
...
<join>
<multicast enabled="false"/>
<tcp-ip enabled="true">
<member>of-node-a.example.com</member>
<member>of-node-b.example.com</member>
</tcp-ip>
</join>
...
</pre>
<p>Please refer to the <a href="http://docs.hazelcast.org/docs/3.12.5/manual/html-single/index.html">
Hazelcast reference manual</a> for more information.
</p>
<h3>A Word About Garbage Collection</h3>
<p>Hazelcast is quite sensitive to delays that may be caused by long-running GC cycles which are typical
of servers using a default runtime JVM configuration. In most cases it will be preferable to activate the
concurrent garbage collector (CMS) or the new G1 garbage collector to minimize blocking within the JVM.
When using CMS, you may be able to counter the effects of heap fragmentation by using JMX to invoke
System.gc() when the cluster is relatively idle (e.g. overnight). This has the effect of temporarily
interrupting the concurrent GC algorithm in favor of the default GC to collect and compact the heap.
</p>
<p>In addition, the runtime characteristics of your Openfire cluster will vary greatly depending on the
number and type of clients that are connected, and which XMPP services you are using in your deployment.
However, note that because many of the objects allocated on the heap are of the short-lived variety,
increasing the proportion of young generation (eden) space may also have a positive impact on performance.
As an example, the following OPENFIRE_OPTS have been shown to be suitable in a three-node cluster of
servers (four CPUs each), supporting approximately 50k active users:
</p>
<pre>
OPENFIRE_OPTS="-Xmx4G -Xms4G -XX:NewRatio=1 -XX:SurvivorRatio=4
-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=1
-XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly
-XX:+PrintGCDetails -XX:+PrintPromotionFailure"
</pre>
<p>This GC configuration will also emit helpful GC diagnostic information to the console to aid further
tuning and troubleshooting as appropriate for your deployment.
</p>
<h2>Configuring Cache expiry times and sizes</h2>
<h3>Core Openfire caches</h3>
When clustering is enabled, the only way to change the size of standard Openfire caches is by
editing <code>plugins/hazelcast/classes/hazelcast-cache-config.xml</code> on every node in the cluster, shutting down
every node in the cluster, and then restarting the nodes. Dynamic configuration is not currently possible, and using
different configurations on different nodes is liable to lead to odd behaviour.
<p>This is different to non-clustered caches, where it is sufficient to edit the
<code>cache.[cache name].maxLifetime</code> and <code>cache.[cache name].size</code> System Properties</p>
<h3>Plugin defined caches</h3>
A plugin can create it's own Cache without the requirement to edit any configuration files. For example;
<pre>
final String cacheName = "my-test-cache";
CacheFactory.setMaxSizeProperty(cacheName, maxCacheSize);
CacheFactory.setMaxLifetimeProperty(cacheName, maxLifetime);
final Cache<JID, Instant> cache = CacheFactory.createCache(cacheName);
</pre>
Notes;
<ul>
<li>
<code>CacheFactory.setMaxSizeProperty</code>/<code>CacheFactory.setMaxLifetimeProperty</code> will set Openfire
System Properties that are used to configure the Cache when it is created.
</li>
<li>
If no Openfire System Properties are set, the default expiry values are used (unlimited size and six hours,
respectively).
</li>
<li>
The first node in the cluster to call <code>CacheFactory.createCache</code> will use the configured expiry
values, subsequent calls to <code>CacheFactory.createCache</code> will simply retrieve the existing Cache
with the previously configured expiry values. It is not possible to change the expiry values after the Cache
has been created.
</li>
</ul>
</body>
</html>