用nginx做代理服务器上网

目前现状:只有1个机器能上网(web),其他机器不能
方法:能上网的做一个代理web服务器中转,其他机器连接它即可。采用nginx

Nginx配置如下:

server{
        resolver x.x.x.x;
        listen 82;
        location / {
                proxy_pass http://$http_host$request_uri;
        }
}

注意项:
1. 不能有hostname
2. 必须有resolver, 即dns,即上面的x.x.x.x,换成你们的DNS服务器ip即可
3 . $http_host和$request_uri是nginx系统变量,不要想着替换他们,保持原样就OK。

查看dns方法
cat /etc/resolv.conf

代理使用

在需要访问外网的机器上执行以下操作之一即可:
1. export http_proxy=http://yourproxyaddress:proxyport
2. gedit ~/.bashrc  
    export http_proxy=http://yourproxyaddress:proxyport
yourproxyaddress也就是你的Nginx服务器的ip了,proxyport就是上面配置中的82,可以根据自己的需要修改。


举例:

  1. worker_processes 1;
  2. master_process off;
  3. daemon off;
  4. #pid /var/run/nginx.pid;

  5. events {
  6. worker_connections 768;
  7. # multi_accept on;
  8. }

  9. http {
  10. include mime.types;
  11. default_type application/octet-stream;

  12. log_format main '$remote_addr - $remote_user [$time_local] "$request" '
  13. '$status $body_bytes_sent "$http_referer" '
  14. '"$http_user_agent" "$http_x_forwarded_for"';

  15. access_log /var/log/nginx/access.log;
  16. error_log /var/log/nginx/error.log;

  17. sendfile on;

  18. server {
  19. resolver 10.57.220.2;
  20. listen 82;
  21. access_log logs/host.access.log main;

  22. location / {
  23. proxy_pass http://$http_host$request_uri;
  24. }


  25. }
  26. }

/bin/bash: bad interpreter: Text file busy

/bin/bash: bad interpreter: Text file busy Error and Solution

Q. I'm getting an error as follows while trying to run a shell script over remote ssh session:

./myscript.sh
/bin/bash: bad interpreter: Text file busy

How do I fix this error message and run the script?

A. This error means some other process or user is accessing your file. Your script file is open and hence bash giving out this error.

24 iostat, vmstat and mpstat Examples for Linux Performance Monitoring

This article provides a total of 24 examples on iostat, vmstat, and mpstat commands.

  • iostat reports CPU, disk I/O, and NFS statistics.
  • vmstat reports virtual memory statistics.
  • mpstat reports processors statictics.

This article is part of our ongoing Linux performance monitoring series.

Please note that iostat and vmstat are part of the sar utility. You should install sysstat package as explained in our sar (sysstat) article to get iostat and vmstat working.

IOSTAT EXAMPLES

1. iostat – Basic example

Iostat without any argument displays information about the CPU usage, and I/O statistics about all the partitions on the system as shown below.

  $ iostat Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  avg-cpu:  %user   %nice %system %iowait  %steal   %idle            5.68    0.00    0.52    2.03    0.00   91.76  Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn sda             194.72      1096.66      1598.70 2719068704 3963827344 sda1            178.20       773.45      1329.09 1917686794 3295354888 sda2             16.51       323.19       269.61  801326686  668472456 sdb             371.31       945.97      1073.33 2345452365 2661206408 sdb1            371.31       945.95      1073.33 2345396901 2661206408 sdc             408.03       207.05       972.42  513364213 2411023092 sdc1            408.03       207.03       972.42  513308749 2411023092

2. iostat – Display only cpu statistics

iostat option -c, displays only the CPU usage statistics as shown below.

  $ iostat -c Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  avg-cpu:  %user   %nice %system %iowait  %steal   %idle            5.68    0.00    0.52    2.03    0.00   91.76

3. iostat – Display only disk I/O statistics

iostat option -d, displays only the disk I/O statistics as shown below.

  $ iostat -d Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn sda             194.71      1096.61      1598.63 2719068720 3963827704 sda1            178.20       773.41      1329.03 1917686810 3295355248 sda2             16.51       323.18       269.60  801326686  668472456 sdb             371.29       945.93      1073.28 2345452365 2661209192 sdb1            371.29       945.91      1073.28 2345396901 2661209192 sdc             408.01       207.04       972.38  513364213 2411024484 sdc1            408.01       207.02       972.38  513308749 2411024484

4. iostat – Display only network statistics

iostat option -n, displays only the device and NFS statistics as shown below.

  $ iostat -n Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)        07/09/2011  avg-cpu:  %user   %nice    %sys %iowait   %idle            4.33    0.01    1.16    0.31   94.19  Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn sda               2.83         0.35         5.39   29817402  457360056 sda1              3.32        50.18         4.57 4259963994  387641400 sda2              0.20         0.76         0.82   64685128   69718576 sdb               6.59        15.53        42.98 1318931178 3649084113 sdb1             11.80        15.53        42.98 1318713382 3649012985  Device:                  rBlk_nor/s   wBlk_nor/s   rBlk_dir/s   wBlk_dir/s   rBlk_svr/s   wBlk_svr/s 192.168.1.4:/home/data      90.67        0.00         0.00         0.00         5.33         0.00 192.168.1.4:/backup         8.74         0.00         0.00         0.00         8.74         0.00 192.168.1.8:/media          0.02         0.00         0.00         0.00         0.01         0.00

