无来

不管你来还是不来
我都在这里,夜夜点亮
不是为了守候
只是为了做好我自己

0%

基本情况

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@test ~]# cat /etc/redhat-release
CentOS release 6.5 (Final)
[root@test ~]# uname -a
Linux test 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
[root@test ~]# file /bin/wipefs
/bin/wipefs: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
[root@test ~]# ll /bin/wipefs
-rwxr-xr-x 1 root root 2384177 Jul 18 2013 /bin/wipefs
[root@monitor ~]# lsattr /bin/wipefs
----i--------e- /bin/wipefs
[root@monitor ~]# lsattr /bin/ddus-uidgen
----i--------e- /bin/ddus-uidgen

改动了dns配置

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@test rc3.d]# stat /etc/resolv.conf
File: `/etc/resolv.conf'
Size: 106 Blocks: 8 IO Block: 4096 regular file
Device: 802h/2050d Inode: 1182160 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-11-22 06:00:02.797144215 +0800
Modify: 2017-11-22 06:00:02.795144215 +0800
Change: 2017-11-22 06:00:02.795144215 +0800
[root@test rc3.d]# cat /etc/resolv.conf
nameserver 208.67.222.222 #加拿大DNS
nameserver 114.114.114.114
nameserver 208.67.222.222
nameserver 114.114.114.114

增加了开机启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@test cron]# ll /etc/init.d/wipefs
lrwxrwxrwx 1 root root 11 Nov 22 06:00 /etc/init.d/wipefs -> /bin/wipefs
[root@test rc3.d]# pwd
/etc/rc.d/rc3.d
[root@test rc3.d]# ll -h |grep wipefs
lrwxrwxrwx 1 root root 18 Nov 22 06:00 S01wipefs -> /etc/init.d/wipefs
[root@test rc3.d]# cd /etc/rc3.d/
[root@test rc3.d]# ll -h |grep wipefs
lrwxrwxrwx 1 root root 18 Nov 22 06:00 S01wipefs -> /etc/init.d/wipefs
[root@test init.d]# pwd
/etc/init.d
[root@test init.d]# ll acpidtd
-rwxr-xr-x 1 root root 1223753 Nov 20 16:03 acpidtd
[root@test rc3.d]# ll -h |grep acpidtd
lrwxrwxrwx 1 root root 19 Nov 20 16:03 S01acpidtd -> /etc/init.d/acpidtd
[root@test rc3.d]# pwd
/etc/rc3.d
[root@test rc3.d]# cd /etc/rc.d/rc3.d/
[root@test rc3.d]# ll -h |grep acpidtd
lrwxrwxrwx 1 root root 19 Nov 20 16:03 S01acpidtd -> /etc/init.d/acpidtd
[root@test rc3.d]# ll /bin/ddus-uidgen
-rwxr-xr-x 1 root root 1223753 Nov 20 16:03 /bin/ddus-uidgen
[root@test rc3.d]# ll /etc/resolv.conf
-rw-r--r-- 1 root root 106 Nov 22 06:00 /etc/resolv.conf

清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
pkill wipefs
echo "nameserver 114.114.114.114" > /etc/resolv.conf
chattr -i /bin/wipefs
chattr -i /bin/ddus-uidgen
chattr -i /etc/init.d/acpidtd
rm -rf /bin/wipefs
rm -rf /etc/init.d/wipefs
rm -rf /bin/ddus-uidgen
rm -rf /etc/init.d/acpidtd
rm -rf /etc/rc0.d/S01wipefs
rm -rf /etc/rc1.d/S01wipefs
rm -rf /etc/rc2.d/S01wipefs
rm -rf /etc/rc3.d/S01wipefs
rm -rf /etc/rc4.d/S01wipefs
rm -rf /etc/rc5.d/S01wipefs
rm -rf /etc/rc6.d/S01wipefs
rm -rf /etc/rc.d/rc0.d/S01wipefs
rm -rf /etc/rc.d/rc1.d/S01wipefs
rm -rf /etc/rc.d/rc2.d/S01wipefs
rm -rf /etc/rc.d/rc3.d/S01wipefs
rm -rf /etc/rc.d/rc4.d/S01wipefs
rm -rf /etc/rc.d/rc5.d/S01wipefs
rm -rf /etc/rc.d/rc6.d/S01wipefs
rm -rf /etc/rc0.d/acpidtd
rm -rf /etc/rc1.d/acpidtd
rm -rf /etc/rc2.d/acpidtd
rm -rf /etc/rc3.d/acpidtd
rm -rf /etc/rc4.d/acpidtd
rm -rf /etc/rc5.d/acpidtd
rm -rf /etc/rc6.d/acpidtd
rm -rf /etc/rc.d/rc0.d/acpidtd
rm -rf /etc/rc.d/rc1.d/acpidtd
rm -rf /etc/rc.d/rc2.d/acpidtd
rm -rf /etc/rc.d/rc3.d/acpidtd
rm -rf /etc/rc.d/rc4.d/acpidtd
rm -rf /etc/rc.d/rc5.d/acpidtd
rm -rf /etc/rc.d/rc6.d/acpidtd

I have noticed that even if you do a fsck on the disk the problem may occur again in a few days.

I have found that the problem is worse on SSD disks than the regular HDD disks. I have found some steps that may fix the problem temporarily.

fsck -fy /dev/sda1

if sda1 is the right partition - the prompt will tell you exactly which one requires fsck.

After that if the systems boots up you may have another problem with the package management system, so if you open a terminal and type sudo apt-get update you may get an error. Do not worry. Run these commands:

1
2
3
4
sudo apt-get update
sudo apt-get clean
sudo apt-get update
sudo apt-get upgrade

My opinion is that there is serious problem in Ubuntu with regard to SSD disks. The community should fix it.

I have found a possible cause of this problem: Probably the system did not shutdown normally.

setup NFS but when trying to mount getting the following errors

mount.nfs: access denied by server while mounting <server ip>:/exports

The

showmount -e <server ip>

command returns the following

/exports <client ip>

操作系统安装时选择了中文字符,导致命令行下的错误提示信息也显示中文。这样无法在google上搜索到相对应的解决方法。所以需要临时修改提示信息为英文
#export LANG=”en_US.UTF-8”

为了搞学习内核编译,需要安装一些开发工具包,索性把光盘镜像设置成本地yum源,这样更快些!以下是一些基本步骤:

  • 1、 首先挂载光盘到/mnt/cd下,mount /dev/cdrom /mnt/cd,这个就不多说了;

  • 2、 进入``/etc/yum.repos.d/目录下,把原来的yum源备份,我给它重新命名了。

    mv CentOS-Vault.repo CentOS-Vault.repo.bak
    mv CentOS-Debuginfo.repo CentOS-Debuginfo.repo.bak
    mv CentOS-Base.repo CentOS-Base.repo.bak
  • 3、新建一个光盘源

vim CentOS-Media.repo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# CentOS-Media.repo
#
# This repo can be used with mounted DVD media, verify the mount point for
# CentOS-6. You can use this repo and yum to install items directly off the
# DVD ISO that we release.
#
# To use this repo, put in your DVD and use it with the other repos too:
# yum --enablerepo=c6-media [command]
#
# or for ONLY the media repo, do this:
#
# yum --disablerepo=\* --enablerepo=c6-media [command]

[c6-media]
name=CentOS-$releasever - Media
baseurl=file:///mnt/cd
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6

保存退出

  • 4、yum clean all (清除缓存)

  • 5、yum makecache (建立新缓存)

带你去探险

刘笑嫣(可乐)

过来吧,我要带你去个美丽的地方!
美丽的地方是树吗?
对,咱们去树里逛一逛!
不用怕,不用怕!
我会陪在你身边,一直等你长大!

过来吧,我要带你去个美丽的地方!
美丽的地方是花吗?
对,我要带你去欣赏花的美丽。
花蜜多么美味呀,甜甜的!
不用怕,不用怕!
我会陪在你身边,一直等你长大!

过来吧,我要带你去个美丽的地方!
美丽的地方是外面吗?
对,我要带你去外面的世界逛一逛!
那是一个多么美丽的地方呀,你也一定会很喜欢哒!
不用怕,不用怕!
我会陪在你身边,一直等你长大!

一个常见的问题,mysql 导出csv格式的语法,已经乱码问题:
由于数据库一般默认的是UTF-8 格式的字符集,而execl默认的是gbk格式的字符集,这里有两种方法解决乱码:

方法一: 先转出.txt格式的文件,然后选择用excel打开时,提示选择哪种编码打开,选择gbk即可

1
select * from mobile_order_region where school_id=6921 into outfile '/tmp/6921.txt'

方法二:导出时直接设置字符集格式:

1
2
3
4
5
6
7
8
9
mysql> select * from mobile_order_region where school_id=6921 into outfile '/tmp/6921.csv'
CHARACTER SET gbk
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n';
Query OK, 6888 rows affected (0.11 sec)


mysql> \q

在Mysql 数据库中存在两种字符串连接操作.具体操作如下

一. 语法:

  1. CONCAT(string1,string2,…) 说明 : string1,string2代表字符串,concat函数在连接字符串的时候,只要其中一个是NULL,那么将返回NULL

    例1:

例2:

  1. CONCAT_WS(separator,str1,str2,…)

    说明 : string1,string2代表字符串,concat_ws 代表 concat with separator,第一个参数是其它参数的分隔符。分隔符的位置放在要连接的两个字符串之间。分隔符可以是一个字符串,也可以是其它参数。如果分隔符为 NULL,则结果为 NULL。函数会忽略任何分隔符参数后的 NULL 值。

    举例1:

    1
    2
    3
    4
    5
    6
    7
       mysql> select concat_ws('#','dbdh=','NorthEastTrcoon',null) AS dbdh_name_three;
    +-----------------------+
    | dbdh_name_three |
    +-----------------------+
    | dbdh=#NorthEastTrcoon |
    +-----------------------+
    1 row in set (0.00 sec)

    例2:

    1
    2
    3
    4
    5
    6
    7
    8
    mysql> select concat_ws(null,'dbdh=','NorthEastTrcoon',null) AS dbdh_name_fourth
    ;
    +------------------+
    | dbdh_name_fourth |
    +------------------+
    | NULL |
    +------------------+
    1 row in set (0.00 sec)

    例3:

    1
    2
    3
    4
    5
    6
    7
         mysql> select concat_ws('*','dbdh=','NorthEastTrcoon',null) AS dbdh_name_fifth;
    +-----------------------+
    | dbdh_name_fifth |
    +-----------------------+
    | dbdh=*NorthEastTrcoon |
    +-----------------------+
    1 row in set (0.00 sec)
  1. MySQL中group_concat函数
    完整的语法如下:
    group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator ‘分隔符’])

基本查询

1
2
3
4
5
6
7
8
9
10
11
12
mysql> select * from stu1;
+------+------+
| id| name |
+------+------+
|1 | 10|
|1 | 20|
|1 | 20|
|2 | 20|
|3 | 200 |
|3 | 500 |
+------+------+
6 rows in set (0.00 sec)

以id分组,把name字段的值打印在一行,逗号分隔(默认)

1
2
3
4
5
6
7
8
9
mysql> select id,group_concat(name) from aa group by id;
+------+--------------------+
| id| group_concat(name) |
+------+--------------------+
|1 | 10,20,20|
|2 | 20 |
|3 | 200,500|
+------+--------------------+
3 rows in set (0.00 sec)

以id分组,把name字段的值打印在一行,分号分隔

1
2
3
4
5
6
7
8
9
mysql> select id,group_concat(name separator ';') from aa group by id;
+------+----------------------------------+
| id| group_concat(name separator ';') |
+------+----------------------------------+
|1 | 10;20;20 |
|2 | 20|
|3 | 200;500 |
+------+----------------------------------+
3 rows in set (0.00 sec)

以id分组,把去冗余的name字段的值打印在一行,

逗号分隔

1
2
3
4
5
6
7
8
9
mysql> select id,group_concat(distinct name) from aa group by id;
+------+-----------------------------+
| id| group_concat(distinct name) |
+------+-----------------------------+
|1 | 10,20|
|2 | 20 |
|3 | 200,500 |
+------+-----------------------------+
3 rows in set (0.00 sec)

以id分组,把name字段的值打印在一行,逗号分隔,以name排倒序

1
2
3
4
5
6
7
8
9
mysql> select id,group_concat(name order by name desc) from aa group by id;
+------+---------------------------------------+
| id| group_concat(name order by name desc) |
+------+---------------------------------------+
|1 | 20,20,10 |
|2 | 20|
|3 | 500,200|
+------+---------------------------------------+
3 rows in set (0.00 sec)

还有一个简单的连接方式为: ||

1
2
3
4
5
6
7
8
9
mysql> select *  from student;
+----+------+-------+----------+------------+
| id | age | score | name | birth |
+----+------+-------+----------+------------+
| 1 | 23 | 78 | 李四 | 2017-10-10 |
| 2 | 24 | 78 | zhangsan | 2017-10-10 |
| 3 | 25 | 99 | 王五 | 2016-05-17 |
+----+------+-------+----------+------------+
3 rows in set (0.00 sec)
1
2
3
4
5
6
7
8
9
mysql> select id+999,name,name+99,name+'999' from student;
+--------+----------+---------+------------+
| id+999 | name | name+99 | name+'999' |
+--------+----------+---------+------------+
| 1000 | 李四 | 99 | 999 |
| 1001 | zhangsan | 99 | 999 |
| 1002 | 王五 | 99 | 999 |
+--------+----------+---------+------------+
3 rows in set, 6 warnings (0.00 sec)

0. 写作背景

好久没有写博客了,前端时间来了一场说走就走的旅行,和爱人一起辞职,去云南玩了半个月,才刚回来,最近一段时间在找工作,没事就上网看看技术相关的文章,充充电,昨天偶尔发现一遍讲解LNMP的并发与资源分配的文章,感觉不错,和大家分享下。
很多时候面试官都会问你,你们的程序性能如何?程序的并发可以达到多少?程序的瓶颈在哪儿?为了满足业务需求应该购买多少台服务器?负载均衡中php应用服务器需要多少台?
可能这些问题在面试中会设置一个应用的场景及一些前提条件,让面试的人去设计,并提出看法建议,能够回答得很好的人还是比较少的。

1. 概念

1.1 LNMP中的N是nginx充当Web Server

内容的分发者,会在文件系统找到相应的文件,就返回给浏览器,如:nginx。如果是静态的文件,就可以直接返回,但是如果是index.php需要解析并执行的脚本文件时,Web Server就无力了,需要将请求转发给相应的脚本语言的解析器来解释并执行,最终将程序的执行结果,返回给Web Server,再返回给浏览器。

1.2 LNMP中的P是php充当后端的逻辑处理程序

那么php与nginx的常规协作方式是如何的呢?需要我们明确几个概念

1.3 cgi

通用网关接口,是HTTP协议中描述的,Web Server与后端处理程序进程间通信的协议

1.4 php-cgi

php实现了cgi协议,使得web server与php共同完成一个动态网页的请求响应

1.5 fastcgi

是为了解决cgi性能问题,而规范的另外一种协议,为什么说解决cgi性能问题,因为在面对各大中型网站的业务需求中,cgi程序表现得越来越无力,因为cgi程序在每次接收到请求时都需要启动新的进程,并初始化环境,然后执行程序,具体的协议内容,在此不引述。

1.6 php-fpm

实现了fastcgi协议,是php-cgi的进程管理器,解决高并发网站的性能问题。
在最终回答LNMP的并发考虑与资源分配还需要明确的几个概念

1.7 并发

一般由单位内完成的请求数来衡量,如,每秒事务数(TPS),每秒HTTP请求数(HPS),每秒查询数(QPS)。通常情况下,我们说PHP的并发,都是指一秒内PHP完成的动态请求的次数。如某网站高峰期的动态请求并发为5000每秒,这个数字不算太高,但也不低。一般日活跃用户数在1000万-5000万的网站应用才能达到这个级别。

1.8 性能

一般是指应用程序的处理速度,如果php的应用程序,打开一个页面(执行一个脚本程序)通常需要在50-100ms完成,这对程序的性能要求还是比较高的。但是这还仅仅只是程序处理,php处理完成之后,还要交给web server,web server再将数据返回浏览器,这中间会有一个网络延迟,通常网络正常的情况下,需要大约100ms,最终一个动态网页的请求大约200ms(理想的情况下)可以到达用户浏览器端(仅仅是一个html结构)。

#2.资源分配

2.1 php-fpm进程数

按照上面的描述,并发为5000每秒,每个请求完成大约200ms(具体页面要具体分析,这里只是一个理想值),如果只有5台PHP应用程序服务器,那么每台机器平均为并发1000每秒,如果是使用nginx+php-fpm的架构,php-fpm的php-cgi进程管理器的配置应该如何呢?我计算的结果为(具体的配置项说明在后文):

  • pm=static
  • pm.max_children=100
    上面的100是如何得来的,由于机器平均并发为1000每秒,每个动态请求的处理时间为100ms,也就是说1个php-fpm的worker处理进程在1秒内可以处理10个请求,100个php-fpm的worker处理进程,就可以处理1000个请求。
    当然需要结合服务器硬件资源来进行配置,如果配置不当,很容易在请求高峰期或者流量猛增导致服务器宕机。

2.2 网络带宽

网络带宽也会是一个重要的因素,如果你的服务处理很强,但是用户的请求和响应不能及时到达也是白忙活,这个参数如何计算呢?
并发5000每秒,每个请求的输出为20K,则5000x20K=100000K=100M
这就要求你的公网负载均衡器外网出口带宽至少要达到100M

2.3 内存

上述中100个php-fpm的worker处理进程,理论上如果服务器只运行php-fpm,那么我们可以将服务器内存的一半分配给php-fpm,通常情况下,我们可以认为一个php-fpm的worker处理进程占用内存20M,那么100x20M=2G,也就是说明服务器的内存大约为4G

2.4 CPU

由于php-fpm是一个多进程的模型应用,CPU进程调度消耗也是很大的,并且PHP应用程序有问题也会导致CPU占用率高,这就没有量化的指标,需要具体情况具体分析了。但是有一个小建议,可以部署一个crontab每隔一分钟检测cpu占用率超过多少就kill掉相应的php-fpm的worker处理进程。

2.5 php-fpm Unix Socket

如果nginx与php在同一台机器,nginx与php-fpm使用unix域套接字代替tcp socke进行通信,这个配置挺关键的,纯echo的ab测试,采用unix域套接字每秒请求数提升10%-20%

即nginx中配置:

1
fastcgi_pass unix:/data/server/var/php/php-fpm.sock;

php-fpm.conf中配置:

1
listen = /data/server/var/php/php-fpm.sock

最后遇到很多同学对php-fpm的进程管理器的核心配置不太了解,下面是我翻译的配置说明:

3. php-fpm配置项

3.1 pm

  • static 一个固定的值,由pm.max_children指定
  • dynamic 动态的(工作方式和Apache的prefork模式一致),但是保持至少一个,由
    • pm.max_children 在同一时间最大的进程数
    • pm.start_servers php-fpm启动时开启的等待请求到来的进程数
    • pm.min_spare_servers 在空闲状态下,运行的最小进程数,如果小于此值,会创建新的进程
    • pm.max_spare_servers 在空闲状态下,运行的最大进程数,如果大于此值,会kill部分进程
    • ondemand 启动时不会创建进程,当请求达到时创建子进程处理请求
    • pm.max_children 在同一时间最大的进程数
    • pm.process_idle_timeout 空闲多少秒之后进程会被kill
  • pm = static

3.2 pm.max_children

在同一时间最大的进程数

  • pm.max_children = 120

3.3 pm.start_servers

php-fpm启动时开启的等待请求到来的进程数,默认值为:min_spare_servers + (max_spare_servers – min_spare_servers) / 2

  • pm.start_servers = 80

3.4 pm.min_spare_servers

在空闲状态下,运行的最小进程数,如果小于此值,会创建新的进程

  • pm.min_spare_servers = 60

3.5 pm.max_spare_servers

在空闲状态下,运行的最大进程数,如果大于此值,会kill部分进程

  • pm.max_spare_servers = 120

3.6 pm.process_idle_timeout

空闲多少秒之后进程会被kill,默认为10s

  • pm.process_idle_timeout = 10s

3.7 pm.max_requests

每个进程处理多少个请求之后自动终止,可以有效防止内存溢出,如果为0则不会自动终止,默认为0

  • pm.max_requests = 5000

3.8 pm.status_path

注册的URI,以展示php-fpm状态的统计信息

  • pm.status_path = /status
    其中统计页面信息有:
    • pool 进程池名称
    • process manager 进程管理器名称(static, dynamic or ondemand)
    • start time php-fpm启动时间
    • start since php-fpm启动的总秒数
    • accepted conn 当前进程池接收的请求数
    • listen queue 等待队列的请求数
    • max listen queue 自启动以来等待队列中最大的请求数
    • listen queue len 等待连接socket队列大小
    • idle processes 当前空闲的进程数
    • active processes 活动的进程数
    • total processes 总共的进程数(idle+active)
    • max active processes 自启动以来活动的进程数最大值
    • max children reached 达到最大进程数的次数

3.9 ping.path

ping url,可以用来测试php-fpm是否存活并可以响应

  • ping.path = /ping

ping.response

ping url的响应正文

  • ping.response = pong

zeroconf (Zero configuration networking), is a techniques that automatically creates a usable Internet Protocol (IP) network without manual operator intervention or special configuration servers. 169.254.0.0/255.255.0.0 route is part of zeroconf under RHEL 6 / CentOS 6 or older versions. To see current routing table, enter:

1
# route -n

Sample outputs:

1
2
3
4
5
6
7
8
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
74.8x.4y.zz 0.0.0.0 255.255.255.248 U 0 0 0 eth1
10.10.29.64 0.0.0.0 255.255.255.192 U 0 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 1003 0 0 eth1
10.0.0.0 10.10.29.65 255.0.0.0 UG 0 0 0 eth0
0.0.0.0 74.8x.yy.zz 0.0.0.0 UG 0 0 0 eth1

Every time the server or Linux desktop boots, the zeroconf route 169.254.0.0 is enabled and added to the kernel routing table. To disable zeroconf route under RHEL / CentOS / Fedora Linux, enter:

1
# vi /etc/sysconfig/network

Append the following directive:

NOZEROCONF=yes
Save and close the file. Reboot the system / server or restart the networking service:

/etc/init.d/network restart

Verify routing table, enter:

route -n

OR

ip route