author:JevonWei

版权声明:原创作品


搭建NAT模式的HTTP环境

网络拓扑图如下



网络环境

RS1 192.168.198.138
RS2 192.168.198.120
LVS:
DIP 192.168.198.128
vip 172.16.253.105
路由R1:
172.16.253.166
192.168.80.128
Client 192.168.80.129
RS1,RS2的网关指向192.168.198.128,client的网关指向R1

实现NAT模式的轮询rr模式

VS

添加路由转发选项
[root@VS ~]# vim /etc/sysctl.d/99-sysctl.conf
net.ipv4.ip_forward=1
[root@VS ~]# sysctl -p /etc/sysctl.d/99-sysctl.conf \\刷新生效
net.ipv4.ip_forward = 1
[root@VS ~]# route add -net 192.168.80.0/24 gw 172.16.253.166
[root@VS ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.198.1 0.0.0.0 UG 100 0 0 ens34
0.0.0.0 192.16.0.1 0.0.0.0 UG 101 0 0 ens33
172.16.0.0 0.0.0.0 255.255.0.0 U 100 0 0 ens33
192.16.0.1 0.0.0.0 255.255.255.255 UH 100 0 0 ens33
192.168.80.0 172.16.253.166 255.255.255.0 UG 0 0 0 ens33
192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0
192.168.198.0 0.0.0.0 255.255.255.0 U 100 0 0 ens34 配置LVS的调度算法为rr轮询
[root@VS ~]# yum -y install ipvsadm
[root@VS ~]# ipvsadm -A -t 172.16.253.105:80 -s rr \\-t指定TCP协议,-s指定调度算法为轮询
[root@VS ~]# ipvsadm -a -t 172.16.253.105:80 -r 192.168.198.138 -m \\添加192.168.198.138 RS1服务器到LVS调度,-m 为nat类型
[root@VS ~]# ipvsadm -a -t 172.16.253.105:80 -r 192.168.198.120 -m
[root@VS ~]# ipvsadm -Ln \\查看LVS调度信息
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.253.105:80 rr
-> 192.168.198.120:80 Masq 1 0 0
-> 192.168.198.138:80 Masq 1 0 0
[root@VS ~]# curl 192.168.198.120
welcome to RS2
[root@VS ~]# curl 192.168.198.138
welcome to RS1 修改LVS的调度模式为wrr
[root@VS ~]# ipvsadm -E -t 172.16.253.105:80 -s wrr
[root@VS ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.253.105:80 wrr
-> 192.168.198.120:80 Masq 1 0 0
-> 192.168.198.138:80 Masq 1 0 0
[root@VS ~]# ipvsadm -e -t 172.16.253.105:80 -r 192.168.198.138 -m -w 3 \\修改192.168.198.138 RS1主机的权重为3,-w 指定权重,-m为nat算法,192.168.198.120权重仍为1
[root@VS ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.253.105:80 wrr
-> 192.168.198.120:80 Masq 1 0 0
-> 192.168.198.138:80 Masq 3 0 0 脚本实现lvs-wrr的配置
[root@VS ~]# vim lvs_nat.sh
#! /bin/bash
vip=172.16.253.105:80
rip1=192.168.198.138
rip2=192.168.198.120:8080
sch=wrr
case $1 in
start)
ipvsadm -A -t $vip -s $sch
ipvsadm -a -t $vip -r $rip1 -m -w 3
ipvsadm -a -t $vip -r $rip2 -m -w 1
;;
stop)
ipvsadm -C
;;
*)
echo "Usage:$(basename $0) start|stop"
exit 1
;;
esac

RS1

[root@RS1 ~]# yum -y install httpd
[root@RS1 ~]# vim /var/www/html/index.html
welcome to RS1
[root@RS1 ~]# service httpd start

RS2

[root@RS2 ~]# yum -y install httpd
[root@RS2 ~]# vim /var/www/html/index.html
welcome to RS2
[root@RS2 ~]# service httpd start

路由器R1

[root@R1 ~]# vim /etc/sysctl.d/99-sysctl.conf
net.ipv4.ip_forward=1
[root@R1 ~]# sysctl -p /etc/sysctl.d/99-sysctl.conf
net.ipv4.ip_forward = 1 [root@R1 ~]# route add -net 172.16.0.0/16 gw 172.16.253.105

