美文网首页
【DNS】调用方通过DNS轮询、请求重试,实现故障转移高可用

【DNS】调用方通过DNS轮询、请求重试,实现故障转移高可用

作者: Bogon | 来源:发表于2024-08-31 11:35 被阅读0次

一、 实验环境的DNS 配置

DNS 服务端

# systemctl  status  named

# cat /var/named/example.com

; Hash: edfbb39e6e6e531e3294cf74d8018973 1692864498
; Zone file for example.com
;
; Ansible managed
;

$ORIGIN example.com.
$TTL 20

@ IN SOA ns1.example.com. admin.example.com. (
  1692864498
  1H
  5M
  1W
  10M )

                     IN  NS     ns1.example.com.



www                 IN  A      192.168.1.110
www                 IN  A      192.168.1.120
ns1                 IN  A      192.168.1.105

DNS 客户端

# cat  /etc/resolv.conf

nameserver   192.168.1.105

二、 客户端测试

客户端服务器上配置的nameserver 将www.example.com 解析在 192.168.1.110 和 192.168.1.120

在服务器上执行curl操作:

# curl   -vvv  http://www.example.com/api

Trying 192.168.1.110:80...
connect to 192.168.1.110 port 80 failed: connection refused

Trying 192.168.1.120:80...Connected to www.example.com(192.168.1.120) port 80 
GET /api HTTP/1.1
Host: www.example.comUser.Agent: curl/7.79.1
Accept: */*
Mark bundle as not supporting multiuseHTP/1.1 302 Found
Server: nginx/1.17.10
Date: sat, 31 Aug 2024 13:19:08 CMTcontent-Length:0
Connection: keep-aliveLocation: http://www.example.com/api/
Connection #o to host www.example.com left intact

对curl的输出做一个解读

根据您提供的信息,我们可以详细解读一下 curl 操作的结果:

解析与连接

  1. DNS 解析

    • www.example.com 被解析为两个 IP 地址:192.168.1.110192.168.1.120
  2. curl 请求的执行

    • curl 首先尝试连接到 192.168.1.110 的端口 80。结果显示连接被拒绝(connection refused),这通常意味着该服务器上的端口 80 没有在监听,或者防火墙阻止了访问。
    • 接着,curl 尝试连接到 192.168.1.120 的端口 80,这次连接成功了。

curl 输出的详细信息

  1. 连接成功

    • Connected to www.example.com (192.168.1.120) port 80 (HTTP/1.1)
  2. 请求发送

    • GET //api HTTP/1.1:请求路径为 //api
    • Host: www.example.com:设置请求的主机头。
  3. 响应

    • HTTP/1.1 302 Found:服务器返回了一个 302 重定向响应。这表示请求的资源已经被临时移动到其他位置。
    • Location: http://www.example.com//api/:重定向的目标 URL 地址是 http://www.example.com//api/
  4. 其他头部信息

    • Server: nginx/1.17.10:响应来自 nginx 服务器,版本为 1.17.10。
    • Date: Sat, 31 Aug 2024 13:19:08 CMT:响应的日期和时间。
    • Content-Length: 0:响应体的长度为 0 字节,因为重定向响应没有内容。
    • Connection: keep-alive:连接保持活动状态,允许后续请求使用相同的连接。

总结

  • 连接到 192.168.1.110 的请求失败,因为该 IP 地址上的端口 80 可能未在监听或被防火墙阻止。
  • 连接到 192.168.1.120 的请求成功,但是服务器返回了一个 302 重定向响应,指示客户端应访问新的 URL http://www.example.com//api/

如果您想要正确访问资源,您需要检查并确保服务器 192.168.1.110 的端口 80 可用,并可能需要处理重定向以访问正确的 URL。如果重定向是预期的行为,那么您应该将请求发送到新的 URL 地址。

对curl 表现出的重试机制做一个解读

curl 的重试机制可以归因于以下几个方面,主要涉及其连接和请求处理流程:

1. DNS 轮询

curl 在发起请求时会尝试使用 DNS 解析得到的 IP 地址列表中的多个地址。这是为了提高可靠性,确保即使某些 IP 地址不可用,curl 也能继续尝试其他地址。这种机制称为 DNS 轮询。

在您的例子中,curl 首先尝试连接到 72.16.211.27,当连接失败时,它会继续尝试其他 IP 地址 72.16.211.50。这是 curl 默认的行为,用于提高网络请求的成功率。

2. 连接管理

  • 多地址尝试curl 会在尝试连接到一个地址时,如果遇到问题(如连接拒绝或超时),会继续尝试下一个地址。这个行为与 DNS 解析有关,并且是为了处理负载均衡和故障转移。

  • 连接超时与重试:虽然在您的案例中并没有显示超时重试,但 curl 允许通过选项设置连接超时和最大重试次数。默认情况下,curl 会尝试连接一次,失败后不会自动重试,但如果配置了相关选项,如 --retry,它会根据配置重试请求。

3. HTTP 重定向

curl 也处理 HTTP 重定向(3xx 响应状态码)。默认情况下,curl 会处理最多 50 次重定向(可以通过 --max-redirs 选项调整)。在您的例子中,服务器返回了 302 重定向,curl 自动处理了重定向并显示了新的目标 URL。

4. 连接保持(Keep-Alive)

curlConnection: keep-alive 头部表明服务器希望保持连接活跃,以便后续请求可以使用相同的连接。虽然在您的情况中,这个头部主要影响了连接的持久性,但也与请求的处理和重试逻辑有关。

实际操作中的机制

  • DNS 解析curl 从 DNS 解析获取到多个 IP 地址后,会依次尝试这些地址,直到成功连接或尝试完所有地址。
  • 重试配置curl 默认不重试请求,但可以使用 --retry 选项配置重试次数和条件。此选项可以指定在遇到网络错误时重新发送请求。

示例:设置重试

如果希望使用 curl 设置重试,可以使用以下选项:

curl --retry 5 http://example.com

这会在请求失败时最多重试 5 次。

总的来说,curl 的重试机制通过 DNS 轮询和对连接失败的处理,确保了请求的鲁棒性和成功率。
如果要深入了解 curl 的更多详细选项和机制,可以查阅 curl 的官方文档

如果java代码要实现类似的重试,是否有现成的轮子可以使用?

curl 的重试机制涉及多个底层组件,包括 DNS 轮询和连接重试。了解这些机制的实现方式可以帮助我们在 Java 中实现类似的功能。下面是对 curl 底层机制的详细解释以及 Java 中相似功能的实现方法。

curl 的底层实现

1. DNS 轮询

curl 使用系统提供的 DNS 解析功能来获取域名对应的多个 IP 地址。curl 会尝试这些 IP 地址中的每一个,直到找到一个可以成功连接的地址。

  • DNS 解析curl 使用系统的 DNS 解析库(如 libcgetaddrinfo)来获取域名对应的 IP 地址列表。
  • 地址尝试curl 会依次尝试这些地址。如果一个地址连接失败(例如超时或拒绝连接),curl 会自动尝试下一个地址。

2. 连接失败处理

curl 连接失败时,它可以尝试以下几种处理方法:

  • 重试机制:虽然 curl 默认不重试请求,但可以通过 --retry 选项配置。底层实现通常包括在遇到网络错误时(如超时、连接拒绝),根据配置重试请求。
  • 超时设置curl 允许设置连接超时和请求超时,以便在指定时间内未能连接成功时停止尝试。

在 Java 中实现类似功能

在 Java 中实现类似的 DNS 轮询和连接重试功能,可以利用现有的库和框架。以下是一些常见的实现方法:

1. 使用现有的库

  • Apache HttpClient:Apache HttpClient 是一个强大的 HTTP 客户端库,它支持配置重试机制和超时设置。

    import org.apache.http.HttpRequest;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpGet;
    
    public class HttpClientExample {
        public static void main(String[] args) throws Exception {
            // Configure request timeout and connection pool
            RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(5000)
                .setSocketTimeout(5000)
                .build();
    
            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
            cm.setMaxTotal(100);
            cm.setDefaultMaxPerRoute(20);
    
            CloseableHttpClient httpClient = HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(cm)
                .setRetryHandler(new StandardHttpRequestRetryHandler(3, true)) // Retry 3 times
                .build();
    
            HttpGet request = new HttpGet("http://example.com/opentalk");
    
            try (CloseableHttpResponse response = httpClient.execute(request)) {
                System.out.println("Response Code: " + response.getStatusLine().getStatusCode());
            }
        }
    }
    
  • OkHttp:另一个流行的 HTTP 客户端库,支持连接重试和超时配置。

    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    
    import java.io.IOException;
    
    public class OkHttpExample {
        public static void main(String[] args) throws IOException {
            OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true) // Enable retries on connection failure
                .build();
    
            Request request = new Request.Builder()
                .url("http://example.com/opentalk")
                .build();
    
            try (Response response = client.newCall(request).execute()) {
                if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
    
                System.out.println("Response: " + response.body().string());
            }
        }
    }
    

2. 自定义实现

如果需要更细粒度的控制,可以自定义实现 DNS 轮询和重试机制。以下是一个简单示例,展示如何在 Java 中实现基础的 DNS 轮询和连接重试:

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.util.List;
import java.util.ArrayList;

public class CustomHttpClient {
    private static final int MAX_RETRIES = 3;
    private static final int TIMEOUT = 5000; // 5 seconds

    public static void main(String[] args) {
        String domain = "example.com";
        List<String> ipAddresses = getIpAddresses(domain);
        String endpoint = "/opentalk";

        for (String ip : ipAddresses) {
            String url = "http://" + ip + endpoint;
            for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
                try {
                    HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
                    connection.setConnectTimeout(TIMEOUT);
                    connection.setReadTimeout(TIMEOUT);
                    connection.setRequestMethod("GET");

                    int responseCode = connection.getResponseCode();
                    System.out.println("Response Code: " + responseCode);
                    if (responseCode == HttpURLConnection.HTTP_OK) {
                        // Process response
                        break; // Exit retry loop on success
                    }
                } catch (IOException e) {
                    System.err.println("Connection failed for IP: " + ip + " on attempt " + (attempt + 1));
                    if (attempt == MAX_RETRIES - 1) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private static List<String> getIpAddresses(String domain) {
        List<String> ipAddresses = new ArrayList<>();
        try {
            InetAddress[] inetAddresses = InetAddress.getAllByName(domain);
            for (InetAddress inetAddress : inetAddresses) {
                ipAddresses.add(inetAddress.getHostAddress());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ipAddresses;
    }
}

总结

  • curl 的重试机制包括 DNS 轮询和连接失败处理。
  • Java 实现 可以使用现有的库(如 Apache HttpClient 或 OkHttp)来轻松实现类似的功能,或者通过自定义代码实现更细粒度的控制。
  • Apache HttpClientOkHttp 提供了内置的重试机制和连接管理功能,适合大多数应用场景。

根据需求选择适合的库或自定义实现方式,可以有效地处理网络请求的重试和容错机制。

curl 的重试机制,有哪些参考文档可以提供一下?

对于了解 curl 的重试机制及其底层实现,以下是一些有用的参考文档和资源,它们详细介绍了 curl 的工作原理、底层组件和相关配置:

官方文档与资源

  1. curl 官方文档

    • curl 的官方文档提供了关于命令行选项、重试机制以及如何配置 curl 的详细信息。
    • curl Manual
    • curl Options — 详细介绍了 curl 支持的各种选项,包括重试和超时设置。
  2. libcurl 文档

    • libcurlcurl 的底层库,文档详细描述了其 API 和实现细节,适合需要深入理解 curl 内部工作机制的开发者。
    • libcurl Documentationlibcurl 的官方文档,涵盖了 API 函数和使用示例。
    • libcurl FAQ — 常见问题解答,涉及常见的使用问题和解决方案。
  3. curl 源代码

    • curl 的源代码可以为理解其重试机制和 DNS 轮询实现提供直接的视角。
    • curl GitHub Repositorycurl 的 GitHub 仓库,其中包含源代码和开发历史。
  4. curl 的 Retry 选项

    • curl 支持的重试选项和如何使用它们的具体说明。
    • Retry Options in curl — 关于 curl--retry 选项的官方说明。

其他参考资源

  1. RFC 文档

  2. 技术博客和文章

    • 许多技术博客和文章讨论了 curl 的使用技巧和底层实现。例如:
  3. 网络编程书籍

实践和实验

  • curl 的实验:通过实际使用 curl 进行各种测试和实验(如设置不同的 DNS 和重试配置)可以帮助理解其行为和机制。

结合上述资源,您可以深入了解 curl 的重试机制、底层实现以及如何在不同的场景中应用和配置 curl

三、参考

libcurl对域名含有多个ip时的超时重试策略
https://xiaochai.github.io/2018/09/11/libcurl-connection-timeout-policy/

DNS Bind服务配置解析
https://www.cnblogs.com/saneri/p/8038070.html

在 BIND DNS 服务器中配置区
https://docs.redhat.com/zh_hans/documentation/red_hat_enterprise_linux/9/html/managing_networking_infrastructure_services/assembly_configuring-zones-on-a-bind-dns-server_assembly_setting-up-and-configuring-a-bind-dns-server

相关文章

  • 基于Docker快速搭建DNS Server

    我们在项目中有时会要使用到DNS服务,比如DNS轮询服务、基于DNS搭建高可用的Eureka注册中心等。 那么如何...

  • 2018-05-23 nginx和keepalived实现IT服

    通过nginx的反向代理功能实现负载均衡,通过keepalived的故障转移功能实现nginx的高可用。 1环境 ...

  • Retrofit & RxJava 实战篇

    一、网络请求实现:轮询、嵌套、合并数据、缓存、出错重试

  • DNS解析

    了解DNS解析吗? 域名到IP地址的映射,DNS解析请求采用UDP数据报,并且明文显示。 DNS解析查询方...

  • 使用DNS权重轮询实现业务流量灰度切换

    DNS设置 首先将DNS解析服务升级为支持权重轮询的版本 添加多条DNS解析A记录 开启权重配置 设置DNS权重值...

  • DNS轮询(转)

    大多数域名注册商都支持对统一主机添加多条A记录,这就是DNS轮询,DNS服务器将解析请求按照A记录的顺序,随机分配...

  • Redis sentinel 学习

    定义 Redis sentinel称为哨兵模式,是Redis 高可用的实现方法,具有故障发现,故障自动转移,配置中...

  • ios开发防止App被抓包(可正常请求)

    实现iOS应用底层所有网络请求拦截(如ajax请求拦截),包含http-dns解决方法,有效防止DNS劫持,用于分...

  • ios开发防止App被抓包

    实现iOS应用底层所有网络请求拦截(如ajax请求拦截),包含http-dns解决方法,有效防止DNS劫持,用于分...

  • 接入层代理层架构设计

    接入层 dns轮询实现负载均衡:以华为云举例 域名解析可以配置50条a记录,dns会根据权重返回ip,智能解析还支...

网友评论

      本文标题:【DNS】调用方通过DNS轮询、请求重试,实现故障转移高可用

      本文链接:https://www.haomeiwen.com/subject/mpdqljtx.html