5. iostat – Display I/O data in MB/second

By default iostat, displays the device I/O statistics in Blocks. To change it to MB, use -m as shown below.

  $ iostat -m Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  avg-cpu:  %user   %nice %system %iowait  %steal   %idle            5.68    0.00    0.52    2.03    0.00   91.76  Device:            tps    MB_read/s    MB_wrtn/s    MB_read    MB_wrtn sda             194.70         0.54         0.78    1327670    1935463 sda1            178.19         0.38         0.65     936370    1609060 sda2             16.51         0.16         0.13     391272     326402 sdb             371.27         0.46         0.52    1145240    1299425 sdb1            371.27         0.46         0.52    1145213    1299425 sdc             407.99         0.10         0.47     250666    1177259 sdc1            407.99         0.10         0.47     250639    1177259

6. iostat – Display I/O statistics only for a device

By default iostat displays I/O data for all the disks available in the system. To view statistics for a specific device (For example, /dev/sda), use the option -p as shown below.

  $ iostat -p sda Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  avg-cpu:  %user   %nice %system %iowait  %steal   %idle            5.68    0.00    0.52    2.03    0.00   91.76  Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn sda             194.69      1096.51      1598.48 2719069928 3963829584 sda2            336.38        27.17        54.00   67365064  133905080 sda1            821.89         0.69       243.53    1720833  603892838

7. iostat – Display timestamp information

By default iostat displays only the current date. To display the current time, use the option -t as shown below.

  $ iostat -t Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  Time: 08:57:52 AM avg-cpu:  %user   %nice %system %iowait  %steal   %idle            5.68    0.00    0.52    2.03    0.00   91.76  Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn sda             194.69      1096.49      1598.45 2719070384 3963829704 sda1            178.18       773.32      1328.88 1917688474 3295357248 sda2             16.51       323.14       269.57  801326686  668472456 sdb             371.25       945.82      1073.16 2345452741 2661228872 sdb1            371.25       945.80      1073.16 2345397277 2661228872 sdc             407.97       207.02       972.27  513364233 2411030200 sdc1            407.97       207.00       972.27  513308769 2411030200

8. iostat – Display Extended status

Use option -x, which will displays extended disk I/O statistics information as shown below.

  $ iostat -x Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  avg-cpu:  %user   %nice %system %iowait  %steal   %idle            5.68    0.00    0.52    2.03    0.00   91.76  Device:         rrqm/s   wrqm/s   r/s   w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util sda              27.86    63.53 61.77 132.91  1096.46  1598.40    13.84     0.21    1.06   2.28  44.45 sda1              0.69    33.22 48.54 129.63   773.30  1328.84    11.80     1.39    7.82   2.28  40.57 sda2             27.16    30.32 13.23  3.28   323.13   269.56    35.90     0.55   32.96   3.44   5.68 sdb              39.15   215.16 202.20 169.04   945.80  1073.13     5.44     1.05    2.78   1.64  60.91 sdb1             39.15   215.16 202.20 169.04   945.77  1073.13     5.44     1.05    2.78   1.64  60.91 sdc               8.90     3.63 356.56 51.40   207.01   972.24     2.89     1.04    2.56   1.55  63.30 sdc1              8.90     3.63 356.55 51.40   206.99   972.24     2.89     1.04    2.56   1.55  63.30

To display extended information for a specific partition (For example, /dev/sda1), do the following.

  $ iostat -x sda1 Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  avg-cpu:  %user   %nice %system %iowait  %steal   %idle            5.68    0.00    0.52    2.03    0.00   91.76  Device:         rrqm/s   wrqm/s   r/s   w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util sda1              0.69    33.21 48.54 129.62   773.23  1328.76    11.80     1.39    7.82   2.28  40.56

9. iostat – Execute Every x seconds (for y number of times)

To execute iostat every 2 seconds (until you press Ctl-C), do the following.

  $ iostat 2 Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  avg-cpu:  %user   %nice %system %iowait  %steal   %idle            5.68    0.00    0.52    2.03    0.00   91.76  Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn sda             194.67      1096.39      1598.33 2719070584 3963891256 sda1            178.16       773.26      1328.79 1917688482 3295418672 sda2             16.51       323.11       269.54  801326878  668472584 sdb             371.22       945.74      1073.08 2345454041 2661251200 sdb1            371.22       945.72      1073.08 2345398577 2661251200 sdc             407.93       207.00       972.19  513366813 2411036564 sdc1            407.93       206.98       972.19  513311349 2411036564 ..

