java - servlet - Obtenga el número de puerto del servidor de tomcat sin una solicitud
servlets java (11)
Anteriormente en un gran proyecto distribuido, el diseño que utilicé consistía en hacer que el servicio centralizado inicializara los diversos servicios con la URL (y el puerto) del servicio central.
Obviamente, esto significa que el servicio central debe mantener una lista de los servicios (URL y puerto) para inicializar.
¿Hay alguna configuración o API de Tomcat disponible que pueda indicarle a una aplicación (probablemente al inicio) qué puerto se está ejecutando sin una solicitud?
Imagine un escenario en el que hay dos aplicaciones web ejecutándose en el mismo Tomcat y una de las cuales necesita invocar un servicio web desde la otra. No queremos que la solicitud abandone Tomcat (si usa el nombre del servidor Apache o la URL absoluta, la solicitud se cerrará y volverá y puede ir a cualquier instancia) y volverá a entrar. Para eso, sé el nombre de la máquina pero no forma de obtener el número de puerto. Sé que puedo codificar esta información pero no quiero hacer esto porque quiero que mi archivo war
sea independiente del servidor de aplicaciones.
Sé que podemos encontrarlo si tenemos una HTTPServletRequest
Esto funciona solo para Tomcat 6 y no funcionará en Tomcat 7
Con este:
List<String> getEndPoints() throws MalformedObjectNameException,
NullPointerException, UnknownHostException, AttributeNotFoundException,
InstanceNotFoundException, MBeanException, ReflectionException {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
QueryExp subQuery1 = Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"));
QueryExp subQuery2 = Query.anySubString(Query.attr("protocol"), Query.value("Http11"));
QueryExp query = Query.or(subQuery1, subQuery2);
Set<ObjectName> objs = mbs.queryNames(new ObjectName("*:type=Connector,*"), query);
String hostname = InetAddress.getLocalHost().getHostName();
InetAddress[] addresses = InetAddress.getAllByName(hostname);
ArrayList<String> endPoints = new ArrayList<String>();
for (Iterator<ObjectName> i = objs.iterator(); i.hasNext();) {
ObjectName obj = i.next();
String scheme = mbs.getAttribute(obj, "scheme").toString();
String port = obj.getKeyProperty("port");
for (InetAddress addr : addresses) {
if (addr.isAnyLocalAddress() || addr.isLoopbackAddress() ||
addr.isMulticastAddress()) {
continue;
}
String host = addr.getHostAddress();
String ep = scheme + "://" + host + ":" + port;
endPoints.add(ep);
}
}
return endPoints;
}
Obtendrás una Lista como esta:
[http://192.168.1.22:8080]
Estos tipos de servidores están diseñados para poder escuchar en puertos (casi) arbitrarios y ocultar estos detalles de las aplicaciones que normalmente no necesitan saber.
La única forma es leer los archivos de configuración usted mismo y tener acceso a los argumentos de la línea de comandos que iniciaron el servidor donde los archivos de configuración pueden haber sido anulados. Debe saber mucho sobre el sistema en el que se está ejecutando para que esto funcione. No hay forma de hacerlo de forma portátil.
Incluso si hubiera, hay casos en los que simplemente no importa estar detrás de un NAT, ciertos firewalls, etc.
Hmm, ¿cómo comenzaría una aplicación en Tomcat sin una solicitud? Tal vez estoy loco por un momento aquí, pero no creo que ninguna clase se cargue hasta que llegue una solicitud. Claro, podrías tener clases independientes de cualquier solicitud en particular, pero necesitarían una solicitud para que las despidieran en algún momento.
No estoy del todo seguro de si puede acceder al puerto Tomcat desde el código en la configuración de entorno que necesita. ¿Consideró realmente tener la URL completa del servicio web pasado como un param / configuración de configuración (probablemente en un archivo .properties) a la aplicación?
De esta forma no tendrías que codificar el puerto y desvincular ambas aplicaciones para que técnicamente puedas tener el servicio web en un tomcat externo, pero aún acceder a él simplemente cambiando la propiedad, evitando la reconstrucción del código.
Para cualquiera que esté interesado en cómo resolvimos esto, aquí está el código simulado
Server server = ServerFactory.getServer();
Service[] services = server.findServices();
for (Service service : services) {
for (Connector connector : service.findConnectors()) {
ProtocolHandler protocolHandler = connector.getProtocolHandler();
if (protocolHandler instanceof Http11Protocol
|| protocolHandler instanceof Http11AprProtocol
|| protocolHandler instanceof Http11NioProtocol) {
serverPort = connector.getPort();
System.out.println("HTTP Port: " + connector.getPort());
}
}
}
Podría usar crossContext . Pero no creo que sea un servidor de aplicaciones agnóstico.
Compartiría una clase personalizada, comportándome como un registro de aplicaciones en ejecución en la misma instancia de tomcat a través de JNDI, como expliqué here .
Durante el inicio, a través de ContextListener
o mediante un evento de contenedor Spring, ContextListener
el registro a través de una búsqueda JNDI, agrego la instancia de mi aplicación web con una URL obtenida del servletcontext.contextpath y finalmente registro un oyente para escuchar otras aplicaciones que se registran. Eso es lo más agnóstico del servidor que se me ocurre.
La obtención del puerto no será independiente del servidor, debe usar un parámetro de contexto.
EDITAR: Lo siento, me olvidé de decir que lo que he descrito es compartir objetos entre contextos, pero no, no puede no saber el puerto a menos que use alguna API de servidor (no es para nada agnóstico).
Si desea acceder a una aplicación en la misma instancia de servidor, simplemente omita la parte del servidor de la URL. Algunos ejemplos de lo que puedes lograr El documento actual está en http://example.com:8080/app2/doc.html
-
xxx.html
convierte enhttp://example.com:8080/app2/xxx.html
-
../xxx.html
convierte enhttp://example.com:8080/xxx.html
-
../xxx.html
convierte enhttp://example.com:8080/xxx.html
-
../foo/xxx.html
convierte enhttp://example.com:8080/foo/xxx.html
-
../../xxx.html
convierte enhttp://example.com:8080/xxx.html
(no hay forma de ir más allá de la raíz) -
/xxx.html
convierte enhttp://example.com:8080/xxx.html
Esto es probablemente lo que buscas. -
//other.com/xxx.html
convierte enhttp://example.com:8080/xxx.html
Útil si desea mantener "https:"
El número de puerto del servidor no existe. Puede tener cualquier cantidad de números de puerto. Entonces, lo que estás preguntando no tiene sentido. El número de puerto asociado con una solicitud específica tiene sentido.
public void getIpAddressAndPort()
throws MalformedObjectNameException, NullPointerException,
UnknownHostException {
MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"),
Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
String host = InetAddress.getLocalHost().getHostAddress();
String port = objectNames.iterator().next().getKeyProperty("port");
System.out.println("IP Address of System : "+host );
System.out.println("port of tomcat server : "+port);
}
- Obtener retención de MBean / JMX Objeto para Tomcat / Server Instance
- Obtenga datos relacionados con la instancia del servidor virtual desde allí
Compruebe http://svn-mirror.glassfish.org/glassfish-svn/tags/embedded-gfv3-prelude-b07/web/web-glue/src/main/java/com/sun/enterprise/web/WebContainer.java para referencia
El contenido del MBeanServer se puede exponer a través de varios protocolos, implementados por conectores de protocolo [RMI / IIOP] o adaptadores de protocolo [SNMP / HTTP] . En este caso, el uso del adaptador SNMP será un mejor enfoque para que se pueda colocar una trampa SNMP sin conocer la IP / puerto exacta de otros servidores de aplicaciones