[root@R1 ~]# vim /etc/sysconfig/network-scripts/route-eth0
172.16.0.0/16 via 172.16.253.105 [root@R1 ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.80.0 192.168.80.129 255.255.255.0 UG 0 0 0 eth1
192.168.80.0 0.0.0.0 255.255.255.0 U 1 0 0 eth1
172.16.0.0 172.16.253.105 255.255.0.0 UG 0 0 0 eth0
172.16.0.0 0.0.0.0 255.255.0.0 U 1 0 0 eth0
0.0.0.0 172.16.0.1 0.0.0.0 UG 0 0 0 eth0

client

[root@client ~]# route add -net 172.16.0.0/16 gw 192.168.80.128

访问rr轮询算法
[root@client ~]# for i in {1..10};do curl 172.16.253.105;sleep 1;done
welcome to RS2
welcome to RS1
welcome to RS2
welcome to RS1
welcome to RS2 访问wrr权重算法
[root@client ~]# for i in {1..10};do curl 172.16.253.105;sleep 1;done
welcome to RS2
welcome to RS1
welcome to RS1
welcome to RS1
welcome to RS2
welcome to RS1
welcome to RS1
welcome to RS1
welcome to RS2
welcome to RS1

查看LVS的信息

[root@VS ~]# ipvsadm -Ln --stats
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Conns InPkts OutPkts InBytes OutBytes
-> RemoteAddress:Port
TCP 172.16.253.105:80 29 158 139 10710 15609
-> 192.168.198.120:80 15 82 69 5554 7923
-> 192.168.198.138:80 14 76 70 5156 7686
[root@VS ~]# ipvsadm -Ln --connection \\查看网络连接数
[root@VS ~]# cut -d " " -f1 /var/log/httpd/access_log | sort -nr | uniq -c| sort -n \\查看网络连接

搭建NAT模式的HTTPS环境

在以上实验的基础上搭建HTTPS

  • 拓扑图

VS搭建CA服务端

[root@VS ~]# cat /etc/pki/tls/openssl.cnf \\查看证书的相关路径
[root@VS ~]# (umask 077;openssl genrsa -out /etc/pki/CA/private/cakey.pem 1024) \\生成私钥文件
Generating RSA private key, 1024 bit long modulus
.............++++++
.........++++++
e is 65537 (0x10001)
[root@VS ~]# cd /etc/pki/CA
[root@VS CA]# openssl req -new -x509 -key private/cakey.pem -out cacert.pem -days 1024 \\生成自签名证书
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:henan
Locality Name (eg, city) [Default City]:zhengzhou
Organization Name (eg, company) [Default Company Ltd]:danran.com
Organizational Unit Name (eg, section) []:it
Common Name (eg, your name or your server's hostname) []:ca.danran.com
Email Address []:
[root@VS CA]# touch index.txt
[root@VS CA]# echo 00 > serial

RS1申请CA证书

[root@RS1 ~]# cd /etc/httpd/conf.d/
[root@RS1 conf.d]# (umask 077;openssl genrsa -out httpd.key 1024)
Generating RSA private key, 1024 bit long modulus
........++++++
...........++++++
e is 65537 (0x10001)
[[root@RS1 conf.d]# openssl req -new -key httpd.key -out httpd.csr -days 10
You are about to be asked to enter information that will be incorporated into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:henan
Locality Name (eg, city) [Default City]:zhengzhou
Organization Name (eg, company) [Default Company Ltd]:danran.com
Organizational Unit Name (eg, section) []:it
Common Name (eg, your name or your server's hostname) []:ca.danran.com
Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request
A challenge password []:
An optional company name []:
[root@RS1 conf.d]# scp httpd.csr 192.168.198.128:/etc/pki/CA \\证书申请文件发送给CA服务端

CA服务端颁发证书

[root@VS CA]# openssl ca -in httpd.csr -out certs/httpd.crt -days 365
Using configuration from /etc/pki/tls/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Validity
Not Before: Aug 19 13:00:12 2017 GMT
Not After : Aug 19 13:00:12 2018 GMT
Subject:
countryName = CN
stateOrProvinceName = henan
organizationName = danran.com
organizationalUnitName = it
commonName = ca.danran.com
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
BB:DC:5C:85:69:2B:0A:41:98:3B:7F:3E:15:69:1D:2B:C3:81:3E:EF
X509v3 Authority Key Identifier:
keyid:91:15:B3:DB:2D:94:91:2E:12:87:26:ED:05:5E:08:78:E0:10:7C:F8 [root@VS CA]# scp certs/httpd.crt 192.168.198.138:/etc/httpd/conf.d \\将证书文件颁发给RS1申请者
[root@VS CA]# scp cacert.pem 192.168.198.138:/etc/httpd/conf.d \\将CA服务端证书发送给申请者

RS1

[root@RS1 conf.d]# scp cacert.pem httpd.crt httpd.key 192.168.198.120:/etc/httpd/conf.d \\将RS1的证书、私钥及CA证书文件发送给RS2
[root@RS1 ~]# yum -y install mod_ssl
[root@RS1 ~]# vim /etc/httpd/conf.d/ssl.conf \\修改如下证书私钥、证书文件及CA证书文件的存放路径
SSLCertificateFile /etc/httpd/conf.d/httpd.crt
SSLCertificateKeyFile /etc/httpd/conf.d/httpd.key
SSLCACertificateFile /etc/httpd/conf.d/cacert.pem 修改后如下所示
# Server Certificate:
# Point SSLCertificateFile at a PEM encoded certificate. If
# the certificate is encrypted, then you will be prompted for a
# pass phrase. Note that a kill -HUP will prompt again. A new
# certificate can be generated using the genkey(1) command.
SSLCertificateFile /etc/httpd/conf.d/httpd.crt # Server Private Key:
# If the key is not combined with the certificate, use this
# directive to point at the key file. Keep in mind that if
# you've both a RSA and a DSA private key you can configure
# both in parallel (to also allow the use of DSA ciphers, etc.)
SSLCertificateKeyFile /etc/httpd/conf.d/httpd.key # Server Certificate Chain:
# Point SSLCertificateChainFile at a file containing the
# concatenation of PEM encoded CA certificates which form the
# certificate chain for the server certificate. Alternatively
# the referenced file can be the same as SSLCertificateFile
# when the CA certificates are directly appended to the server
# certificate for convinience.
#SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt # Certificate Authority (CA):
# Set the CA certificate verification path where to find CA
# certificates for client authentication or alternatively one
# huge file containing all of them (file must be PEM encoded)
SSLCACertificateFile /etc/httpd/conf.d/cacert.pem
[root@RS1 conf.d]# service httpd restart

RS2

[root@RS2 ~]# cd /etc/httpd/conf.d/
[root@RS2 conf.d]# ls
cacert.pem httpd.key php.conf welcome.conf
httpd.crt mod_dnssd.conf README
[root@RS2 conf.d]# yum -y install mod_ssl \\安装软件包
[root@RS2 conf.d]# vim ssl.conf \\修改如下证书私钥、证书文件及CA证书文件的存放路径
SSLCertificateFile /etc/httpd/conf.d/httpd.crt
SSLCertificateKeyFile /etc/httpd/conf.d/httpd.key
SSLCACertificateFile /etc/httpd/conf.d/cacert.pem 修改后如下所示
# Server Certificate:
# Point SSLCertificateFile at a PEM encoded certificate. If
# the certificate is encrypted, then you will be prompted for a
# pass phrase. Note that a kill -HUP will prompt again. A new
# certificate can be generated using the genkey(1) command.
SSLCertificateFile /etc/httpd/conf.d/httpd.crt # Server Private Key:
# If the key is not combined with the certificate, use this
# directive to point at the key file. Keep in mind that if
# you've both a RSA and a DSA private key you can configure
# both in parallel (to also allow the use of DSA ciphers, etc.)
SSLCertificateKeyFile /etc/httpd/conf.d/httpd.key # Server Certificate Chain:
# Point SSLCertificateChainFile at a file containing the
# concatenation of PEM encoded CA certificates which form the
# certificate chain for the server certificate. Alternatively
# the referenced file can be the same as SSLCertificateFile
# when the CA certificates are directly appended to the server
# certificate for convinience.
#SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt # Certificate Authority (CA):
# Set the CA certificate verification path where to find CA
# certificates for client authentication or alternatively one
# huge file containing all of them (file must be PEM encoded)
SSLCACertificateFile /etc/httpd/conf.d/cacert.pem
[root@RS2 conf.d]# service httpd restart

VS

[root@VS ~]# vim lvs_nat.sh
#! /bin/bash
vip=172.16.253.105:443
rip1=192.168.198.138
rip2=192.168.198.120
sch=wrr
case $1 in
start)
ipvsadm -A -t $vip -s $sch
ipvsadm -a -t $vip -r $rip1 -m -w 3
ipvsadm -a -t $vip -r $rip2 -m -w 1
;;
stop)
ipvsadm -C
;;
*)
echo "Usage:$(basename $0) start|stop"
exit 1
;;
esac
[root@VS ~]# bash lvs_nat.sh stop
[root@VS ~]# bash lvs_nat.sh start
[root@VS ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.253.105:443 wrr
-> 192.168.198.120:443 Masq 1 0 0
-> 192.168.198.138:443 Masq 3 0 0

client客户端

[root@client ~]# for i in {1..10};do curl -k https://172.16.253.105;done  \\-k跳过证书
welcome to RS1
welcome to RS1
welcome to RS1
welcome to RS2
welcome to RS1
welcome to RS1
welcome to RS1
welcome to RS2
welcome to RS1
welcome to RS1

实现一个LVS调用一组不同服务

VS

搭建https的LVS_nat

[root@VS ~]# vim lvs_nat.sh
#! /bin/bash
vip=172.16.253.105:443
rip1=192.168.198.138
rip2=192.168.198.120
sch=wrr
case $1 in
start)
ipvsadm -A -t $vip -s $sch
ipvsadm -a -t $vip -r $rip1 -m -w 3
ipvsadm -a -t $vip -r $rip2 -m -w 1
;;
stop)
ipvsadm -C
;;
*)
echo "Usage:$(basename $0) start|stop"
exit 1
;;
esac
[root@VS ~]# bash lvs_nat.sh stop
[root@VS ~]# bash lvs_nat.sh start
[root@VS ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.253.105:443 wrr
-> 192.168.198.120:443 Masq 1 0 0
-> 192.168.198.138:443 Masq 3 0 0
[root@VS ~]# bash lvs_nat.sh start

搭建http的LVS_nat

[root@VS ~]# vim lvs_nat2.sh
#! /bin/bash
vip=172.16.253.105:80
rip1=192.168.198.138
rip2=192.168.198.120:8080
sch=wrr
case $1 in
start)
ipvsadm -A -t $vip -s $sch
ipvsadm -a -t $vip -r $rip1 -m -w 3
ipvsadm -a -t $vip -r $rip2 -m -w 1
;;
stop)
ipvsadm -C
;;
*)
echo "Usage:$(basename $0) start|stop"
exit 1
;;
esac
[root@VS ~]# bash lvs_nat.sh stop
[root@VS ~]# bash lvs_nat.sh start
[root@VS ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.253.105:443 wrr
-> 192.168.198.120:443 Masq 1 0 0
-> 192.168.198.138:443 Masq 3 0 0
[root@VS ~]# bash lvs_nat2.sh start [root@VS ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.253.105:80 wrr
-> 192.168.198.120:8080 Masq 1 0 0
-> 192.168.198.138:80 Masq 3 0 0
TCP 172.16.253.105:443 wrr
-> 192.168.198.120:443 Masq 1 0 0
-> 192.168.198.138:443 Masq 3 0 0

client

[root@client ~]# for i in {1..10};do curl -k https://172.16.253.105;done
welcome to RS2
welcome to RS1
welcome to RS1
welcome to RS2
welcome to RS1
welcome to RS1
welcome to RS2
welcome to RS1
welcome to RS1
welcome to RS2
[root@client ~]# for i in {1..10};do curl -k http://172.16.253.105;done
welcome to RS2
welcome to RS1
welcome to RS1
welcome to RS1
welcome to RS2
welcome to RS1
welcome to RS1
welcome to RS1
welcome to RS2
welcome to RS1

LVS-NAT搭建HTTP及HTTPS的更多相关文章

  1. 负载均衡集群介绍、LVS介绍、LVS调度算法、LVS NAT模式搭建

    7月4日任务 18.6 负载均衡集群介绍18.7 LVS介绍18.8 LVS调度算法18.9/18.10 LVS NAT模式搭建 扩展lvs 三种模式详解 http://www.it165.net/a ...

  2. Linux centosVMware 负载均衡集群介绍、LVS介绍、LVS调度算法、LVS NAT模式搭建

    一.负载均衡集群介绍 主流开源软件LVS.keepalived.haproxy.nginx等 其中LVS属于4层(网络OSI 7层模型),nginx属于7层,haproxy既可以认为是4层,也可以当做 ...

  3. 负载均衡集群相关、LVS介绍、LVS调度算法、LVS NAT模式搭建

    1.负载均衡集群相关 2.LVS的三种模式:NAT.DR .IP tunnel 3. LVS的调度算法(共有8种) 4.LVS NAT模式搭建准备条件:   在分发服务器上安装:yum install ...

  4. LVS-NAT:搭建HTTP及HTTPS负载均衡集群

    目录 LVS-NAT:搭建HTTP及HTTPS负载均衡集群 环境说明: 搭建NAT模式的HTTP负载集群 1. 配置好IP地址信息 2. DR上开启IP转发 3.DR上配置lvs-nat的转发机制 4 ...

  5. LVS NAT模型

    1,环境 VMWare10, CentOS6.3 2,LVS NAT网络规划 可以看到Director机器有2个IP,也就是说需要2张网卡:Real Server只需要一个网卡. VIP: 虚拟IP, ...

  6. lvs+keep搭建高可用web服务

    title: lvs+keep搭建高可用web服务 date: 2015-11-26 22:11:55 tags: --- 第一部分 概念 负载均衡 生产环境下必不可少的基础手段当前大部分互联网都使用 ...

  7. [转载]LVS快速搭建教程

    LVS配置教程 作者:oldjiang 一.前言 相信专程来读此文的读者对LVS必然有一定的了解,首先看图: 毋庸置疑,Load Balancer是负载调度器,由它将网络请求无缝隙调度到真实服务器,至 ...

  8. LVS-DR:搭建HTTP和HTTPS负载均衡集群

    目录 LVS-DR实战:搭建HTTP和HTTPS负载均衡集群 1. 搭建lvs-dr模式的http负载集群 1.1 LVS上配置IP 1.2 RS上配置arp内核参数 1.3 RS上配置VIP 1.4 ...

  9. Vmware虚拟机配置LVS/NAT模式遇到的坑。

    这两天在研究LVS的负载均衡,先从最简单的LVS/NAT模式开始入手. 最后配置完之后能够相互之间Ping通,并且能够直接访问real服务器提供的web服务,而且防火墙也已经关闭了. 但是通过访问LV ...

  10. CentOS 6.3下部署LVS(NAT)+keepalived实现高性能高可用负载均衡【转】

    CentOS 6.3下部署LVS(NAT)+keepalived实现高性能高可用负载均衡   一.简介 VS/NAT原理图: 二.系统环境 实验拓扑: 系统平台:CentOS 6.3 Kernel:2 ...

随机推荐

  1. 初学 Python(十三)——匿名函数

    初学 Python(十三)--匿名函数 初学 Python,主要整理一些学习到的知识点,这次是匿名函数. # -*- coding:utf-8 -*- #关键字lambda定义的函数都是匿名函数 #做 ...

  2. Charles录制App的接口har文件

    Charles录制App的接口har文件 如果我们想录制我们自己App后台请求接口的信息,并生成har文件,要怎么做呢?其实很简单,就是通过Charles,让手机的访问请求走这个Charles代理就行 ...

  3. 仿PC版微信的练手项目(可实时通讯)

    仿PC版微信的DEMO 本项目是由一个仿PC版微信的vue前端项目,和一个使用leancloud进行数据存储的.提供WebSocket的node后端项目构成. 本项目使用的技术栈:vue + vue- ...

  4. java web 中有效解决中文乱码问题-pageEncoding与charset区别, response和request的setCharacterEncoding 区别

    这里先写几个大家容易搞混的编码设置代码: 在jsp代码中的头部往往有这两行代码 pageEncoding是jsp文件本身的编码contentType的charset是指服务器发送给客户端时的内容编码J ...

  5. zabbix前台jsrpc注入

    zabbix是一个开源的企业级性能监控解决方案. 官方网站:http://www.zabbix.com zabbix的jsrpc的profileIdx2参数存在insert方式的SQL注入漏洞,攻击者 ...

  6. TCP/IP 主机路由表获取

    介绍在IP协议中主机的路由表获取方法: 主机初始化路由表: 直接相连路由:接口初始化时,自动获取直连主机和网络的路由信息 间接相连路由:通过执行route命令,手动初始化路由表 ICMP路由请求和通告 ...

  7. 计算机四级网络工程师--《操作系统(Operating System)》重点内容学习

    开篇语 今天开始看<操作系统>,没办法,计算机网络技术还算有点底子.至于操作系统要不是以前看过一些这方面的书籍,以及上学期学了单片机工作原理,我估计我真的是懵逼的!所幸,在网上找的233网 ...

  8. 70. Climbing Stairs【leetcode】递归,动态规划,java,算法

    You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...

  9. MQ选型对比文档

    几种MQ产品说明:     ZeroMQ :  扩展性好,开发比较灵活,采用C语言实现,实际上他只是一个socket库的重新封装,如果我们做为消息队列使用,需要开发大量的代码    RabbitMQ  ...

  10. UNIX 技巧: UNIX 高手的另外 10 个习惯

    让我们面对现实吧:坏习惯很难改变.但是您已经熟悉的习惯可能更难克服.有时,重新审视某些事情可能让您遇到“啊哈,我没想到它能做到这一点!”的时刻.在 Michael Stutz 的优秀文章“UNIX 高 ...