To execute every 2 seconds for a total of 3 times, do the following.

  $ iostat 2 3

10. iostat – Display LVM statistic (and version)

To display the LVM statistics use option -N as shown below.

  $ iostat -N

To display the version of iostat, use -V. This will really display the version information of sysstat, as iostat is part of sysstat package.

  $ iostat -V sysstat version 7.0.2 (C) Sebastien Godard

VMSTAT EXAMPLES

11. vmstat – Basic example

vmstat by default will display the memory usage (including swap) as shown below.

  $ 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 305416 260688  29160 2356920    2    2     4     1    0    0  6  1 92  2  0

vmstat output contains the following fields:

  • Procs – r: Total number of processes waiting to run
  • Procs – b: Total number of busy processes
  • Memory – swpd: Used virtual memory
  • Memory – free: Free virtual memory
  • Memory – buff: Memory used as buffers
  • Memory – cache: Memory used as cache.
  • Swap – si: Memory swapped from disk (for every second)
  • Swap – so: Memory swapped to disk (for every second)
  • IO – bi: Blocks in. i.e blocks received from device (for every second)
  • IO – bo: Blocks out. i.e blocks sent to the device (for every second)
  • System – in: Interrupts per second
  • System – cs: Context switches
  • CPU – us, sy, id, wa, st: CPU user time, system time, idle time, wait time

12. vmstat – Display active and inactive memory

By default vmstat doesn't display this information. Use option -a, to display active and inactive memory information as shown below.

  $ vmstat -a procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------  r  b   swpd   free  inact active   si   so    bi    bo   in   cs us sy id wa st  0  0 305416 253820 1052680 2688928    2    2     4     1    0    0  6  1 92  2  0 

13. vmstat – Display number of forks since last boot

This displays all the fork system calls made by the system since the last boot. This displays all fork, vfork, and clone system call counts.

  $ vmstat -f      81651975 forks

14. vmstat – Execute Every x seconds (for y number of times)

To execute every 2 seconds, do the following. You have to press Ctrl-C to stop this.

  $ vmstat 2 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st  1  0      0 537144 182736 6789320    0    0     0     0    1    1  0  0 100  0  0  0  0      0 537004 182736 6789320    0    0     0     0   50   32  0  0 100  0  0 ..

To execute every 2 seconds for 10 times, do the following. You don't need to press Ctrl-C in this case. After executing 10 times, it will stop automatically.

  $ vmstat 2 10 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st  1  0      0 537144 182736 6789320    0    0     0     0    1    1  0  0 100  0  0  0  0      0 537004 182736 6789320    0    0     0     0   50   32  0  0 100  0  0 ..

15. vmstat – Display timestamp

When you use vmstat to monitor the memory usage repeately, it would be nice to see the timestap along with every line item. Use option -t to display the time stamp as shown below.

  $ vmstat -t 1 100 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------ ---timestamp---  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st  0  0      0 3608728 148368 3898200    0    0     0     0    1    1  0  0 100  0  0     2011-07-09 21:16:28 PDT  0  0      0 3608728 148368 3898200    0    0     0     0   60   15  0  0 100  0  0     2011-07-09 21:16:29 PDT  0  0      0 3608712 148368 3898200    0    0     0     0   32   28  0  0 100  0  0     2011-07-09 21:16:30 PDT

For me, the timestamp option worked in the following version.

  $ vmstat -V procps version 3.2.8

Note: If you use a older version of vmstat, option -t might not be available. In that case, use the method we suggested earlier to display timestamp in vmstat output.

16. vmstat – Display slab info

Use option -m, to display the slab info as shown below.

  $ vmstat -m Cache                       Num  Total   Size  Pages fib6_nodes                    5    113     32    113 ip6_dst_cache                 4     15    256     15 ndisc_cache                   1     15    256     15 RAWv6                         7     10    768      5 UDPv6                         0      0    640      6 tw_sock_TCPv6                 0      0    128     30 ...

17. vmstat – Display statistics in a table format

Instead of displays the values in the record format, you can display the output of vmstat in table format using option -s as shown below.

  $ vmstat -s       4149928  total memory       3864824  used memory       2606664  active memory       1098180  inactive memory        285104  free memory         19264  buffer memory       2326692  swap cache       4192956  total swap        274872  used swap       3918084  free swap    1032454000 non-nice user cpu ticks         14568 nice user cpu ticks      89482270 system cpu ticks   16674327143 idle cpu ticks     368965706 IO-wait cpu ticks       1180468 IRQ cpu ticks ..

18. vmstat – Display disk statistics

Use option -d to display the disk statistics as shown below. This displays the reads, writes, and I/O statistics of the disk.

  $ vmstat -d disk- ------------reads------------ ------------writes----------- -----IO------        total merged sectors      ms  total merged sectors      ms    cur    sec sda   153189971 69093708 2719150864 737822879 329617713 157559204 3965687592 4068577985      0 1102243 sdb   501426305 97099356 2345472425 731613156 419220973 533565961 2661869460 1825174087      0 1510434 sdc   884213459 22078974 513390701 452540172 127474901 8993357 2411187300 2133226954      0 1569758

