标签: 配置

  • 分布式服务框架 Zookeeper-安装和配置详解

    本文介绍的 Zookeeper 是以 3.2.2 这个稳定版本为基础,最新的版本可以通过官网 http://hadoop.apache.org/zookeeper/来获取,Zookeeper 的安装非常简单,下面将从单机模式和集群模式两个方面介绍 Zookeeper 的安装和配置。

    单机模式

    单机安装非常简单,只要获取到 Zookeeper 的压缩包并解压到某个目录如:/home/zookeeper-3.2.2 下,Zookeeper 的启动脚本在 bin 目录下,Linux 下的启动脚本是 zkServer.sh,在 3.2.2 这个版本 Zookeeper 没有提供 windows 下的启动脚本,所以要想在 windows 下启动 Zookeeper 要自己手工写一个,如清单 1 所示:

    清单 1. Windows 下 Zookeeper 启动脚本
    setlocal
    set ZOOCFGDIR=%~dp0%..\conf
    set ZOO_LOG_DIR=%~dp0%..
    set ZOO_LOG4J_PROP=INFO,CONSOLE
    set CLASSPATH=%ZOOCFGDIR%
    
    set CLASSPATH=%~dp0..\*;%~dp0..\lib\*;%CLASSPATH%
    set CLASSPATH=%~dp0..\build\classes;%~dp0..\build\lib\*;%CLASSPATH%
    set ZOOCFG=%ZOOCFGDIR%\zoo.cfg
    set ZOOMAIN=org.apache.zookeeper.server.ZooKeeperServerMain
    java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%"
    -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %*
    endlocal
    

    在你执行启动脚本之前,还有几个基本的配置项需要配置一下,Zookeeper 的配置文件在 conf 目录下,这个目录下有 zoo_sample.cfg 和 log4j.properties,你需要做的就是将 zoo_sample.cfg 改名为 zoo.cfg,因为 Zookeeper 在启动时会找这个文件作为默认配置文件。下面详细介绍一下,这个配置文件中各个配置项的意义。

    tickTime=2000
    dataDir=D:/devtools/zookeeper-3.2.2/build
    clientPort=2181
    
    • tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
    • dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
    • clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。

    当这些配置项配置好后,你现在就可以启动 Zookeeper 了,启动后要检查 Zookeeper 是否已经在服务,可以通过 netstat – ano 命令查看是否有你配置的 clientPort 端口号在监听服务。

    集群模式

    Zookeeper 不仅可以单机提供服务,同时也支持多机组成集群来提供服务。实际上 Zookeeper 还支持另外一种伪集群的方式,也就是可以在一台物理机上运行多个 Zookeeper 实例,下面将介绍集群模式的安装和配置。

    Zookeeper 的集群模式的安装和配置也不是很复杂,所要做的就是增加几个配置项。集群模式除了上面的三个配置项还要增加下面几个配置项:

    initLimit=5
    syncLimit=2
    server.1=192.168.211.1:2888:3888
    server.2=192.168.211.2:2888:3888
    
    • initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 10 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒
    • syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2*2000=4 秒
    • server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端口号。

    除了修改 zoo.cfg 配置文件,集群模式下还要配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面就有一个数据就是 A 的值,Zookeeper 启动时会读取这个文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是那个 server。

    数据模型

    Zookeeper 会维护一个具有层次关系的数据结构,它非常类似于一个标准的文件系统,如图 1 所示:

    图 1 Zookeeper 数据结构

    图 1 Zookeeper 数据结构

    Zookeeper 这种数据结构有如下这些特点:

    1. 每个子目录项如 NameService 都被称作为 znode,这个 znode 是被它所在的路径唯一标识,如 Server1 这个 znode 的标识为 /NameService/Server1
    2. znode 可以有子节点目录,并且每个 znode 可以存储数据,注意 EPHEMERAL 类型的目录节点不能有子节点目录
    3. znode 是有版本的,每个 znode 中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据
    4. znode 可以是临时节点,一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除,Zookeeper 的客户端和服务器通信采用长连接方式,每个客户端和服务器通过心跳来保持连接,这个连接状态称为 session,如果 znode 是临时节点,这个 session 失效,znode 也就删除了
    5. znode 的目录名可以自动编号,如 App1 已经存在,再创建的话,将会自动命名为 App2
    6. znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基于这个特性实现的,后面在典型的应用场景中会有实例介绍

    转载请注明:爱开源 » 分布式服务框架 Zookeeper-安装和配置详解

  • Vsftp配置文件详解

    FTP 分为两类,一种为PORT FTP,也就是一般的FTP﹔另一类是PASV FTP,分述如下:

    PORT FTP 这是一般形式的FTP,首先会建立控制频道,默认值是port 21,也就是跟port 21 建立联机,并透过此联机下达指令。第二,由FTP server 端会建立数据传输频道,默认值为20,也就是跟port 20 建立联机,并透过port 20 作数据的传输。

    PASV FTP 跟PORT FTP 类似,首先会建立控制频道,默认值是port 21,也就是跟port 21 建立联机,并透过此联机下达指令。第二,会由client 端做出数据传输的请求,包括数据传输port 的数字。

    这两者的差异为何?PORT FTP当中的数据传输port是由FTP server 指定,而PASV FTP的数据传输port是由FTP client决定。通常我们使用PASV FTP,是在有防火墙的环境之下,透过client 与server 的沟通,决定数据传输的port。

    BOOLEAN OPTIONS

    allow_anon_ssl
    如果ssl_enable是active的,设置为yes的话,匿名用户则可以使用SSL连接,默认No
    anon_mkdir_write_enable
    如果设置为yes,匿名用户则在一定的条件下(write_enable是active,并且在父目录有写权限)可以创建目录,默认No

    BOOLEAN OPTIONS

    allow_anon_ssl
    如果ssl_enable是active的,设置为yes的话,匿名用户则可以使用SSL连接,默认No
    anon_mkdir_write_enable
    如果设置为yes,匿名用户则在一定的条件下(write_enable是active,并且在父目录有写权限)可以创建目录,默认No
    anon_other_write_enable
    如果设置为yes,匿名用户除了有upload和创建目录权限外,还有写操作权限,如:删除或者重命名文件,默认No
    anon_upload_enable
    如果设置为yes,匿名用户在一定的条件下(write_enable必须是active,匿名用户必须有写权限)可以上传文件,默认No
    anon_world_readable_only
    如置为yes,匿名用户则可以下载可读的文档,默认Yes
    anonymous_enable
    是否允许匿名用户可以登录ftp,如果设置为yes,ftp和anonymous用户都可以作为匿名用户登录,默认Yes
    ascii_download_enable
    如果开启,则以ASCII模式下载数据,默认No
    ascii_upload_enable
    如果开启,则以ASCII模式上传数据,默认No
    async_abor_enable
    如果开启,则一个特殊的ftp命令会被开启,类似于’async ABOR’,是否允许客户端使用rsync,默认No
    background
    如果开启,vsftp将以监听(listen)模式启动,默认No
    check_shell
    只有在PAM没有嵌入vsftp的时候起作用,如果禁止,vsftp则不会在/etc/shells中检查本地用户的shell,默认Yes
    chmod_enable
    如果开启,则允许使用’SITE CHMOD’命令,这个只针对本地用户有效,对匿名用户无效。默认Yes
    chown_uploads
    如果开启,所有的匿名用户……………………
    chroot_list_enable
    如果开启,你需要提供一个本地用户列表以指定谁在chroot()中,如果chroot_local_user设置为Yes,则这个列表表示哪些用户不在chroot()中,默认列表文件是/etc/vsftpd.chroot_list,你也可以通过chroot_list_file参数重置。默认No
    chroot_local_user
    如果开启,则本地用户默认使用chroot(),默认No
    connect_from_port_20
    控制在PORT模式下,数据连接是否使用20端口。默认是No
    debug_ssl
    如果开启,openssl连接诊断会在记录在vsftp日志中,默认No
    delete_failed_uploads
    如果开启,任何上传失败的文件都会被删除,默认No
    deny_email_enable

    dirlist_enable
    如果关闭,所有的列目录命令都会被禁止,默认Yes
    dirmessage_enable
    如果开启,则在用户第一次进入一个目录时,会显示该目录下的.message文件的内容信息,你可以通过message_file重置该文件的设置,默认No
    download_enable

    dual_log_enable
    如果开启,两个日志文件会被同时产生,/var/log/xferlog和/var/log/vsftpd.log,前者是wu-ftpd类型日志,后者是vsftp自己风格的日志,默认No
    force_dot_files
    如果开启,以’.’开头的文件和目录会被列出来,即使没有使用a标记(ls -a),也就是显示隐藏文件,默认No
    force_anon_data_ssl
    只在ssl_enable是active时有效,如果设置为yes,则所有的匿名用户都被强制要求使用ssl发送和接收数据,默认No
    force_anon_logins_ssl
    只在ssl_enable是active时有效,如果设置为yes,则所哟的匿名用户都被强制要求使用ssl发送密码,默认No
    force_local_data_ssl
    只在ssl_enable是active时有效,如果设置为yes,所有的非匿名用户都被强制要求使用ssl发送和接收数据,默认Yes
    force_local_logins_ssl
    只在ssl_enable是active时有效,如果设置为yes,所有的非匿名用户都被强制要求使用ssl发送密码,默认Yes
    guest_enable
    如果开启,所有的非匿名用户以’guest’登录,一个’guest’登录后,会映射到guest_username指定文件中的用户上面,默认No
    hide_ids
    如果开启,所有的用户和组信息在目录中被显示时,都会显示为ftp,默认No
    implicit_ssl

    listen
    如果开启,vsftp则运行在standalone模式,默认No
    listen_ipv6
    类似listen,不过使用IPv6 socket代替IPv4,和listen参数互相排斥,默认No
    local_enable
    控制是否允许本地登录,如果开启,则在/etc/passwd中的用户都可以登录,默认No
    lock_upload_files
    如果开启,则所有的上传进程在上传文件时都会有一个写锁,所有的下载进程在下载文件时共享读锁,默认Yes
    log_ftp_protocol
    如果开启,假如xferlog_std_format参数没有开启,则所有的ftp请求和回应都会被记录进日志,默认No
    ls_recurse_enable
    如果开启,则允许用户使用’ls -R’命令,默认No
    mdtm_write
    如果开启,则允许MDTM修改文件访问时间,默认Yes
    no_anon_password
    如果开启,则阻止vsftp询问匿名用户密码,匿名用户会直接登录,默认No
    no_log_lock
    如果开启,则阻止vsftp在写日志文件时获取文件锁,默认No
    passwd_chroot_enable

    pasv_addr_resolve
    如果你想在pasv模式使用主机名,则可以设置为yes,默认No
    pasv_enable
    如果你想关闭PASV模式,则设置no,默认Yes
    pasv_promiscuous
    如果你想关闭PASV模式的安全检查(它保证数据连接和控制连接源自同一个ip),可以设置为yes,默认No
    port_enable
    如果你想禁止通过PORT方法获取数据连接,可以设置为No,默认Yes
    port_promiscuous
    如果你想禁止PORT模式的安全检查(确保出去的数据连接可以连接到客户端),则可以设置为Yes,默认No
    require_cert
    如果设置为yes,则所有的客户端ssl连接需要提供一个客户端证书,默认No
    syslog_enable
    如果开启,则所有记录到/var/log/vsftpd.log的日志都会记录到system log中,默认No
    tcp_wrappers
    如果开启,vsftp支持tcp_wrappers,默认No
    text_userdb_names

    tilde_user_enable

    use_localtime
    如果开启,vsftp将会在显示目录时显示你的本地时间,默认No
    use_sendfile
    一个内部用来测试sendfile()系统调用效果的参数,默认Yes
    userlist_deny
    如果userlist_enable选项开启,则会检查这个选项,如果设置为no,只有在userlist_file中指定的用户会被允许登录,默认Yes
    userlist_enable
    如果开启,vsftp会加载userlist_file指定的用户列表,如果用户使用列表中的用户名登录,则会被拒绝掉,默认No
    validate_cert
    如果开启,所有的ssl客户端证书必须是验证ok的,而自己签名的证书则不会通过认证,默认No
    virtual_use_local_privs
    如果开启,虚拟用户则会和本地用户具有相同的权限,默认情况下,虚拟用户和匿名用户具有的权限一样,默认No
    write_enable
    是否允许FTP命令改变文件系统,这些命令是:STOR, DELE,RNFR, RNTO, MKD, RMD, APPE and SITE,默认No
    xferlog_enable
    如果开启,一个日志文件会维护上传和下载的详细信息,默认记录在/var/log/vsftpd.log,可以通过vsftpd_log_file重置,默认No
    xferlog_std_format
    如果开启,传输日志则会采用标准的xferlog格式,默认是/var/log/xferlog,可以通过xferlog_file重置,默认No

    NUMERIC OPTIONS

    accept_timeout
    远程客户端使用PASV模式时,建立数据连接的超时时间,默认60s
    anon_max_rate
    最大数据传输速率,默认0(不限制)
    anon_umask
    匿名用户创建一个文件的umask值,默认077
    chown_upload_mode
    匿名用户上传文件的mode值,默认0600
    connect_timeout
    远程客户端使用PORT模式时,响应数据连接的超时时间,默认60s
    data_connection_timeout
    一个允许数据传输粗略的超时时间,默认300s
    delay_failed_login
    暂停报告之前失败的登录,默认1s
    delay_successful_login
    暂停之前一个成功的登录,默认1s
    file_open_mode
    上传的文件具有的权限,默认0666
    ftp_data_port
    PORT模式的连接端口,默认20
    idle_session_timeout
    在两次输入ftp命令之间的空闲时间,默认300s
    listen_port
    如果vsftp工作在standalone模式,该端口监听进入的连接,默认21
    local_max_rate
    本地用户的最大数据传输速率,默认0(不限制)
    local_umask
    本地用户创建一个文件锁具有的umask,默认077
    max_clients
    如果vsftp工作在standalone模式,有多少用户可以连接进来,默认0(不限制)
    max_login_fails
    多少次登录失败后,kill这个连接,默认3
    max_per_ip
    如果vsftp工作在standalone模式,同一个ip源地址可以有多少个连接进来,默认0(不限制)
    pasv_max_port
    在PASV模式下,可以分配给数据连接的最大端口号,默认0(可以使用任何端口)
    pasv_min_port
    在PASV模式下,可以分配给数据连接的最小端口号,默认0(可以使用任何端口)
    trans_chunk_size
    一般不会改变该参数的值,不过你可以设置为8192试试,默认0

    STRING OPTIONS

    anon_root
    在匿名用户成功登录后,跳转到那个目录,默认none
    banned_email_file
    该文件包含了哪些匿名的e-mail 密码是不允许登录的,如果deny_email_enable被开启,则会检查这个参数,默认/etc/vsftpd.banned_emails
    banner_file
    当一个用户登录后,显示的信息,如果设置了该文件,则会覆盖ftpd_banner的设置,默认none
    ca_certs_file
    加载的CA文件的名称,默认none
    chown_username

    匿名用户上传文件所具有的owner,只有在chown_uploads被设置的时候有效,默认root

    chroot_list_file
    该文件包含哪些本地用户使用chroot(),只有在chroot_list_enable被开启的时候起作用,如果chroot_local_user开启,则列表文件变成哪些用户不使用chroot(),默认/etc/vsftpd.chroot_list
    cmds_allowed
    该选项用逗号分割一个允许执行的ftp命令列表(例如在POST模式下,cmds_allowed=USER, PASS,QUIT),默认none
    cmds_denied
    该选项用逗号分割一个不允许执行的ftp命令列表,默认none
    deny_file
    设置一个文件名模式,该匹配的文件都不许被访问,例如deny_file={.mp3,.mov,.private},默认none
    dsa_cert_file
    ssl加密连接中使用的DSA文件的位置,默认none
    dsa_private_key_file
    DSA私钥的位置,默认none
    ftp_username
    使用该帐号处理匿名登录的情况,默认ftp
    ftpd_banner
    可以使用该选项替换第一次登陆时显示的默认欢迎语句,默认none
    guest_username
    把一个真实用户映射到guest user上面,默认ftp
    hide_file

    listen_address
    如果vsftp工作在standalone模式,则可以通过该选项替换默认监听地址,默认none
    listen_address6
    和listen_address一样,替换ipv6
    local_root
    在本地用户登录后,vsftp跳转到那个目录,默认none
    message_file
    如果dirmessage_enable开启,该选项设置默认的信息文件,默认.message
    nopriv_user
    该用户完全没有特权,默认nobody
    pam_service_name
    vsftp使用的PAM服务的名称,默认vsftpd
    secure_chroot_dir
    该选项设置一个空目录名称,该目录不能被ftp用户有写权限,该目录作为一个安全的chroot()环境,vsftp不需要文件系统访问它
    ssl_ciphers
    ssl加密算法,默认DES-CBC3-SHA
    user_config_dir
    允许你覆盖任何配置文件中的选项,它是基于每一个用户的配置,如果你设置user_config_dir为/etc/vsftpd_user_conf,当你用chris用户登录时,vsftp会申请加载/etc/vsftpd_user_conf/chris文件在该会话期内,不是所有的参数都在个人配置文件中生效的,默认none
    user_sub_token
    用来连接虚拟账户,它会为每个虚拟帐号自动生成家目录,例如设置guest_username为/home/virtual/$USER,user_sub_token为$USER,当fred虚拟账户登录时,fred的家目录为/home/virtual/fred,默认none
    userlist_file
    当userlist_enable开启的时候,会加载该参数设置的文件,默认/etc/vsftpd.user_list
    vsftpd_log_file
    在xferlog_enable被设置,并且xferlog_std_format没有设置的时候,来指定vsftp风格的日志写到哪个文件中,如果你设置了syslog_enable,则日志会写入系统日志文件中,默认/var/log/vsftpd.log
    xferlog_file
    在xferlog_enable和xferlog_std_format被设置的时候,wu-ftpd风格的传输日志写入哪个文件中,默认/var/log/xferlog

    相关文章

    转载请注明:爱开源 » Vsftp配置文件详解

  • nginx if 多重判断

    nginx的配置中不支持if条件的逻辑与&& 逻辑或|| 运算 ,而且不支持if的嵌套语法,否则会报下面的错误:nginx: [emerg] invalid condition。

    我们可以用变量的方式来间接实现。

    要实现的语句:

    if ($remote_addr = “192.168.3.21” && $uri ~* “php”) {
     return 404;
    }
    

    如果按照这样来配置,就会报nginx: [emerg] invalid condition错误。

    可以这么来实现,如下所示:

    set $tag 0;
    if ($remote_addr = "192.168.3.21"){
    set $tag "${tag}1";
    }
    if ($uri ~* “php”){
    set $tag "${tag}1";
    }
    if ($tag = "011"){
    return 404;
    }
    

    相关文章

    转载请注明:爱开源 » nginx if 多重判断

  • MySQL 性能优化:性能提升 50%,延迟降低 60%

    当我进入 Pinterest 时,我的头三个星期是在本部度过的,在那里最新工程把解决生产问题的成果应用到了整个软件栈中。在本部,我们通过构建 Pinterest 来学习 Pinterest 是怎样被构建的,并且,仅仅在几天里就提交代码、做出有意义的贡献也不是不常见。在 Pinterest ,新进来的工程师可以灵活地选择参加哪个组,而且作为在本部工作经历的一部分,编写不同部分的代码可以有助于做出这个选择。本部的人通常会做不同的项目,而我的项目则是深入研究 MySQL 的性能优化。

    Pinterest, MySQL 和 AWS,我的天!

    我们的 MySQL 完全运行在 AWS 中。尽管使用了相当高性能的实例类型(配备 SSD RAID-0 阵列),和相当简单的工作负载(很多基于主键或简单范围的单点查询),峰值大约 2000 QPS,我们还是无法达到期望的 IO 性能水平。

    写 IOPS 一旦超过800,就会导致无法接受的延迟和复制滞后。复制滞后或者从库的读性能不足减慢 ETL 和批处理任务,依靠这些批处理任务的任何小组都会受到负面影响。唯一可行的选项是要么选择一个更大的实例,这样会使我们的开销翻倍、消除我们的效率,或者找办法使现存的系统运行地更好。

    我从我的同事 Rob Wultsch 那接手了这个项目,他已做出很重要的发现:当在 AWS 的 SSD 上运行时,Linux内核版本非常重要。Ubuntu 12.04 携带的默认 3.2 版本没有减少开销, AWS 推荐的最低 3.8 版本也没有(尽管 3.8 仍比 3.2 快了两倍多)。在一个 i2.2xlarge(双SSD RAID-0 阵列)实例、3.2 内核版本上运行 sysbench 勉强在 16 K 随机写时达到 100 MB/sec。将内核升级至 3.8 会使我们在同样的测试上达到 350 MB/sec,但这比期望值还是差了很多。看到这样一个简单的改变引起这样的提升,为我们揭示了许多新的关于低效和差的配置选项的问题和猜想:我们能否从一个更新的内核上获得更好的性能?我们应该改变 OS 级别的其他设置吗?有没有优化可以在 my.conf 中找到?我们怎样能使 MySQL 运行地更快?

    在寻找答案中,我设计了 60 个基本不同的 sysbench 文件 IO 测试配置,配合不同的内核、文件系统、挂载选项和 RAID 块大小。一旦从这些实验中选取了最佳配置,我又运行另外 20 个左右的 sysbench OLTP ,使用其他的系统配置。基本的测试方法在所有的测试中是相同的:运行测试一个小时,每隔 1 秒收集数据,然后考虑缓存热身时间去掉开始的 600 秒的数据,最后处理剩下的数据。识别出最优配置后,我们重新构建了我们最大的、最重要的服务器,把这些改变放进了生产环境。

    从 5000 QPS 到 26000 QPS : 扩展 MySQL 性能,无需扩展硬件

    让我们看看这些改变对一些基本的 sysbench OLTP 测试的影响,我们在 16 线程、 32 线程和几个不同的配置下衡量 p99 响应时间和吞吐量两个指标来看看效果。

    这里是每种数字代表的含义:

    • CURRENT : 3.2 内核和标准的 MySQL 配置
    • STOCK:3.18 内核和标准的 MySQL 配置
    • KERNEL:3.18 内核和少量的 IO/内存 sysctl 微调
    • MySQL:3.18 内核和优化过的 MySQL 配置
    • KERN + MySQL:3.18 内核和 #3、#4 中的微调
    • KERN + JE::3.18内核和 #3 中的微调和 jemalloc
    • MySQL + JE:3.18 内核和 #4 中的 MySQL 配置和 jemalloc
    • ALL:3.18 内核和 #3,#4 和 jemalloc

    1473517859-8556-8611gw1f7dc61am67j20f00da0w2

    1473517859-3900-8611gw1f7dc62i4wcj20f006e75u

    当我们启用所有的优化后,我们发现在 16 和 32 线程下取得了大概 500% 多的读写吞吐量,同时在读写两个方向上降低了 500 ms 的 p99 延迟。在读方面,我们从大概 4100 – 4600 QPS 达到 22000 – 25000以上,分值取决于并发数。在写方面,我们从大概 1000 QPS 达到 5100 – 6000 QPS。这些是在仅仅通过一些简单改变获得的巨大增长空间和性能提升。

    当然,所有这些人工基准测试如果不能转换成实际成果,都是没有意义的。下图展示了从客户端和服务端的角度看,我们主要集群上的延迟,时间跨度为从升级前几天到升级后几天。这个过程花了一个星期才完成。

    1473517860-9689-8611gw1f7dc62wq0sj20f008lgnu

    红线表示客户端感觉到的延迟,绿线表示服务端测到的延迟。从客户端看,p99 延迟从一个波动较大、有时超过 100 ms 的 15 – 35 ms 降至相当平稳的 15 ms、有时会有 80 ms 或更低的异常值。服务端测量的延迟也从波动较大的 5 – 15 ms降至基本平稳的 5 ms,其中每天会有一个由系统维护引起的 18 ms 的尖值。此外,从年初起,我们的高峰吞吐量增加了 50%,所以我们不仅在处理相当大的负载(仍然在我们估计的容量里),同时我们还拥有更好、更可预计的吞吐量。而且,对于每个想睡个安稳觉的人而言的好消息是,与系统性能或通用服务器负载相关的换页事件从三月的 300 降至四月和五月两个月加起来的几个。

    相关文章

    转载请注明:爱开源 » MySQL 性能优化:性能提升 50%,延迟降低 60%

  • nginx配置反向代理或跳转出现400问题处理记录

    午休完上班后,同事说测试站点访问接口出现400 Bad Request Request Header Or Cookie Too Large提示,心想还好是测试服务器出现问题,影响不大,不过也赶紧上服务器进行测试查看,打开nginx与ugwsi日志与配置,发现后端服务日志记录正常,而测试站点的访问日志有7百多M(才运行两三天没几个访问,几M的话才是正常现象),在浏览器里直接访问后端服务接口也正常没有问题(我们的服务器软件架构是微服务架构,将很多模块分拆后分别部署,前端是一个纯HTML站点,通过AJAX访问后端各个服务,由于访问量不大,所以前端站点的nginx配置时,做了反向代理访问后端其他服务,这样就不会出现跨域和需要处理多子域名事情——即访问不同的服务时,只需要使用当前域名就可以了,这样前端开发人员不必要知道后端挂载了多少服务需要使用什么对应的域名访问)。访问这台服务器上的其他站点都能正常访问,而问题站点的html页面也能正常打开……在测试过程中发现,每访问一下问题接口,访问日志就增加30多M,刷了几次,nginx日志大小直线上升……

    由于日志比较大,只能使用tail -n 5000 xxx_access.log >> xxx.log截取一下最新的日志记录下载下来,打开一看发现同一时间一个访问,生产了2000多条重复循环的访问记录,而日志尾部$http_x_forwarded_for部分,有规律的存储了相同的由多到少的IP字串,即:最后一条有一个IP字串(真实IP),倒数第二条有两个IP字串(真实IP + 服务器本地IP),倒数第三条有三个IP字串(真实IP + 两个服务器本地IP),以至类推

    百度了一下“400 Bad Request Request Header Or Cookie Too Large”,查找出来的几乎都是说“nginx 400 Bad request是request header过大所引起,request过大,通常是由于cookie中写入了较大的值所引起。在nginx.conf中,将client_header_buffer_size和large_client_header_buffers都调大后可解决”,一看就知道这肯定不是我这种情况的解决办法,这是由于不知道什么原因引起的死循环将IP地址串写入请求头,直到缓存爆了才返回400,如果将缓存设置更大,只会造成日志增加速度变大而已。从分析来看应该是nginx出现的问题。

    没有办法只能在打开nginx配置文件分析,问题站点的配置文件,如下图,并没有发现什么问题

    打开nginx.conf进行慢慢研究,发现多了几行代码

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    

    这是用来将当前访问用户的IP传给后端服务器用的,将它们删除重新启动一下服务器nginx后测试了一下,发现能正常访问了…o my god,再将它放回去,重启,访问,挂了,去掉,重启,访问,正常……重试了好几次,终于确定就是突然多出来的几行代码引起的。(后来问了一下同事才知道是他进服务器添加的)

    难道真的是不能使用吗?记得以前用过还是正常的。尝试访问预生产环境接口,正常。打开预生产环境的nginx配置,包函有这三行代码,如下图

    全面对比后发现,生产环境用的nginx配置是域名,而预生产环境用的是IP+端口,除此之外没有任何区别,使用跳转方式与反向代码方式测试,结果都是一样,添加port_in_redirect、server_name_in_redirect配置也没能解决

    综合分析,应该是nginx在使用proxy_pass做跳转时,如果直接使用域名,且需要向后端提交当前访问的IP地址时,引发nginx的bug造成死循环,不知道大家有没有遇到过这种情况。

    # 使用反向代理方式
    # 正常的配置 upstream xxx{ server 127.0.0.1:23456; } upstream yyy { server 127.0.0.1:123455; }
    # 异常配置 upstream xxx1{ server xx.xxx.com; } upstream yyy2 { server yyy.xxx.com; }
    
    # 使用跳转方式 # 正常配置 proxy_pass http://127.0.0.1:23456; # 异常配置 proxy_pass http://xx.xxx.com;
    

    转载请注明:爱开源 » nginx配置反向代理或跳转出现400问题处理记录

  • Mac 设置指南 推荐

    如何配置一个高效的 Mac 工作环境

    Table of Contents

    1. OS X

    一直想写这么一篇文章,把我从同事那里学到的经验分享出来。市面上有很多类似的文章,写得都非常好,让我受益匪浅。不过我还是有一些自己总结出来的经验想要分享。

    在工作中,我一般会在 1 到 10 人的团队中,经常会结对编程,即两个人共用一台 Mac 工作,因此也经常会把 Mac 外接一个大显示器、鼠标和键盘。我的常用开发平台有 Java、Ruby、Node.js、Web 等,使用JetBrains的开发工具,比如 IntelliJ IDEA、RubyMine、WebStorm 等。

    我深知自己的知识有限,所以写下本文以便和大家切磋交流。同时更有效率的方法和更好的工具也在不断涌现,我也贪心的希望把更好的方法和工具都收集更到到这里,我会不断更新本文,让它尽量不过时。最新内容请访问:https://github.com/macdao/ocds-guide-to-setting-up-mac。欢迎通过 GitHub 的Issues或者直接Pull Requests方式来分享你的经验。期待你的反馈。

    我认为“一个高效的 Mac 工作环境”有以下几个特点:

    • 自动化 举个例子。手动安装一个应用,需要1)打开浏览器,2)搜索应用的名字,3)打开应用网站,4)寻找下载链接和安装方法,5)下载并等待下载完成,6)安装下载文件,7)可能还有后续的安装步骤。而自动化安装一个应用,只需要1)打开终端工具,2)敲入安装命令,3)等待完成这几个步骤。
      自动化可以大大简化操作,提高效率。
    • 统一 我经常结对编程,偶尔会遇到快捷键不一样,命令不同等问题。我强烈建议,至少在一个团队中,大家尽量使用相同的快捷键、命令等环境。(我记得有个实践就是这个,可是我一直没找到该实践的名字和出处,求告诉)
    • 够用 够用就好,如果系统本身已经满足了我的需求,我不会再使用第三方工具。
    • 效率 效率,一切都是为了效率。

    本文对于第三方应用如何安装和使用只有最简单的介绍,具体还请参考官方网站和相关文档。

    有些章节标题标注了[OCD],意思是这些章节带有我强烈的个人色彩,如果你跟我臭味相投,欢迎借鉴,如果你并不认同,请忽略掉好了。

    PS:虽然本文名为“强迫症”,但其实并不是真正意义上的强迫症,真正意义上的强迫症是一种会对患者的日常生活产生负面影响的疾病。

    1. OS X

    本节介绍操作系统本身的一些设置。

    功能键

    默认情况下,F1-F12 都是特殊功能,比如调节屏幕亮度。而当你需要键入 F1-F12 时(比如在使用 IntelliJ IDEA 的快捷键时),需要同时按住 Fn。这对于开发人员来说是非常不方便的。

    把 F1-F12 改成标准功能键:选择System Preferences>Keyboard,在Keyboard标签页中选中Use all F1, F2, etc. keys as standard function keys

    全键盘控制

    当你在 Sublime Text 里关闭文件时,可能会遇到这样的对话框:

    注意这个Save按钮跟其他两个按钮不太一样,它的底色是蓝的。像这种按钮,除了用鼠标点击触发外,还可以通过回车键触发。

    那么问题来了,如果你不想保存,想点击Don't Save,是不是只能用鼠标点击了呢?

    并不是这样:选择System Preferences>Keyboard,在Shortcuts标签页中选择All controls;或者使用快捷键⌃F7。之后这个对话框会变成这样:

    这个Don't Save按钮有了一圈蓝边,这个意味着你可以通过空格键触发。不仅如此,你还可以用Tab键把蓝边转移到其他按钮,来实现全键盘控制。

    Spotlight 快捷键

    中文版 OS X 的 Spotlight 的快捷键是⌃Space。这个快捷键有一些问题:

    • JetBrains 的 IDE,比如 IntelliJ IDEA、WebStorm 等都使用⌃Space作为自动完成这个最常用功能的快捷键。我不建议更改 IDE 的快捷键,而建议更改 Spotlight 的快捷键。
    • 对于没有添加中文输入法的 Mac 来说,Spotlight 的快捷键是⌘Space。英语国家的人都是这样的。所以我建议把 Spotlight 的快捷键设置为⌘Space,跟他们一致。

    输入法快捷键

    一般来说切换输入法的快捷键是⌘Space。由于我建议把 Spotlight 的快捷键设置为⌘Space,所以我建议把切换输入法的快捷键设置为⌥Space

    其他快捷键

    让双手尽量多的键盘和快捷键,少使用鼠标和触摸板,可以大大提高效率。

    • Mac keyboard shortcts 苹果官方文档。当你在写代码,怎么通过快捷键让光标转移到行首、行尾、向上翻页或者将光标移左移一个词?都在这片文档里。
    • Mac keyboard shortcuts for accessibility features 苹果官方文档。回车触发蓝底按钮,空格触发蓝边按钮,都出自这里。

    设置 Trackpad 轻拍以点击

    默认情况下按下触摸板才是点击。我喜欢设置成用轻拍作为点击:

    选择System Preferences>Trackpad,在Point & Click标签页中选中Tap to click

    语音

    OS X 自带了语音功能,可以用say命令让 Mac 开口说话:

    say hello
    

    可以和&&或者;配合使用来提示你某任务已经完成:

    brew update && brew upgrade && brew cleanup ; say mission complete
    

    通过命令行来听取发音还是有点麻烦。其实我们几乎可以在任何地方选中单词,然后使用快捷键⌥+ESC发音。仅仅需要这样设置一下:选择System Preferences>Dictation & Speech,在Text to Speech标签页中选中Speak selected text when the key is pressed

    词典

    OS X 自带了词典(Dictionary)。你几乎可以在任何应用中通过三指轻拍触摸板来现实对应单词的释义。

    也可以打开 Dictionary 应用来查找单词。

    可以在 Dictionary 应用中添加英汉汉英词典。

    Dock Position

    默认 Dock 在屏幕下方。我们的屏幕一般都是 16:10,Dock 在屏幕下方的话会占据本来就不大的垂直空间。建议把 Dock 放到左边或者右边。

    Remove all Dock icons[OCD]

    本条目对于强迫症适用。

    默认情况下 Dock 被一堆系统自带的应用占据着,而其中大部分我都很少使用,当我打开几个常用应用后,Dock 上会有很多图标,每个图标都会被挤得很小。所以我会把所有 Dock 上固定的图标都删掉,这样一来 Dock 上只有我打开的应用。

    PS:Finder 图标是删不掉的。

    重置 Launchpad 上图标位置[OCD]

    本条目对于强迫症适用。

    新的应用被安装后,经常会跑到 Launchpad 的第一屏,所以它们的位置跟安装的顺序有关系,而我更希望它们可以按照某种更加稳定的顺序排列,比如按照系统默认的顺序:

    defaults write com.apple.dock ResetLaunchPad -bool true; killall Dock
    

    在默认顺序中,Launchpad 第一屏只有 Apple 自家应用。

    2. 常用工具

    本节介绍一些常用的,跟开发没有直接关系的第三方应用及其设置。

    Homebrew

    包管理工具,官方称之为The missing package manager for OS X

    安装步骤:先打开 Terminal 应用,输入:

    ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    

    有了 brew 以后,要下载工具,比如 MySQL、Gradle、Maven、Node.js 等工具,就不需要去网上下载了,只要一行命令就能搞定:

    brew install mysql gradle maven node
    

    PS:安装 brew 的时候会自动下载和安装 Apple 的 Command Line Tools。

    brew 的替代品有MacPorts,现在基本没人用它。

    Homebrew Cask

    brew-cask 允许你使用命令行安装 OS X 应用。比如你可以这样安装 Chrome:brew cask install google-chrome。还有 Evernote、Skype、Sublime Text、VirtualBox 等都可以用 brew-cask 安装。

    brew-cask 是社区驱动的,如果你发现 brew-cask 上的应用不是最新版本,或者缺少你某个应用,你可以自己提交 pull request。

    安装:

    brew install caskroom/cask/brew-cask
    

    应用也可以通过 App Store 安装,而且有些应用只能通过 App Store 安装,比如 Xcode 等一些 Apple 的应用。App Store 没有对应的命令行工具,还需要 Apple ID。倒是更新起来很方便。

    几乎所有常用的应用都可以通过 brew-cask 安装,所以你要安装新的应用时,建议用 brew-cask 安装。如果你不知道应用在 brew-cask 中的 ID,可以先用brew cask search命令搜索。

    iTerm2

    iTerm2 是最常用的终端应用,是 Terminal 应用的替代品。提供了诸如Split Panes等一群实用特性。它默认的黑色背景让我毫不犹豫的抛弃了 Terminal。

    安装:

    brew cask install iterm2
    

    感谢 brew-cask,我们可以通过命令行自动安装 iTerm2 了。

    在终端里,除了可以用⌃E等快捷键(详见其他快捷键)之外,还可以使用⌥B⌥F等快捷键(具体可以参考这里)。前提是这样设置一下:

    选择Iterm菜单 >Preferences>Profiles,选择你在使用的 Profile(默认是Default),在Keys标签页中把Left option (⌥) key acts asRight option (⌥) key acts as都设置成+ESC

    在打开新的窗口/标签页的时候,默认情况下新窗口总是 HOME 目录,还需要我每次敲命令才能进入工作目录。如果想要这个新窗口在打开的时候就自动进入工作目录,需要如下设置:

    选择Iterm菜单 >Preferences>Profiles,选择你在使用的 Profile(默认是Default),在General标签页中的Working Directory部分中选择Reuse previous seesion's directory

    至此,Terminal 应用已经出色的完成了其历史使命。后面就交给 iTerm2 啦。

    Oh My Zsh

    默认的 Bash 是黑白的,没有色彩。而 Oh My Zsh 可以带你进入彩色时代。Oh My Zsh 同时提供一套插件和工具,可以简化命令行操作。后面我们会看到很多介绍,你会看到我爱死这家伙了。

    安装:

    sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
    

    目前我使用的插件有:git z sublime history rbenv bundler rake

    Oh My Zsh 使用了 Z shell(zsh),一个和 Bash 相似的 Shell,而非 Bash。

    在 Z shell 中,~/.zshrc是最重要的配置文件。Oh My Zsh 在安装的时候会把当前环境的$PATH写入~/.zshrc中。这并不是我期望的行为,因为使用了 brew,我们基本不再需要去定制$PATH,而 Oh My Zsh 提供的默认$PATH$HOME/bin:/usr/local/bin:$PATH是非常合适的一个值,它把$HOME/bin加入了$PATH,可以让我们把自己用的脚本放到$HOME/bin下。

    所以建议把~/.zshrc重置:

    cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc
    

    Oh My Zsh 还有很多有价值的插件。

    替代品有Oh My Fish,使用了Fishshell作为基础。

    Git 常用别名

    几乎每个人都会使用一些方法比如 Git 别名来提高效率,几乎所有人都会把使用git st来代替git status。然而这需要手动设置,每个人也都不完全一样。

    Oh My Zsh 提供了一套系统别名(alias),来达到相同的功能。比如gst作为git status的别名。而且 Git 插件是 Oh My Zsh 默认启用的,相当于你使用了 Oh My Zsh,你就拥有了一套高效率的别名,而且还是全球通用的。是不是棒棒哒?下面是一些我常用的别名:

    Alias Command
    gapa git add --patch
    gc! git commit -v --amend
    gcl git clone --recursive
    gclean git reset --hard && git clean -dfx
    gcm git checkout master
    gcmsg git commit -m
    gco git checkout
    gd git diff
    gdca git diff --cached
    glola git log --graph --pretty = format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --all
    gp git push
    grbc git rebase --continue
    gst git status
    gup git pull --rebase
    gwip git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit -m "--wip--"

    完整列表请参考:https://github.com/robbyrussell/oh-my-zsh/wiki/Plugin:git

    Scroll Reverser

    当你在浏览一个很长的网页时,你看完了当前显示的内容,想要看后续的内容,你可以在 Trackpad 上双指上滑,或者鼠标滚轮向上滚动。这是被称作“自然”的滚动方向。

    然而在 Windows 里鼠标滚动的行为是相反的:鼠标滚轮向下滚动才会让浏览器显示后续的内容,向上滚动会达到页面的顶部。你可以在 OS X 的系统偏好设置里修改(选择System Preferences>Trackpad,在Scroll & Zoom标签页中不选中Scroll direction: natural),但是这样会同时改变鼠标滚轮的方向和 Trackpad 的方向。

    要想只改变鼠标滚轮的方向,而保持 Trackpad 依旧是“自然”的,我们需要 Scroll Reverser:

    brew cask install scroll-reverser
    

    PS:这货会让三指点击失效

    ShiftIt

    原生 OS X 下只能手动调整窗口大小,所以我们需要窗口管理工具。我用过很多窗口管理工具,可惜大部分工具都存在快捷键冲突的问题(对我来说主要是 IntelliJ IDEA)。ShiftIt 是少见的没有冲突的窗口管理工具:

    brew cask install shiftit
    

    PS:ShiftIt的旧版本需要安装 X11,最新版本已经修正了这个问题。

    替代者有 SizeUp,主要快捷键和 ShiftIt 相同。

    当然如果喜欢 hacking,Slate是个不错的 hackable 的窗口管理工具。配置可以参照http://thume.ca/howto/2012/11/19/using-slate/

    Sublime Text 2

    安装:

    brew cask install sublime-text
    

    在命令行中指定使用 Sublime Text 打开某文件,是一个非常常用的功能,一般我们会按照OS X Command Line中所说执行ln -s "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl" ~/bin/subl来增加subl链接。但是如果你用 brew-cask 安装的话,恭喜你,你不需要运行这个命令,因为 brew-cask 自动帮你做了这件事情。而且你卸载 Sublime Text 的时候 brew-cask 会自动删掉这个链接。

    同时 Oh My Zsh 也提供了 Sublime Text 插件,叫做sublime。参考:https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins/sublime,这个插件和通过 brew-cask 安装的 Sublime Text 完美兼容。

    替代品有 TextMate,Sublime Text 3 等。

    MacDown

    MacDown 是 Markdown 编辑器。由于 Mou 一直不支持代码高亮,我就转向了 MacDown。完美支持GFM。

    我特别喜欢Markdown,我用 Makdown 来写文章(包括本文),写幻灯片(reveal.js)。Markdown 可以让我专注于内容本身,而无需花精力在排版和样式上。

    安装:

    brew cask install macdown
    

    z

    在打开终端后,你是怎么进入项目的工作目录?是cd xxx⌃R还是用别名?

    z工具可以帮你快速进入目录。比如在我的 Mac 上运行z cask就会进入/usr/local/Library/Taps/caskroom/homebrew-cask/Casks目录。

    这货的安装非常方便,甚至都不需要下载任何东西,因为它已经整合在了 Oh My Zsh 中。编辑~/.zshrc文件,在plugins=(git)这行中加上z变成plugins=(git z),然后运行source ~/.zshrc重新加载配置文件,就可以使用 z 了。

    替代品有 autojump。autojump 需要使用 brew 安装。

    Vimium

    Vimium 是一个 Google Chrome 扩展,让你可以纯键盘操作 Chrome,把你的 Chrome 变成“黑客的浏览器”。

    安装方法请参考官方网站。

    其他浏览器也有类似的工具,比如 FireFox 的KeySnail。

    LastPass

    LastPass 是管理密码的工具,支持二次验证,提供所有浏览器插件以及 Mac 桌面版本。

    最重要的是,它提供命令行的版本,可以直接通过 brew 安装

    brew install lastpass-cli --with-pinentry
    

    之后,只需要登陆:

    lpass login you@email.com
    

    就可以拷贝密码或者集成到其他命令中了:

    lpass show --password gmail.com -c
    

    3. 开发工具

    Java

    现在 OS X 都不会自带 JDK 了,所以进行 Java 开发的话,需要下载 JDK。在 brew-cask 之前,我们需要从https://developer.apple.com/downloads/或者 Oracle 网站上下载。还有更麻烦的--卸载 JDK 和升级 JDK。

    JDK 安装文件是 pkg 格式,卸载和.app不一样,且没有自动卸载方式。

    而 brew-cask 提供了自动安装和卸载功能,能够自动从官网上下载并安装 JDK 8。

    brew cask install java
    

    如果你需要安装 JDK 7 或者 JDK 6,可以使用homebrew-cask-versions

    brew tap caskroom/versions
    brew cask install java6
    

    在 OS X 上,你可以同时安装多个版本的 JDK。你可以通过命令/usr/libexec/java_home -V来查看安装了哪几个 JDK。

    那问题来了,当你运行java或者 Java 程序时使用的是哪个 JDK 呢?在 OS X 下,java也就是/usr/bin/java在默认情况下指向的是已经安装的最新版本。但是你可以设置环境变量JAVA_HOME来更改其指向:

    $ java -version
    java version "1.8.0_60"
    Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
    Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
    $ JAVA_HOME=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home java -version
    java version "1.6.0_65"
    Java(TM) SE Runtime Environment (build 1.6.0_65-b14-466.1-11M4716)
    Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-466.1, mixed mode)
    

    其中JAVA_HOME=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home可以用`JAVA_HOME=/usr/libexec/java_home -v 1.6“`这种更加通用的方式代替。

    jEnv

    也可以使用 jEnv 来管理不同版本的 JDK,这个工具跟rbenv类似,通过当前目录下的.java-version来决定使用哪个 JDK。jEnv 也可以用 brew 安装。不过要使用 jEnv 要有几个问题:

    • 需要手动把eval "$(jenv init -)"加入 profile,没有 Oh My Zsh 插件。这点是我非常反感的。 可以把eval "$(jenv init -)"加入~/.zlogin,这样可以避免修改~/.zshrc
    • 需要手动添加 JDK,不会自动采集系统 JDK。跟 Ruby 不同,OS X 已经提供/usr/libexec/java_home工具来管理安装的 JDK。
    • 需要jenv rehash。这个是跟 rbenv 学的。

    所以我建议不要使用 jEnv。

    Java[OCD]

    作为一个强迫症患者,每当我看到 Java 的错误写法就想纠正过来。

    当指编程语言时,Java 的正确写法是首字母大写,其余小写。其他写法比如JAVAjava都是不对的。

    在其他一些地方会使用小写的java

    • java命令
    • 原文件Main.java
    • 包名java.lang

    只有在全大写的标题里使用JAVA或者环境变量JAVA_HOME

    IntelliJ IDEA

    Java 开发必备工具 IntelliJ IDEA。可以安装 Ultimate Edition:

    brew cask install intellij-idea
    

    也可以安装开源免费的 Community Edition:

    brew cask install intellij-idea-ce
    

    IntelliJ IDEA 有几套内建的快捷键方案(Keymap)。其中适用于 OS X 的有Mac OS XMac OS X 10.5+两种。区别是:

    • Mac OS X方案和其他平台上的快捷键类似,
    • Mac OS X 10.5+更加符合 OS X 常用的快捷键。

    一个团队使用不同的快捷键会严重影响效率。可以用View | Quick Switch Scheme⌃ Back Quote)快速切换 Keymap。

    如果可以选择的话,我建议使用Mac OS X方案。因为我经常遇到使用 Windows 的客户,而 Windows 平台上的快捷键和Mac OS X方案类似。

    rbenv

    人人都需要一个 Ruby 版本管理工具。rbenv 就是这样一个轻量级工具,它可以通过 brew 安装。

    安装:

    brew install rbenv ruby-build
    

    然后在~/.zshrc中加上rbenv插件。否则你需要手动添加eval "$(rbenv init -)"~/zshrc或者~/.zprofile文件里。

    有时候项目会依赖一些奇怪的版本号,比如ruby-2.1.0,这个时候你需要rbenv-aliases帮忙:

    brew install rbenv-aliases
    

    替代品有 RVM、chruby。因为 RVM 不能通过 brew 安装,并且安装的时候会没有节操的修改一堆文件,所以被我早早的弃用了。chruby 也是一个轻量级工具,而且可以完美的和 Oh My Zsh 集成在一起,我看到有些生产环境在用它。

    Ruby 常用别名

    几乎所有 Ruby 开发人员都会把bi作为bundle install的别名。Oh My Zsh 提供builder插件,这个插件提供了一套别名,比如bibe。同时还能让你在运行一些常用 gem 的时候直接输入rspec,不需要be rspec这样了。具体包括哪些命令请参考这里

    Z shell 对于[]符号有特殊的处理,所以在运行rake task[parameter]的时候会报错,你需要改成rake task[parameter]或者noglob rake task[parameter]。然而 Oh My Zsh 已经看穿这一切,自带的 rake 插件已经解决了这个问题:brake task[parameter]

    添加插件的时候注意把rake放到bundler后面,例如这样:

    plugins=(git z sublime history rbenv bundler rake)

    参考资料

    转载请注明:爱开源 » Mac 设置指南 推荐

  • 百万并发之 tcp_mem

    The “Out of socket memory” error

    Out of Socket memory

    关于 Out of Socket memory

    在服务端,连接达到一定数量,诸如50W时,有些隐藏很深的问题,就不断的抛出来。 通过查看dmesg命令查看,发现大量TCP: too many of orphaned sockets错误,也很正常,下面到了需要调整tcp socket参数的时候了。

    第一个需要调整的是tcp_rmem,即TCP读取缓冲区,单位为字节,查看默认值

    cat /proc/sys/net/ipv4/tcp_rmem
    4096 87380 4161536
    

    默认值为87380 byte ≈ 86K,最小为4096 byte=4K,最大值为4064K。

    第二个需要调整的是tcp_wmem,发送缓冲区,单位是字节,默认值

    cat /proc/sys/net/ipv4/tcp_wmem
    4096 16384 4161536
    

    解释同上

    第三个需要调整的tcp_mem,调整TCP的内存大小,其单位是页,1页等于4096字节。系统默认值:

    cat /proc/sys/net/ipv4/tcp_mem
    932448 1243264 1864896
    

    tcp_mem(3个INTEGER变量):low, pressure, high

    • low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。
    • pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。
    • high:允许所有tcp sockets用于排队缓冲数据报的页面量,当内存占用超过此值,系统拒绝分配socket,后台日志输出“TCP: too many of orphaned sockets”。

    一般情况下这些值是在系统启动时根据系统内存数量计算得到的。 根据当前tcp_mem最大内存页面数是1864896,当内存为(1864896*4)/1024K=7284.75M时,系统将无法为新的socket连接分配内存,即TCP连接将被拒绝。

    实际测试环境中,据观察大概在99万个连接左右的时候(零头不算),进程被杀死,触发out of socket memory错误(dmesg命令查看获得)。每一个连接大致占用7.5K内存(下面给出计算方式),大致可算的此时内存占用情况(990000 * 7.5 / 1024K = 7251M)。

    这样和tcp_mem最大页面值数量比较吻合,因此此值也需要修改。

    三个TCP调整语句为:

    echo "net.ipv4.tcp_mem = 786432 2097152 3145728">> /etc/sysctl.conf
    echo "net.ipv4.tcp_rmem = 4096 4096 16777216">> /etc/sysctl.conf
    echo "net.ipv4.tcp_wmem = 4096 4096 16777216">> /etc/sysctl.conf
    

    备注: 为了节省内存,设置tcp读、写缓冲区都为4K大小,tcp_mem三个值分别为3G 8G 16G,tcp_rmemtcp_wmem最大值也是16G。

    目标达成

    经过若干次的尝试,最终达到目标,1024000个持久连接。1024000数字是怎么得来的呢,两台物理机器各自发出64000个请求,两个配置为6G左右的centos测试端机器(绑定7个桥接或NAT连接)各自发出640007 = 448000。也就是 1024000 = (64000) + (64000) + (640007) + (64000*7), 共使用了16个网卡(物理网卡+虚拟网卡)。
    终端输出

    ......
    online user 1023990
    online user 1023991
    online user 1023992
    online user 1023993
    online user 1023994
    online user 1023995
    online user 1023996
    online user 1023997
    online user 1023998
    online user 1023999
    online user 1024000
    

    在线用户目标达到1024000个!

    服务器状态信息

    服务启动时内存占用:

                     total       used       free     shared    buffers     cached
        Mem:         10442        271      10171          0         22         78
        -/+ buffers/cache:        171      10271
        Swap:         8127          0       8127
    

    系统达到1024000个连接后的内存情况(执行三次 free -m 命令,获取三次结果):

                     total       used       free     shared    buffers     cached
        Mem:         10442       7781       2661          0         22         78
        -/+ buffers/cache:       7680       2762
        Swap:         8127          0       8127
    
                     total       used       free     shared    buffers     cached
        Mem:         10442       7793       2649          0         22         78
        -/+ buffers/cache:       7692       2750
        Swap:         8127          0       8127
    
                     total       used       free     shared    buffers     cached
        Mem:         10442       7804       2638          0         22         79
        -/+ buffers/cache:       7702       2740
        Swap:         8127          0       8127
    

    这三次内存使用分别是7680,7692,7702,这次不取平均值,取一个中等偏上的值,定为7701M。那么程序接收1024000个连接,共消耗了 7701M-171M = 7530M内存, 7530M*1024K / 1024000 = 7.53K, 每一个连接消耗内存在为7.5K左右,这和在连接达到512000时所计算较为吻合。
    虚拟机运行Centos内存占用,不太稳定,但一般相差不大,以上数值,仅供参考。

    执行top -p 某刻输出信息:

        top - 17:23:17 up 18 min,  4 users,  load average: 0.33, 0.12, 0.11
        Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
        Cpu(s):  0.2%us,  6.3%sy,  0.0%ni, 80.2%id,  0.0%wa,  4.5%hi,  8.8%si,  0.0%st
        Mem:  10693580k total,  6479980k used,  4213600k free,    22916k buffers
        Swap:  8323056k total,        0k used,  8323056k free,    80360k cached
    
          PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
         2924 yongboy   20   0 82776  74m  508 R 51.3  0.7   3:53.95 server
    

    执行vmstate:

    vmstat
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
     r b swpd free buff cache si so bi bo in cs us sy id wa st
     0 0 0 2725572 23008 80360 0 0 21 2 1012 894 0 9 89 2 0
    

    获取当前socket连接状态统计信息:

    cat /proc/net/sockstat
    sockets: used 1024380
    TCP: inuse 1024009 orphan 0 tw 0 alloc 1024014 mem 2
    UDP: inuse 11 mem 1
    UDPLITE: inuse 0
    RAW: inuse 0
    FRAG: inuse 0 memory 0
    

    获取当前系统打开的文件句柄:

    sysctl -a | grep file
    fs.file-nr = 1025216 0 1048576
    fs.file-max = 1048576
    

    此时任何类似于下面查询操作都是一个慢,等待若干时间还不见得执行完毕。

    netstat -nat|grep -i "8000"|grep ESTABLISHED|wc -l
    netstat -n | grep -i "8000" | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
    

    以上两个命令在二三十分钟过去了,还未执行完毕,只好停止。

    小结

    本次从头到尾的测试,所需要有的linux系统需要调整的参数也就是那么几个,汇总一下:

        echo "* - nofile 1048576" >> /etc/security/limits.conf
    
        echo "fs.file-max = 1048576" >> /etc/sysctl.conf
        echo "net.ipv4.ip_local_port_range = 1024 65535" >> /etc/sysctl.conf
    
        echo "net.ipv4.tcp_mem = 786432 2097152 3145728" >> /etc/sysctl.conf
        echo "net.ipv4.tcp_rmem = 4096 4096 16777216" >> /etc/sysctl.conf
        echo "net.ipv4.tcp_wmem = 4096 4096 16777216" >> /etc/sysctl.conf
    

    其它没有调整的参数,仅仅因为它们暂时对本次测试没有带来什么影响,实际环境中需要结合需要调整类似于SO_KEEPALIVE、tcpmax_orphans等大量参数。

    相关文章

    转载请注明:爱开源 » 百万并发之 tcp_mem

  • bind options 详解

    options语句

    options语句的定义和使用:

    options语句用来设置可以被整个BIND使用的全局选项。这个语句在每个配置文件中只有一处。如果出现多个options语句,则第一个options的配置有效,并且会产生一个警告信息。

    如果没有options语句,则每个选项使用缺省值。

    options {

    [ version version_string; ]

    [ directory path_name; ]

    [ named-xfer path_name; ]

    [ tkey-domain domainname; ]

    [ tkey-dhkey key_name key_tag; ]

    [ dump-file path_name; ]

    [ memstatistics-file path_name; ]

    [ pid-file path_name; ]

    [ statistics-file path_name; ]

    [ zone-statistics yes_or_no; ]

    [ auth-nxdomain yes_or_no; ]

    [ deallocate-on-exit yes_or_no; ]

    [ dialup dialup_option; ]

    [ fake-iquery yes_or_no; ]

    [ fetch-glue yes_or_no; ]

    [ has-old-clients yes_or_no; ]

    [ host-statistics yes_or_no; ]

    [ minimal-responses yes_or_no; ]

    [ multiple-cnames yes_or_no; ]

    [ notify yes_or_no | explicit; ]

    [ recursion yes_or_no; ]

    [ rfc2308-type1 yes_or_no; ]

    [ use-id-pool yes_or_no; ]

    [ maintain-ixfr-base yes_or_no; ]

    [ forward ( only | first ); ]

    [ forwarders { ip_addr [port ip_port] ; [ ip_addr [port ip_port] ; … ] }; ]

    [ check-names ( master | slave | response )( warn | fail | ignore ); ]

    [ allow-notify { address_match_list }; ]

    [ allow-query { address_match_list }; ]

    [ allow-transfer { address_match_list }; ]

    [ allow-recursion { address_match_list }; ]

    [ allow-v6-synthesis { address_match_list }; ]

    [ blackhole { address_match_list }; ]

    [ listen-on [ port ip_port ] { address_match_list }; ]

    [ listen-on-v6 [ port ip_port ] { address_match_list }; ]

    [ query-source [ address ( ip_addr | * ) ] [ port ( ip_port | * ) ]; ]

    [ max-transfer-time-in number; ]

    [ max-transfer-time-out number; ]

    [ max-transfer-idle-in number; ]

    [ max-transfer-idle-out number; ]

    [ tcp-clients number; ]

    [ recursive-clients number; ]

    [ serial-query-rate number; ]

    [ serial-queries number; ]

    [ transfer-format ( one-answer | many-answers ); ]

    [ transfers-in number; ]

    [ transfers-out number; ]

    [ transfers-per-ns number; ]

    [ transfer-source (ip4_addr | *) [port ip_port] ; ]

    [ transfer-source-v6 (ip6_addr | *) [port ip_port] ; ]

    [ notify-source (ip4_addr | *) [port ip_port] ; ]

    [ notify-source-v6 (ip6_addr | *) [port ip_port] ; ]

    [ alsonotify { ip_addr [port ip_port] ; [ ip_addr [port ip_port] ; … ] }; ]

    [ max-ixfr-log-size number; ]

    [ coresize size_spec ; ]

    [ datasize size_spec ; ]

    [ files size_spec ; ]

    [ stacksize size_spec ; ]

    [ cleaning-interval number; ]

    [ heartbeat-interval number; ]

    [ interface-interval number; ]

    [ statistics-interval number; ]

    [ topology { address_match_list }];

    [ sortlist { address_match_list }];

    [ rrset-order { order_spec ; [ order_spec ; … ] } };

    [ lame-ttl number; ]

    [ max-ncache-ttl number; ]

    [ max-cache-ttl number; ]

    [ sig-validity-interval number ; ]

    [ min-roots number; ]

    [ use-ixfr yes_or_no ; ]

    [ provide-ixfr yes_or_no; ]

    [ request-ixfr yes_or_no; ]

    [ treat-cr-as-space yes_or_no ; ]

    [ min-refresh-time number ; ]

    [ max-refresh-time number ; ]

    [ min-retry-time number ; ]

    [ max-retry-time number ; ]

    [ port ip_port; ]

    [ additional-from-auth yes_or_no ; ]

    [ additional-from-cache yes_or_no ; ]

    [ random-device path_name ; ]

    [ max-cache-size size_spec ; ]

    [ match-mapped-addresses yes_or_no; ]

    };

    version

    回答针对服务器版本的请求时的内容。缺省返回的是服务器的真实版本。

    directory

    服务器的工作目录。配置文件中所有使用的相对路径,指的都是在这里配置的目录下。大多数服务器的输出文件(如named.run)都缺省生成在这个目录下。如果没有设定目录,工作目录缺省设置为服务器启动时的目录‘.’。指定的目录应该是一个绝对路径。

    named-xfer

    这个选项已经被废弃了。它在BIND8 中,它用来给named-xfer程序设定路径名。在BIND9中,不需要单独的named-xfer程序;它的功能已经内置在域名服务器中。

    tkey-domain

    这个域名将会附带在由TKEY 生成的所有共享密匙名字的后面。当用户请求进行TKEY交换时,它会为密匙设定或不设定所要求的名称。如果设置了tkey_domain,共享密匙的名字将会是”client specified part”(用户设定的部分)+ “tkey-domain”。否则,共享密匙的名字将是”random hex digits”(随机的16 进制数)+ “tkey-domain”。在大多数情况下,domainname应该是服务器的域名。

    tkey-dhkey

    针对使用Diffie-Hellman 的TKEY模式的用户,服务器用来生成共享密匙的Diffie-Hellman 密匙。服务器必须可以从工作目录中调入公共和私人密匙。大多数情况下,密匙的名称应该是服务器的主机名。

    dump-file

    当执行rndc dumpdb命令时,服务器存放数据库文件的路径名。如果没有指定,缺省名字是named_dump.db。

    memstatistics-file

    服务器输出的内存使用统计文件的路径名。如果没有指定,默认值为named.memstats。

    注意:还没有在BIND9中实现!

    pid-file

    进程ID文件的路径名。如果没有指定,默认为/var/run/named.pid。pid-file是给那些需要向运行着的服务器发送信号的程序使用的。

    statistics-file

    当使用rndc stats命令的时候,服务器会将统计信息追加到的文件路径名。如果没有指定,默认为named.stats在服务器程序的当前目录中。

    port

    服务器用来接收和发送DNS协议数据的UDP/TCP端口号。默认为53。这个选项主要用于服务器的检测;因为如果不使用53端口的话,服务器将不能与其它的DNS进行通讯。

    random-device

    服务器使用的entropy源:entropy主要用于DNSSEC操作,如TKEY的数据交换和加密域的动态更新。此选项指定了entropy将会从哪个设备(或文件)中读取信息。如果它是一个文件,则当文件耗尽后,需要entropy的操作将会失败。如果没有指定,默认值是/dev/random(或等价的),如果它存在,否则就是没有。random-device选项是在服务器启动时,初始化配置时起作用的,在以后的重启时则被忽略。

    A.Boolean 选项

    auth-nxdomain

    如果是yes,那么AA位将一直设置成NXDOMAIN响应,甚至在服务器不是授权服务器的情况下都是这样的。默认值是no;这与BIND8不同。如果用户使用的是非常老版本的DNS软件,则有必要把它设置成yes。

    deallocate-on-exit

    此选项在BIND8中用于检查出口处内存泄露。BIND9忽略此选项,并始终进行检查。

    dialup

    如果是yes,那么服务器将会像在通过一条按需拨号的链路进行域传送一样,对待所有的域(按需拨号就是在服务器有流量的时候,链路才连通)。根据域类型的不同它有不同的作用,并将集中域的维护操作,这样所有有关的操作都会集中在一段很短的时间内完成,每个heartbeat-interval一次,一般是在一次调用之中完成。它也禁止一些正常的域维护的流量。默认值是no。

    dialup选项也可以定义在view和zone语句中,这样就会代替了全局设置中dialup的选项。

    如果域是一个主域,服务器就会对所有辅域发送NOTIFY请求。这将激活辅域名服务器中的对域的序列号的检验。这样当建立一个连接时,辅域名服务器才能确认这个域的传输合法性。

    如果这个域是一个辅域或是末梢域(stub zone),那么服务器将会禁止通常的“zone up to date”(refresh)请求,为了能发送NOTIFY请求,只有在heartbeat-interval 过期之后才执行。

    通过下列的设置,可以实现更好的控制。

    1、notify 只发送NOTIFY信息。

    2、notify-passive 发送NOTIFY信息,并禁止普通的刷新(refresh)请求。

    3、refresh 禁止普通的刷新处理,当heartbeat-interval 过期时才发送刷新请求。

    4、passive 只用于关闭普通的刷新处理。

    fake-iquery

    在BIND8中,此选项用来模拟陈旧的DNS查询类型IQUERY。BIND9不再进行IQUERY模拟。

    fetch-glue

    这个选项以后不再使用。

    has-old-clients

    这个选项在BIND8中执行有问题,BIND9则忽略了这个选项。为了达到has-old-clients yes的预期效果,可以设定两个独立选项auth-nxdomain yes和rfc2308-type1 no来代替。

    host-statistics

    在BIND8中,它可以保留每台和域名服务器交互的主机统计信息。BIND9中不支持。

    maintain-ixfr-base

    此选项不再使用了。在BIND8用于判定是否保存了增量域传输的处理日志。BIND9任何可能的时候都会保存传输日志。如果需要禁止流出的增量域传输,可以使用provide-ixfr no。

    minimal-responses

    如果是yes,当产生响应的时候,服务器将只会按照需要将记录添加到authority和additional的数据部分。(例如,delegations,negative responses)。这样会改善服务器的性能。默认值为no。

    multiple-cnames

    这个选项在BIND8中使用,允许一个域名承认多条CNAME记录(与DNS标准相违

    背)。BIND9.2在主hosts文件和动态更新中都严格强制执行CNAME规则。

    notify

    如果是yes(默认),当一个授权的服务器修改了一个域后,DNS NOTIFY信息被发送出去。此信息将会发给列在域NS记录上的服务器(除了由SOA MNAME标示的主域名服务器)和任何列在also-notify选项中的服务器。

    如果是explicit,则notify将只发给列在also-notify中的服务器。如果是no,就不会发出任何报文。

    notify选项也可能设定在zone语句中,这样它就替代了options中的notify 语句。如果notify会使得辅域名服务器崩溃,就需要将此选项关闭。

    recursion

    如果是yes,并且一个DNS询问要求递归,那么服务器将会做所有能够回答查询请求的工作。如果recursion是off的,并且服务器不知道答案,它将会返回一个推荐(referral)响应。默认值是yes。注意把recursion设为no,不会阻止用户从服务器的缓存中得到数据,它仅仅阻止新数据作为查询的结果被缓存。服务器的内部操作还是可以影响本地的缓存内容,如NOTIFY地址查询。

    rfc2308-type1

    设置成yes 将会使得服务器发送NS 记录和关于negative answer 的SOA记录。默认值为no。

    注:BIND9 中还不支持。

    use-id-pool

    此选项已经不再使用。BIND9 始终都是从池中分配请求ID的。

    zone-statistics

    如果是yes,缺省情况下,服务器将会收集在服务器所有域的统计数据。这些统计数据可以通过使用rndc stats来访问,rndc stats命令可以将这些信息转储到statistics-file定义的文件中去。

    use-ixfr

    这个选项不再使用。如果需要针对一个或多个特殊的服务器关闭IXFR,可以参考provide-ixfr中的内容。

    provide-ixfr

    参阅中关于provide-ixfr的陈述。

    request-ixfr

    参阅关于request-ixfr的陈述。

    treat-cr-as-space

    这个选项应用于BIND8中,使服务器正确处理回车(”\r”)字符,就象其它的空格或tab字符一样。这样可以便于在unix系统上加载由NT或DOS系统生成的域文件。在BIND9中,UNIX”的\n”和DOS 的”\r\n”都可以正确处理为换新行,这个选项就被忽略了。

    additional-from-auth

    additional-from-cache

    当回答具有additional数据的请求,或者当在CNAME 和DNAME串的后面时,这些选项控制一个权威服务器的操作。

    当这两个选项都被设成yes(默认状态),并且查询的是授权的数据(这个域就配置在本地服务器中)时,回答中的additional部分的数据将使用来自于其它授权域和cache。

    在许多情况下这是不需要的,比如在缓存内容的正确性受到怀疑的情况下,或是在某些辅域可能被非法修改的服务器。还有,避免对这些additional数据的搜索将会加速服务器运转。

    例如,如果一个查询需要主机foo.example.com的MX记录,找到的记录是”MX 10

    mail.example.net”,如果知道的话, mail.example.net的地址记录(A,A6 和AAAA)也会被提供出来。把选项设置为no,则禁止了这种操作。

    这些选项用于授权的服务器,或者是授权的视图中。把它们设成no,但没有同时设置recursion no,将会使得服务器忽略这些选项,并记录一个警告日志。

    设定additional-from-cache为no实际上针对additional信息的查询和正在响应的查询,都禁止了缓存的使用。这常常使用在一台授权的服务器中,因为在这里缓存数据的正确性非常重要。

    当一台域名服务器不提供递归查询时,并且查询的名称并不在本地域中,一般会对根服务器或者其他已知的上级服务器回答”upwards referral(向上推荐)”。既然在向上查询中的数据来自于缓存,那么当additional-from-cache被设定为no时,服务器就不能提供向上推荐。相反,它会使用REFUSED(拒绝)回答这些查询。因为向上推荐不是在用户解析过程中需要的,所以就不会出任何问题。

    match-mapped-addresses

    如果是yes,那么一个ipv4映射成的ipv6地址就会匹配任何地址匹配表中能匹配于对应的ipv4 地址的记录。打开这个选项,对于运行了ipv6的linux系统有时非常有用,这样通过地址映射,就可以使得ipv4的TCP连接(如域传送)实现在Ipv6的soket上,因为地址匹配列表是给Ipv4设计的。

    B. 转发

    转发功能可以用来在一些服务器上产生一个大的缓存,从而减少到外部服务器链路上的流量。它可以使用在和internet没有直接连接的内部域名服务器上,用来提供对外部域名的查询。只有当服务器是非授权的,并且缓存中没有相关记录时,才会进行转发。

    forward

    此选项只有当forwarders列表中有内容的时候才有意义。当值是First,默认情况下,使服务器先查询设置的forwarders,如果它没有得到回答,服务器就会自己寻找答案。如果设定的是only,服务器就只会把请求转发到其它服务器上去。

    forwarders

    设定转发使用的ip地址。默认的列表是空的(不转发)。转发也可以设置在每个域上,这样全局选项中的转发设置就不会起作用了。用户可以将不同的域转发到服务器上,或者对不同的域可以实现forward only或first的不同方式,也可以根本就不转发。

    C. 访问控制

    可以根据用户请求使用的IP地址进行限制。

    allow-notify

    设定哪个主机上的辅域(不包括主域)已经进行了修改。allow-notify也可以在zone语句中设定,这样全局options中的allow-notify选项在这里就不起作用了。但它只对辅域有效。如果没有设定,默认的是只从主域发送notify信息。

    allow-query

    设定哪个主机可以进行普通的查询。allow-query也能在zone语句中设定,这样全局options中的allow-query选项在这里就不起作用了。默认的是允许所有主机进行查询。

    allow-recursion

    设定哪台主机可以进行递归查询。如果没有设定,缺省是允许所有主机进行递归查询。注意禁止一台主机的递归查询,并不能阻止这台主机查询已经存在于服务器缓存中的数据。

    allow-v6-synthesis

    设定哪台主机能接收对ipv6的响应。

    allow-transfer

    设定哪台主机允许和本地服务器进行域传输。allow-transfer也可以设置在zone语句中,这样全局options中的allow-transfer选项在这里就不起作用了。如果没有设定,默认值是允许和所有主机进行域传输。

    blackhole

    设定一个地址列表,服务器将不会接收来自这个列表的查询请求,或者解析这些地址。从这些地址来的查询将得不到响应。默认值是none。

    D. 接口

    接口和端口(服务器回答来自于此的询问)可以使用listen-on选项来设定。listen-on使用可选的端口和一个地址匹配列表(address_match_list)。服务器将会监听所有匹配地址列表中所允许的端口。如果没有设定端口,就使用默认的53。

    允许使用多个listen-on语句。例如:

    listen-on { 5.6.7.8; };

    listen-on port 1234 { !1.2.3.4; 1.2/16; };

    将在5.6.7.8 的ip地址上打开53端口,在除了1.2.3.4的1.2 网段上打开1234 端口。

    如果没有设定listen-on,服务器将在所有接口上监听端口53。

    listen-on-v6选项用来设定监听进入服务器的ipv6请求的端口。

    服务器并不象在ipv4中那样对每个IPV6端口地址绑定一个独立的socket。相反,它一直监听ipv6通配的地址。这样,对于listen-on-v6语句唯一的address_match_list的参数就是:{ any; }和{ none;}

    多个listen-on-v6选项可以用来监听多个端口:

    listen-on-v6 port 53 { any; };

    listen-on-v6 port 1234 { any; };

    要使服务器不监听任何ipv6地址,使用:

    listen-on-v6 { none; };

    如果没有设定listen-on-v6语句,服务器将不会监听任何ipv6地址。

    E. 查询地址

    如果服务器查不到要解析的地址,它将会查询其它域名服务器。query-source可以用来设定这类请求所使用的地址和端口。对于使用ipv6发送的查询,有一个独立的query-source-v6选项。如果address是*或者被省略了,则将会使用一个通配的IP地址

    (INADDR ANY)。如果port是*或者被省略了,则将会使用一个随机的大于1024的端口。

    默认为:

    query-source address * port *;

    query-source-v6 address * port *;

    注:query-source选项中设置的地址是同时用于UDP和TCP两种请求的,但是port仅仅用

    于UDP请求。TCP请求使用的是随机的大于1024的端口。

    F. 域传输

    BIND有适当的机制来简化域传输,并限定系统传输的负载量。下列设定应用于域传输:

    also-notify

    定义一个用于全局的域名服务器IP地址列表。无论何时,当一个新的域文件被调入系统,域名服务器都会向这些地址,还有这些域中的NS记录发送NOTIFY信息。这有助于更新的域文件尽快在相关的域名服务器上收敛同步。如果一个also-notify列表配置在一个zone语句中,全局options中的also-notify语句就会在这里失效。当一个zone-notify语句被设定为no,系统就不会向在全局中also-notify列表中的IP地址发送NOTIFY消息。缺省状态为空表(没有全局通知列表)。

    max-transfer-time-in

    比设定时间更长的进入的域传输将会被终止。默认值是120分钟(2小时)。

    max-transfer-idle-in

    在设定时间下没有任何进展的进入域传输将会被终止。默认为60分钟(1小时)。

    max-transfer-time-out

    运行时间比设定的时间长的发出的域传输将会被终止。默认为120分钟(2小时).

    max-transfer-idle-out

    在设定时间下没有任何进展的发出的域传输将会被终止。默认为60分钟(1小时)。

    serial-query-rate

    辅域名服务器将会定时查询主域名服务器,来确定域的串号是否改变。每个查询将会占用一些辅域名服务器网络带宽。为限制占用的带宽,BIND9可以限制每个查询发送的频率。serial-query-rate的值是一个整数,就是每秒能发送的最大查询数。默认值为20。

    serial-queries

    在BIND8中, serial-queries选项设定了在任何时候允许达到的最大的并发查询数。BIND9不限制串号查询的数量并忽略了serial-queries选项。它会使用serial-query-rate选项来限制查询的频率。

    transfer-format

    域传输可以用两种不同格式,one-answer和many-answer。transfer-format选项使用在主域名服务器上,用来确定发送哪种格式。one-answer在每个资源记录传输中使用一个

    DNS消息。many-answer则将尽可能多的资源记录集中在一个消息中。many-answer是

    更加有效的,但只有相对比较新的辅域名服务器才支持它,如BIND9、BIND8.x 和打了补丁的BIND4.9.5。默认的设置为many-answer。使用server语句中的相关选项,可以替代全局选项中的transfer-format设置。

    transfers-in

    可以同时运行的进入的域传输的最大值。默认值为10。增加transfers-in的值,可以加速辅域的收敛速度,但也可能增加本地系统的负载。

    transfers-out

    可以同时运行的发出的传输的最大值。超过限定的域传输请求将会被拒绝。默认值为10。

    transfers-per-ns

    从一台指定的远程域名服务器,同时进行的进入的域传输的最大值。默认值2。增加

    transfers-per-ns的值,会加速辅域的收敛速度,但也可能增加远程系统的负载。使用

    server语句中的transfer短语可以替代全局选项中的transfers-per-ns。

    transfer-source

    transfer-source决定在从外部域名服务器上得到域传送数据时,选哪个本地的ip地址使用在IPV4的TCP连接中。它可以选定IPV4的源地址,和可选的UDP端口,用于更新的查询和转发的动态更新。不过不做设置,它会缺省挑选一个系统中的地址(常常是最靠近远程终端服务器的接口地址)。但这个地址必须已经配置在远程终端的allow-tranfer选项中,才能进行域传送。此语句为所有的域设定了transfer-source,但如果view或zone中也使用了transfer-source语句,则全局选项中的配置就在这里失效了。

    transfer-source-v6

    和transfer-source一样,只是域传输是通过IPV6执行的。

    notify-source

    notify-source确定使用哪些本地的源地址和可选的UDP端口,用于发送NOTIFY消息。这个地址必须在辅域名服务器的master域或在allow-notify中设置。它会为所有域设定

    notify-source, 但如果view或zone中也使用了notify-source语句,则全局选项中的配置就在这里失效了。

    notify-source-v6

    与notify-source类似,但应用于ipv6地址的notify报文的发送。

    G. 操作系统资源限制

    可以限制服务器对许多系统资源的使用。这些就是通过调节资源限制的数值来完成的。例如,1G可以代替1073741824,限定一个十亿字节的限制。Unlimited 要求不限制使用,或者最大可用量。Default 将会使用服务器启动时的缺省值。

    下列选项设定了域名服务器进程的操作系统资源占用限制。一些操作系统可能不支持一些

    或所有的限制。在这样的系统中,当使用不被支持的限制时,会产生一个告警。

    coresize

    core dump文件的最大值尺寸。默认值为default

    datasize

    服务器可以使用的最大数据内存量。默认值为default。这是一个在服务器系统内存中

    已经设置了的参数。如果服务器要超过这个限制的内存量,则会失败,这将使服务器不能

    提供DNS服务。所以,这个选项作为一种限制服务器所使用的内存量的方式就不太有效,但是它能够将操作系统设置的太小的缺省数据尺寸增大。如果要限制服务器使用的内存量,可以使用max-cache-size和recursive-clients选项。

    files

    服务器可以同时打开的最大文件数。默认是unlimited。

    stacksize

    服务器可以使用最大的堆栈内存量。默认值为default。

    H. 服务器资源限制

    下列选项设定了服务器资源使用限制,这是由域名服务内部做的而不是操作系统设定的。

    max-ixfr-log-size

    此选项比较老;它由BIND8兼容接受或者忽略。

    recursive-clients

    服务器同时为用户执行的递归查询的最大数量。默认值1000,因为每个递归用户使用许多位内存,一般为20KB,主机上的recursive-clients选项值必须根据实际内存大小调整。

    tcp-clients

    服务器同时接受的TCP连接的最大数量,默认值100。

    max-cache-size

    服务器缓冲使用的最大内存量,用比特表示。但在缓存数据的量达到这个界限,服务器将会使记录提早过期这样限制就不会被突破。在多视图的服务器中,限制分别使用于每个视图的缓存。默认值没有限制,意味着只有当总的限制被突破的时候记录才会被缓存清除。

    I. 周期性任务间隔

    cleaning-interval

    服务器将在cleaning-interval的每一时间中从缓存中清除过期的资源记录。默认为60分钟,如果设置为0,就不会有周期性清理。

    heartbeat-interval

    服务器将会为所有标记dialup的域运行维护任务,无论它的间隔在何时到期。默认为60分钟,合理值不超过1天(1440 分钟)。如果设定为0,不会为这些域产生域维护。

    interface-interval

    服务器将在每个interface-interval时间扫描网络接口表。默认为60分钟。如果设置为0,仅当配置文件被加载时才会进行接口扫描。在扫描之后,所有新接口上的监听器将会被打开(listen-on配置使用的接口)。关闭接口上的监听器将会被清除。

    statistics-interval

    域名服务器统计将会在每个statistics-interval时刻被记入日志。默认值60分钟,如果设为0,就没有统计数据记入日志。

    注意:BIND9 不支持

    J. 拓扑

    当服务器从一个域名服务器列表中选择一个域名服务器查询时,这些域名服务器是没有什么不同的,但是服务器会先选择在拓扑结构上距离自己最近的服务器去做解析。拓扑语句使用一个地址匹配列表并且以一个特殊方式解释它。每个顶层列表元素被赋了一段距离,非否定元素得到它们在列表中的位置的距离,匹配距离表的开头越近,它离服务器的距离就越小。否定匹配元素将会从服务器分配最大距离;没有匹配的地址将会得到一个比任何非否定表元素都远的并且比任何否定元素近的距离。例如:

    topology {

    10/8;

    !1.2.3/24;

    { 1.2/16; 3/8; };

    };

    最优先网段10的服务器,然后是在网络1.2.0.0(网络掩码255.255.0.0)和3.0.0.0(网络掩

    码255.0.0.0);再就是没列出来的,但是没有否定的网段。否定的网段1.2.3 的主机(网络掩

    码255.255.255.0)。

    默认拓扑为:

    topology { localhost; localnets; };

    注意:BIND9不支持拓扑选项。

    K. sortlist 语句

    对一个DNS询问的响应包括形成一个资源记录集(RR集)的多资源记录(RRs)。名称服务器将会以不确定的顺序返回在RRset中的RRs(参见rrset-order语句)。用户端的解答器会重新适当的排列,也就是说,使用任何在本地网上的地址优先于其他的地址。尽管如此,不是所有的解答器可以做到或者正确配置。当用户使用一个本地服务器的时候,服务器可以基于用户地址进行分类。这只要求配置名称服务器,而不是所有用户端。

    sortlist语句(如下)使用一个地址匹配表甚至比拓扑语句还要特殊的解释它。每个在sortlist 的顶层语句必须自己就是一个清楚的拥有一个或两个元素的地址匹配表。每个顶级表的第一个元素(可能是一个IP地址,一个IP前缀,一个ACL名称或者一个地址匹配表)与查询源地址进行匹配检查直到找到匹配的地址。

    一旦查询的源地址被匹配,如果顶级语句只包括一个元素的话,真正的匹配于源地址的原始元素就被用来选择地址,对应的转移到了响应的开始。如果语句是两个元素的表,那么第二个元素遵照拓扑语句中地址匹配表的方式进行处理。每个顶级元素被赋予一个距离和与响应的开头距离最近的地址。

    在下列例子中,任何来自于任何主机地址的查询将会得到本地网上第一首选地址的响应。下一个首选地址在网段192.168.1/24上,既可以在192.168.2/24或192.168.3/24网段之后。从一台在192.168.1/24网段上的主机收到的查询将会优先本网段和192.168.2/24和192.168.3/24网。而来自192.168.4/24或192.168.5/24上主机的查询将只优先直连的网段。

    sortlist {

    { localhost; //IF 主机名

    { localnets;

    192.168.1/24; //THEN 在下列网中最适合

    { 192.168.2/24; 192.168.3/24; }; }; }; //IF 在C类192.168.1

    { 192.168.1/24; //THEN 使用.1, 或.2 或.3

    { 192.168.2/24; 192.168.3/24; }; }; };

    { 192.168.2/24; //IF C类192.168.1

    { 192.168.2/24; //THEN使用2, 或.1 或.3

    { 192.168.1/24; 192.168.3/24; }; }; };

    { 192.168.3/24; //IF 在C类192.168.3

    { 192.168.1/24; 192.168.2/24; }; }; }; //THEN使用.3 或.1 或.2

    };

    };

    下个例子将给出一个本地主机和直接连接到网上的主机的合理的状态(behavior)。它很象BIND4.9.x分类的地址状态。从本地主机发给查询的响应支持任何直接连接的网络,从其他直接连接网络上的主机发送给查询的响应优先在相同网段上的地址。对其他查询的响应没有分类。

    sortlist {

    { localhost; localnets; };

    { localnets; };

    };

    L. RRset 排序

    当多重记录在一个解答中被返回的时候,设定在响应中的记录顺序是很有用的.。

    rrset-order语句允许对在多记录响应下的记录顺序的设定。参见sortlist语句。

    一个order_spec定义如下:

    [ class class_name ][ type type_name ][ name “domain_name”] order ordering

    如果没有设定类,默认值为ANY。如果没有设定类型,默认值为ANY。如果没有设定

    名称,默认值为”*”。

    合法的排序值是:

    fixed:记录以它们在域文件中的顺序

    random:记录以随机顺序被返回

    cyclic:记录以环顺序被返回

    例如:

    rrset-order {

    class IN type A name “host.example.com” order random;

    order cyclic;

    };

    将会使得任何处于IN类中的A类记录的响应以随机顺序返回,IN 类以”host.example.com”为后缀。其他的记录以循环记录被返回。

    如果多重rrset-order语句出现,它们并不组合在一起,只适用于最后一个条。

    注意:rrset-order语句不被BIND9支持,BIND9目前只支持”random-cyclic”排序,服务器随机选择RRset集中的开始点,有顺序返回在那个点开始的记录。如果需要的话围绕RRset

    结尾。

    M. 合成的IPV6响应

    许多现存的子域解答器支持ipv6的DNS查询(定义在RFC1986 中,使用AAAA 记录进行前向查询和ip6.int域中的”nibble labels”进行反向查询)但是不支持RFC2874-style 查询(使用A6记录和在ip6.arpa 中的二进制标签)对于那些希望继续使用子域解答器而不是转到

    BIND9 lightweight 解答器的人来说,BIND 9提供一种自动把RFC1886-型查询转换成

    RFC2874-型查询的方法。返回合成的AAAA和PTR记录。

    这个性质默认下是无效的,可以在分用户基础上添加一个allow-v6-synthesis

    { address_match_list };子句到选项或者视图语句中。当它被激活时,递归AAAA查询使服

    务器先进行A6查询,如果失败,执行AAAA查询。不管哪个成功,结果都作为一个合成的AAAA 记录返回。

    类似的,在ip6.int中的递归PTR查询将会促使一个ip6.arpa查询使用二进制标签,如果失败,执行另一个在ip6.int中的查询,结果将会以在ip6.int中的合成PTR记录返回。合成记录的TTL 为0值。合成响应的DNSSEC确认当前并不被支持;也没有了AD标记。

    注:allow-v6-synthesis仅为提供了递归服务的用户执行。

    N. 调谐

    lame-ttl

    设定缓存有问题服务器指示的秒数。0使不缓存(不被推荐)。默认值600(10 分钟)。最大值1800(30 分钟)。

    max-ncache-ttl

    为降低网络流量和提升服务器存储否定回答的性能。max-ncache-ttl以秒为单位设定这些回答的保存时间。默认max-ncache-ttl是10800秒(3小时)。max-ncache-ttl不能超过7天,如果设成一个更大的值,则将会被自动减为7天。

    max-cache-ttl

    max-cache-ttl设定了服务器储存普通(肯定)答案的最大时间。默认值一周(7 天)。

    min-roots

    一个请求要求的最小的根服务器数量。默认为2。

    注意:不被BIND9 支持

    sig-validity-interval

    设定未来作为动态更新结果的自动生成的DNSSEC信号过期的天数。默认是30天。信号的初始时间无条件设为在当前时间的前一个小时,以允许一个有限的时钟偏差。

    min-refresh-time

    max-refresh-time

    min-retry-time

    max-retry-time

    这些选项控制了服务器在更新一个域(询问SOA变化)或者重试失败的传输时的状态。通常域的SOA值(但是这些值是由主服务器设定的)几乎不给此级服务器管理者对它们内容的控制。

    这些选项允许管理者为每域,每个视图或者全局设定一个最小或者最大更新和重试时间。这些选项对于此级和根域是有效的并且设定SOA更新和重试时间。

    O. 统计文件

    由BIND9产生的统计文件和由BIND8产生的类似,但不完全一样。

    一个统计数据开始于行+++ Statistics Dump +++ (973798949),这里出现的数字是一个标准UNIX型的时间戳,从1970年1月1日开始以秒计。紧跟这行的是一系列行,包括一个

    记数器类型,记数器值,任意的域名和任意的视图名,没有所列的视图和域的行是整个服务器的整体统计。具有域和视图的行以给定的视图和域命名(对默认的视图来说视图名缺省)。

    这个统计数据以行— Statistics Dump —(973798949)结束,在这数字是和开始行的数字一样的。Success对服务器或者域做出的成功查询。定义一个成功查询是查询返回非错误响应而不是返回推荐响应。

    Referral:导致推荐响应查询

    Nxrrset:导致没有数据的非错误查询的响应

    Nxdomain:导致NXDOMAIN 的查询数量

    Recursion:使服务器运行递归以找出最后答案的查询数量

    Failure:导致失败的查询数量

    相关文章

    转载请注明:爱开源 » bind options 详解

  • OpenStack 环境整体迁移过程记录

    1. 环境概述

    本次迁移的OpenStack环境涉及4台物理服务器,状况如下:

    主机名 角色 eth0 eth1
    controller 控制节点,存储节点 192.168.16.1 (VLAN Trunk) 192.168.17.1
    node01 计算节点,存储节点 192.168.16.2 (VLAN Trunk) 192.168.17.2
    node02 计算节点,存储节点 192.168.16.3 (VLAN Trunk) 192.168.17.3
    node03 计算节点,存储节点 192.168.16.4 (VLAN Trunk) 192.168.17.4

    服务器的两块网卡都已启用。

    • eth0作为云主机业务的出口,绑定网桥,主要承载云主机的网络业务,设置网关
    • eth1作为数据业务的出口,运行分布式文件系统GlusterFS以及OpenStack的系统服务

    我们此系统使用OpenStack Fosolm版本。

    2. 迁移过程

    1. 迁移要求

    • 服务器需要全部跨机房搬迁;
    • 新机房的基础网络环境需要重新规划,有原来的 192.168.16.0/24, 192.168.17.0/24 网段变成 10.0.16.0/24, 10.0.17.0/24 网段。

    2. 分布式文件系统迁移

    首先,遇到的问题是分布式文件系统的迁移,因为当时在环境搭建的时候并没有考虑到机房可能会出现搬迁,更没有想到机房搬迁之后IP地址等网络设置会发生变化,所以在安装配置GlusterFS文件系统的时候都是采用IP地址添加peer以及建立Volume的。接到迁移任务之后,我们首先开始考虑基础数据的问题,考虑各种潜在的数据风险,于是预先就把GlusterFS内的文件拷贝出来以防万一。

    在这里特别要称赞GlusterFS文件系统的一个特性,那就是文件在GlusterFS上不会切块进行存储,也不会进行数据格式转换,而是数据文件按照原样存储,这样一旦GlusterFS系统不工作,可以在相应的数据文件目录下将相关的文件拷出直接使用。

    如果数据量比较小,那么可以考虑在将数据文件拷出之后,对集群各节点采取以下步骤进行迁移:

    1 停止,删除卷

    $ gluster volume stop data_vol
    $ gluster volume delete data_vol
    

    2 删除节点

    $ gluster peer detach node_x
    

    3 修改IP地址 4 添加节点

    $ gluster peer probe node_x
    

    5 创建,开始卷

    $ gluster volume create data_vol ip:path,ip:path
    $ gluster volume start data_vol
    

    6 拷回数据

    但是由于我们的GlusterFS存储了云主机镜像文件以及云硬盘的数据,大约有几个TB的数据,所以将数据拷出再拷回来的方法显然不太现实。下面就是如何在不重装,不删卷(Volume),不重加节点(peer)的情况下更改GlusterFS的地址。

    GlusterFS在进行peer probe操作以及create volume的操作之后,会将相应的IP地址信息写入配置文件中,并且某的些文件的文件名也会包含IP地址信息,经过grep以及find命令的查找,需要修改的地方如下(对于编译安装的GlusterFS):

    1 修改 /var/lib/glusterd 下配置文件,peer 在 /var/lib/glusterd/peers/* 下,卷在 /var/lib/glusterd 下

    $ grep -rl 192.168.17.1 /var/lib/glusterd | xargs sed -i 's/192.168.17.1/10.0.17.1/g'
    $ grep -rl 192.168.17.2 /var/lib/glusterd | xargs sed -i 's/192.168.17.2/10.0.17.2/g'
    $ grep -rl 192.168.17.3 /var/lib/glusterd | xargs sed -i 's/192.168.17.3/10.0.17.3/g'
    $ grep -rl 192.168.17.4 /var/lib/glusterd | xargs sed -i 's/192.168.17.4/10.0.17.4/g'
    

    2 修改 /var/lib/glusterd/vols/ 下的卷配置文件,配置文件的名字上带有HOSTNAME要改

    $ cd /var/lib/glusterd/vols/data_vol/
    
    $ rename 's/192.168.17.1/10.0.17.1/g' *
    $ rename 's/192.168.17.2/10.0.17.2/g' *
    $ rename 's/192.168.17.3/10.0.17.3/g' *
    $ rename 's/192.168.17.4/10.0.17.4/g' *
    
    $ rename 's/192.168.17.1/10.0.17.1/g' */*
    $ rename 's/192.168.17.2/10.0.17.2/g' */*
    $ rename 's/192.168.17.3/10.0.17.3/g' */*
    $ rename 's/192.168.17.4/10.0.17.4/g' */*
    

    3 通过debug模式执行看是否有问题

    /usr/local/sbin/glusterd --debug
    

    如果没有问题,开启服务并验证

    $ service glusterd restart
    $ gluster peer status
    $ gluster volume info
    

    这下算是将分布式文件系统迁移完毕,这给后面的韵味提个醒,最好在搭建之初就想到可能会有系统迁移的情况,因此在peer probe和volume create的时候尽量选用主机名标记各节点,这样如果不幸需要搬迁,只需要修改/etc/hosts文件即可无缝迁移。

    3. OpenStack系统服务调整

    这一点单拎出来说,是为了提供一些OpenStack搭建方面的经验。由于OpenStack系统按照模块划分功能,各个模块之间通过API进行通讯,系统核心服务通过消息队列进行任务排序,所以不免的在各个模块的配置文件中都会涉及到需要填写各服务的IP地址信息。

    如果在初始平台安装部署的时候没有考虑到迁移的可能性,而全部使用IP地址来填写的话,就会给迁移工作带来极大不便。而我们在进行平台搭建的时候就将各模块的配置文件中涉及到IP地址的部分用主机名或域名来代替,然后在/etc/hosts文件中填写相应的对应关系。涉及到的模块大概有:

    nova
    keystone
    glance
    cinder
    ceilometer
    navigator
    savanna
    swift
    

    4. 虚拟机网络调整

    下面是虚拟机的网络调整,这部分是本次迁移最麻烦的一个步骤,因为我们的OpenStack是采用的VLAN的模式,需要在交换机一侧配置VLAN Trunk,这次整体迁移,划分新网段的同时还需在新交换机上配置相关的VLAN。

    采用VLAN模式之后,云主机就不需要绑定浮动IP地址了,此时固定IP即是云主机的IP。并且本次迁移的过程不希望重建云主机,但是IP地址必须要做出调整,所以必须在已有的数据基础上重新配置云主机网络。

    以下是我们的执行步骤,因为不能够删虚机,所以不能通过删除资源,创建网络再重新发起的方式来恢复,所以只好直接修改数据库。

    在此我要提醒,不是到万不得已请勿直接修改OpenStack数据库,以避免出现数据不一致的状况进而造成严重的问题,如果一定要修改数据库,请预先做好备份工作。

    修改步骤:

    1.修改数据库 nova.networks

    这个表的内容是在根据tenant建立网络的时候记录的信息,包括网络的地址、子网掩码、网关、组播地址、vlan号等信息,需要根据新的网络环境做相应的修改。

    2.修改数据库 nova.instance_info_caches

    这个表的network_info字段以文本的格式记录了云主机的网络信息,包括地址、所在桥、网关等信息,请一并改掉。当然,挑选未删除的云主机(deleted=0)修改即可,不用所有的都改。

    3.修改数据库 nova.fixed_ips

    这个表记录的是每个云主机的固定IP映射关系,如果采用的是vlan的模式,需要将这个表中的address字段修改为新的地址段。如果采用其他的网络模式,这张表就不用修改,但是需要修改nova.floating_ips,将对应的新地址填入address字段中。

    在此,我们的建议是在更换地址时,保持后两段不变,只改动前两段,例如 (192.168.16.0/24 -> 10.0.16.0/24),这样的话,原有的虚拟机只需要将地址的前两段更换即可,也可以写脚本批量替换。

    4.修改云主机libvirt配置文件

    假设云主机的镜像存放路径是 $OPENSTACK_ROOT/data/nova/instances/instance-xxxxxxxx

    那么修改文件 $OPENSTACK_ROOT/data/nova/instances/instance-xxxxxxxx/libvirt.xml 将其中相应的地址换成新的。

    ...
    <interface type="bridge">
      <mac address="fa:16:3e:47:c8:3f"/>
      <model type="virtio"/>
      <source bridge="br16"/>
      <filterref filter="nova-instance-instance-00000097-fa163e47c83f">
        <parameter name="IP" value="10.0.16.5"/>
        <parameter name="DHCPSERVER" value="10.0.16.18"/>
        <parameter name="PROJNET" value="10.0.16.0"/>
        <parameter name="PROJMASK" value="255.255.255.0"/>
      </filterref>
    </interface>
    ...
    

    这下重启OpenStack服务应该云主机的网络就能够正常了,如果云主机在启动之后不能够上网,请用virsh edit instance-xxxxxxxx 检查对应的libvirt网络信息是否正确,有可能仍然是之前的地址,如果是的话,改掉即可(virsh edit的使用方法跟vim一样)。

    3. 总结

    首先,底层文件系统的安装,请尽量使用主机名和域名,这会为迁移带来很大的便利。

    然后,OpenStack的安装也尽量使用主机名或域名,将对IP地址的依赖减少到最小。

    再然后,万不得已别直接修改数据库,如果非要改请先备份,再加一万分认真。

    新的网络规划尽量保证与原网络环境的一致,能做到批量修改最好。

    最后,不到万不得已,正式环境不要做如此的环境迁移,即便要迁移环境,请尽量保持网络环境跟之前是一致的,如果连这个也无法保证,那么我只能祝您好运了,我在此提到的只是我们所遇到的一些问题,很可能在不同的情况下会遇到不同的问题,请冷静分析解决。

    相关文章

    转载请注明:爱开源 » OpenStack 环境整体迁移过程记录

  • Nginx 引入线程池,提升 9 倍性能

    介绍

    众所周知,NGINX 采用异步、事件驱动的方式处理连接。意味着无需对每个请求创建专门的进程或线程,它用一个工作进程(worker process)处理多个连接和请求。为了达到这个目的,NGINX采用非阻塞模式的 socket,并利用诸如 epoll 和 kqueue 的高效方法。

    全量进程(full-weight process)数很少(通常是一个 CPU 核只有一个)而且恒定、内存开销少、CPU 周期不会浪费在任务切换上。此方法的优势因为NGINX而广为人知。它能同时处理成千上万请求,而且容易扩展。

    7cc829d3gw1ettldadt07j21kw19ldkt

    每个进程消耗额外的内存,进程之间的每次切换都会消耗 CPU 周期和丢弃 CPU 缓存

    不过异步、事件驱动方式依然存在一个问题,或者可以说是敌人。其名字就是:阻塞。不幸的是,许多第三方模块采用阻塞方式调用,用户(有时甚至这些模块的开发者)都没有意识到这个缺陷。阻塞操作会毁掉 NGINX 性能,必须采取一切手段避免这样的问题。

    甚至在当前 NGINX 官方代码中,也无法在每个例子中避免阻塞操作,为了解决这个问题,NGINX 1.7.11 版实现了新的线程池机制。它是什么,如何使用?我会在后面说明。我们先看看我们的敌人。

    问题

    首先,为了更好的理解问题,我们先简单看看NGINX是如何工作的。

    总体来说,NGINX 是一个事件处理器,一个从内核接收所有发生在连接上的事件信息的控制器,然后给操作系统发布命令。实际上,NGINX 通过编排操作系统做了全部的辛苦工作,操作系统则做了读字节和发送字节等日常工作。可见 NGINX 快速及时响应是如此重要。

    7cc829d3gw1ettldb1ijoj20ly0fimy5

    工作进程监听、处理来自内核中的事件。

    事件可能是某个超时,或者socket准备读取或者写入的通知,或者错误发生的通知。NGINX 接收一串事件,接着挨个处理,做一些必要的动作。这些处理都在线程队列的简单循环中完成。NGINX 从队列中放出一个事件,接着做出反应,例如写或者读一个 socket。在许多案例中,这非常快(也许只需要很少的CPU 周期就可以将数据复制到内存中),并且 NGINX 会立即处理队列中所有的事件。

    7cc829d3gw1ettldczik1j21kw12jae7

    所有处理是在一个简单的循环中由某个线程完成的

    但是如果遇到某些又长又重操作,又会怎样呢?整个事件处理周期可能会卡在那里等待此操作结束。

    我们说的“阻塞操作”是指会让处理循环明显停止一段时间的操作。阻塞的原因多种多样。比如,NGINX忙于漫长的 CPU 密集型处理,或者不得不等待获取某个资源,比如硬件驱动、某个互斥锁、库函数以同步方式调用数据库响应。最关键的是处理诸如此类的操作,工作进程就没有办法做其他的事情,处理其他的事件,即使系统有更多可用资源可供队列中某些事件使用。

    试想商店里售货员,面前排着一个很长的队列。排第一的顾客需要的货物在仓库,不是在店里,售货员去仓库搬运货物。为此整个队列需要等待几个小时,等待的人都会不高兴的。你能想象人们的反应么?队列中每个人等待时间因为这几个小时而增加,但他们想买的货物或许就在商店里。

    7cc829d3gw1ettldeygofj21kw19j0y5

    队列中的每一个人不得不等待第一个人的订单

    几乎同样的场景发生在 NGINX 中,需要读取一个文件,但它没有缓存在内存,不得不从硬盘中读取。硬盘很慢(特别是旋转的机械硬盘),然而队列中其他等待的请求即使无需读取硬盘,也被迫等待。结果增加了延迟,系统资源没有被充分利用。

    7cc829d3gw1ettldfoeaqj20ly08owet

    仅仅一个阻塞操作就能长时间地延迟接下来所有的操作

    某些操作系统(比如 FreeBSD)提供了一个读文件和发送文件的异步接口,NGINX可以调用这个接口(见 aio 指令)。不幸的是,Linux 并非都如此。尽管 Linux系统也提供了读取文件的异步接口,但它有两个重大缺陷。其一是文件读取和缓存时需要对齐,不过 NGINX 能处理地很好。第二个问题更糟,异步接口需要在文件描述符上作 O_DIRECT 标记,这样任何获取文件的操作越过内存级的缓存,增加了硬盘负载。在很多例子中,这真不是一个好的选择。

    为解决这个问题,NGINX 1.7.11 引入了线程池。NGINX Plus 默认状态下没有线程池,如果你想给 NGINX Plus R6 构建一个线程池,请联系销售。

    让我们深入探究什么是线程池、它是如何工作的。

    线程池

    让我们回到刚才那个可怜的销售助理,从很远的仓库取货物。但是他变聪明了,也或许因为愤怒地顾客鄙视变得聪明了?购买了一套配送服务。现在有人想购买远距离仓库中的货物,销售助理无需前往,只需要将订单转给配送服务,后者会处理这个订单,销售助理可以继续为其他顾客服务。由此只有货物不再商铺的顾客需要等待货物提取,其他顾客能够快速得到服务。

    7cc829d3gw1ettldhi71cj21kw19sdl8
    把订单转给配送服务,这样就不会阻塞队列了

    对 NGINX 而言,线程池就是充当配送服务的角色,它由一个任务队列和一组处理队列的线程组成。一旦工作进程需要处理某个可能的长操作,不用自己操作,将其作为一个任务放出线程池的队列,接着会被某个空闲线程提取处理。

    7cc829d3gw1ettldjbltaj21kw0wq0xg

    工作进程把阻塞操作转给线程池

    像是拥有了一个新的队列,不过本例中的队列局限于某个特定的资源。从硬盘中读取数据的速度不会超过硬盘生成数据的速度。硬盘没有延迟处理其他事件,仅仅需要获取文件的请求在等待。

    硬盘读取操作通常就是阻塞操作,不过NGINX中的线程池可以用来处理任何在主工作周期不适合处理的任务。

    此刻分派给线程池的任务主要有两个:许多操作系统上 read() 方法的系统调用,以及 Linux 系统的 sendfile()方法。我们会继续测试(test)和基准测试(benchmark),未来发布的版本或许将其他的操作分派给线程池。

    基准测试

    I到了从理论到实践的时候了,为了展示利用线程池的效果,我们打算设置一个合成基准模拟最糟糕的阻塞或者非阻塞操作。

    数据集不能超出内存,在一个 48GB 内存机器上,生成 256GB 随机数据,每个文件大小 4MB ,接着配置 NGINX 1.9.0 为其提供服务。

    配置及其简单:

    worker_processes 16;
    
    events {
        accept_mutex off;
    }
    
    http {
        include mime.types;
        default_type application/octet-stream;
    
        access_log off;
        sendfile on;
        sendfile_max_chunk 512k;
    
        server {
            listen 8000;
    
            location / {
                root /storage;
            }
        }
    }
    

    正如你所看到的,为了获得更好的性能,我们做了一些调优:关闭了 loggin 和 accept_mutex,同时开启了 sendfile(),设置 sendfile_max_chunk 大小为512K。最后面的指令可以减少阻塞方法 sendfile() 调用的所花费的最大时间,即 NGINX 每次无需发送整个文件,只发送 512KB 的块数据。

    计算机含有两个英特尔至强 E5645 处理器(Intel Xeon E5645),以及 10Gbps 网络接口。硬盘子系统由四个西部数据 WD1003FBYX 硬盘按放在一个 RAID10 阵列中。所有硬件由Ubuntu Server 14.04.1 LTS进行管理。

    7cc829d3gw1ettldla6knj21kw0wq430
    为基准测试,配置 NGINX 和负载生成器

    客户端由两个配置相同的计算机组成,其中一台,wrk 通过用 Lua 脚本创建负载。脚本通过 200 个并行连接,随机向服务器请求文件。每一个请求可能导致缓存失效、产生一个硬盘阻塞读操作。姑且称这种负载叫随机负载。

    在第二台客户端计算机上,我们运行另一个 wrk 拷贝,用 50 个并行连接多次访问同一个文件。因为文件高频访问,它会一直留在内存中。通常,NGINX可以非常快地处理这些请求,不过工作进程一旦阻塞被其他请求阻塞,性能就会下滑。姑且称这种负载为恒定负载。

    利用 ifstat 命令获取第二台客户端的 wrk 结果,来监控服务器吞吐量,并以此测定服务器性能。

    第一次没有线程池参与的运行,并没有带给我们什么惊喜的结果:

    % ifstat -bi eth2
    eth2
    Kbps in  Kbps out
    5531.24  1.03e+06
    4855.23  812922.7
    5994.66  1.07e+06
    5476.27  981529.3
    6353.62  1.12e+06
    5166.17  892770.3
    5522.81  978540.8
    6208.10  985466.7
    6370.79  1.12e+06
    6123.33  1.07e+06
    

    正如你所看到的,如此配置的服务器产生流量总计大约为 1 Gbps。从 top 的输出信息,我们可以看出所有工作进程在阻塞输入输出上花费的时间:

    top - 10:40:47 up 11 days,  1:32,  1 user,  load average: 49.61, 45.77 62.89
    Tasks: 375 total,  2 running, 373 sleeping,  0 stopped,  0 zombie
    %Cpu(s):  0.0 us,  0.3 sy,  0.0 ni, 67.7 id, 31.9 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem:  49453440 total, 49149308 used,   304132 free,    98780 buffers
    KiB Swap: 10474236 total,    20124 used, 10454112 free, 46903412 cached Mem
    
      PID USER     PR  NI    VIRT    RES     SHR S  %CPU %MEM    TIME+ COMMAND
     4639 vbart    20   0   47180  28152     496 D   0.7  0.1  0:00.17 nginx
     4632 vbart    20   0   47180  28196     536 D   0.3  0.1  0:00.11 nginx
     4633 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.11 nginx
     4635 vbart    20   0   47180  28136     480 D   0.3  0.1  0:00.12 nginx
     4636 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.14 nginx
     4637 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.10 nginx
     4638 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.12 nginx
     4640 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.13 nginx
     4641 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.13 nginx
     4642 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.11 nginx
     4643 vbart    20   0   47180  28276     536 D   0.3  0.1  0:00.29 nginx
     4644 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.11 nginx
     4645 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.17 nginx
     4646 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.12 nginx
     4647 vbart    20   0   47180  28208     532 D   0.3  0.1  0:00.17 nginx
     4631 vbart    20   0   47180    756     252 S   0.0  0.1  0:00.00 nginx
     4634 vbart    20   0   47180  28208     536 D   0.0  0.1  0:00.11 nginx
     4648 vbart    20   0   25232   1956    1160 R   0.0  0.0  0:00.08 top
    25921 vbart    20   0  121956   2232    1056 S   0.0  0.0  0:01.97 sshd
    25923 vbart    20   0   40304   4160    2208 S   0.0  0.0  0:00.53 zsh
    

    I本例中,吞吐量的短板为硬盘子系统,CPU大多数时间都在空闲。wrk 输出结果看吞吐量很低:

    Running 1m test @ http://192.0.2.1:8000/1/1/1
      12 threads and 50 connections
      Thread Stats   Avg    Stdev     Max  +/- Stdev
        Latency     7.42s  5.31s   24.41s   74.73%
        Req/Sec     0.15    0.36     1.00    84.62%
      488 requests in 1.01m, 2.01GB read
    Requests/sec:      8.08
    Transfer/sec:     34.07MB
    

    记住,应该从内存送达文件。巨大的延迟是因为所有的工作进程忙于从硬盘读取文件,响应第一个客户端的 200 个连接创建的随机负载,无法处理我们的请求。

    是时候让线程登场了,为此我们给 location 模块添加了 aio threads 指令:

    location / {
        root /storage;
        aio threads;
    }
    

    请求NGINX重载其配置

    重新测试结果:

    % ifstat -bi eth2
    eth2
    Kbps in  Kbps out
    60915.19  9.51e+06
    59978.89  9.51e+06
    60122.38  9.51e+06
    61179.06  9.51e+06
    61798.40  9.51e+06
    57072.97  9.50e+06
    56072.61  9.51e+06
    61279.63  9.51e+06
    61243.54  9.51e+06
    59632.50  9.50e+06
    

    此时服务器产生 9.5 Gbps 流量,而没有线程池参与时只产生大约 1 Gbps 的流量。

    甚至可以产生更多流量,不过这已经达到实际网络最大容量。由此可见本测试中,制约NGINX因素为网络接口。工作进程大部分时间在休眠和等待新事件,参见top输出S state

    top - 10:43:17 up 11 days,  1:35,  1 user,  load average: 172.71, 93.84, 77.90
    Tasks: 376 total,  1 running, 375 sleeping,  0 stopped,  0 zombie
    %Cpu(s):  0.2 us,  1.2 sy,  0.0 ni, 34.8 id, 61.5 wa,  0.0 hi,  2.3 si,  0.0 st
    KiB Mem:  49453440 total, 49096836 used,   356604 free,    97236 buffers
    KiB Swap: 10474236 total,    22860 used, 10451376 free, 46836580 cached Mem
    
      PID USER     PR  NI    VIRT    RES     SHR S  %CPU %MEM    TIME+ COMMAND
     4654 vbart    20   0  309708  28844     596 S   9.0  0.1  0:08.65 nginx
     4660 vbart    20   0  309748  28920     596 S   6.6  0.1  0:14.82 nginx
     4658 vbart    20   0  309452  28424     520 S   4.3  0.1  0:01.40 nginx
     4663 vbart    20   0  309452  28476     572 S   4.3  0.1  0:01.32 nginx
     4667 vbart    20   0  309584  28712     588 S   3.7  0.1  0:05.19 nginx
     4656 vbart    20   0  309452  28476     572 S   3.3  0.1  0:01.84 nginx
     4664 vbart    20   0  309452  28428     524 S   3.3  0.1  0:01.29 nginx
     4652 vbart    20   0  309452  28476     572 S   3.0  0.1  0:01.46 nginx
     4662 vbart    20   0  309552  28700     596 S   2.7  0.1  0:05.92 nginx
     4661 vbart    20   0  309464  28636     596 S   2.3  0.1  0:01.59 nginx
     4653 vbart    20   0  309452  28476     572 S   1.7  0.1  0:01.70 nginx
     4666 vbart    20   0  309452  28428     524 S   1.3  0.1  0:01.63 nginx
     4657 vbart    20   0  309584  28696     592 S   1.0  0.1  0:00.64 nginx
     4655 vbart    20   0  30958   28476     572 S   0.7  0.1  0:02.81 nginx
     4659 vbart    20   0  309452  28468     564 S   0.3  0.1  0:01.20 nginx
     4665 vbart    20   0  309452  28476     572 S   0.3  0.1  0:00.71 nginx
     5180 vbart    20   0   25232   1952    1156 R   0.0  0.0  0:00.45 top
     4651 vbart    20   0   20032    752     252 S   0.0  0.0  0:00.00 nginx
    25921 vbart    20   0  121956   2176    1000 S   0.0  0.0  0:01.98 sshd
    25923 vbart    20   0   40304   3840    2208 S   0.0  0.0  0:00.54 zsh
    

    仍有充裕的CPU资源。

    wrk执行结果:

    Running 1m test @ http://192.0.2.1:8000/1/1/1
      12 threads and 50 connections
      Thread Stats   Avg      Stdev     Max  +/- Stdev
        Latency   226.32ms  392.76ms   1.72s   93.48%
        Req/Sec    20.02     10.84    59.00    65.91%
      15045 requests in 1.00m, 58.86GB read
    Requests/sec:    250.57
    Transfer/sec:      0.98GB
    

    处理一个 4 MB文件的平均时间由 7.42 秒降到 226.32 毫秒,降低至少33倍。同时,每秒请求数提高31倍。

    这是因为我们的请求无需在事件队列中等待处理,即使工作进程阻塞在读操作上,请求可以由空闲的线程来完成处理。只要硬盘子系统表现出色,NGINX很好地为来自第一个客户端的随机负载服务,它就可以利用剩余的CPU资源和网络容量,从内存读取,为第二个客户端的请求服务。

    这并非是银弹

    当我们经历了阻塞操作的带来的恐惧以及线程池带来的兴奋感之后,或许我们中的多数人已经打算在服务器中配置线程池。别急!

    幸运的是,多数读写文件操作无需处理缓慢的硬盘,如果你有足够的内存,操作系统会足够聪明把那些高频次访问的文件缓存到一个称之为“页面缓存”(page cache)中。

    页面缓存表现优异,使得 NGINX 几乎在通常的用例中性能表现突出。从页面缓存中读取速度非常快,没有人认为类操作是“阻塞”的。换言之,分派负载给线程池会带来一些开销。

    所以,如果有合适的内存,并且数据集不大,那么无需线程池,NGINX 就可以在最佳性能下工作。

    分派读操作给线程池是一种对针对特定任务的技术。频次非常高的请求内容不适合放入操作系统虚拟缓存中,这时候线程池就很有了。或许就是如此,例如,重量级基于NGINX负载流媒体服务器。我们的基准测试已模仿这个场景。

    如果能将读操作分派给线程池是极好的,我们所要做的是需要的文件数据是否在内存中,如果不在内存中,那么我们就应该将读操作分派给某个线程。

    回到销售的例子,当下销售员面临的情况是,不知道请求物品是否在店铺,要么将所有的订单传给提取货物服务,要么他自己处理这些订单。

    要命的是,操作系统可能永远没有这个功能。第一次尝试是 2010 年 linux 中引入 fincore() 系统调用方法,没有成功。接着做了一系列尝试,例如引入新的带有 RWF_NONBLOCK 标记的 preadv2() 系统调用方法。所有的这些补丁前景依旧不明朗。比较悲剧的是,因为持续的口水战,导致这些补丁一直没有被内核接受。

    另一个原因是,FreeBSD用户根本不会关心这个。因为 FreeBSD 已经有一个非常高效的异步文件读取接口,完全可以不用线程池。

    配置线程池

    如果确信你的用例采用线程池可以获利,那么是时候深入其配置了。

    线程池配置非常容易而且灵活。首先你需要 NGINX 1.7.11 版,或者更新的版本,采用配置文件中的参数 –with-threads 进行编译。最简单的例子,配置看起来相当的容易,所有你需要做的的事情就是给http、server或者location上下文中添加 aio threads 指令。

    aio threads;
    

    这可能是最简短的线程池配置了,实际上,下面这个配置是一个简化版的:

    thread_pool default threads=32 max_queue=65536;
    aio threads=default;
    

    它定义一个名为 default的线程池,拥有 32 个工作线程,任务队列容纳的最大请求数为 65536。一旦任务队列过载,NGINX日志会报错并拒绝这一请求:

    thread pool "NAME" queue overflow: N tasks waiting
    

    报错意味着线程可能处理工作的速度跟不上任务添加进队列的速度,你可以试着增加队列的到最大容量。如果还是不起作用,可能是系统服务请求的数量已达到了上线。

    正如你所看到的,可以用thread_pool指令设置线程数量、队列最大容量、为某个线程池命名。为某个线程池命名意味着你可以设置多个独立的线程池,在不同的配置文件用于不同目的。

    http {
        thread_pool one threads=128 max_queue=0;
        thread_pool two threads=32;
    
        server {
            location /one {
                aio threads=one;
            }
    
            location /two {
                aio threads=two;
            }
        }
    …
    }
    

    如果没有指定max_queue参数,它的默认值为65536。如上面所展示的,可以将max_queue设置为0。这意味这,如在本例,线程池只能处理分派给线程那些任务;因为队列中没有存储任何等待的任务。

    试想你的服务器有三个硬盘,你希望服务器能像缓存代理一样作用,缓存所有来自后端的响应,预期缓存的数据量远远超过了现有的内存。这个缓存节点为私人内容分发网络(CDN)服务,当然本例中最重要的事情就是从硬盘那里获取最大性能。

    一种选择是设置一个磁盘阵列,这种方式有其优点和缺点。NGINX采用另外一种方式:

    # 假定每个硬盘驱动挂载一个文件目录上
    # We assume that each of the hard drives is mounted on one of the directories:
    # /mnt/disk1, /mnt/disk2, or /mnt/disk3 accordingly
    proxy_cache_path /mnt/disk1 levels=1:2 keys_zone=cache_1:256m max_size=1024G
                     use_temp_path=off;
    proxy_cache_path /mnt/disk2 levels=1:2 keys_zone=cache_2:256m max_size=1024G
                     use_temp_path=off;
    proxy_cache_path /mnt/disk3 levels=1:2 keys_zone=cache_3:256m max_size=1024G
                     use_temp_path=off;
    
    thread_pool pool_1 threads=16;
    thread_pool pool_2 threads=16;
    thread_pool pool_3 threads=16;
    
    split_clients $request_uri $disk {
        33.3%     1;
        33.3%     2;
        *         3;
    }
    
    location / {
        proxy_pass http://backend;
        proxy_cache_key $request_uri;
        proxy_cache cache_$disk;
        aio threads=pool_$disk;
        sendfile on;
    }
    

    在这个设置中,用到了三个独立的缓存,对应一个硬盘。同样也有三个独立的线程池对应某个硬盘。

    split_clients 模块用于缓存之间的负载平衡,很好地满足这个任务。

    proxy_cache_path 指令中的参数 use_temp_path=off 指示 NGINX 存储临时文件到缓存数据对应的相同目录中;在缓存更新时,避免磁盘之间拷贝响应数据。

    做的所有这一切,都是为了使当前硬盘子系统性能达到最大,因为NGINX中每个线程池与磁盘的交互都是独立并行的。每个磁盘由 16 个独立的线程为其服务,即处理某个特定任务队列中文件的读取和发送。

    我猜测你的客户端采用类似客户定制的方式,那么同样确保你的硬盘驱动也采用类似的方式。

    本例很好的展示了NGINX如何灵活地针对特定硬盘做出调优,就像你给出指令,告诉NGINX与计算机以及数据集的最佳交互方式。通过细粒度的NGINX调优,可以确保软件、操作系统、硬件处在一种最佳的工作状态,即尽可能有效地利用系统资源。

    结论

    总之,线程池是一个非常棒的特性,它能促使NGINX性能上一个新台阶,移除了众所周知的顽疾——阻塞,特别是涉及海量数据的时候。

    当然远非这些,正如前面所提到的,新的接口可能会允许我们分派任何长的阻塞操作,而且不会有性能损失。NGINX开辟了新天地,拥有一批新的模块和功能。而许多流行的库依旧没有提供某种异步非阻塞接口,这样很难和NGINX兼容。或许我们需要花费很多时间和资源,开发一些我们自己的非阻塞原生库,但这样做值得么?随着线程池特性的上线,这些库在不影响模块性能的前提下会更加相对简单易用。

    敬请期待!

    转载请注明:爱开源 » Nginx 引入线程池,提升 9 倍性能