/** * Copyright (c) 2015 The original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.reveno.atp.clustering.core.providers; import org.jgroups.JChannel; import org.reveno.atp.clustering.api.Address; import org.reveno.atp.clustering.api.Cluster; import org.reveno.atp.clustering.api.ClusterBuffer; import org.reveno.atp.clustering.api.InetAddress; import org.reveno.atp.clustering.core.RevenoClusterConfiguration; import org.reveno.atp.clustering.core.buffer.ClusterProvider; import org.reveno.atp.clustering.core.jgroups.JChannelReceiver; import org.reveno.atp.clustering.core.jgroups.JGroupsCluster; import org.reveno.atp.clustering.util.ResourceLoader; import org.reveno.atp.utils.Exceptions; import org.w3c.dom.Element; import java.io.ByteArrayInputStream; import java.io.File; import java.util.List; import java.util.Optional; import java.util.Properties; /** * Abstract provider where {@link Cluster} implementation is always used as * JGroups, and {@link ClusterBuffer} may vary. */ public abstract class JGroupsClusterProvider implements ClusterProvider { public static final String CLUSTER_NAME = "rvno_jg"; abstract ClusterBuffer createBuffer(); abstract void setProperties(Properties properties); @Override public void initialize(RevenoClusterConfiguration config) { this.config = config; Properties props = new Properties(); props.put("jgroups.tcp.bind_addr", ((InetAddress) config.currentNodeAddress()).getHost()); props.put("jgroups.tcp.bind_port", Integer.toString(((InetAddress) config.currentNodeAddress()).getPort())); props.put("jgroups.tcpping.initial_hosts", makeInitialHostsString(config.nodesAddresses())); props.put("jgroups.auth.token", Optional.ofNullable(config.authToken()).orElse("")); setProperties(props); try { String protocol; if (jGroupsConfigFile.startsWith("classpath:/")) { protocol = ResourceLoader.loadResource(new ByteArrayInputStream(DEFAULT_CONFIG.getBytes("UTF-8")), props); } else { protocol = ResourceLoader.loadResource(new File(jGroupsConfigFile), props); } Element xml = ResourceLoader.loadXMLFromString(protocol).getDocumentElement(); channel = new JChannel(xml); channel.setReceiver(new JChannelReceiver()); channel.setDiscardOwnMessages(true); cluster = new JGroupsCluster(config, channel); buffer = createBuffer(); } catch (Exception e) { throw Exceptions.runtime(e); } isInitialized = true; } @Override public Cluster retrieveCluster() { checkInitialized(); return cluster; } @Override public ClusterBuffer retrieveBuffer() { checkInitialized(); return buffer; } protected String makeInitialHostsString(List<Address> addresses) { StringBuilder sb = new StringBuilder(); addresses.stream().map(a -> (InetAddress)a).forEach(a -> sb.append(a.getHost()) .append("[").append(a.getPort()).append("]").append(",")); sb.setLength(sb.length() - 1); return sb.toString(); } protected void checkInitialized() { if (!isInitialized) throw new IllegalStateException("Provider must be initialized first."); } protected String jGroupsConfigFile; protected RevenoClusterConfiguration config; protected JGroupsCluster cluster; protected ClusterBuffer buffer; protected JChannel channel; protected boolean isInitialized = false; protected static final String DEFAULT_CONFIG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<config xmlns=\"urn:org:jgroups\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + " xsi:schemaLocation=\"urn:org:jgroups http://www.jgroups.org/schema/JGroups-3.4.xsd\"><TCP_NIO2 bind_addr=\"" + "${jgroups.tcp.bind_addr}\" bind_port=\"${jgroups.tcp.bind_port}\" port_range=\"0\" recv_buf_size=\"" + "${tcp.recv_buf_size:5M}\" send_buf_size=\"${tcp.send_buf_size:5M}\" max_bundle_size=\"4k\" max_bundle_timeout=\"" + "1\" max_read_batch_size=\"${max.read.batch.size:10}\" sock_conn_timeout=\"300\" timer_type=\"new3\" timer.min_threads=\"4\"" + " timer.max_threads=\"10\" timer.keep_alive_time=\"3000\" timer.queue_max_size=\"3000\" thread_pool.enabled=\"true\"" + " thread_pool.min_threads=\"${jgroups.threads.min:5}\" thread_pool.max_threads=\"${jgroups.threads.max:60}\"" + " thread_pool.keep_alive_time=\"5000\" thread_pool.queue_enabled=\"${jgroups.threads.queue:false}\" " + "thread_pool.queue_max_size=\"${jgroups.threads.queue.size:500000}\" thread_pool.rejection_policy=\"Run\"" + " oob_thread_pool.enabled=\"true\" oob_thread_pool.min_threads=\"5\" oob_thread_pool.max_threads=\"15\" " + "oob_thread_pool.keep_alive_time=\"50000\" oob_thread_pool.queue_enabled=\"true\" oob_thread_pool.queue_max_size=\"10000\"" + " oob_thread_pool.rejection_policy=\"Run\" /><TCPPING timeout=\"${nodes.ping.timeout:2000}\" initial_hosts=\"" + "${jgroups.tcpping.initial_hosts}\" port_range=\"0\" /><MERGE3 min_interval=\"1000\" max_interval=\"3000\"" + " /><FD_SOCK /><VERIFY_SUSPECT timeout=\"1500\" /><BARRIER /><pbcast.NAKACK2 use_mcast_xmit=\"false\"" + " discard_delivered_msgs=\"true\" /><UNICAST3 xmit_interval=\"${retransmit.interval:100}\" max_retransmit_time=\"" + "${max.retransmit.time:150}\" /><SCOPE /><pbcast.STABLE stability_delay=\"1000\" desired_avg_gossip=\"50000\"" + " max_bytes=\"4M\" /><pbcast.GMS print_local_addr=\"true\" join_timeout=\"3000\" view_bundling=\"true\" " + "/><pbcast.STATE_TRANSFER /><pbcast.FLUSH timeout=\"0\" /></config>"; }