19. vmstat – Increase the width of the display

The default output without increasing the width is shown below.

  $ vmstat 1 3 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 3608688 148368 3898204    0    0     0     0    1    1  0  0 100  0  0  0  0      0 3608804 148368 3898204    0    0     0     0   72   30  0  0 100  0  0  0  0      0 3608804 148368 3898204    0    0     0     0   60   27  0  0 100  0  0

Use option -w to increase the width of the output columns as shown below. This give better readability.

  $ vmstat -w 1 3 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    3608712     148368    3898204    0    0     0     0    1    1   0  0 100  0  0  0  0          0    3608712     148368    3898204    0    0     0     0   93   23   0  0 100  0  0  0  0          0    3608696     148368    3898204    0    0     0     0   35   34   0  0 100  0  0

20. vmstat – Display statistics for a partition

To display the disk I/O statistics of a specific disk partition use option -p as shown below.

  $ vmstat -p sdb1 sdb1          reads   read sectors  writes    requested writes            501423248 2345417917  419221612 2661885948

21. vmstat – Display in MB

By default vmstat displays the memory information in kb. To disply in MB, use the option "-S m" as shown below.

  $ vmstat -S m 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    281    288     19   2386    0    0     4     1    0    0  6  1 92  2  0

MPSTAT EXAMPLES

22. mpstat – Display basic info

By default mpstat displays CPU statistics as shown below.

  $ mpstat Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011  10:25:32 PM  CPU   %user   %nice    %sys %iowait    %irq   %soft  %steal   %idle    intr/s 10:25:32 PM  all    5.68    0.00    0.49    2.03    0.01    0.02    0.00   91.77    146.55

23. mpstat – Display all information

Option -A, displays all the information that can be displayed by the mpstat command as shown below. This is really equalivalent to "mpstat -I ALL -u -P ALL" command.

  $ mpstat -A Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011      _x86_64_        (4 CPU)  10:26:34 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle 10:26:34 PM  all    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   99.99 10:26:34 PM    0    0.01    0.00    0.01    0.01    0.00    0.00    0.00    0.00   99.98 10:26:34 PM    1    0.00    0.00    0.01    0.00    0.00    0.00    0.00    0.00   99.98 10:26:34 PM    2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00 10:26:34 PM    3    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00  10:26:34 PM  CPU    intr/s 10:26:34 PM  all     36.51 10:26:34 PM    0      0.00 10:26:34 PM    1      0.00 10:26:34 PM    2      0.04 10:26:34 PM    3      0.00  10:26:34 PM  CPU     0/s     1/s     8/s     9/s    12/s    14/s    15/s    16/s    19/s    20/s    21/s    33/s   NMI/s   LOC/s   SPU/s   PMI/s   PND/s   RES/s   CAL/s   TLB/s   TRM/s   THR/s   MCE/s   MCP/s   ERR/s   MIS/s 10:26:34 PM    0    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    7.47    0.00    0.00    0.00    0.00    0.02    0.00    0.00    0.00    0.00    0.00    0.00    0.00 10:26:34 PM    1    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    4.90    0.00    0.00    0.00    0.00    0.03    0.00    0.00    0.00    0.00    0.00    0.00    0.00 10:26:34 PM    2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.04    0.00    0.00    0.00    0.00    0.00    3.32    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00 10:26:34 PM    3    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    4.17    0.00    0.00    0.00    0.00    0.01    0.00    0.00    0.00    0.00    0.00    0.00    0.00

24. mpstat – Display CPU statistics of individual CPU (or) Core

Option -P ALL, displays all the individual CPUs (or Cores) along with its statistics as shown below.

  $ mpstat -P ALL Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011      _x86_64_        (4 CPU)  10:28:04 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle 10:28:04 PM  all    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   99.99 10:28:04 PM    0    0.01    0.00    0.01    0.01    0.00    0.00    0.00    0.00   99.98 10:28:04 PM    1    0.00    0.00    0.01    0.00    0.00    0.00    0.00    0.00   99.98 10:28:04 PM    2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00 10:28:04 PM    3    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00 

To display statistics information of a particular CPU (or core), use option -P as shown below.

  $ mpstat -P 0 Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011      _x86_64_        (8 CPU)  10:28:53 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle 10:28:53 PM    0    0.01    0.00    0.01    0.01    0.00    0.00    0.00    0.00   99.98  $ mpstat -P 1 Linux 2.6.32-100.28.5.el6.x86_64 (dev-db)       07/09/2011      _x86_64_        (8 CPU)  10:28:55 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle 10:28:55 PM    1    0.00    0.00    0.01    0.00    0.00    0.00    0.00    0.00   99.98

