分类:未分类


redis持久化rdb和aof


redis如果不把数据做持久化,则是把数据存储在内存里面的,所以速度很快。而持久化的目的是为了保证数据安全。redis提供了两种持久化的方案:rdb和appendonly(简称aof)。

简单讲,rdb形式就是把具体的数据(key以及value)存到到一个文件中;而aof这种是把操作过程以日志的形式存到一个文件里,类似于mysql的bin-log。

rdb,需要定义dir,也就是数据所存放的目录,其实aof文件也会放到这个目录下面,而dbfilename则定义了rdb文件的名字。配置文件中有一个参数“save”来定义redis按照什么样的规则把内存中的数据刷新到磁盘上的rdb文件里。当然我们也可以用命令“save”或者“bgsave”来手动把内存里面的数据更新到磁盘上。如果想要关闭rdb持久化,可以直接把save参数的值设置为空,即save “”,或者在redis-cli命令行里面执行:config set save “”.

aof,有一个参数是控制它的开关。“appendonly yes” 只有这个是yes时,才会在dir目录中生成一个aof文件,文件名字默认是appendonly.aof,当然我们可以用appendfilename参数去修改它。

对于aof文件,如果我们不做特殊操作,它会无限制增长,以至于把磁盘撑爆。如果业务对redis数据没有那么高的要求的话,可以不用开启aof。另外有一个方法可以实现aof文件的优化,就是执行命令 BGREWRITEAOF,这样就会重写aof文件,最终将aof文件的体积减小。这个过程可能会耗费一定的资源,建议在凌晨执行。

最后,再来总结一下。rdb并不会实时把内存数据写到磁盘中,意外断电时会丢一部分数据的,但是aof就安全得多,它几乎是实时记录日志的,通过参数appendfsync控制,默认everysec是一秒记录一次。两种持久化的方式是可以并存的,当数据恢复时,以aof优先。

CGI、FastCGI与php-fpm


这几个概念是在lamp、lnmp架构我们遇到的。好多同学对这几个概念一直不明白。下面我就简单说说我的理解。

在web开发领域中有个大概念大家必须要搞清楚,那就是静态和动态。静态就是一些诸如html、图片、js、css等请求元素,它们的特点是不需要和其他资源(比如数据库)打交道,可以直接由web server(httpd、nginx等)处理。如下图:

 

而动态则是需要和数据库等资源打交道的请求,在lamp/lnmp架构中,需要一个中间件(PHP解释器)去解析php代码,然后和mysql数据库打交道。如图:

当Web Server收到 index.php 这个请求后,会启动对应的 CGI 程序,这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以规定CGI规定的格式返回处理后的结果,退出进程,Web server再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程,接下来再引出这些概念,就好理解多了。

我们再来解释几个概念:

CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。

FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。同样,SCGI 协议与 FastCGI 类似。

PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。

PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务管理。

WEB 中,Web Server 一般指Apache、Nginx、IIS、Lighttpd、Tomcat等服务器,Web Application 一般指PHP、Java、Asp.net等应用程序。

在这里我们还要了解另外一种方式 —— Apache的Module方式。 lamp架构中,php是以一个模块(libphp5.so)的方式被httpd调用的,这个模块是通过sapi把数据交给了php的解释器。

SAPI提供了一个和外部通信的接口,有点类似于socket,使得PHP可以和其他应用进行交互数据(apache,nginx等)。php默认提供了很多种SAPI,常见的提供给apache和nginx的php5_module、CGI、FastCGI,给IIS的ISAPI,以及Shell的CLI。

最后再来说一说,FastCGI为什么比CGI优秀。

不管是CGI还是FastCGI都是专门用来和WEB服务器打交道的,web服务器收到用户请求,就会把请求提交给cgi/fastcgi程序(如php-cgi),cgi/fastcgi程序根据请求提交的参数作应处理(解析php),然后输出标准的html语句,返回给web服服务器,WEB服务器再返回给客户端,这就是普通cgi/fastcgi的工作原理。

