init
This commit is contained in:
103
java/org/apache/catalina/ha/backend/CollectedInfo.java
Normal file
103
java/org/apache/catalina/ha/backend/CollectedInfo.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.catalina.ha.backend;
|
||||
|
||||
/* for MBean to read ready and busy */
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.apache.tomcat.util.modeler.Registry;
|
||||
|
||||
/*
|
||||
* Listener to provider informations to mod_heartbeat.c
|
||||
* *msg_format = "v=%u&ready=%u&busy=%u"; (message to send).
|
||||
* send the multicast message using the format...
|
||||
* what about the bind(IP. port) only IP makes sense (for the moment).
|
||||
* BTW:v = version :-)
|
||||
*/
|
||||
public class CollectedInfo {
|
||||
|
||||
/* Collect info via JMX */
|
||||
protected MBeanServer mBeanServer = null;
|
||||
protected ObjectName objName = null;
|
||||
|
||||
int ready;
|
||||
int busy;
|
||||
|
||||
int port = 0;
|
||||
String host = null;
|
||||
|
||||
public CollectedInfo(String host, int port) throws Exception {
|
||||
init(host, port);
|
||||
}
|
||||
public void init(String host, int port) throws Exception {
|
||||
int iport = 0;
|
||||
String shost = null;
|
||||
mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
|
||||
String onStr = "*:type=ThreadPool,*";
|
||||
ObjectName objectName = new ObjectName(onStr);
|
||||
Set<ObjectInstance> set = mBeanServer.queryMBeans(objectName, null);
|
||||
for (ObjectInstance oi : set) {
|
||||
objName = oi.getObjectName();
|
||||
String name = objName.getKeyProperty("name");
|
||||
|
||||
/* Name are:
|
||||
* http-8080
|
||||
* jk-10.33.144.3-8009
|
||||
* jk-jfcpc%2F10.33.144.3-8009
|
||||
*/
|
||||
String [] elenames = name.split("-");
|
||||
String sport = elenames[elenames.length-1];
|
||||
iport = Integer.parseInt(sport);
|
||||
String [] shosts = elenames[1].split("%2F");
|
||||
shost = shosts[0];
|
||||
|
||||
if (port==0 && host==null)
|
||||
break; /* Take the first one */
|
||||
if (host==null && iport==port)
|
||||
break; /* Only port done */
|
||||
if (shost.compareTo(host) == 0)
|
||||
break; /* Done port and host are the expected ones */
|
||||
}
|
||||
if (objName == null)
|
||||
throw new Exception("Can't find connector for " + host + ":" + port);
|
||||
this.port = iport;
|
||||
this.host = shost;
|
||||
|
||||
}
|
||||
|
||||
public void refresh() throws Exception {
|
||||
if (mBeanServer == null || objName == null) {
|
||||
throw new Exception("Not initialized!!!");
|
||||
}
|
||||
Integer imax = (Integer) mBeanServer.getAttribute(objName, "maxThreads");
|
||||
|
||||
// the currentThreadCount could be 0 before the threads are created...
|
||||
// Integer iready = (Integer) mBeanServer.getAttribute(objName, "currentThreadCount");
|
||||
|
||||
Integer ibusy = (Integer) mBeanServer.getAttribute(objName, "currentThreadsBusy");
|
||||
|
||||
busy = ibusy.intValue();
|
||||
ready = imax.intValue() - ibusy.intValue();
|
||||
}
|
||||
}
|
||||
125
java/org/apache/catalina/ha/backend/HeartbeatListener.java
Normal file
125
java/org/apache/catalina/ha/backend/HeartbeatListener.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.catalina.ha.backend;
|
||||
|
||||
import org.apache.catalina.ContainerEvent;
|
||||
import org.apache.catalina.ContainerListener;
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.LifecycleEvent;
|
||||
import org.apache.catalina.LifecycleListener;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/*
|
||||
* Listener to provider informations to mod_heartbeat.c
|
||||
* *msg_format = "v=%u&ready=%u&busy=%u"; (message to send).
|
||||
* send the multicast message using the format...
|
||||
* what about the bind(IP. port) only IP makes sense (for the moment).
|
||||
* BTW:v = version :-)
|
||||
*/
|
||||
public class HeartbeatListener implements LifecycleListener, ContainerListener {
|
||||
|
||||
private static final Log log = LogFactory.getLog(HeartbeatListener.class);
|
||||
|
||||
/* To allow to select the connector */
|
||||
private int port = 0;
|
||||
private String host = null;
|
||||
|
||||
/* for multicasting stuff */
|
||||
private final String ip = "224.0.1.105"; /* Multicast IP */
|
||||
private final int multiport = 23364; /* Multicast Port */
|
||||
private final int ttl = 16;
|
||||
|
||||
public String getHost() { return host; }
|
||||
public String getGroup() { return ip; }
|
||||
public int getMultiport() { return multiport; }
|
||||
public int getTtl() { return ttl; }
|
||||
|
||||
/**
|
||||
* Proxy list, format "address:port,address:port".
|
||||
*/
|
||||
private final String proxyList = null;
|
||||
public String getProxyList() { return proxyList; }
|
||||
|
||||
/**
|
||||
* URL prefix.
|
||||
*/
|
||||
private final String proxyURL = "/HeartbeatListener";
|
||||
public String getProxyURL() { return proxyURL; }
|
||||
|
||||
private CollectedInfo coll = null;
|
||||
|
||||
private Sender sender = null;
|
||||
|
||||
@Override
|
||||
public void containerEvent(ContainerEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lifecycleEvent(LifecycleEvent event) {
|
||||
|
||||
if (Lifecycle.PERIODIC_EVENT.equals(event.getType())) {
|
||||
if (sender == null) {
|
||||
if (proxyList == null)
|
||||
sender = new MultiCastSender();
|
||||
else
|
||||
sender = new TcpSender();
|
||||
}
|
||||
|
||||
/* Read busy and ready */
|
||||
if (coll == null) {
|
||||
try {
|
||||
coll = new CollectedInfo(host, port);
|
||||
this.port = coll.port;
|
||||
this.host = coll.host;
|
||||
} catch (Exception ex) {
|
||||
log.error("Unable to initialize info collection: " + ex);
|
||||
coll = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start or restart sender */
|
||||
try {
|
||||
sender.init(this);
|
||||
} catch (Exception ex) {
|
||||
log.error("Unable to initialize Sender: " + ex);
|
||||
sender = null;
|
||||
return;
|
||||
}
|
||||
|
||||
/* refresh the connector information and send it */
|
||||
try {
|
||||
coll.refresh();
|
||||
} catch (Exception ex) {
|
||||
log.error("Unable to collect load information: " + ex);
|
||||
coll = null;
|
||||
return;
|
||||
}
|
||||
String output = "v=1&ready=" + coll.ready + "&busy=" + coll.busy +
|
||||
"&port=" + port;
|
||||
try {
|
||||
sender.send(output);
|
||||
} catch (Exception ex) {
|
||||
log.error("Unable to send collected load information: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
84
java/org/apache/catalina/ha/backend/MultiCastSender.java
Normal file
84
java/org/apache/catalina/ha/backend/MultiCastSender.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.catalina.ha.backend;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MulticastSocket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/*
|
||||
* Sender to proxies using multicast socket.
|
||||
*/
|
||||
public class MultiCastSender
|
||||
implements Sender {
|
||||
|
||||
private static final Log log = LogFactory.getLog(HeartbeatListener.class);
|
||||
|
||||
HeartbeatListener config = null;
|
||||
|
||||
/* for multicasting stuff */
|
||||
MulticastSocket s = null;
|
||||
InetAddress group = null;
|
||||
|
||||
@Override
|
||||
public void init(HeartbeatListener config) throws Exception {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int send(String mess) throws Exception {
|
||||
if (s == null) {
|
||||
try {
|
||||
group = InetAddress.getByName(config.getGroup());
|
||||
if (config.getHost() != null) {
|
||||
InetAddress addr = InetAddress.getByName(config.getHost());
|
||||
InetSocketAddress addrs = new InetSocketAddress(addr, config.getMultiport());
|
||||
s = new MulticastSocket(addrs);
|
||||
} else
|
||||
s = new MulticastSocket(config.getMultiport());
|
||||
|
||||
s.setTimeToLive(config.getTtl());
|
||||
s.joinGroup(group);
|
||||
} catch (Exception ex) {
|
||||
log.error("Unable to use multicast: " + ex);
|
||||
s = null;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] buf;
|
||||
buf = mess.getBytes(StandardCharsets.US_ASCII);
|
||||
DatagramPacket data = new DatagramPacket(buf, buf.length, group, config.getMultiport());
|
||||
try {
|
||||
s.send(data);
|
||||
} catch (Exception ex) {
|
||||
log.error("Unable to send collected load information: " + ex);
|
||||
s.close();
|
||||
s = null;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
31
java/org/apache/catalina/ha/backend/Proxy.java
Normal file
31
java/org/apache/catalina/ha/backend/Proxy.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.catalina.ha.backend;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
/*
|
||||
* This class represents a front-end httpd server.
|
||||
*
|
||||
*/
|
||||
public class Proxy {
|
||||
|
||||
public InetAddress address = null;
|
||||
public int port = 80;
|
||||
}
|
||||
40
java/org/apache/catalina/ha/backend/Sender.java
Normal file
40
java/org/apache/catalina/ha/backend/Sender.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.catalina.ha.backend;
|
||||
|
||||
/**
|
||||
* Interface to send data to proxies.
|
||||
*/
|
||||
public interface Sender {
|
||||
|
||||
/**
|
||||
* Set the configuration parameters
|
||||
* @param config The heartbeat listener configuration
|
||||
* @throws Exception An error occurred
|
||||
*/
|
||||
public void init(HeartbeatListener config) throws Exception;
|
||||
|
||||
/**
|
||||
* Send the message to the proxies
|
||||
* @param mess The message that will be sent
|
||||
* @return <code>0</code> if no error occurred, <code>-1</code> otherwise
|
||||
* @throws Exception An error occurred
|
||||
*/
|
||||
public int send(String mess) throws Exception;
|
||||
}
|
||||
208
java/org/apache/catalina/ha/backend/TcpSender.java
Normal file
208
java/org/apache/catalina/ha/backend/TcpSender.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.catalina.ha.backend;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/*
|
||||
* Sender to proxies using multicast socket.
|
||||
*/
|
||||
public class TcpSender
|
||||
implements Sender {
|
||||
|
||||
private static final Log log = LogFactory.getLog(HeartbeatListener.class);
|
||||
|
||||
HeartbeatListener config = null;
|
||||
|
||||
/**
|
||||
* Proxies.
|
||||
*/
|
||||
protected Proxy[] proxies = null;
|
||||
|
||||
|
||||
/**
|
||||
* Active connections.
|
||||
*/
|
||||
|
||||
protected Socket[] connections = null;
|
||||
protected BufferedReader[] connectionReaders = null;
|
||||
protected BufferedWriter[] connectionWriters = null;
|
||||
|
||||
|
||||
@Override
|
||||
public void init(HeartbeatListener config) throws Exception {
|
||||
this.config = config;
|
||||
StringTokenizer tok = new StringTokenizer(config.getProxyList(), ",");
|
||||
proxies = new Proxy[tok.countTokens()];
|
||||
int i = 0;
|
||||
while (tok.hasMoreTokens()) {
|
||||
String token = tok.nextToken().trim();
|
||||
int pos = token.indexOf(':');
|
||||
if (pos <=0)
|
||||
throw new Exception("bad ProxyList");
|
||||
proxies[i] = new Proxy();
|
||||
proxies[i].port = Integer.parseInt(token.substring(pos + 1));
|
||||
try {
|
||||
proxies[i].address = InetAddress.getByName(token.substring(0, pos));
|
||||
} catch (Exception e) {
|
||||
throw new Exception("bad ProxyList");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
connections = new Socket[proxies.length];
|
||||
connectionReaders = new BufferedReader[proxies.length];
|
||||
connectionWriters = new BufferedWriter[proxies.length];
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int send(String mess) throws Exception {
|
||||
if (connections == null) {
|
||||
log.error("Not initialized");
|
||||
return -1;
|
||||
}
|
||||
String requestLine = "POST " + config.getProxyURL() + " HTTP/1.0";
|
||||
|
||||
for (int i = 0; i < connections.length; i++) {
|
||||
if (connections[i] == null) {
|
||||
try {
|
||||
if (config.getHost() != null) {
|
||||
connections[i] = new Socket();
|
||||
InetAddress addr = InetAddress.getByName(config.getHost());
|
||||
InetSocketAddress addrs = new InetSocketAddress(addr, 0);
|
||||
connections[i].setReuseAddress(true);
|
||||
connections[i].bind(addrs);
|
||||
addrs = new InetSocketAddress(proxies[i].address, proxies[i].port);
|
||||
connections[i].connect(addrs);
|
||||
} else
|
||||
connections[i] = new Socket(proxies[i].address, proxies[i].port);
|
||||
connectionReaders[i] = new BufferedReader(new InputStreamReader(connections[i].getInputStream()));
|
||||
connectionWriters[i] = new BufferedWriter(new OutputStreamWriter(connections[i].getOutputStream()));
|
||||
} catch (Exception ex) {
|
||||
log.error("Unable to connect to proxy: " + ex);
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
if (connections[i] == null)
|
||||
continue; // try next proxy in the list
|
||||
BufferedWriter writer = connectionWriters[i];
|
||||
try {
|
||||
writer.write(requestLine);
|
||||
writer.write("\r\n");
|
||||
writer.write("Content-Length: " + mess.length() + "\r\n");
|
||||
writer.write("User-Agent: HeartbeatListener/1.0\r\n");
|
||||
writer.write("Connection: Keep-Alive\r\n");
|
||||
writer.write("\r\n");
|
||||
writer.write(mess);
|
||||
writer.write("\r\n");
|
||||
writer.flush();
|
||||
} catch (Exception ex) {
|
||||
log.error("Unable to send collected load information to proxy: " + ex);
|
||||
close(i);
|
||||
}
|
||||
if (connections[i] == null)
|
||||
continue; // try next proxy in the list
|
||||
|
||||
/* Read httpd answer */
|
||||
String responseStatus = connectionReaders[i].readLine();
|
||||
if (responseStatus == null) {
|
||||
log.error("Unable to read response from proxy");
|
||||
close(i);
|
||||
continue;
|
||||
} else {
|
||||
responseStatus = responseStatus.substring(responseStatus.indexOf(' ') + 1, responseStatus.indexOf(' ', responseStatus.indexOf(' ') + 1));
|
||||
int status = Integer.parseInt(responseStatus);
|
||||
if (status != 200) {
|
||||
log.error("Status is " + status);
|
||||
close(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// read all the headers.
|
||||
String header = connectionReaders[i].readLine();
|
||||
int contentLength = 0;
|
||||
while (!"".equals(header)) {
|
||||
int colon = header.indexOf(':');
|
||||
String headerName = header.substring(0, colon).trim();
|
||||
String headerValue = header.substring(colon + 1).trim();
|
||||
if ("content-length".equalsIgnoreCase(headerName)) {
|
||||
contentLength = Integer.parseInt(headerValue);
|
||||
}
|
||||
header = connectionReaders[i].readLine();
|
||||
}
|
||||
if (contentLength > 0) {
|
||||
char[] buf = new char[512];
|
||||
while (contentLength > 0) {
|
||||
int thisTime = (contentLength > buf.length) ? buf.length : contentLength;
|
||||
int n = connectionReaders[i].read(buf, 0, thisTime);
|
||||
if (n <= 0) {
|
||||
log.error("Read content failed");
|
||||
close(i);
|
||||
break;
|
||||
} else {
|
||||
contentLength -= n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close connection.
|
||||
* @param i The index of the connection that will be closed
|
||||
*/
|
||||
protected void close(int i) {
|
||||
try {
|
||||
if (connectionReaders[i] != null) {
|
||||
connectionReaders[i].close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
connectionReaders[i] = null;
|
||||
try {
|
||||
if (connectionWriters[i] != null) {
|
||||
connectionWriters[i].close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
connectionWriters[i] = null;
|
||||
try {
|
||||
if (connections[i] != null) {
|
||||
connections[i].close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
connections[i] = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user