Finally, as we mentioned earlier mpstat is part of the sysstat package. When you do mpstat -V, it will really display the version number of the systat package as shown below.

  $ mpstat -V sysstat version 9.0.4 (C) Sebastien Godard (sysstat  orange.fr)

大批量任务处理总结

最近几天在做一个集群间数据迁移的任务,要做的事很简单,就是给定一个任务文件,文件中每一行对应一个source:dest形式的迁移任务(source和dest均为文件名),任务数在千万级别。要做的事情其实很简单,读取每一行,解析出source和dest,并根据给定的集群信息从源集群读取source,并写到目标集群的dest。

经历了写程序、执行任务、分析日志、改程序、再执行子任务...等多次折磨之后,发现自己在处理任务的过程中走了很多弯路,原因是刚开始没有意识到问题的复杂性,处理的方式太过简单,这里说下我对这种大批量任务处理的一些心得。

大批量任务处理主要从以下两个方面来考虑:性能和正确性。

性能主要通过任务执行的时间来衡量,主要是通过优化每条任务的执行时间并行处理实现。本次数据迁移的任务,每次迁移需要从源集群读取文件,并且写到目标集群,由于读写都是通过客户端接口实现,故能优化的地方仅仅是在读的时候合理的利用好客户端cache;文件名字空间是扁平化的,文件之间看不出任何的联系,也无法通过重新组织任务顺序的方式来进行优化。由于子任务之间没有任何依赖和联系,迁移任务是非常适合采用多进程或多线程的方式并行处理的,1000w条任务,每秒大约能处理30个任务(可理解为一次随机读和一次随机写的时间),单进程(单线程)顺序处理时间约为3.9天,如果10个进程(线程)同时处理,则处理时间约为10个小时。为了减少编码工作量,我采用的方式是编写一个简单的单线程读写程序,在外围将待处理任务文件分成10个子文件,启动10个进程分别处理各个子文件。这里划分成10个任务的原因:(1)方便计算;(2)10个小时的处理时间能够接受,刚好睡一觉程序就跑完了;(3)10个进程并行,网卡也快跑满了。

接下来说说正确性,其实这块是最不好处理的,每一条迁移任务执行失败可能有很多原因,如任务描述不合法、读取源(这里又可细分为多个阶段)失败,写目标失败(这里也可细分),对于发生错误的情况,有些错误时必然错误,如给定的源或目标不符合规则、源文件不存在等;有些错误可能只是偶然,如读取某个文件,只成功读到一部分,这种错误通过重新执行任务可以避免;还有些错误可能是由于程序(工具程序、甚至是客户端库)的bug造成,通过修改程序可避免这类错误。

通过日志的方式可以区分以上提到的不同情况,这几天发现打印日志其实是很需要技巧的,并不是随便输出点信息就叫打了日志(这里不讨论日志分级别打印的情况,只讨论日志内容输出的技巧)。

首先对于描述错误信息的日志,必须能从日志中迅速定位出错误位置及原因,在日志内容上的描述上,日志内容需要能很方便喂给grep、awk等工具分析,从而避免再去写日志分析工具。另外日志中最好详尽的描述错误的任务信息,这样再二次处理的时候就不用再去从任务文件中分析出错误的任务,我刚开始写迁移程序的时候,在读不到source的时候,只打印了source的信息,结果要二次处理这些任务项时,还另外写了一个python脚本,把这些项从任务文件分析出来,而如果把source:dest,只需awk下就能从日志中得到错误的任务,重新处理。

对于本文提到的数据迁移任务,我总结了一套行之有效的日志打印方法:

1. 区分错误日志和结果日志,错误日志记录发生错误时的具体信息,结果日志为任务执行的具体结果(成功还是失败)。处理一个任务可能对应很多条(包括使用到的库打印的错误日志),但每个任务只对一个一条结果日志,结果日志最好包含任务描述的所有信息;

2. 将任务划分成多个阶段,该信息在任务处理过程中不断更新,如果某个任务出错,在结果日志中打印任务执行到的阶段和错误信息(错误码),通过阶段信息快速定位错误位置,通过错误描述信息(错误码)进行错误分类。

3. 将错误日志和结果日志输出到不同的位置,简单的实现可将错误和结果日志分别输出到stderr和stdout,并将stderr和stdour重定向到不同的文件。

通过结果日志,可以将执行完的任务按照错误信息快速分类(grep),对不同的错误情况进行不同的处理;而当需要确切知道出错的具体原因时,则可分析错误日志。

最后,如果对迁移后的数据正确性要求很高,可在迁移过程中做下crc或md5检查,或是编写额外的check工具进行全面的检查工作,而这个工作的性质与迁移的工作非常类似。

nginx中锁的设计以及惊群的处理

nginx中使用的锁是自己来实现的,这里锁的实现分为两种情况,一种是支持原子操作的情况,也就是由NGX_HAVE_ATOMIC_OPS这个宏来进行控制的,一种是不支持原子操作,这是是使用文件锁来实现。

