背景说明

在我们的工具开发过程中,需要向预发环境请求一些数据。在本地请求预发环境的数据,我们是通过本地绑定host地址来访问预发链接的。我们应用是部署在Docker容器中的,发送HTTP请求用的是HttpClient工具包。那么在工具中要如何实现我们的这个需求呢,本文就基于我们实际的操作过程做一个简单记录。

hosts文件的作用

在开始之前,我们需要明确一下本地绑定host的作用原理。

DNS(Domain Name System)服务是和 HTTP 协议一样位于应用层的协议,它提供域名到 IP 地址之间的解析服务。用户通常使用主机名或域名来访问对方的计算机,而不是直接通过 IP 地址访问。因为与 IP 地址的一组纯数字相比,用字母配合数字的表示形式来指定计算机名更符合人类的记忆习惯。

但要让计算机去理解名称,相对而言就变得困难了。因为计算机更擅长处理一长串数字。为了解决上述的问题,DNS 服务应运而生。DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。(——from 《图解HTTP》)

而Hosts文件是一个没有扩展名的操作系统文件,以表的形式存储了主机名和IP地址的映射关系。Hosts又称host table,译为“主机表”。现代系统中,虽然DNS取代了主机表,但主机表的应用依旧很广。和DNS不同的是,用户可以直接对Hosts文件进行控制。Hosts文件是大多数系统都存在的一个小型主机表。Hosts文件中包含了本地网络重要的主机名和地址信息,查询Hosts文件得到的结果比通过查询DNS得到的结果优先级更高。(——from 维基百科)

如下图所示,正常的DNS域名解析过程如下,而如果要解析的域名配置了hosts文件,那么就不需要再去DNS服务器做域名解析了。

docker 容器里执行host指令(用HttpClient发送需要绑定hosts的请求)(1)

解决方案思路一:在Docker容器中也配置一下hosts文件

这是很自然就想到的一个方案,因为既然本地可以通过配置hosts文件实现预发环境的访问,那么在Docker容器中配置hosts文件应该也能实现同样的效果。而要实现在Docker容器中配置hosts文件,通常也可以用不同的方式实现。

  • 方式一:Docker容器启动之后,进入Docker容器中直接编辑/etc/hosts文件

这种方式很直接,就是进入Docker容器之后sudo vim /etc/hosts,然后把要绑定的域名映射加到文件中即可。但是这种方式有很大的局限性:一是一旦Docker容器重启,之前配置的hosts内容就会失效;二是如果机器比较多,配置器起来就相当麻烦

  • 方式二:通过Docker启动参数配置hosts

示例如下:

# 添加单个hosts docker run -it nginx --add-host=localhost:127.0.0.1 # 添加多个hosts docker run -it nginx --add-host=localhost:127.0.0.1 --add-host=example.com:127.0.0.1 COPY

  • 方式三:在项目的Dockerfile中实现/etc/hosts的配置

如下,首先我在Dockerfile设置环境信息的地方加了一下内容:

# 在预发配置hosts echo "setting hosts..." if [ "$CURRENT_ENV" = "staging" ]; then echo "now is staging env, need to setting hosts..." sh "$APP_HOME/bin/hosts.sh" fiCOPY

然后,在hosts.sh脚本中实现/etc/hosts配置(注意:普通用户一般没有/etc/hosts文件的写权限,所以在写入文件之前,增加写权限至关重要,否则会报“权限不够”从而配置失败)

#!/bin/sh echo "starting to config hosts..." echo "current user is: " whoami sudo chmod 666 /etc/hosts sudo echo "# custom hosts" >> /etc/hosts sudo echo "123.123.123.123 pre1-xx.yy.com" >> /etc/hosts sudo echo "123.123.123.123 pre2-xx.yy.com" >> /etc/hosts echo "hosts config done"COPY

思路二:通过HttpClient的DnsResolver解决

这种方式的核心是实现 org.apache.http.conn.DnsResolver的resolve()方法,然后在HttpClientConnectionManager实例化的时候传入DnsResolver的实例。

package com.amwalle.walle.util; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.DnsResolver; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.BasicHttpClientConnectionManager; import org.apache.http.util.EntityUtils; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; /** * @program: walle * @description: send a request * @author: pengyongjun * @create: 2022.07.18 19:18 **/ public class HttpUtil { public static CloseableHttpClient getHttpClient() throws KeyManagementException, NoSuchAlgorithmException { TrustManager[] trustManagers = new TrustManager[1]; TrustManager trustManager = new TheTrustManager(); trustManagers[0] = trustManager; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagers, null); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); Registry socketFactoryRegistry = RegistryBuilder.create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", socketFactory).build(); MyDnsResolver dnsResolver = new MyDnsResolver("pre1-xx.yy.com", "123.123.123.123"); dnsResolver.addResolve("pre2-xx.yy.com", "123.123.123.123"); HttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(socketFactoryRegistry, null, null, dnsResolver); return HttpClients.custom().setConnectionManager(connectionManager).build(); } private static class TheTrustManager implements TrustManager, X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } private static class MyDnsResolver implements DnsResolver { private final Map MAPPINGS; public MyDnsResolver(String host, String ip) { MAPPINGS = new HashMap<>(); try { MAPPINGS.put(host, new InetAddress[]{InetAddress.getByName(ip)}); } catch (UnknownHostException e) { e.printStackTrace(); } } public void addResolve(String host, String ip) { try { MAPPINGS.put(host, new InetAddress[]{InetAddress.getByName(ip)}); } catch (UnknownHostException e) { e.printStackTrace(); } } @Override public InetAddress[] resolve(String host) throws UnknownHostException { return MAPPINGS.containsKey(host) ? MAPPINGS.get(host) : new InetAddress[0]; } } public static void main(String[] args) { try (final CloseableHttpClient httpClient = HttpUtil.getHttpClient()) { final HttpGet httpGet = new HttpGet("https://pre1-xx.yy.com/scene/sceneOpt.do?_lang=zh&SSO_TICKET=533e04b85f4542dfbb41f128b493e300240dca00"); CloseableHttpResponse response = httpClient.execute(httpGet); System.out.println(EntityUtils.toString(response.getEntity())); } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) { e.printStackTrace(); } } } COPY

发布者:CoolQA,转转请注明出处:https://www.coolqa.com/more/working/20220719_4096.html

,