Java 无需请求即可从tomcat获取服务器端口号

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3867197/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 06:03:32  来源:igfitidea点击:

Get the server port number from tomcat without a request

javatomcatjakarta-eetomcat6application-server

提问by Teja Kantamneni

Is there any Tomcat API or configuration available which can tell an application (probably on startup), what port its running on without a request?

是否有任何可用的 Tomcat API 或配置可以告诉应用程序(可能在启动时),它在没有请求的情况下运行在哪个端口上?

Imagine a scenario where there are two web applications running in the same Tomcat and one of which need to invoke a web service from the other one. We don't want the request to leave the Tomcat (if you use the Apache server name or absolute URL, the request will go out and come back again and it can go to any instance) and come back in. For that I know the name of the machine but no way to get the port number. I know I can hard code this information but I don't want to do this as I want my warfile to be application server agnostic.

想象一个场景,在同一个 Tomcat 中运行着两个 Web 应用程序,其中一个需要从另一个调用 Web 服务。我们不希望请求离开 Tomcat(如果您使用 Apache 服务器名称或绝对 URL,请求将出去并再次返回,它可以转到任何实例)并返回。为此我知道机器的名称,但无法获取端口号。我知道我可以对这些信息进行硬编码,但我不想这样做,因为我希望我的war文件与应用程序服务器无关。

I know that we can find it if we have a HTTPServletRequest

我知道如果我们有 HTTPServletRequest 就可以找到它

This works only for Tomcat 6 and will not work on Tomcat 7

这仅适用于 Tomcat 6,不适用于 Tomcat 7

采纳答案by Teja Kantamneni

For anybody who is interested in how we solved this, here is the mock code

对于任何对我们如何解决这个问题感兴趣的人,这里是模拟代码

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());
                }
            }


        }

回答by Jay

Hmm, how would an application get started in Tomcat without a request? Maybe I'm going brain dead for a moment here, but I don't think any classes will load until a request hits. Sure, you could have classes independent of any particular request, but they'd need a request to get them fired off at some point.

嗯,应用程序如何在没有请求的情况下在 Tomcat 中启动?也许我在这里会死掉一会儿,但我认为在请求命中之前不会加载任何类。当然,您可以拥有独立于任何特定请求的类,但是它们需要一个请求才能在某个时候触发它们。

回答by Sarat

I am not entirely sure if you can access the Tomcat port from code in the environment configuration you need. Did you consider actually having the full URL to the web service passed as a configuration param/setting (probably in a .properties file) to the app?

我不完全确定您是否可以从所需的环境配置中的代码访问 Tomcat 端口。您是否考虑过将 Web 服务的完整 URL 作为配置参数/设置(可能在 .properties 文件中)传递给应用程序?

This way you wouldn't have to hardcode the port and de-couple both your apps so that you could technically have the web service on an external tomcat but still access it by just changing the property, avoiding code re-build.

通过这种方式,您不必对端口进行硬编码并将两个应用程序解耦,这样您就可以在技术上在外部 tomcat 上拥有 Web 服务,但仍然只需更改属性即可访问它,避免重新构建代码。

回答by mschonaker

You could use crossContext. But I don't think that's app server agnostic.

您可以使用crossContext。但我认为这与应用服务器无关。

I would share a custom class, behaving as a registry of running applications in the same tomcat instance through JNDI, as I explained here.

我将共享一个自定义类,作为通过 JNDI 在同一个 tomcat 实例中运行应用程序的注册表,正如我在此处解释的。

During startup, through a ContextListeneror through an Spring container event, I would obtain the registry through a JNDI lookup, add my web app instance with an url obtained from the servletcontext.contextpath, and finally register a listener to hear other applications registering themselves. That's the more server agnostic I can think of.

在启动期间,通过一个ContextListener或一个 Spring 容器事件,我将通过 JNDI 查找获取注册表,使用从 servletcontext.contextpath 获取的 url 添加我的 web 应用程序实例,最后注册一个侦听器以听取其他应用程序的注册。这是我能想到的更多的服务器不可知论者。

Obtaining the port won't be server agnostic, you should use a context parameter.

获取端口与服务器无关,您应该使用上下文参数。

EDIT: I'm sorry, forgot to say that what I've described is to share objects among contexts, but no, you can't not know the port unless you use some server API (not agnostic at all).

编辑:对不起,忘了说我所描述的是在上下文之间共享对象,但是不,除非您使用某些服务器 API(根本不可知),否则您不能不知道端口。

回答by Ashley Walton

Previously on a large distributed project, the design I used was to have the centralised service initialise the several services with the central service's URL(& port).