cgi和fastcgi不同在于,cgi需要在每一次WEB服务器把请求交给它时都需要启动一次进程,然后处理结束后还要关闭进程。这样当并发量很大时,就会造成服务器资源耗费严重。而fastcgi则为常驻进程,也就是说进程启动后会一直在线,不用频繁开启和关闭。另外fastcgi还支持分布式,也就是说可以支持在web服务器外的其他服务器上运行,通过tcp传输数据。

shell中的select用法


select也是循环的一种,它比较适合用在用户选择的情况下。
比如,我们有一个这样的需求,运行脚本后,让用户去选择数字,选择1,会运行w命令,选择2运行top命令,选择3运行free命令,选择4退出。脚本这样实现:

#!/bin/bashecho “Please chose a number, 1: run w, 2: run top, 3: run free, 4: quit”

echo

select command in w top free quit

do

case $command in

w)

w

;;

top)

top

;;

free)

free

;;

quit)

exit

;;

*)

echo “Please input a number:(1-4).”

;;

esac

done

执行结果如下:
sh select.sh
Please chose a number, 1: run w, 2: run top, 3: run free, 4: quit

1) w
2) top
3) free
4) quit
#? 1

16:06:58 up 109 days, 22:01,  1 user,  load average: 0.11, 0.05, 0.01

USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT

root     pts/0    222.128.156.84   16:05    0.00s  0.00s  0.00s w

#? 3
total       used       free     shared    buffers     cached
Mem:       1020328     943736      76592          0      86840     263624
-/+ buffers/cache:     593272     427056
Swap:      2097144      44196    2052948
#?

我们发现,select会默认把序号对应的命令列出来,每次输入一个数字,则会执行相应的命令,命令执行完后并不会退出脚本。它还会继续让我们再次输如序号。序号前面的提示符,我们也是可以修改的,利用变量PS3即可,再次修改脚本如下:

#!/bin/bash

PS3=”Please select a number: ”

echo “Please chose a number, 1: run w, 2: run top, 3: run free, 4: quit”

echo

select command in w top free quit

do

case $command in

w)

w

;;

top)

top

;;

free)

free

;;

quit)

exit

;;

*)

echo “Please input a number:(1-4).”

esac

done

如果想要脚本每次输入一个序号后就自动退出,则需要再次更改脚本如下:

#!/bin/bash

PS3=”Please select a number: ”

echo “Please chose a number, 1: run w, 2: run top, 3: run free, 4: quit”

echo

select command in w top free quit

do

case $command in

w)

w;exit

;;

top)

top;exit

;;

free)

free;exit

;;

quit)

exit

;;

*)

echo “Please input a number:(1-4).”;exit

esac

done

tomcat单机多实例


需求是: 同一个tomcat服务下,可以访问多个域名。

先来看看tomcat配置文件server.xml的结构

<Server>

<Service name=”xxx”>

<Host  name=”aaa.com” appBase=”dir1″>

</Host>

</Service>

<Server>

说明:

Service下面可以定义监听的端口,包含了<Host></Host>,而<Host></Host>里面定义虚拟主机,即,定义站点的域名和站点的目录。

 

两种方案:

1 可以在<Service></Service>里面定义多个<Host></Host>,这样就定义了多个站点,但端口不能变。

2 可以在<Server></Server>里面定义多个<Service></Service>,<Service></Service>里再定义<Host></Host>,这样做的好处是,可以更改监听端口。

 

假设需求为:

同一个服务器上跑两个站点

www.aaa.com  监听端口是8080

www.bbb.com  监听端口是8081

 

配置文件内容为:

<GlobalNamingResources>
<!– Editable user database that can also be used by
UserDatabaseRealm to authenticate users
–>
<Resource name=”UserDatabase” auth=”Container”
type=”org.apache.catalina.UserDatabase”
description=”User database that can be updated and saved”
factory=”org.apache.catalina.users.MemoryUserDatabaseFactory”
pathname=”conf/tomcat-users.xml” />
</GlobalNamingResources>

