标签: bind

  • prometheus bind_exporter

    prometheus bind_exporter

    named.conf

    [root@aikaiyuan ~]# cat /etc/named.conf | more
    
    statistics-channels {
        inet 127.0.0.1 port 58053 allow { 127.0.0.1; };
    };
    

    bind_exporter

    https://github.com/prometheus-community/bind_exporter/releases

    [root@aikaiyuan ~]# wget https://github.com/prometheus-community/bind_exporter/releases/download/v0.3.0/bind_exporter-0.3.0.linux-amd64.tar.gz
    [root@aikaiyuan ~]# tar zxvf bind_exporter-0.3.0.linux-amd64.tar.gz
    [root@aikaiyuan ~]# cd bind_exporter-0.3.0.linux-amd64/
    [root@aikaiyuan bind_exporter-0.3.0.linux-amd64]#  ./bind_exporter -bind.stats-url="http://127.0.0.1:58053/" -web.listen-address="192.168.1.3:59100"
    
    • -bind.stats-url=”http://127.0.0.1:58053/” 指向 named.conf statistics-channels
    • -web.listen-address=”192.168.1.3:59100″ 监听本地端口, 由 Prometheus 调用

    Make sure BIND was built with libxml2 support. You can check with the following command: named -V | grep libxml2.

    Prometheus

    https://grafana.com/grafana/dashboards/12309

    [root@aikaiyuan ~]# cat prometheus.yml
      - job_name: 'dns'
        scrape_interval: 30s
        static_configs:
        - targets:
          - 192.168.1.3:59100
          labels:
            alias: localdns
    

    转载请注明:爱开源 » prometheus bind_exporter

  • couldn’t add command channel 127.0.0.1#953: address in use

    Spent sometime around this error using chroot bind 9.x
    couldn’t add command channel 127.0.0.1#953: address in use

    Guys, assuming your bind config is correct – this error is related to service “portserve” running and using this port!

    ps -ef|grep portreserve|grep -v grep
    root      1349     1  0 May06 ?        00:00:00 /sbin/portreserve
    

    stop the service and disable from startup:

    service portreserve stop
    Stopping portreserve: [  OK  ]
    chkconfig portreserve off
    

    转载请注明:爱开源 » couldn’t add command channel 127.0.0.1#953: address in use

  • bind 空主机头 cname CNAME and other data

    CNAME and other data

    做named服务,bind原生版本编译完之后不支持空主机头的cname解析。注释掉 lib/dns/rbtdb.c 文件的

    if (rbtversion != NULL &&

    cname_and_other_data(rbtnode, rbtversion->serial))

    return (DNS_R_CNAMEANDOTHER);

    再进行编译,就可以使空主机头的cnae记录生效。

    相关文章

    转载请注明:爱开源 » bind 空主机头 cname CNAME and other data

  • bind logging 日志详解

    在默认情况下,BIND9 把日志消息写到 /var/log/messages 文件中,而这些日志消息是非常少的,主要就是启动,关闭的日志记录和一些严重错误的消息;而将调试日志信息写入 BIND 服务器工作目录中的 named.run 文件。
    BIND 9 的日志是可以灵活配置的,要详细记录服务器的运行状况,要在配置文件 named.conf 中使用 logging 语句来定制自己所需要的日志记录。

    BIND 日志的常用术语
    在讲述 logging 语句的语法之前,先要熟悉一些常用术语
    术语 含义
    channel(通道) 日志输出方式,如:syslog、文本文件、标准错误输出或 /dev/null
    category(类别) 日志的消息类别,如:查询消息或动态更新消息等
    module(模块) 产生消息的来源模块名称
    facility(设备) syslog 设备名
    severity(严重性) 消息的严重性等级

    logging 语句的语法
    logging 语句的语法为:
    logging {
    channel channel_name { // 定义通道
    file log_file [versions number | unlimited] [size sizespec]; | syslog optional_facility; | null; | stderr; // 定义输出方式
    severity log_severity; // 定义消息严重性
    [print-time boolean;] // 是否在消息中添加时间前缀,仅用于 file 日志
    [print-severity boolean;] // 是否在消息中添加消息严重性前缀
    [print-category boolean;] // 是否在消息中添加消息类别名前缀
    };
    category category_name { // 定义类别
    channel_name;
    ……
    };
    };
    配置日志时,首先要定义通道,然后将不同的日志类别的数据指派到指定的通道上输出。
    BIND 9 的默认配置是:
    logging {
    // 由于使用了默认通道,所以没有通道定义部分
    category “default” { “default_syslog”; “default_debug”; };
    };

    channel 语句
    channel 语句用于定义通道。指定应该向哪里发送日志数据,需要在以下四种之间则其一:
    file : 输出到纯文本文件
    log_file 指定一个文件名
    version 指定允许同时存在多少个版本的该文件,比如指定 3 个版本(version 3),就会保存 query.log、query.log0、query.log1 和query.log2。
    size 指定文件大小的上限,如果只设定了size 而没有设定 version,当文件达到指定的文件大小上限时,服务器停止写入该文件。如果设定了version,服务器会进行循环,如把 log_file 变成 log_file.log1,log_file.log1 变成 log_file.log2 等,然后建立一个新的 log_file.log 进行写入。

    syslog optional_facility :输出到 syslog,其中 optional_facility 是 syslog 的设备名,通常为以下几个:
    daemon
    local0 到 local7

    null :输出到空设备

    stderr :输出到标准错误输出,默认为屏幕

    severity 语句用于指定消息的严重性等级, log_severity 的取值为(按照严重性递减的顺序):
    critical
    error
    warning
    notice
    info
    debug [ level ]
    dynamic 是一个特殊的值,它匹配服务器当前的调试级别
    定义了某个严重性级别后,系统会记录包括该级别以及比该级别更严重的级别的所有消息。比如定义级别为 error,则会记录 critical 和error 两个级别的信息。
    对于系统管理员来说,一般记录到 info 级别就可以了。
    BIND 9 预制了如下四个默认通道;
    channel “default_syslog” {
    syslog daemon; // 发送给 syslog 的 daemon 设备
    severity info; // 只发送此 info 及其更高优先级的信息
    };
    channel “default_debug” { // 只有当服务器的 debug 级别非 0 时,才产生输出。
    file “named.run”; // 写入工作目录下的 named.run 文件
    severity dynamic; // 按照服务器当前的debug 级别记录日志
    };
    channel “default_stderr” {
    stderr; // 写到stderr
    severity info; // 只发送此 info 及其更高优先级的信息
    };
    channel “null” {
    null; // 丢弃所有发到此通道的信息
    };

    category 语句
    category 语句是指定哪一种类别的信息使用哪个或者哪几个已经定义了的通道输出。
    BIND 9 中可用的类别名(category_name)有:
    类别 说明
    client 处理客户端请求。
    config 配置文件分析和处理。
    database 同BIND内部数据库相关的消息,用来存储区数据和缓存记录。
    default 匹配所有未明确指定通道的类别。
    dnssec 处理 DNSSEC 签名的响应。
    general 包括所有未明确分类的 BIND 消息。
    lame-servers 发现错误授权,即残缺服务器。
    network 网络操作。
    notify 区更新通知消息。
    queries 查询日志
    resolver 名字解析,包括对来自解析器的递归查询信息。
    security 批准/非批准的请求。
    update 动态更新事件。
    xfer-in 从远程名字服务器到本地名字服务器的区传送。
    xfer-out 从本地名字服务器到远程名字服务器的区传送。

    例如要记录查询消息,可以在 named.conf 中添加如下配置:
    logging {
    channel query_log {
    file “query.log” versions 3 size 20m;
    severity info;
    print-time yes;
    print-category yes;
    };
    category queries {
    query_log;
    };
    };
    这样服务器会在工作目录(directory 语句所指定的目录,Ubuntu 为:/var/cache/bind)下创建 query.log 文件,并把运行过程产生的 queries 消息写如到此文件中。

    BIND 配置文件(named.conf) logging 的使用

    bind options 详解

    转载请注明:爱开源 » bind logging 日志详解

  • CLOSE_WAIT状态的原因与解决方法

    这个问题之前没有怎么留意过,是最近在面试过程中遇到的一个问题,面了两家公司,两家公司竟然都面到到了这个问题,不得不使我开始关注这个问题。说起CLOSE_WAIT状态,如果不知道的话,还是先瞧一下TCP的状态转移图吧。

    4444180

    关闭socket分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况。前者是指有本地主机主动发起的关闭;而后者则是指本地主机检测到远程主机发起关闭之后,作出回应,从而关闭整个连接。将关闭部分的状态转移摘出来,就得到了下图:
    产生原因
    通过图上,我们来分析,什么情况下,连接处于CLOSE_WAIT状态呢?
    在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。
    通常来讲,CLOSE_WAIT状态的持续时间应该很短,正如SYN_RCVD状态。但是在一些特殊情况下,就会出现连接长时间处于CLOSE_WAIT状态的情况。

    出现大量close_wait的现象,主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下errno,如果不是AGAIN,就断开连接。

    参考资料4中描述,通过发送SYN-FIN报文来达到产生CLOSE_WAIT状态连接,没有进行具体实验。不过个人认为协议栈会丢弃这种非法报文,感兴趣的同学可以测试一下,然后把结果告诉我;-)

    为了更加清楚的说明这个问题,我们写一个测试程序,注意这个测试程序是有缺陷的。
    只要我们构造一种情况,使得对方关闭了socket,我们还在read,或者是直接不关闭socket就会构造这样的情况。
    server.c:

    #include
    #include
    #include #define MAXLINE 80
    #define SERV_PORT 8000
    int main(void)
    {
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    int i, n;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    bind(listenfd, (struct sockaddr )&servaddr, sizeof(servaddr));
    listen(listenfd, 20);
    printf(“Accepting connections …\n”);
    while (1) {
    cliaddr_len = sizeof(cliaddr);
    connfd = accept(listenfd,
    (struct sockaddr
    )&cliaddr, &cliaddr_len);
    //while (1)
    {
    n = read(connfd, buf, MAXLINE);
    if (n == 0) {
    printf(“the other side has been closed.\n”);
    break;
    }
    printf(“received from %s at PORT %d\n”,
    inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
    ntohs(cliaddr.sin_port));
    for (i = 0; i < n; i++)
    buf[i] = toupper(buf[i]);
    write(connfd, buf, n);
    }
    //这里故意不关闭socket,或者是在close之前加上一个sleep都可以
    //sleep(5);
    //close(connfd);
    }
    }

    client.c:

    #include
    #include
    #include
    #include
    #include
    #include #define MAXLINE 80
    #define SERV_PORT 8000
    int main(int argc, char argv[])
    {
    struct sockaddr_in servaddr;
    char buf[MAXLINE];
    int sockfd, n;
    char
    str;
    if (argc != 2) {
    fputs(“usage: ./client message\n”, stderr);
    exit(1);
    }
    str = argv[1];
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, “127.0.0.1”, &servaddr.sin_addr);
    servaddr.sin_port = htons(SERV_PORT);
    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    write(sockfd, str, strlen(str));
    n = read(sockfd, buf, MAXLINE);
    printf(“Response from server:\n”);
    write(STDOUT_FILENO, buf, n);
    write(STDOUT_FILENO, “\n”, 1);
    close(sockfd);
    return 0;
    }

    结果如下:

    debian-wangyao:~$ ./client a
    Response from server:
    A
    debian-wangyao:~$ ./client b
    Response from server:
    B
    debian-wangyao:~$ ./client c
    Response from server:
    C
    debian-wangyao:~$ netstat -antp | grep CLOSE_WAIT
    (Not all processes could be identified, non-owned process info
    will not be shown, you would have to be root to see it all.)
    tcp 1 0 127.0.0.1:8000 127.0.0.1:58309 CLOSE_WAIT 6979/server
    tcp 1 0 127.0.0.1:8000 127.0.0.1:58308 CLOSE_WAIT 6979/server
    tcp 1 0 127.0.0.1:8000 127.0.0.1:58307 CLOSE_WAIT 6979/server

    解决方法
    基本的思想就是要检测出对方已经关闭的socket,然后关闭它。

    1.代码需要判断socket,一旦read返回0,断开连接,read返回负,检查一下errno,如果不是AGAIN,也断开连接。(注:在UNP 7.5节的图7.6中,可以看到使用select能够检测出对方发送了FIN,再根据这条规则就可以处理CLOSE_WAIT的连接)
    2.给每一个socket设置一个时间戳last_update,每接收或者是发送成功数据,就用当前时间更新这个时间戳。定期检查所有的时间戳,如果时间戳与当前时间差值超过一定的阈值,就关闭这个socket。
    3.使用一个Heart-Beat线程,定期向socket发送指定格式的心跳数据包,如果接收到对方的RST报文,说明对方已经关闭了socket,那么我们也关闭这个socket。
    4.设置SO_KEEPALIVE选项,并修改内核参数

    前提是启用socket的KEEPALIVE机制:
    //启用socket连接的KEEPALIVE
    int iKeepAlive = 1;
    setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&iKeepAlive, sizeof(iKeepAlive));

    tcp_keepalive_intvl (integer; default: 75; since Linux 2.4)
    The number of seconds between TCP keep-alive probes.

    tcp_keepalive_probes (integer; default: 9; since Linux 2.2)
    The maximum number of TCP keep-alive probes to send before giving up and killing the connection if no response is obtained from the other end.

    tcp_keepalive_time (integer; default: 7200; since Linux 2.2)
    The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes. Keep-alives are only sent when the SO_KEEPALIVE socket option is enabled. The default value is 7200 seconds (2 hours). An idle connec‐tion is terminated after approximately an additional 11 minutes (9 probes an interval of 75 seconds apart) when keep-alive is enabled.

    echo 120 > /proc/sys/net/ipv4/tcp_keepalive_time
    echo 2 > /proc/sys/net/ipv4/tcp_keepalive_intvl
    echo 1 > /proc/sys/net/ipv4/tcp_keepalive_probes

    除了修改内核参数外,可以使用setsockopt修改socket参数,参考man 7 socket。

    int KeepAliveProbes=1;
    int KeepAliveIntvl=2;
    int KeepAliveTime=120;
    setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT, (void )&KeepAliveProbes, sizeof(KeepAliveProbes));
    setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE, (void
    )&KeepAliveTime, sizeof(KeepAliveTime));
    setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&KeepAliveIntvl, sizeof(KeepAliveIntvl));

    参考:
    http://blog.chinaunix.net/u/20146/showart_1217433.html
    http://blog.csdn.net/eroswang/archive/2008/03/10/2162986.aspx
    http://haka.sharera.com/blog/BlogTopic/32309.htm
    http://learn.akae.cn/media/ch37s02.html
    http://faq.csdn.net/read/208036.html
    http://www.cndw.com/tech/server/2006040430203.asp
    http://davidripple.bokee.com/1741575.html
    http://doserver.net/post/keepalive-linux-1.php
    man 7 tcp

    相关文章

    转载请注明:爱开源 » CLOSE_WAIT状态的原因与解决方法

  • CentOS 7 安装配置 NFS

    环境

    nps 192.168.1.97

    client 192.168.1.98

    一、yum 安装

    yum -y install nfs-utils rpcbind

    nfs 的配置文件 /etc/expots

    默认为空

    vi /etc/exports

    /opt/test/ 192.168.1.0/24(rw,no_root_squash,no_all_squash,sync,anonuid=501,anongid=501)

    二、使配置生效

    exportfs -r

    注:配置文件说明:

    /opt/test 为共享目录

    192.168.1.0/24 可以为一个网段,一个IP,也可以是域名,域名支持通配符 如: *.qq.com

    rw:read-write,可读写;

    ro:read-only,只读;

    sync:文件同时写入硬盘和内存;

    async:文件暂存于内存,而不是直接写入内存;

    no_root_squash:NFS客户端连接服务端时如果使用的是root的话,那么对服务端分享的目录来说,也拥有root权限。显然开启这项是不安全的。

    root_squash:NFS客户端连接服务端时如果使用的是root的话,那么对服务端分享的目录来说,拥有匿名用户权限,通常他将使用nobody或nfsnobody身份;

    all_squash:不论NFS客户端连接服务端时使用什么用户,对服务端分享的目录来说都是拥有匿名用户权限;

    anonuid:匿名用户的UID值,可以在此处自行设定。

    anongid:匿名用户的GID值。

    三、启动 nfs

    service rpcbind start

    service nfs start

    chkconfig rpcbind on

    chkconfig nfs on

    四、客户端挂载:

    showmount -e 192.168.1.97 #查看可挂载

    Export list for 192.168.1.97:

    /opt/test 192.168.1.0/24

    客户端挂载

    mount -t nfs 192.168.1.97:/opt/test /mnt

    无提示 既为成功

    客户端在挂载的时候遇到的一个问题如下,可能是网络不太稳定,NFS默认是用UDP协议,换成TCP协议即可:

    mount -t nfs 192.168.1.97:/opt/test /mnt -o proto=tcp -o nolock

    ————————————–分割线 ————————————–

    Ubuntu 12.04安装NFS server http://www.linuxidc.com/Linux/2012-09/70728.htm

    NFS服务器安装配置实现Ubuntu 12.04与ARM文件共享 http://www.linuxidc.com/Linux/2012-10/73159.htm

    Ubuntu搭建nfs服务器 http://www.linuxidc.com/Linux/2012-10/71930.htm

    文件服务器NFS配置详解 http://www.linuxidc.com/Linux/2013-06/86542.htm

    Ubuntu下搭建NFS网络文件系统服务器 http://www.linuxidc.com/Linux/2013-07/87367.htm

    Heartbeat_ldirector+LB+NFS实现HA及LB、文件共享 http://www.linuxidc.com/Linux/2013-06/85292.htm

    CentOS 5.5配置NFS服务器教程 http://www.linuxidc.com/Linux/2013-03/81737.htm

    Ubuntu 12.10下NFS的安装使用 http://www.linuxidc.com/Linux/2013-03/80478.htm

    转载请注明:爱开源 » CentOS 7 安装配置 NFS

  • Starting NFS daemon: rpc.nfsd: writing fd to kernel failed: errno 111 (Connection refused)

    启动 nfs 报错

    /etc/init.d/nfs start
    Starting NFS services: [ OK ]
    Starting NFS mountd: [FAILED]
    Starting NFS daemon: rpc.nfsd: writing fd to kernel failed: errno 111 (Connection refused)
    rpc.nfsd: unable to set any sockets for nfsd
    [FAILED]
    

    Starting NFS daemon: rpc.nfsd: writing fd to kernel failed: errno 111 (Connection refused)

    解决:

    yum install rpcbind
    /etc/init.d/rpcbind restart
    
    /etc/init.d/nfs start
    

    转载请注明:爱开源 » Starting NFS daemon: rpc.nfsd: writing fd to kernel failed: errno 111 (Connection refused)

  • CDN架构以及原理分析

    在不同地域的用户访问网站的响应速度存在差异,为了提高用户访问的响应速度、优化现有Internet中信息的流动,需要在用户和服务器间加入中间层CDN. 使用户能以最快的速度,从最接近用户的地方获得所需的信息,彻底解决网络拥塞,提高响应速度,是目前大型网站使用的流行的应用方案.

    1. CDN 概述

    • CDN的全称是Content Delivery Network,即内容分发网络。其目的是通过在现有的Internet中增加一层新的CACHE(缓存)层,将网站的内容发布到最接近用户的网络“边缘”的节点,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均等原因,提高用户访问网站的响应速度。cdn_overview
    • Cache层的技术,消除数据峰值访问造成的结点设备阻塞。Cache服务器具有缓存功能,所以大部分网页对象(Web page object),如html, htm, php等页面文件,gif,tif,png,bmp等图片文件,以及其他格式的文件,在有效期(TTL)内,对于重复的访问,不必从原始网站重新传送文件实体, 只需通过简单的认证(Freshness Validation)- 传送几十字节的Header,即可将本地的副本直接传送给访问者。由于缓存服务器通常部署在靠近用户端,所以能获得近似局域网的响应速度,并有效减少广域带宽的消耗。不仅能提高响应速度,节约带宽,对于加速Web服务器,有效减轻源服务器的负载是非常有效的。
    • 根据加速对象不同,分为 客户端加速 和 服务器加速

    • 客户端加速 : Cache部署在网络出口处,把常访问的内容缓存在本地,提高响应速度和节约带宽;

    • 服务器加速 : Cache部署在服务器前端,作为Web服务器的代理缓存机,提高Web服务器的性能,加速访问速度
      如果多台Cache加速服务器且分布在不同地域,需要通过有效地机制管理Cache网络,引导用户就近访问(比如通过DNS引导用户),全局负载均衡流量,这是CDN内容传输网络的基本思想.
    • CDN对网络的优化作用主要体现在如下几个方面 – 解决服务器端的“第一公里”问题 – 缓解甚至消除了不同运营商之间互联的瓶颈造成的影响 – 减轻了各省的出口带宽压力 – 缓解了骨干网的压力 – 优化了网上热点内容的分布

    2. CDN 的工作原理

    2.1. 传统访问过程(未加速缓存服务)

    我们先看传统的未加缓存服务的访问过程,以便了解CDN缓存访问方式与未加缓存访问方式的差别:

    normal

    由上图可见,用户访问未使用CDN缓存网站的过程为:

    1. 用户输入访问的域名,操作系统向 LocalDns 查询域名的ip地址.
    2. LocalDnsROOT DNS 查询域名的授权服务器(这里假设LocalDns缓存过期)
    3. ROOT DNS将域名授权dns记录回应给 LocalDns
    4. LocalDns得到域名的授权dns记录后,继续向域名授权dns查询域名的ip地址
    5. 域名授权dns 查询域名记录后,回应给 LocalDns
    6. LocalDns 将得到的域名ip地址,回应给 用户端
    7. 用户得到域名ip地址后,访问站点服务器
    8. 站点服务器应答请求,将内容返回给客户端.

    2.2. CDN访问过程(使用缓存服务)

    CDN网络是在用户和服务器之间增加Cache层,主要是通过接管DNS实现,将用户的请求引导到Cache上获得源服务器的数据
    下面让我们看看访问使用CDN缓存后的网站的过程:

    cdn

    通过上图,我们可以了解到,使用了CDN缓存后的网站的访问过程变为:

    1. 用户输入访问的域名,操作系统向 LocalDns 查询域名的ip地址.
    2. LocalDnsROOT DNS 查询域名的授权服务器(这里假设LocalDns缓存过期)
    3. ROOT DNS将域名授权dns记录回应给 LocalDns
    4. LocalDns得到域名的授权dns记录后,继续向域名授权dns查询域名的ip地址
    5. 域名授权dns 查询域名记录后(一般是CNAME),回应给 LocalDns
    6. LocalDns 得到域名记录后,向智能调度DNS查询域名的ip地址
    7. 智能调度DNS 根据一定的算法和策略(比如静态拓扑,容量等),将最适合的CDN节点ip地址回应给 LocalDns
    8. LocalDns 将得到的域名ip地址,回应给 用户端
    9. 用户得到域名ip地址后,访问站点服务器
    10. CDN节点服务器应答请求,将内容返回给客户端.(缓存服务器一方面在本地进行保存,以备以后使用,二方面把获取的数据返回给客户端,完成数据服务过程)

    通过以上的分析我们可以得到,为了实现对普通用户透明(使用缓存后用户客户端无需进行任何设置)访问,需要使用DNS(域名解析)来引导用户来访问Cache服务器,以实现透明的加速服务. 由于用户访问网站的第一步就是 域名解析 ,所以通过修改dns来引导用户访问是最简单有效的方式.

    2.3. CDN网络的组成要素

    对于普通的Internet用户,每个CDN节点就相当于一个放置在它周围的网站服务器.
    通过对dns的接管,用户的请求被透明地指向离他最近的节点,节点中CDN服务器会像网站的原始服务器一样,响应用户的请求.
    由于它离用户更近,因而响应时间必然更快.

    从上面图中 虚线圈起来的那块,就是CDN层,这层是位于 用户端 和 站点服务器之间.

    • 智能调度DNS(比如f5的3DNS)智能调度DNS是CDN服务中的关键系统.当用户访问加入CDN服务的网站时,域名解析请求将最终由 智能调度DNS 负责处理.
      它通过一组预先定义好的策略,将当时最接近用户的节点地址提供给用户,使用户可以得到快速的服务.
      同时它需要与分布在各地的CDN节点保持通信,跟踪各节点的健康状态,容量等,确保将用户的请求分配到就近可用的节点上.
    • 缓存功能服务

    • 负载均衡设备(如lvs,F5的BIG/IP)

    • 内容Cache服务器(如squid)
    • 共享存储(根据缓存数据量多少决定是否需要)

    3. CDN 智能调度Dns 实例分析

    • 分析img.alibaba.com域名在系统中,执行dig命令,输出如下:
      “`
      #dig img.alibaba.com

      ; 部分省略

      ;; QUESTION SECTION:
      ;img.alibaba.com. IN A

      ;; ANSWER SECTION:
      img.alibaba.com. 600 IN CNAME img.alibaba.com.edgesuite.net.
      img.alibaba.com.edgesuite.net. 7191 IN CNAME img.alibaba.com.georedirector.akadns.net.
      img.alibaba.com.georedirector.akadns.net. 3592 IN CNAME a1366.g.akamai.net.
      a1366.g.akamai.net. 12 IN A 204.203.18.145
      a1366.g.akamai.net. 12 IN A 204.203.18.160

      ; 部分省略
      从上面查询结果可以看出 img.alibaba.com. CNAME img.alibaba.com.edgesuite.net. 后面的CNAME是由 Akamai(CDN服务商) 去跳转到 智能调度器上的.
      - 分析[www.discovery.com](http://www.discovery.com/)域名在系统中,继续执行dig命令,输出如下:

      dig www.discovery.com

      ; 部分省略

      ;; QUESTION SECTION:
      ;www.discovery.com. IN A

      ;; ANSWER SECTION:
      www.discovery.com. 1077 IN CNAME www.discovery.com.edgesuite.net.
      www.discovery.com.edgesuite.net. 21477 IN CNAME a212.g.akamai.net.
      a212.g.akamai.net. 20 IN A 204.203.18.154
      a212.g.akamai.net. 20 IN A 204.203.18.147

      ; 部分省略
      “`
      从上面查询结果可以看出 www.discovery.com. IN CNAME www.discovery.com.edgesuite.net. 后面的CNAME是由 Akamai(CDN服务商) 去跳转到 智能调度器上的.总结:一般来说,网站需要使用到CDN服务时,一般都是将需要加速访问的域名 CNAME到 CDN服务商的域名上.
      缓存服务和调度功能都是由服务商来完成.

    4. CDN的 智能调度Dns 简化实现

    4.1. 调度策略说明

    在用户请求解析域名的时候,智能DNS判断用户的LocalDns的IP,然后跟DNS服务器内部的IP表范围匹配一下,看看用户是电信还是网通用户,然后给用户返回对应的IP地址
    这里使用的是静态拓扑的方法,只是判断LocalDns的IP.要想使用更复杂的调度算法可以考虑商业产品,如F5的3DNS.

    4.2. 假设CDN节点规划

    在这里我们将使用 BIND 的View功能来实现运营商的区分,假设我们在每个运营商的机房都放有一个CDN节点,列表如下:

    域名 运营商(view) 服务地址
    www.cdntest.com 网通(CNC) 192.168.0.1
    www.cdntest.com 电信(TELECOM) 192.168.0.2
    www.cdntest.com 教育网(EDU) 192.168.0.3
    www.cdntest.com 默认(ANY) 192.168.0.4

    4.3. bind view 配置

    • 以下是named.conf配置文件的部分截取,只是涉及到 View 的部分,其他细节可参考互联网.
      “`
      acl “cnc_iprange”{ //定义ip范围(网通)
      192.168.1.0/24;
      192.168.2.0/24;
      //此处只是示例,其他省略
      };

      acl “tel_iprange”{ //定义ip范围(电信)
      192.168.3.0/24;
      192.168.4.0/24;
      //其他省略
      };

      acl “edu_iprange”{ //定义ip范围(教育网)
      192.168.5.0/24;
      192.168.6.0/24;
      //其他省略
      };

      acl “default_iprange”{ //定义ip范围(默认)
      192.168.7.0/24;
      192.168.8.0/24;
      //其他省略
      };

      view “CNC” {
      Match-clients{cnc_iprange};
      zone “.” IN {
      type hint;
      file “named.root”;
      };

      zone "localhost" IN {
              type master;
              file "localhost.zone";
              allow-update { none; };
      };
      
      zone "cdntest.com" IN {
              type master;
              file "cnc_cdntest.zone";
      };
      

      };

      view “TEL” {
      Match-clients{tel_iprange};
      zone “.” IN {
      type hint;
      file “named.root”;
      };

      zone "localhost" IN {
              type master;
              file "localhost.zone";
              allow-update { none; };
      };
      
      zone "cdntest.com" IN {
              type master;
              file "tel_cdntest.zone";
      };
      

      };

      view “EDU” {
      Match-clients{edu_iprange};
      zone “.” IN {
      type hint;
      file “named.root”;
      };

      zone "localhost" IN {
              type master;
              file "localhost.zone";
              allow-update { none; };
      };
      
      zone "cdntest.com" IN {
              type master;
              file "edu_cdntest.zone";
      };
      

      };

      view “DEFAULT” {
      Match-clients{default_iprange};
      zone “.” IN {
      type hint;
      file “named.root”;
      };

      zone "localhost" IN {
              type master;
              file "localhost.zone";
              allow-update { none; };
      };
      
      zone "cdntest.com" IN {
              type master;
              file "default_cdntest.zone";
      };
      

      };
      “`
      – zone文件的配置说明这4个zone配置文件(cnc_cdntest.zone,tel_cdntest.zone,edu_cdntest.zone,default_cdntest.zone)中,只有www.cndtest.com的A记录不一样,其他的都是一样.

    域名 zone配置文件 A记录地址
    www.cdntest.com cnc_cdntest.zone 192.168.0.1
    www.cdntest.com tel_cdntest.zone 192.168.0.2
    www.cdntest.com edu_cdntest.zone 192.168.0.3
    www.cdntest.com default_cdntest.zone 192.168.0.4

    以上只列出了 www.cdntest.com 的A记录地址,其他关于zone的语法 请参考互联网.

    • 域名解析流程简要说明
    1. 用户向 LocalDns 查询域名 www.cdntest.com
    2. LocalDns 向 授权DNS 查询www.cdntest.com
    3. 授权DNS 判断用户使用的 LocalDns的ip地址,匹配上述设置的ip范围,如果范围在网通,就将网通对应的ip地址(192.168.0.1),回应给LocalDns(其他依此类推)
    4. LocalDns 将得到的域名ip地址,回应给 用户端 (域名解析完成)说明:再此过程中,我们简化了主DNS智能DNS 之间的CNAME过程(为了简要说明问题).
      这里使用的是静态拓扑(根据ip范围)的方法,也称为地域化方法,只是判断LocalDns的IP.
    • 此简化方案中的存在的问题
    1. 如果用户设置错误的dns,可能会导致用户访问比原来慢(比如网通用户设置了电信的DNS)
    2. 不能判断CDN节点服务器的健康状态和容量状态,可能会把用户定向到不可用的CDN节点
    3. 由于静态拓扑方法,可能存在用户访问的CDN节点不是最优化和最快的
    4. …..可能还有其他想不到的….

    5. 总结(Summary)

    在建立CDN网路时,最关键的就是 智能调度DNS,这个是CND网络总协调,通过高效的调度算法,可以使用户得到最佳的访问体验.
    其次就是 CND节点的管理,比如涉及到 内容的同步机制,配置文件的更新等等,都需要有一套机制来保证.
    当然在大型网站中,也要考建设CDN体系的成本和回报率.

    转载请注明:爱开源 » CDN架构以及原理分析

  • shell解决DNS负载均衡RS的健康检测

    DNS负载均衡,是最早的实现负载均衡技术的。在DNS的配置文件中为多个地址配置同一个名字,即配置多条指向不同ip的A记录,而客户端在查询这条A记录的时候将随机获得其中一个地址。通过以上描述不难发现,DNS负载均衡有着配置简单,性能优异,没有修改架构的开销等特点。因此,经常被用在内网。

    说了优点,也要说说缺点。DNS负载均衡采用的是简单的轮循负载算法,不能分辨服务器的差异,不能根据后端服务器的运行状态进行动态调整,即健康检查。由于实现算法的随机性,不能为性能较好的服务器更多的分配请求,经常会出现将请求集中在某一台服务器上的现象。

    如果你负载均衡的要求很高,不如使用其他负载均衡技术来的容易,比如LVS,Nginx或者HAproxy。修改算法,不仅要看明白洋洋散散几万行源码,还要将自己的代码完美融合进去,这个成本因人而异,但肯定不是一朝一夕之功。但如果只是后端服务器的健康检测问题,使用shell脚本就可以办到。

    思路:DNS服务器通过某种机制对后端RS主机的运行状态进行判断,如果后端主机出现故障,那么DNS所要做的是修改配置文件,将问题主机从配置文件中提出并重启服务。更进一步,当RS主机恢复时,DNS主机还要将其恢复到配置文件中。

    首先是健康检测机制。如果RS主机是web服务,那么可选的至少有三个,icmp,telnet,curl分别工作在第三层,第四层,和第七层。我们在这里使用icmp。修改配置文件,可以通过将提前准备好的配置文件覆盖原文件做到,当然还有sed -i,我们在这里使用sed -i。最后使用while及if elif将这些元素合理的嵌套。

    web1192.168.1.1    web2 192.168.1.2
    DNS配置文件:
    
    www INA192.168.1.1
    www INA192.168.1.2
    
    #! /bin/bash
    whiletrue;do#定义无限循环,让脚本不间断工作。
    ping-c1192.168.1.1&>/dev/null#由于linux下的ping命令会无限进行下去,
    所以我们要使用-c参数指定数据包个数,并将标准错误和标准输出全部重定向到/dev/null这个脏目录中。
    if![$?-eq0];then#如果$?的返回值不为零,即ping不通。
        sed-i'/192.168.1.1/s/^/;/'dns#修改配置文件,将包含192.168.1.1的行开头替换成;号。
    (dns配置文件的注释是;号)
        /etc/init.d/bind restart
    else
        sed-i'/192.168.1.1/s/;//'dns#如果可以ping通,那么去掉;
        /etc/init.d/bind restart
    fi
    ping-c1192.168.1.2&>/dev/null
    if![$?-eq0];then
        sed-i'/192.168.1.2/s/^/;/'dns
        /etc/init.d/bind restart
    else
        sed-i'/192.168.1.2/s/;//'dns
        /etc/init.d/bind restart
    fi
        sleep5#休息5秒,继续工作。
    done
    

    如果使用以上的shell脚本,确实可以完成RS主机的高可用,但我们也会发现,如果后端主机出了问题,配置文件会在问题主机ip所在行前不断的增加;号的同时,不断的重启dns服务。这是我们不能允许的,修改后的脚本如下:

    #! /bin/bash
    caipan1=0
    caipan2=0#定义两个裁判变量记录状态
    whiletrue;do
    ping-c1192.168.1.1&>/dev/null
    state=$?
    if![state-eq0]&&[$caipan1-eq0];then
        sed-i'/192.168.1.1/s/^/;/'dns
        /etc/init.d/bind restart
        caipan1=1
    elif[state-eq0]&&[$caipan1-eq1];then
        sed-i'/192.168.1.1/s/;//'dns
        /etc/init.d/bind restart
        caipan1=0
    fi
    ping-c1192.168.1.2&>/dev/null
    state=$?
    if![state-eq0]&&[$caipan2-eq0];then
        sed-i'/192.168.1.2/s/^/;/'dns
        /etc/init.d/bind restart
        caipan2=1
    elif[state-eq0]&&[$caipan2-eq1];then
        sed-i'/192.168.1.2/s/;//'dns
        /etc/init.d/bind restart
        caipan2=0
    fi
        sleep5#休息5秒,继续工作。
    done
    

    caipan变量的目的只有一个,就是增加判断条件,修改配置及重启服务必须要满足双重条件才能进行,通过重置变量让条件不再满足。当然,解决办法还有很多。篇幅所限,在这里我们只给出了变量的办法,希望大家能提出更好的建议,发动集体智慧,开拓思路,写出更好脚本。

    转载请注明:爱开源 » shell解决DNS负载均衡RS的健康检测

  • HTTP/HTTPS自动加密反向代理方案

    这里主要介绍电脑无需任何设置,就能够自动加密代理特定网站的HTTP/HTTPS协议。由于某墙的存在,作用你懂的。敏感磁太多,这里就不多说了。

    方案介绍

    涉及到的软件

    • BIND: 一个流行的域名解析服务器,我们可以设置哪些域名需要走加密线路。
    • Stunnel: 使用TLS对tcp协议进行加密,也就是对tcp建立一条加密线路。
    • SNI Proxy: 代理软件。对于HTTP协议,它可以根据Host请求头解析得出目标站IP;对于HTTPS协议,它可以根据SNI扩展中的域名解析得出目标站IP。

    此方案优缺点

    优点:
    无需手动设置任何代理,就能够自动加密代理特定网站的HTTP或HTTPS协议
    相对于我们常用的ssh隧道,ssh隧道是单路,而此方案是支持多并发连接,可以极大加速网站访问。

    缺点:
    对于代理HTTPS协议,需要发起HTTPS连接的客户端,比如浏览器支持TLS的SNI扩展。好消息是目前浏览器几乎都支持此扩展,但对于一些非浏览器的客户端,不支持SNI扩展。我们只能设置正向代理来解决此问题。

    方案原理

    流程图:
    %E5%8A%A0%E5%AF%86%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86

    原理介绍:

    • 1、首先我们需要准备三台服务器,一台是内网DNS服务器(安装bind),一台是内网代理服务器(安装stunnel),另一台国外服务器(安装stunnel,sniproxy)。
    • 2、我们还需要设置DNS为内网的DNS,并在内网bind dns设置谷歌域名解析的IP为内网代理服务器
    • 3、当我们访问谷歌网站时,首先会向内网DNS服务器发送DNS A记录查询,此时内网DNS服务器会返回内网代理服务器的IP。
    • 4、浏览器得到谷歌域名的解析IP后(即内网代理服务器的IP),会向内网代理服务器发送HTTP或HTTPS请求。
    • 5、此时内网代理服务器(即stunnel),会接收到请求,经过加密,把请求转发到国外服务器(stunnel)的指定端口上。
    • 6、国外服务器(stunnel)接收到来自国内服务器(stunnel)的加密数据后,经过解密,把请求转发到sniproxy。
    • 7、sniproxy再根据HTTP Host请求头或者HTTPS sni扩展的域名解析出谷歌服务器的IP,并把请求转发给谷歌服务器。
    • 8、谷歌服务器收到来自sniproxy发送的请求后,马上返回网页内容给sniproxy,sniproxy再原路返回数据给浏览器。

    方案实施

    由于时间有限,我们仅在Ubuntu server 12.04演示安装。

    环境介绍

    • 系统:Ubuntu server 12.04
    • 内网DNS IP: 10.96.153.201(主),10.96.153.204(从)
    • 内网代理服务器: 10.96.153.204
    • 国外服务器IP: 1.2.3.4

    安装BIND9

    1、在主DNS和从DNS安装bind,即10.96.153.201(主),10.96.153.204(从)。

    wget http://www.isc.org/downloads/file/bind-9-10-0b1-2/?version=tar.gz -O bind-9-10-0b1-2.tar.gz
    tar xzf bind-9-10-0b1-2.tar.gz
    cd bind-9-10-0b1-2
    ./configure --prefix=/usr/local/bind
    make && make install
    

    2、配置主DNS服务器(10.96.153.201)
    2.1、生成/usr/local/bind/etc/rndc.key密钥文件

    /usr/local/bind/sbin/rndc-confgen -a -k rndckey -c /usr/local/bind/etc/rndc.key
    

    2.2、编辑/usr/local/bind/etc/named.conf,写入如何内容:

    include "/usr/local/bind/etc/rndc.key";
    controls { inet 127.0.0.1 port 953 allow { 127.0.0.1; } keys { "rndckey"; }; };
    logging {
    channel default_syslog { syslog local2; severity notice; };
    channel audit_log { file "/var/log/bind.log"; severity notice; print-time yes; };
    category default { default_syslog; };
    category general { default_syslog; };
    category security { audit_log; default_syslog; };
    category config { default_syslog; };
    category resolver { audit_log; };
    category xfer-in { audit_log; };
    category xfer-out { audit_log; };
    category notify { audit_log; };
    category client { audit_log; };
    category network { audit_log; };
    category update { audit_log; };
    category queries { audit_log; };
    category lame-servers { audit_log; };
    };
    options {
        directory "/usr/local/bind/etc";
    pid-file "/usr/local/bind/var/run/bind.pid";
    transfer-format many-answers;
    interface-interval 0;
    forward only;
    forwarders { 202.96.128.166;202.96.134.133; };
    allow-query {any;};
    };
    zone "google.com" {
    type master;
    file "google.com.zone";
    allow-transfer { 10.96.153.204; };
    };
    

    在这个named.conf文件中,我们只需要关心如下内容:
    对于options{}区域,202.96.128.166和202.96.134.133这两个是ISP提供的本地DNS,需要修改为自己所在ISP的本地DNS。
    对于zone “google.com”{}区域,这里定义了google.com域名的区域文件google.com.zone,还有允许10.96.153.204(即从DNS)同步区域文件。
    2.3、建立google.com.zone区域文件

    $TTL 3600
    @ IN SOA ns1.google.com. hostmaster.google.com. (
    2014072015  ; Serial
    3600    ; Refresh
    900 ; Retry
    3600000 ; Expire
    3600 )  ; Minimum
    @ IN NS ns1.google.com.
    @ IN NS ns2.google.com.
    ns1 IN A 10.96.153.201
    ns2 IN A 10.96.153.204
    @ IN A 10.96.153.204
    * IN A 10.96.153.204
    

    对于这个区域文件,
    ns1 IN A 10.96.153.201 指向第一个dns服务器,即主DNS。
    ns2 IN A 10.96.153.204 指向第二个dns服务器,即从DNS。
    @ IN A 10.96.153.204和* IN A 10.96.153.204指向内网的代理服务器(stunnel)。我们只需要修改这三个地方就好了。

    3、配置从DNS服务器(10.96.153.204)
    编辑named.conf,写入如下内容

    logging {
    channel default_syslog { syslog local2; severity notice; };
    channel audit_log { file "/var/log/bind.log"; severity notice; print-time yes; };
    category default { default_syslog; };
    category general { default_syslog; };
    category security { audit_log; default_syslog; };
    category config { default_syslog; };
    category resolver { audit_log; };
    category xfer-in { audit_log; };
    category xfer-out { audit_log; };
    category notify { audit_log; };
    category client { audit_log; };
    category network { audit_log; };
    category update { audit_log; };
    category queries { audit_log; };
    category lame-servers { audit_log; };
    };
    options {
        directory "/usr/local/bind/etc";
    pid-file "/usr/local/bind/var/run/bind.pid";
    transfer-format many-answers;
    interface-interval 0;
    forward only;
    forwarders { 202.96.128.166;202.96.134.133; };
    allow-query {any;};
    };
    
    zone "google.com" {
    type slave;
    file "google.com.zone";
    masters { 10.96.153.201; };
    };
    

    配置从DNS就简单得多,只需要写入如上内容到named.conf文件。同样的,
    options{}中202.96.128.166和202.96.134.133这两个是当地ISP本地dns。
    zone “google.com”{}中10.96.153.201指明主DNS服务器IP。
    4、启动bind dns服务器

    /usr/local/bind/sbin/named
    

    安装Stunnel

    1、在内网代理服务器和国外主机安装stunnel

    apt-get install stunnel4
    

    2、内网代理服务器stunnel配置
    编辑/etc/default/stunnel4,设置ENABLED=1。
    编辑/etc/stunnel/stunnel.conf,内容如下:

    client = yes
    pid = /etc/stunnel/stunnel.pid
    [http]
    accept = 80
    connect = 1.2.3.4:8082
    
    [https]
    accept = 443
    connect = 1.2.3.4:4433
    

    此配置文件表示,监听了80端口,并把此端口流量转发到1.2.3.4:8082,监听了443端口,并把此端口流量转发到1.2.3.4:4433
    3、国外服务器stunnel配置
    3.1、生成ssl证书stunnel.pem文件

    openssl genrsa -out key.pem 2048
    openssl req -new -x509 -key key.pem -out cert.pem -days 1095
    cat key.pem cert.pem >> /etc/stunnel/stunnel.pem
    

    3.2、编辑/etc/stunnel/stunnel.conf文件

    client = no
    [http]
    accept = 1.2.3.4:8082
    connect = 127.0.0.1:8082
    cert = /etc/stunnel/stunnel.pem
    
    [https]
    accept = 1.2.3.4:4433
    connect = 127.0.0.1:4433
    cert = /etc/stunnel/stunnel.pem
    

    此配置文件表示,监听了1.2.3.4:8082,并转发此地址流量到127.0.0.1:8082,监听了1.2.3.4:4433,并转发给地址流量到127.0.0.1:4433。
    3.3、编辑/etc/default/stunnel4,设置ENABLED=1。
    4、启动stunnel

    service stunnel4 start
    

    安装sniproxy

    sniproxy项目地址:https://github.com/dlundquist/sniproxy
    1、安装sniproxy
    同样只演示在ubuntu server 12.04安装。
    1.1、安装UDNS

    mkdir udns_packaging
    cd udns_packaging
    wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4-1.dsc
    wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4.orig.tar.gz
    wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4-1.debian.tar.gz
    tar xfz udns_0.4.orig.tar.gz
    cd udns-0.4/
    tar xfz ../udns_0.4-1.debian.tar.gz
    dpkg-buildpackage
    cd ..
    dpkg -i *.deb
    

    1.2、安装sniproxy

    apt-get install autotools-dev cdbs debhelper dh-autoreconf dpkg-dev gettext libev-dev libpcre3-dev libudns-dev pkg-config
    wget https://github.com/dlundquist/sniproxy/archive/master.zip
    unzip master.zip
    cd sniproxy-master/
    dpkg-buildpackage
    cd ..
    dpkg -i *.deb
    

    2、配置sniproxy
    /etc/sniproxy.conf内容如下:

    user daemon
    pidfile /var/run/sniproxy.pid
    error_log {
        syslog deamon
        priority notice
    }
    listen 127.0.0.1:8082 {
        proto http
        table http_hosts
    }
    table http_hosts {
            .*      *:80
    }
    
    listen 127.0.0.1:4433 {
        proto tls
        table https_hosts
    }
    table https_hosts {
    .*  *:443
    }
    

    此配置文件表示,监听了127.0.0.1:8082地址,并解析http协议中的Host请求头为IP,然后转发请求到此IP;监听了127.0.0.1:4433地址,并解析TLS中SNI扩展中的域名为IP,并转发请求到此IP。
    3、启动sniproxy

    sniproxy
    

    结束

    到目前为止,我们已经搭建完成了整套HTTP/HTTPS加密代理方案。方案中的HTTP明文协议,利用stunnel使用了TLS加密,变成了HTTPS协议,使得数据包无法被解析出明文。方案中的HTTPS协议,本身是加密的,但为了防止SNI扩展的中域名被嗅探,还是走了stunnel的加密通道。对于发送HTTPS请求而不支持SNI扩展的客户端,需要手动设置下代理。下一篇博文我们来介绍加密的正向代理方案。

    转载请注明:爱开源 » HTTP/HTTPS自动加密反向代理方案