英文:
DefaultHttpClient call throws connection refused in the same tomcat with public ip
问题
以下是你提供的内容的翻译:
centos 7,tomcat 8.5。
`a.war` 和 `rest.war` 在 **同一个 tomcat** 中。
`a.war` 使用以下代码调用 `rest.war`:
```java
import org.apache.http.impl.client.DefaultHttpClient;
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
StringEntity se = new StringEntity(json.toString());
se.setContentType("text/json");
se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
httpPost.setEntity(se);
HttpResponse response = httpClient.execute(httpPost);
然而,如果 HttpPost(url)
的 url
是 <public ip>:80
,那么 httpClient.execute(httpPost)
将会抛出 connection refused
异常。
而如果 HttpPost(url)
的 url
是 localhost:80
或者 127.0.0.1:80
,那么 httpClient.execute(httpPost)
就会成功。
为什么会这样?如何解决这个问题?
注意:如果我在我的计算机上使用公共 IP 地址访问 a.war
,例如 http://<public ip>/a
,所有操作都是成功的。
我的 tomcat 连接器配置如下:
<Connector
port="80"
protocol="HTTP/1.1"
connectionTimeout="60000"
keepAliveTimeout="15000"
maxKeepAliveRequests="-1"
maxThreads="1000"
minSpareThreads="200"
maxSpareThreads="300"
minProcessors="100"
maxProcessors="900"
acceptCount="1000"
enableLookups="false"
executor="tomcatThreadPool"
maxPostSize="-1"
compression="on"
compressionMinSize="1024"
redirectPort="8443" />
我的服务器没有域名,只有一个公共 IP 地址,其 /etc/hosts
文件内容如下:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
附带一些在服务器上运行的命令结果:
ss -nltp
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:111 *:* users:(("rpcbind",pid=643,fd=8))
LISTEN 0 128 *:80 *:* users:(("java",pid=31986,fd=53))
LISTEN 0 128 *:22 *:* users:(("sshd",pid=961,fd=3))
LISTEN 0 1 127.0.0.1:8005 *:* users:(("java",pid=31986,fd=68))
LISTEN 0 128 :::111 :::* users:(("rpcbind",pid=643,fd=11))
LISTEN 0 128 :::22 :::* users:(("sshd",pid=961,fd=4))
LISTEN 0 80 :::3306 :::* users:(("mysqld",pid=1160,fd=19))
netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 643/rpcbind
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 31986/java
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 961/sshd
tcp 0 0 127.0.0.1:8005 0.0.0.0:* LISTEN 31986/java
tcp6 0 0 :::111 :::* LISTEN 643/rpcbind
tcp6 0 0 :::22 :::* LISTEN 961/sshd
tcp6 0 0 :::3306 :::* LISTEN 1160/mysqld
ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 1396428 bytes 179342662 (171.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1396428 bytes 179342662 (171.0 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
p2p1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.25 netmask 255.255.255.0 broadcast 192.168.1.255
ether f8:bc:12:a3:4f:b7 txqueuelen 1000 (Ethernet)
RX packets 5352432 bytes 3009606926 (2.8 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2839034 bytes 559838396 (533.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: p2p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether f8:bc:12:a3:4f:b7 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.25/24 brd
<details>
<summary>英文:</summary>
centos 7, tomcat 8.5.
`a.war` and `rest.war` are **in the same tomcat**.
`a.war` use following code to call `rest.war`:
import org.apache.http.impl.client.DefaultHttpClient;
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
StringEntity se = new StringEntity(json.toString());
se.setContentType("text/json");
se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
httpPost.setEntity(se);
HttpResponse response = httpClient.execute(httpPost);
however, if `url` of `HttpPost(url)` is `<public ip>:80`, then `httpClient.execute(httpPost)` will throw `connection refused`.
while if `url` of `HttpPost(url)` is `localhost:80` or `127.0.0.1:80`, then `httpClient.execute(httpPost)` is success.
why? and how can solve this problem?
**Note: if I access `a.war` from browser with public ip like `http://<public ip>/a` in my computer, all operations are success.**
my tomcat connector is:
<Connector
port="80"
protocol="HTTP/1.1"
connectionTimeout="60000"
keepAliveTimeout="15000"
maxKeepAliveRequests="-1"
maxThreads="1000"
minSpareThreads="200"
maxSpareThreads="300"
minProcessors="100"
maxProcessors="900"
acceptCount="1000"
enableLookups="false"
executor="tomcatThreadPool"
maxPostSize="-1"
compression="on"
compressionMinSize="1024"
redirectPort="8443" />
my server has no domain, only has a public ip, its `/etc/hosts` is:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
**updated with some commands run in server:**
ss -nltp
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 :111 : users:(("rpcbind",pid=643,fd=8))
LISTEN 0 128 :80 : users:(("java",pid=31986,fd=53))
LISTEN 0 128 :22 : users:(("sshd",pid=961,fd=3))
LISTEN 0 1 127.0.0.1:8005 : users:(("java",pid=31986,fd=68))
LISTEN 0 128 :::111 ::: users:(("rpcbind",pid=643,fd=11))
LISTEN 0 128 :::22 ::: users:(("sshd",pid=961,fd=4))
LISTEN 0 80 :::3306 ::: users:(("mysqld",pid=1160,fd=19))
netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 643/rpcbind
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 31986/java
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 961/sshd
tcp 0 0 127.0.0.1:8005 0.0.0.0:* LISTEN 31986/java
tcp6 0 0 :::111 :::* LISTEN 643/rpcbind
tcp6 0 0 :::22 :::* LISTEN 961/sshd
tcp6 0 0 :::3306 :::* LISTEN 1160/mysqld
ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 1396428 bytes 179342662 (171.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1396428 bytes 179342662 (171.0 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
p2p1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.25 netmask 255.255.255.0 broadcast 192.168.1.255
ether f8:bc:12:a3:4f:b7 txqueuelen 1000 (Ethernet)
RX packets 5352432 bytes 3009606926 (2.8 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2839034 bytes 559838396 (533.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: p2p1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether f8:bc:12:a3:4f:b7 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.25/24 brd 192.168.1.255 scope global noprefixroute dynamic p2p1
valid_lft 54621sec preferred_lft 54621sec
route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default gateway 0.0.0.0 UG 100 0 0 p2p1
192.168.1.0 0.0.0.0 255.255.255.0 U 100 0 0 p2p1
ip route
default via 192.168.1.1 dev p2p1 proto dhcp metric 100
192.168.1.0/24 dev p2p1 proto kernel scope link src 192.168.1.25 metric 100
iptables -L -n -v --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
</details>
# 答案1
**得分**: 3
你可能已经配置了以下其中一种情况:
1. 防火墙可能封锁了公共IP的端口,导致流量无法通过。
2. Tomcat可能绑定了特定的IP地址,例如localhost(请参阅tomcat的server.xml中的Connector元素)。
3. Apache httpd、nginx或其他反向代理可能处理各种虚拟主机名,并且它们可能会对localhost和公共IP处理方式不同。
4. 端口转发 - 如果你只是将localhost:80转发到localhost:8080(tomcat的默认端口),那么公共IP:80上可能没有将流量转发到那个端口的内容。
在你的评论后进行编辑:
5. 进入流量似乎没有问题,但是出站流量存在问题。补充来自@stringy05的评论:检查所涉及的IP地址是否可以从你的服务器路由通。你正在从该服务器连接到任何IP地址,因此可以使用其他方法创建出站连接,例如`curl`。
关于#1和#3的解释:
如果你连接到外部的http服务器,它会根据使用的主机名来不同方式处理请求。很可能IP“主机名”被封锁,可能是由于高级防火墙,或者是由于Web服务器本身与URL不同的处理方式。在大多数情况下,你可以通过从任何其他系统连接到相关的Web服务器来进行检查,例如使用你自己的浏览器。
<details>
<summary>英文:</summary>
You probably have configured one of these:
1. Firewall public IP's ports, so that nothing goes through.
2. Tomcat may bind a specific IP, e.g. localhost (see Connector elements in tomcat's server.xml)
3. Apache httpd, nginx or another reverse proxy might handle various virtual host names, and also they might handle localhost different than the public IP
4. Port Forwarding - if you only forward localhost:80 to localhost:8080 (tomcat's default port), you might not have anything on publicip:80 that forwards that traffic as well.
Edit after your comment:
5. incoming traffic seems to be fine, but outgoing you do have those problems. Adding from @stringy05's comment: Check if the IP in question is routable from your server: You're connecting to whatever IP *from that server*, so use another means to create an outgoing connection, e.g. `curl`.
Explanation for #1 & #3:
If you connect to an external http server, it will handle the request differently based on the hostname used. It might well be that the IP "hostname" is blocked, either by a high level firewall, or just handled differently than the URL by the webserver itself. In most cases you can check this by connecting to the webserver in question from any other system, e.g. your own browser.
</details>
# 答案2
**得分**: 1
尝试将您的公共域名添加到服务器的本地/etc/hosts文件中,就像这样:
127.0.0.1 localhost YOURPUBLIC.DOMAIN.NAME
这样,您的Java代码就不需要尝试使用外部IP地址,而是直接连接到Tomcat。
祝您好运!
<details>
<summary>英文:</summary>
Try putting your public domain names into the local /etc/hosts file of your server like this:
127.0.0.1 localhost YOURPUBLIC.DOMAIN.NAME
This way your Java code does not need to try to use the external IP-adres but instead connects directly to Tomcat.
Good luck!
</details>
# 答案3
**得分**: 1
如果Tomcat正在侦听(绑定)您的公共IP地址,它应该可以工作,但如果您的公共IP地址属于其他设备,比如家用SOHO路由器,那么您的问题类似于这种情况:
https://superuser.com/questions/208710/public-ip-address-answered-by-router-not-internal-web-server-with-port-forwardi
但是如果没有DNS名称,您不能简单地在/etc/hosts中添加一行,但您可以将公共IP地址添加到其中一个网络接口卡(NIC),如lo(环回)、eth0等,如下面这些文章所述:
- https://www.garron.me/en/linux/add-secondary-ip-linux.html
- https://www.thegeekdiary.com/centos-rhel-6-how-to-addremove-additional-ip-addresses-to-a-network-interface/
例如,对于公共IP地址1.2.3.4,您需要执行以下操作(这只会在下次重新启动之前生效,最坏的情况可能会干扰您连接到服务器,比如SSH!):
`sudo ip addr add 1.2.3.4/32 dev lo`
为了更好地理解您的设置,获取以下命令的输出可能会很有用,请在问题中分享它,但请务必将公共IP地址匿名化处理:
任选以下之一(ss = 套接字状态,是对老式netstat的更新替代):
1. ss -nltp
2. netstat -nltp
以及以下之一:
1. ifconfig
2. ip addr show
最后,任选以下之一:
1. route
2. ip route
我并不认为我们需要了解您的防火墙配置,但如果您使用防火墙,在进行上述操作时关注它可能会很有趣:
iptables -L -n -v --line-numbers
<details>
<summary>英文:</summary>
If Tomcat is listening (bound) to your public IP-adres it should work, but maybe your public IP-adres belongs to some other device, like a SOHO router, than your problem is similar to this:
https://superuser.com/questions/208710/public-ip-address-answered-by-router-not-internal-web-server-with-port-forwardi
But without an DNS name you cannot simply add a line to /etc/hosts but you can add the public IP-adres to one of your Network Interfaces Cards (NIC) like lo (loopback), eth0, etc. as described in one of these articles:
- https://www.garron.me/en/linux/add-secondary-ip-linux.html
- https://www.thegeekdiary.com/centos-rhel-6-how-to-addremove-additional-ip-addresses-to-a-network-interface/
E.g. with public IP-address 1.2.3.4 you would need (which will only be effective until next reboot and worst case might interfere with your ability to connect to the server with e.g. SSH!):
`sudo ip addr add 1.2.3.4/32 dev lo`
It may be useful to have the output of these commands to better understand your setup, feel free to share it in your question, with consistently anonymized public IP-adres):
Either one of these (ss = socket stat, newer replacement for good old netstat):
1. ss -nltp
1. netstat -nltp
And one of these:
1. ifconfig
1. ip addr show
And last but not least either one of these:
1. route
1. ip route
I don't expect that we need to know your firewall config, but if you use it, it may be interesting to keep an eye on it while you are at it:
iptables -L -n -v --line-numbers
</details>
# 答案4
**得分**: 1
我认为 curl 的超时解释了这个问题 - 你的某个防火墙规则阻止了服务器访问公共 IP 地址。
如果没有理由不能使用 localhost 或本地主机名访问服务,那就这么做。但是,如果你需要通过公共 IP 调用服务,那么问题就是要弄清楚为什么请求从服务器那里超时。
一些常见的可能原因:
- 服务器实际上可能没有互联网访问权限 - 你可以尝试执行 `curl https://www.google.com` 吗?
- 可能需要使用转发代理 - 系统管理员会了解这种情况。
- 可能在服务器周围的某些基础设施上设置了 IP 白名单 - 比如 AWS 安全组、负载均衡器 IP 白名单之类的。要修复这个问题,你需要知道服务器的公共 IP,可以执行 `curl https://canihazip.com/s` 来获取并将其添加到白名单中。
<details>
<summary>英文:</summary>
I think the curl timeout explains it - you have a firewall rule somewhere that is stopping the server accessing the public IP address.
If there's no reason the service can't be accessed using localhost or the local hostname then do that but if you need to call the service via a public IP then it's a matter of working out why the request gets a timeout from the server.
Some usual suspects:
- The server might not actually have internet access - can you `curl https://www.google.com`?
- There might be a forward proxy required - a sys admin will know this sort of thing
- There might be IP whitelisting on some infra around your server - think AWS security groups, load balancer IP whitelists that sort of thing. To fix that you need to know the public IP of your server `curl https://canihazip.com/s` and get that added to the whitelist
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论