<!– A “Service” is a collection of one or more “Connectors” that share
a single “Container” Note: A “Service” is not itself a “Container”,
so you may not define subcomponents such as “Valves” at this level.
Documentation at /docs/config/service.html
–>
<Service name=”Catalina”>

<Connector port=”8080″ protocol=”HTTP/1.1″
connectionTimeout=”20000″
redirectPort=”8443″ />
<Connector port=”8009″ protocol=”AJP/1.3″ redirectPort=”8443″ />

<Engine name=”Catalina” defaultHost=”localhost”>

<Realm className=”org.apache.catalina.realm.LockOutRealm”>
<Realm className=”org.apache.catalina.realm.UserDatabaseRealm”
resourceName=”UserDatabase”/>
</Realm>

<Host name=”www.aaa.com” appBase=”webapps”
unpackWARs=”true” autoDeploy=”true”>

<Valve className=”org.apache.catalina.valves.AccessLogValve” directory=”logs”
prefix=”aaa.com_access” suffix=”.log”
pattern=”%h %l %u %t &quot;%r&quot; %s %b” />

</Host>
</Engine>
</Service>
<Service name=”aming”>

<Connector port=”8081″ protocol=”HTTP/1.1″
connectionTimeout=”20000″
redirectPort=”8443″ />

<Engine name=”aming” defaultHost=”localhost”>

<Realm className=”org.apache.catalina.realm.LockOutRealm”>
<Realm className=”org.apache.catalina.realm.UserDatabaseRealm”
resourceName=”UserDatabase”/>
</Realm>

<Host name=”www.bbb.com” appBase=”/data/tomcat”
unpackWARs=”true” autoDeploy=”true”>

<Valve className=”org.apache.catalina.valves.AccessLogValve” directory=”logs”
prefix=”bbb.com_access” suffix=”.log”
pattern=”%h %l %u %t &quot;%r&quot; %s %b” />

</Host>
</Engine>
</Service>

</Server>


nginx的alias和root的区别


假设,网站的域名为 http://abc.com/,下面分别写两个配置来说明alias和root的区别。

location /123/ {

root /data/wwwroot/abc.com;

}

location /123/ {

alias /data/dir/;

}

上面两段配置,如果访问的链接地址为 http://abc.com/123/1.jpg

第一段会访问服务器上的/data/wwwroot/abc.com/123/1.jpg

第二段配置会访问服务器上的/data/dir/1.jpg

总结一下,用root时,要加上location后面的目录/123/,用alias时,不加/123/目录。

另外还需要注意下面要求:

1) 使用alias时,后面跟的目录结尾必须要加/

2) alias只能在location中使用


httpd的三种模式比较


查看你的httpd使用了哪种模式:

/usr/local/apache2/bin/httpd -V |grep ‘Server MPM’

使用哪种模式,需要在编译的时候指定

–with-mpm=prefork|worker|event

当然也可以编译的时候,让三者都支持:

–enable-mpms-shared=all

然后在配置文件中,修改

LoadModule mpm_worker_module modules/mpd_mpm_worker.so

2.2版本默认为worker,2.4版本默认为event

再来比较一下三种模式的差异

1 prefork

prefork模式可以算是很古老但是非常稳定的Apache模式。Apache在启动之初,就预先fork一些子进程,然后等待请求进来。之所以这样做,是为了减少频繁创建和销毁进程的开销。每个子进程只有一个线程,在一个时间点内,只能处理一个请求。
优点:成熟稳定,兼容所有新老模块。同时,不需要担心线程安全的问题。(我们常用的mod_php,PHP的拓展不需要支持线程安全)
缺点:一个进程相对占用更多的系统资源,消耗更多的内存。而且,它并不擅长处理高并发请求,在这种场景下,它会将请求放进队列中,一直等到有可用进程,请求才会被处理。

2 worker