首先我们要知道在用户空间进程间锁实现的原理,起始原理很简单,就是能弄一个让所有进程共享的东西,比如mmap的内存,比如文件,然后通过这个东西来控制进程的互斥。

说起来锁很简单,就是共享一个变量,然后通过设置这个变量来控制进程的行为。

我们先来看核心的数据结构,也就是说用来控制进程的互斥的东西。

这个数据结构可以看到和我上面讲得一样,那就是通过宏来分成两种。

1 如果支持原子操作,则我们可以直接使用mmap,然后lock就保存mmap的内存区域的地址

2 如果不支持原子操作,则我们使用文件锁来实现,这里fd表示进程间共享的文件句柄,name表示文件名。

Java代码 收藏代码
  1. typedef struct {
  2. #if (NGX_HAVE_ATOMIC_OPS)
  3. ngx_atomic_t *lock;
  4. #else
  5. ngx_fd_t fd;
  6. u_char *name;
  7. #endif
  8. } ngx_shmtx_t;


接着来看代码,先来看支持原子操作的情况下的实现方式。这里要注意下,下面的函数基本都会有两个实现,一个是支持原子操作,一个是不支持的,我这里全部都是分开来分析的。

先来看初始化,初始化代码在ngx_event_module_init中。

下面这段代码是设置将要设置的共享区域的大小,这里cl的大小最好是要大于或者等于cache line。
通过代码可以看到这里将会有3个区域被所有进程共享,其中我们的锁将会用到的是第一个。
Java代码 收藏代码
  1. size_t size, cl;
  2. cl = 128;
  3. //可以看到三个区域。
  4. size = cl /* ngx_accept_mutex */
  5. + cl /* ngx_connection_counter */
  6. + cl; /* ngx_temp_number */



下面这段代码是初始化对应的共享内存区域。然后保存对应的互斥体指针。
Java代码 收藏代码
  1. //这个是一个全局变量,保存的是共享区域的指针。
  2. ngx_atomic_t *ngx_accept_mutex_ptr;
  3. //这个就是我们上面介绍的互斥体。
  4. ngx_shmtx_t ngx_accept_mutex;
  5. ngx_shm_t shm;
  6. //开始初始化
  7. shm.size = size;
  8. shm.name.len = sizeof("nginx_shared_zone");
  9. shm.name.data = (u_char *) "nginx_shared_zone";
  10. shm.log = cycle->log;
  11. //分配对应的内存,使用mmap或者shm之类的。
  12. if (ngx_shm_alloc(&shm) != NGX_OK) {
  13. return NGX_ERROR;
  14. }
  15. shared = shm.addr;
  16. ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
  17. //初始化互斥体。
  18. if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data)
  19. != NGX_OK)
  20. {
  21. return NGX_ERROR;
  22. }


下面我们来看ngx_shmtx_create的实现。
可以看到如果支持原子操作的话,非常简单,就是将共享内存的地址付给loc这个域。
Java代码 收藏代码
  1. ngx_int_t
  2. ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name)
  3. {
  4. mtx->lock = addr;
  5. return NGX_OK;
  6. }


然后来看nginx中如何来获得锁,以及释放锁。

我们先来看获得锁。

这里nginx分为两个函数,一个是trylock,它是非阻塞的,也就是说它会尝试的获得锁,如果没有获得的话,它会直接返回错误。

而第二个是lock,它也会尝试获得锁,而当没有获得他不会立即返回,而是开始进入循环然后不停的去获得锁,知道获得。不过nginx这里还有用到一个技巧,就是每次都会让当前的进程放到cpu的运行队列的最后一位,也就是自动放弃cpu。

先来看trylock

这个很简单,首先判断lock是否为0,为0的话表示可以获得锁,因此我们就调用ngx_atomic_cmp_set去获得锁,如果获得成功就会返回1,负责为0.

Java代码 收藏代码
  1. static ngx_inline ngx_uint_t
  2. ngx_shmtx_trylock(ngx_shmtx_t *mtx)
  3. {
  4. return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid));
  5. }


接下来详细描述下ngx_atomic_cmp_set,这里这个操作是一个原子操作,这是因为由于我们要进行比较+赋值两个操作,如果不是原子操作的话,有可能在比较之后被其他进程所抢占,此时再赋值的话就会有问题了,因此这里就必须是一个原子操作。

我们来看这个函数的实现,如果系统库不支持这个指令的话,nginx自己还用汇编实现了一个,其实实现也很简单,比如x86的话有一个cmpxchgl的指令,就是做这个的。

先来看如果系统库支持的情况,此时直接调用OSAtomicCompareAndSwap32Barrier。

Java代码 收藏代码
  1. #define ngx_atomic_cmp_set(lock, old, new) \
  2. OSAtomicCompareAndSwap32Barrier(old, new, (int32_t *) lock)


来看函数的原型:
Java代码 收藏代码
  1. OSAtomicCompareAndSwap32Barrier(old, new, addr)