以前在一个大型分布式项目中,我使用的设计是让集中式服务使用中央服务的 URL(& 端口) 初始化几个服务。

Obviously this means that the central service must maintain a list of the services (URL & port) to initialise.

显然,这意味着中央服务必须维护要初始化的服务列表(URL 和端口)。

回答by user207421

Theserver port number doesn't exist. It can have any number of port numbers. So what you're asking doesn't make sense. Theport number associated with a specific request doesmake sense.

服务器端口号不存在。它可以有任意数量的端口号。所以你问的没有意义。与特定请求关联端口号确实有意义。

回答by Puspendu Banerjee

  • Get Hold of MBean/JMX Object for Tomcat/Server Instance
  • Get Virtual Server Instance Related Data from there
  • 获取 Tomcat/服务器实例的 MBean/JMX 对象
  • 从那里获取虚拟服务器实例相关数据

Check http://svn-mirror.glassfish.org/glassfish-svn/tags/embedded-gfv3-prelude-b07/web/web-glue/src/main/java/com/sun/enterprise/web/WebContainer.javafor reference

检查http://svn-mirror.glassfish.org/glassfish-svn/tags/embedded-gfv3-prelude-b07/web/web-glue/src/main/java/com/sun/enterprise/web/WebContainer.java以供参考

The content of the MBeanServer can then be exposed through variousprotocols, implemented by protocol connectors[RMI/IIOP], or protocol adapters[SNMP/HTTP]. In this case, use of SNMP adapter will be a better approach so that a SNMP trap can be placed without knowing the exact IP/port of other Application Servers

然后 MBeanServer的内容可以通过各种协议公开,由协议连接器 [RMI/IIOP] 或协议适配器 [SNMP/HTTP] 实现。在这种情况下,使用 SNMP 适配器将是一种更好的方法,以便可以在不知道其他应用程序服务器的确切 IP/端口的情况下放置 SNMP 陷阱

回答by Aaron Digulla

If you want to access an application on the same server instance, just omit the server part of the URL. Some examples what you can achieve. The current document is at http://example.com:8080/app2/doc.html

如果要访问同一服务器实例上的应用程序,只需省略 URL 的服务器部分。您可以实现的一些示例。当前文档位于http://example.com:8080/app2/doc.html

  • xxx.htmlbecomes http://example.com:8080/app2/xxx.html
  • ../xxx.htmlbecomes http://example.com:8080/xxx.html
  • ../xxx.htmlbecomes http://example.com:8080/xxx.html
  • ../foo/xxx.htmlbecomes http://example.com:8080/foo/xxx.html
  • ../../xxx.htmlbecomes http://example.com:8080/xxx.html(there is no way to go beyond the root)
  • /xxx.htmlbecomes http://example.com:8080/xxx.htmlThis is probably what you look for.
  • //other.com/xxx.htmlbecomes http://example.com:8080/xxx.htmlUseful if you want to keep "https:"
  • xxx.html变成 http://example.com:8080/app2/xxx.html
  • ../xxx.html变成 http://example.com:8080/xxx.html
  • ../xxx.html变成 http://example.com:8080/xxx.html
  • ../foo/xxx.html变成 http://example.com:8080/foo/xxx.html
  • ../../xxx.html变成http://example.com:8080/xxx.html(没有办法超越根)
  • /xxx.html变成http://example.com:8080/xxx.html这可能就是你要找的。
  • //other.com/xxx.htmlhttp://example.com:8080/xxx.html如果您想保留“https:”,则变得有用

回答by Allen

These types of servers are designed to be able to listen on (almost) arbitrary ports and to hide these details from the contained applications which normally do not need to know.

这些类型的服务器被设计为能够侦听(几乎)任意端口,并将这些细节隐藏在通常不需要知道的包含应用程序中。

The only way is to read the configuration files yourself andhave access to the command line arguments that started the server where the configuration files may have been overridden. You have to know a lot about the system you are running on for this to work. There is no way of doing it portably.

唯一的方法是自己读取配置文件访问启动服务器的命令行参数,其中配置文件可能已被覆盖。您必须对正在运行的系统有很多了解才能使其正常工作。没有办法便携地做到这一点。

Even if there were, there are cases in which it simply does not matter like being behind a NAT, certain firewalls, etc.

即使有,在某些情况下也无所谓,例如位于 NAT、某些防火墙等之后。

回答by ggrandes

With this:

有了这个:

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;
}

You will get a List like this:

你会得到一个这样的列表:

[http://192.168.1.22:8080]