worker模式比起上一个,是使用了多进程和多线程的混合模式。它也预先fork了几个子进程(数量比较少),然后每个子进程创建一些线程,同时包括一个监听线程。每个请求过来,会被分配到1个线程来服务。线程比起进程会更轻量,因为线程通常会共享父进程的内存空间,因此,内存的占用会减少一些。在高并发的场景下,因为比起prefork有更多的可用线程,表现会更优秀一些。
有些人会觉得奇怪,那么这里为什么不完全使用多线程呢,还要引入多进程?
原因主要是需要考虑稳定性,如果一个线程异常挂了,会导致父进程连同其他正常的子线程都挂了(它们都是同一个进程下的)。为了防止这场异常场景出现,就不能全部使用线程,使用多个进程再加多线程,如果某个线程出现异常,受影响的只是Apache的一部分服务,而不是整个服务。

优点:占据更少的内存,高并发下表现更优秀。

缺点:必须考虑线程安全的问题,因为多个子线程是共享父进程的内存地址的。如果使用keep-alive的长连接方式,某个线程会一直被占据,也许中间几乎没有请求,需要一直等待到超时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用。(该问题在prefork模式下,同样会发生)

注:keep-alive的长连接方式,是为了让下一次的socket通信复用之前创建的连接,从而,减少连接的创建和销毁的系统开销。保持连接,会让某个进程或者线程一直处于等待状态,即使没有数据过来。

3  event

这个是Apache中最新的模式,在现在版本里的已经是稳定可用的模式。它和worker模式很像,最大的区别在于,它解决了keep-alive场景下,长期被占用的线程的资源浪费问题(某些线程因为被keep-alive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)。event MPM中,会有一个专门的线程来管理这些keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场景下的请求处理能力。

event MPM在遇到某些不兼容的模块时,会失效,将会回退到worker模式,一个工作线程处理一个请求。官方自带的模块,全部是支持event MPM的。

注意一点,event MPM需要Linux系统(Linux 2.6+)对EPoll的支持,才能启用。

还有,需要补充的是HTTPS的连接(SSL),它的运行模式仍然是类似worker的方式,线程会被一直占用,知道连接关闭。部分比较老的资料里,说event MPM不支持SSL,那个说法是几年前的说法,现在已经支持了。

php中几个和mysql相关的扩展模块


在编译php的时候,有这么几个和mysql相关的编译选项:

–with-mysql

–with-mysqli

–with-pdo-mysql

–enable-mysqlnd

那这几个选项到底有什么不同呢?

php官方文档上有这么一段话:

MySQL:This extension is deprecated as of PHP 5.5.0, and has been removed as of php 7.0.0.
MYSQLI: mysql Improved Extension
MySQLND: MySQL Native Drive
PDO:The PHP Data Objects。extension defines a lightweight, consistent interface for accessing databases in PHP。

翻译成中文大概意思是:

MySQL扩展:在5.5.0版本中开始废弃,7.0.0版本中已经移除(也就是不支持了)

MySQLI扩展:是mysql扩展的增强版

MySQLnd:这是官方驱动,或者叫做原生驱动

PDO:PHP Data Objects,是PHP应用中的一个数据库抽象层规范

在PHP扩展的角度上看,MYSQL和MYSQLi还是比较上层的扩展,依赖更底层的库去连接和访问数据库。 MYSQLND 就是所说的底层的数据库驱动。

从应用的层面上看,我们通过PHP 的MYSQL或者MYSQLi扩展提供的API去操作数据库。

从底层来看,MYSQLND提供了底层和数据库交互的支持(可以简单理解为和MySQL server进行网络协议交互)。

而PDO,则提供了一个统一的API接口,使得你的PHP应用不去关心具体要连接的数据库服务器系统类型。也就是说,如果你使用PDO的API,可以在任何需要的时候无缝切换数据库服务器。比如MYSQL,SQLITE任何数据库都行。

即从大部分功能上看,PDO提供的API接口和MYSQLI提供的接口对于普通的增删改查效果是一致的。

最后再贴几个php的代码,来区分几个扩展的用法:

1) mysql连接

<?php
$conn = @ mysql_connect(“localhost”, “root”, “”) or die(“数据库连接错误”);
mysql_select_db(“bbs”, $conn);
mysql_query(“set names ‘utf8′”);
echo “数据库连接成功”;
?>