然后这个函数翻译成伪码的话就是这个:

Java代码 收藏代码
  1. f (*addr == oldvalue) {
  2. *addr = newvalue;
  3. return 1;
  4. } else {
  5. return 0;
  6. }


这个代码就不解释了,很浅显易懂。

因此上面的trylock的代码:
Java代码 收藏代码
  1. ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)
的意思就是如果lock的值是0的话,就把lock的值修改为当前的进程id,否则返回失败。

然后来看这个的汇编实现,这里nginx实现了多个平台的比如x86,sparc,ppc.
我们来看x86的:

Java代码 收藏代码
  1. static ngx_inline ngx_atomic_uint_t
  2. ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
  3. ngx_atomic_uint_t set)
  4. {
  5. u_char res;
  6. __asm__ volatile (
  7. NGX_SMP_LOCK
  8. " cmpxchgl %3, %1; "
  9. " sete %0; "
  10. : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");
  11. return res;
  12. }


具体的这些指令和锁可以去看intel的相关手册。

接下来来看lock的实现,lock最终会调用ngx_spinlock,因此下面我要主要来分析这个函数。
Java代码 收藏代码
  1. #define ngx_shmtx_lock(mtx) ngx_spinlock((mtx)->lock, ngx_pid, 1024)


我们来看spinklock,必须支持原子指令,才会有这个函数,这里nginx采用宏来控制的.

这里和trylock的处理差不多,都是利用原子指令来实现的,只不过这里如果无法获得锁,则会继续等待。

我们来看代码的实现:

Java代码 收藏代码
  1. void
  2. ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin)
  3. {
  4. #if (NGX_HAVE_ATOMIC_OPS)
  5. ngx_uint_t i, n;
  6. for ( ;; ) {
  7. //如果lock为0,则说明没有进程持有锁,因此设置lock为value(为当前进程id),然后返回。
  8. if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
  9. return;
  10. }
  11. //如果cpu个数大于1(也就是多核),则进入spin-wait loop阶段。
  12. if (ngx_ncpu > 1) {
  13. //开始进入循环。
  14. for (n = 1; n < spin; n <<= 1) {
  15. //下面这段就是纯粹的spin-loop wait。
  16. for (i = 0; i < n; i++) {
  17. //这个函数其实就是执行"PAUSE"指令,接下来会解释这个指令。
  18. ngx_cpu_pause();
  19. }
  20. //然后重新获取锁,如果获得则直接返回。
  21. if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
  22. return;
  23. }
  24. }
  25. }
  26. //这个函数调用的是sched_yield,它会强迫当前运行的进程放弃占有处理器。
  27. ngx_sched_yield();
  28. }
  29. #else
  30. #if (NGX_THREADS)
  31. #error ngx_spinlock() or ngx_atomic_cmp_set() are not defined !
  32. #endif
  33. #endif
  34. }


通过上面的代码可以看到spin lock实现的很简单,就是一个如果无法获得锁,就进入忙等的过程,不过这里nginx还多加了一个处理,就是如果忙等太长,就放弃cpu,直到下次任务再次占有cpu。

接下来来看下PAUSE指令,这条指令主要的功能就是告诉cpu,我现在是一个spin-wait loop,然后cpu就不会因为害怕循环退出时,内存的乱序而需要处理,所引起的效率损失问题。

下面就是intel手册的解释:

引用

Improves the performance of spin-wait loops. When executing a “spin-wait loop,” a
Pentium 4 or Intel Xeon processor suffers a severe performance penalty when exiting
the loop because it detects a possible memory order violation. The PAUSE instruction
provides a hint to the processor that the code sequence is a spin-wait loop. The
processor uses this hint to avoid the memory order violation in most situations,
which greatly improves processor performance. For this reason, it is recommended
that a PAUSE instruction be placed in all spin-wait loops.


内核的spin lock也有用到这条指令的。

接下来就是unlokck。unlock比较简单,就是和当前进程id比较,如果相等,就把lock改为0,说明放弃这个锁。

Java代码 收藏代码
  1. #define ngx_shmtx_unlock(mtx) (void) ngx_atomic_cmp_set((mtx)->lock, ngx_pid, 0)


然后就是不支持原子操作的情况,此时使用文件锁来实现的,这里就不介绍这种实现了,基本原来和上面的差不多,想要了解的,可以去看nginx的相关代码。

接下来我们来看nginx如何利用lock来控制子进程的负载均衡以及惊群。

先来大概解释下这两个概念。

负载均衡是为了解决有可能一个进程处理了多个连接,因此就需要让多个进程更平均的处理连接。

惊群也就是当我们多个进程阻塞在epoll这类调用的时候,当有数据可读的时候,多个进程会被同时唤醒,此时如果去accept的话,只能有一个进程accept到句柄。

在看代码之前,我们先来看ngx_use_accept_mutex这个变量,如果有这个变量,说明nginx有必要使用accept互斥体,这个变量的初始化在ngx_event_process_init中。

这里还有两个变量,一个是ngx_accept_mutex_held,一个是ngx_accept_mutex_delay,其中前一个表示当前是否已经持有锁,后一个表示,当获得锁失败后,再次去请求锁的间隔时间,这个时间可以看到可以在配置文件中设置的。

Java代码 收藏代码
  1. //如果使用了master worker,并且worker个数大于1,并且配置文件里面有设置使用accept_mutex.的话,设置ngx_use_accept_mutex
  2. if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
  3. ngx_use_accept_mutex = 1;
  4. //下面这两个变量后面会解释。
  5. ngx_accept_mutex_held = 0;
  6. ngx_accept_mutex_delay = ecf->accept_mutex_delay;
  7. } else {
  8. ngx_use_accept_mutex = 0;
  9. }



这里还有一个变量是ngx_accept_disabled,这个变量是一个阈值,如果大于0,说明当前的进程处理的连接过多。
下面就是这个值的初始化,可以看到初始值是全部连接的7/8(注意是负值0.

Java代码 收藏代码
  1. ngx_accept_disabled = ngx_cycle->connection_n / 8
  2. - ngx_cycle->free_connection_n;


然后来看ngx_process_events_and_timers中的处理。

Java代码 收藏代码
  1. //如果有使用mutex,则才会进行处理。
  2. if (ngx_use_accept_mutex) {
  3. //如果大于0,则跳过下面的锁的处理,并减一。
  4. if (ngx_accept_disabled > 0) {
  5. ngx_accept_disabled--;
  6. } else {
  7. //试着获得锁,如果出错则返回。
  8. if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
  9. return;
  10. }
  11. //如果ngx_accept_mutex_held为1,则说明已经获得锁,此时设置flag,这个flag后面会解释。
  12. if (ngx_accept_mutex_held) {
  13. flags |= NGX_POST_EVENTS;
  14. } else {
  15. //否则,设置timer,也就是定时器。接下来会解释这段。
  16. if (timer == NGX_TIMER_INFINITE
  17. || timer > ngx_accept_mutex_delay)
  18. {
  19. timer = ngx_accept_mutex_delay;
  20. }
  21. }
  22. }
  23. }


然后先来看NGX_POST_EVENTS标记,设置了这个标记就说明当socket有数据被唤醒时,我们并不会马上accept或者说读取,而是将这个事件保存起来,然后当我们释放锁之后,才会进行accept或者读取这个句柄。

Java代码 收藏代码
  1. //如果ngx_posted_accept_events不为NULL,则说明有accept event需要nginx处理。
  2. if (ngx_posted_accept_events) {
  3. ngx_event_process_posted(cycle, &ngx_posted_accept_events);
  4. }


而如果没有设置NGX_POST_EVENTS标记的话,nginx会立即accept或者读取句柄。

然后是定时器,这里如果nginx没有获得锁,并不会马上再去获得锁,而是设置定时器,然后在epoll休眠(如果没有其他的东西唤醒).此时如果有连接到达,当前休眠进程会被提前唤醒,然后立即accept。否则,休眠 ngx_accept_mutex_delay时间,然后继续try lock.

最后是核心的一个函数,那就是ngx_trylock_accept_mutex。这个函数用来尝试获得accept mutex.

Java代码 收藏代码
  1. ngx_int_t
  2. ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
  3. {
  4. //尝试获得锁
  5. if (ngx_shmtx_trylock(&ngx_accept_mutex)) {
  6. //如果本来已经获得锁,则直接返回Ok
  7. if (ngx_accept_mutex_held
  8. && ngx_accept_events == 0
  9. && !(ngx_event_flags & NGX_USE_RTSIG_EVENT))
  10. {
  11. return NGX_OK;
  12. }
  13. //到达这里,说明重新获得锁成功,因此需要打开被关闭的listening句柄。
  14. if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
  15. ngx_shmtx_unlock(&ngx_accept_mutex);
  16. return NGX_ERROR;
  17. }
  18. ngx_accept_events = 0;
  19. //设置获得锁的标记。
  20. ngx_accept_mutex_held = 1;
  21. return NGX_OK;
  22. }
  23. //如果我们前面已经获得了锁,然后这次获得锁失败,则说明当前的listen句柄已经被其他的进程锁监听,因此此时需要从epoll中移出调已经注册的listen句柄。这样就很好的控制了子进程的负载均衡
  24. if (ngx_accept_mutex_held) {
  25. if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
  26. return NGX_ERROR;
  27. }
  28. //设置锁的持有为0.
  29. ngx_accept_mutex_held = 0;
  30. }
  31. return NGX_OK;
  32. }


这里可以看到大部分情况下,每次只会有一个进程在监听listen句柄,而只有当ngx_accept_disabled大于0的情况下,才会出现一定程度的惊群。

而nginx中,由于锁的控制(以及获得锁的定时器),每个进程都能相对公平的accept句柄,也就是比较好的解决了子进程负载均衡。