2)mysqli连接

<?php
$conn = mysqli_connect(‘localhost’, ‘root’, ”, ‘bbs’);
if(!$conn){
die(“数据库连接错误” . mysqli_connect_error());
}
else{
echo”数据库连接成功”;
}
?>

3)PDO连接

<?php
try{
$pdo=new pdo(“mysql:host=localhost;dbname=bbs”,”root”,””);
}
catch(PDDException $e){
echo”数据库连接错误”;
}
echo”数据库连接成功”;
?>

进程与线程


计算机上运行的程序都会有进程,你可以打开windows的进程管理器来查看系统所有的进程信息,而linux可以使用top或者ps来查看所有的进程信息。

线程是比进程还要细分的一个单元,它在进程里面,一个进程下面可以有多个线程。

下表为进程和线程的主要区别:

项目 进程 线程
unit 分配系统资源(cpu时间、内存等)的基本单位 进程内的一个执行单元,CPU调度和分派的基本单位
地址空间 有独立的地址空间 和同进程内的其他线程共享地址空间
健壮性 很健壮,一个进程崩溃对其他进程没有影响 不健壮,相同进程内,一个线程崩溃(意味着进程崩溃),其他线程也会崩溃
调度(切换) 进程上下文切换,资源耗费大 轻量级进程切换,不用重新加载内存,一个进程内的线程切换要比不同进程内的线程切换快5-50倍
创建 拷贝父进程的地址空间,耗时较长 直接创建,比进程创建要快10-100倍
通信 进程间通信,用到IPC通信机制(共享内存、socket、信号、管道等) 可以直接和相同进程内的其他线程通信
编程容易度 多进程间耦合性较小,比较容易 线程间耦合度较大,共享全局变量,需要考虑关键变量的互斥操作

SSL工作原理


(1) 浏览器发送一个https的请求给服务器;

(2) 服务器要有一套数字证书,可以自己制作(但使用自己制作的证书,浏览器访问的时候会提示不信任),也可以向组织申请,区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面,这套证书其实就是一对公钥和私钥;

(3) 服务器会把公钥传输给客户端;

(4) 客户端(浏览器)收到公钥后,会验证其是否合法有效,无效会有警告提醒,有效则会生成一串随机数,并用收到的公钥加密;

(5) 客户端把加密后的随机字符串传输给服务器;

(6) 服务器收到加密随机字符串后,先用私钥解密(公钥加密,私钥解密),获取到这一串随机数后,再用这串随机字符串加密传输的数据(该加密为对称加密,所谓对称加密,就是将数据和私钥也就是这个随机字符串通过某种算法混合在一起,这样除非知道私钥,否则无法获取数据内容);

(7) 服务器把加密后的数据传输给客户端;

(8) 客户端收到数据后,再用自己的私钥也就是那个随机字符串解密;

pagent免密钥密码


使用putty远程登录Linux,假如使用密钥验证,为了更加安全我们会增加一个密钥的密码,只有输入正确的密码才能成功远程登录Linux,其实这个输入密码的环节是可以省略的。这就用到了 pagent.exe 程序。它的作用就是把密钥加载到内存中,只需在第一次加载到内存时输入密码,之后就不再输入密码了。

双击pagent.exe, 虽然没有弹出什么窗口,但是它已经运行了,在电脑右下角,双击小图标把它调到前台来,点 “Add key” , 找到我们要加载到内存的私钥,这时会弹出一个小窗口让我们输入私钥的密码,等输入完成后,点 “OK” 就可以了,然后点”close”。

此时我们再运行putty.exe,打开远程主机时,不再输入私钥的密码了。但是以后每天开机后,你可不能忘记运行这个小程序,并加载一下私钥文件。为了让其更加自动化,可以让它开机启动:

在开始菜单中找到”启动” ,右键点击选择”打开”, 把pagent.exe 的快捷方式先发送到桌面,然后再剪切到该文件夹下。这样就开机启动了。