php7 lnmp+swoole+workerman+redis+memcached+mongodb源码安装

2016/07/13
闲来无事  动手测试下swoole和workerman的性能  突然想到用php7是不是更爽,教程本不应该存在的东西
因为网上太多的安装教程  一键安装  rpm安装  考虑到有的同学可能喜欢编译安装 就随便写了这个
但是还是不应该存在的东西 因为我们没必要重复的造轮子 
主要是会配置环境 这就像编码 你说是自己开发简单还是修改别人的简单 技术决定思维的结果
安装没有按顺序的 想到什么就安装什么了
博文都是原创  复制请谨慎

安装需要的库

首先安装各种库吧 如果不安装也没事的 
linux系统很智能 安装软件的时候会提示并报错的 根据错误安装对应的库即可
yum install -y gcc gcc-c++ autoconf libjpeg libjpeg-devel enchant-devel pam-devel libc-client libc-client-devel libpng libpng-devel freetype freetype-devel libpng libpng-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses curl openssl-devel gdbm-devel db4-devel libXpm-devel libX11-devel gd-devel gmp-devel readline-devel libxslt-devel expat-devel xmlrpc-c xmlrpc-c-devel

安装libmcrypt库

wget ftp://mcrypt.hellug.gr/pub/crypto/mcrypt/libmcrypt/libmcrypt-2.5.7.tar.gz
tar zxf libmcrypt-2.5.7.tar.gz
cd libmcrypt-2.5.7
./configure
make && make install

安装php7

groupadd www
useradd -g www www 创建www用户到www用户组
  
下载php7
tar -zxvf 进行解压
cd 进入php7目录 名字用解压后的和自己重新起都可以的
执行
./configure  --prefix=/usr/local/php7/  --with-config-file-path=/usr/local/php7/etc  --with-config-file-scan-dir=/usr/local/php7/etc/conf.d  --enable-fpm  --with-fpm-user=web  --with-fpm-group=www  --enable-soap  --with-openssl  --with-openssl-dir  --with-mcrypt  --with-pcre-regex  --with-zlib  --with-iconv  --with-bz2  --enable-calendar  --with-curl  --with-cdb  --enable-dom  --enable-exif  --with-pcre-dir  --enable-ftp  --with-gd  --with-jpeg-dir  --with-png-dir  --with-freetype-dir  --with-gettext  --with-gmp  --with-mhash  --enable-mbstring  --with-libmbfl  --with-onig  --enable-pdo  --with-pdo-mysql  --with-zlib-dir  --with-readline  --enable-session  --enable-shmop  --enable-simplexml  --enable-sockets  --enable-sysvmsg  --enable-sysvsem  --enable-sysvshm  --enable-wddx  --with-libxml-dir  --with-xsl  --enable-zip  --enable-mysqlnd  --with-mysqli  --without-pear
make && make install

在源安装文件下有这个文件 移动
cp php.ini-production /usr/local/php7/etc/php.ini

重新命名fpm.conf
cp /usr/local/php7/etc/php-fpm.conf.default /usr/local/php7/etc/php-fpm.conf

fpm扩展配置文件
cp /usr/local/php7/etc/php-fpm.d/www.conf.default www.conf

vim配置php-fpm.conf  include选项
把include路径设置为加载  www.conf的目录  一般不用改动

如果你听不懂我说的什么  那下面的就不用看了 谢谢

vim  www.conf  配置下用户名和用户组

接下来执行
执行php-fpm命令 cd php目录下的sbin目录直接执行即可
ps aux | grep php 即可看到fpm进程

那怎么杀死它呢

pkill php-fpm结束进程 即可

将php编译生成的bin目录添加到当前Linux系统的环境变量中
echo -e '\nexport PATH=/usr/local/php7/bin:/usr/local/php7/sbin:$PATH\n' >> /etc/profile && source /etc/profile

好php 安装完毕

安装完毕后咱们在进行一些fpm的优化
如果你有进程的概念这些参数对你来说很好理解的
就是一个软件他们做的时候  会预先生成一定数量的进程来支持软件的运行的。这个数量要根据系统承载来设置
大了系统扛不住  小了软件运行不好。
所以fpm也很智能  优化的时候分了静态和动态分配进程的方式

看下参数
他们分别是:pm、pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers
pm:表示使用那种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)
下面4个参数的意思分别为:

pm.max_children:静态方式下开启的php-fpm进程数量
pm.start_servers:动态方式下的起始php-fpm进程数量
pm.min_spare_servers:动态方式下的最小php-fpm进程数
pm.max_spare_servers:动态方式下的最大php-fpm进程数量

如果pm设置为 static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。
如果pm设置为 dynamic,那么pm.max_children参数失效,后面3个参数生效。
系统会在php-fpm运行开始 的时候启动pm.start_servers个php-fpm进程,
然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数

总结
上面优化什么意思哪  就是进程分配分为静态和动态  如果 pm=static就是静态的
那么 pm.max_children参数生效  那么这些进程会在fpm启动的时候生成固定数量的进程
而且会常驻内存

如果pm=dynamic就是动态的

pm.start_servers:动态方式下的起始php-fpm进程数量
pm.min_spare_servers:动态方式下的最小php-fpm进程数
pm.max_spare_servers:动态方式下的最大php-fpm进程数量
这三个参数生效 

一定要记住就是如果用动态的  那么pm.max_children进程数量要>= pm.max_spare_servers

不然进程就起不来  

根据自己系统合理设置即可  如果不了解自己google


编译pcntl扩展 为下面运行wokerman做准备 (pcntl为操作进程准备的)

先去ext扩展目录  进入pcntl目录  (ext目录就是你的php源文件目录下的目录。。。。。)
执行
phpize
./configure
make && make install安装
完事后生成编译后的扩展存放目录  记下来
vim php.ini
添加 
extension_dir =  "/usr/local/php7/lib/php/extensions/no-debug-non-zts-20151012/"
extension=pcntl.so

kill掉fpm进程  重启  
php -m查看即可

编译nginx

groupadd nginx
useradd -g nginx nginx 创建nginx用户到nginx用户组

yum -y install pcre*
yum -y install openssl*
wget http://nginx.org/download/nginx-1.7.8.tar.gz
tar -zxvf nginx-1.7.8.tar.gz
cd nginx-1.7.8
./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_spdy_module --with-http_stub_status_module --with-pcre
make
make install
添加这样一条开放80端口的规则后保存
vim /etc/sysconfig/iptables
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
./usr/local/nginx/sbin/nginx 执行nginx命令
vim /usr/local/nginx/conf/nginx.conf 配置nginx
添加用户  user  nginx; 打开
添加include 虚拟主机配置目录 /usr/local/nginx/vhost/default.com.conf
创建mkdir vhost vim default.com.conf 配置
server
  {
   listen       80;
   server_name  localhost;
   index index.php index.html index.htm;
   root  /usr/local/nginx/html;

 location / {

        if (!-e $request_filename){
          rewrite ^(.*)$ /index.php?s=/$1 last;
          rewrite ^(.*)$ /index.php/$1 last;
         }
        }
    location ~ .*\.(php|php5)
    {
           fastcgi_pass  127.0.0.1:9000;
           fastcgi_index index.php;
           include fastcgi.conf;
        }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
      expires      30d;
    }
    location ~ .*\.(js|css)?$
    {
      expires      1h;
    }
autoindex on;
access_log  /usr/local/nginx/logs/default.logs;
}
重启nginx即可
进入/usr/local/nginx/html 创建index.php进行测试即可

这样nginx就把任务交给fpm进程来处理了。
访问即可看到你熟悉的画面
ps aux | grep nginx
以nginx用户身份运行


vue动静分离模式

server {
	listen       8081;
	server_name  localhost;
	#charset koi8-r;
	#access_log  logs/host.access.log  main;
	location / {
		root   F:\.../vue-admin/dist;
		index  index.html;
	}
    location ^~/api/ {
        proxy_pass   http://localhost:9090/api/admin/;
    }
	
	error_page   500 502 503 504  /50x.html;
	location = /50x.html {
		root   html;
	}
}


#   proxy_set_header Host              $host:$server_port;
	proxy_set_header X-Forwarded-For   $remote_addr;
	proxy_set_header X-Forwarded-Proto $scheme;

安装redis

wget http://download.redis.io/redis-stable.tar.gz
tar xvzf redis-stable.tar.gz
cd redis-stable
make
cd src 下 执行 
mkdir /usr/local/redis
cp redis-server /usr/local/redis/
cp redis-benchmark /usr/local/redis/
cp redis-cli /usr/local/redis/
cp redis.conf /usr/local/redis/

配置redis.conf   把daemonize设置为yes(这样redis就可以以守护进程的方式运行了)

启动redis
./redis-server  redis.conf
ps aux | grep redis   查看  进程是否正常启动

安装memcached


下载memcached
解压
cd memcache
执行
./configure --prefix=/usr/local/memcache --with-libevent=/usr/local/libevent/lib/libevent-1.2.so.1
如果没有libevent库 先安装libevent
这个东东是个好东西 可以提高系统的运行性能的  是一个网络库 
如果不安装有的软件会直接用系统的select模型的 比如workerman  workerman也会用它进行高性能的

如果没安装  启动memcache的时候会报错  根据报错完成安装即可
make && make install 

cd  /usr/local/memcache/bin

启动
./memcached -d -m 900 -u root -l 127.0.0.1 -p 11211 -c 256 -P /tmp/memcached.pid

netstat -an | grep 11211 ok

telnet 127.0.0.1 11211
ok

swoole和workerman可以干什么

这个需要有点网络的基础的 这样会更明白些的
socket api  封装了tcp/ip的通讯接口
这些个框架利用这些通讯的接口封装成了框架
网络通讯框架  专注网络传输
可以干嘛?
理论上可以开发你想到的各种应用  包括服务器软件 都可以重写
都支持长链接 开发比如智能设备通讯 游戏后台服务器  聊天 异步 队列
github上有现成的代码  没事可以搜索下
php根据url访问 天生阻塞  想执行异步  各种通讯  那就是最好的选择
还有就是每个安装成功都写了一个小的demo  这些官网都是有的
我们只是安装运行 不会太多的写这些东西

安装swoole扩展

官网下载swoole
cd swoole
phpize
./configure
make && make install
生成扩展目录  记得复制下 如果上面流程走过  就不用记下了。
去php.ini   设置
extension=swoole.so
然后重启fpm 
php -m 

测试:创建文件 webserver.php 异步task投递 可用于耗时操作 不影响worker进程处理请求

<?php
$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$server->set(array('task_worker_num' => 4));

$server->set(array(
    'heartbeat_check_interval' => 5,
    'heartbeat_idle_time' => 15,
        'package_eof' => "\r\n",
    'open_eof_check' => true,
));

$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
});

$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
        echo strlen($frame->data);

    $server->task(json_encode([
                'fd' => $frame->fd,
                'name' => $frame->data,
        ]));
});

$server->on('close', function ($ser, $fd) {
    echo "client {$fd} closed\n";
});

$server->on('task', function ($server, $task_id, $reactor_id, $data) {
        $data = json_decode($data, true);
        sleep(2);
        $server->push($data['fd'], "send server to {$data['name']}\n");
    $server->finish("$data -> OK");
});

$server->on('finish', function ($server, $task_id, $data) {
    echo "AsyncTask[$task_id] finished: {$data}\n";
});

$server->on('WorkerStart', function ($server, $worker_id){
	if($worker_id==0) {
		$server->tick(1000, function ($id) use ($server) {
			foreach($server->connections as $fd){
				$server->push($fd,'ping');
			}
		});
	} else { //所有子进程要执行的
	
	}
});

$server->start();



<?php
//创建websocket服务器
$ws = new swoole_websocket_server(’0.0.0.0′,9504);
/*
open:建立连接
$request:请求信息 get/post
*/
$ws->on(‘open’,function($ws,$request){

var_dump($request);
$ws->push($request->fd,”welcome \n”);
});
//message 接收信息
$ws->on(‘message’,function($ws,$request){

echo “Message:$request->data”;
$ws->push($request->fd,”get it message”);
});
//close 关闭连接
$ws->on(‘close’,function($ws,$request){

echo “close\n”;
});
//
$ws->start();

cli 写直接运行该文件  我已经开启守护进程了 好了现在你打开你的浏览器  控制台直接用websocket对象进程链接把



聊天小demo

?php

class CommentServer
{
    private $_serv;
    public $key = '^manks.top&swoole$';
    // 用户id和fd对应的映射,key => value,key是用户的uid,value是用户的fd
    public $user2fd = [];

    public function __construct()
    {
        $this->_serv = new swoole_websocket_server("127.0.0.1", 9501);
        $this->_serv->set([
            'worker_num' => 1,
            'heartbeat_check_interval' => 60,
            'heartbeat_idle_time' => 125,
        ]);
        $this->_serv->on('open', [$this, 'onOpen']);
        $this->_serv->on('message', [$this, 'onMessage']);
        $this->_serv->on('close', [$this, 'onClose']);
    }

    /**
     * @param $serv
     * @param $request
     * @return mixed
     */
    public function onOpen($serv, $request)
    {
        // 连接授权
        $accessResult = $this->checkAccess($serv, $request);
        if (!$accessResult) {
            return false;
        }
        // 始终把用户最新的fd跟uid映射在一起
        if (array_key_exists($request->get['uid'], $this->user2fd)) {
            $existFd = $this->user2fd[$request->get['uid']];
            $this->close($existFd, 'uid exists.');
            $this->user2fd[$request->get['uid']] = $request->fd;
            return false;
        } else {
            $this->user2fd[$request->get['uid']] = $request->fd;
        }
    }

    /**
     * @param $serv
     * @param $frame
     * @return mixed
     */
    public function onMessage($serv, $frame)
    {
        // 校验数据的有效性,我们认为数据被`json_decode`处理之后是数组并且数组的`event`项非空才是有效数据
        // 非有效数据,关闭该连接
        $data = $frame->data;
        $data = json_decode($data, true);
        if (!$data || !is_array($data) || empty($data['event'])) {
            $this->close($frame->fd, 'data format invalidate.');
            return false;
        }
        // 根据数据的`event`项,判断要做什么,`event`映射到当前类具体的某一个方法,方法不存在则关闭连接
        $method = $data['event'];
        if (!method_exists($this, $method)) {
            $this->close($frame->fd, 'event is not exists.');
            return false;
        }
        $this->$method($frame->fd, $data);
    }
    public function onClose($serv, $fd)
    {
        echo "client {$fd} closed.\n";
    }

    /**
     * 校验客户端连接的合法性,无效的连接不允许连接
     * @param $serv
     * @param $request
     * @return mixed
     */
    public function checkAccess($serv, $request)
    {
        // get不存在或者uid和token有一项不存在,关闭当前连接
        if (!isset($request->get) || !isset($request->get['uid']) || !isset($request->get['token'])) {
            $this->close($request->fd, 'access faild.');
            return false;
        }
        $uid = $request->get['uid'];
        $token = $request->get['token'];
        // 校验token是否正确,无效关闭连接
        if (md5(md5($uid) . $this->key) != $token) {
            $this->close($request->fd, 'token invalidate.');
            return false;
        }
        return true;
    }

    /**
     * @param $fd
     * @param $message
     * 关闭$fd的连接,并删除该用户的映射
     */
    public function close($fd, $message = '')
    {
        // 关闭连接
        $this->_serv->close($fd);
        // 删除映射关系
        if ($uid = array_search($fd, $this->user2fd)) {
            unset($this->user2fd[$uid]);
        }
    }

    public function alertTip($fd, $data)
    {
        // 推送目标用户的uid非真或者该uid尚无保存的映射fd,关闭连接
        if (empty($data['toUid']) || !array_key_exists($data['toUid'], $this->user2fd)) {
            $this->close($fd);
            return false;
        }
        $this->push($this->user2fd[$data['toUid']], ['event' => $data['event'], 'msg' => '收到一条新的回复.']);
    }
    /**
     * @param $fd
     * @param $message
     */
    public function push($fd, $message)
    {
        if (!is_array($message)) {
            $message = [$message];
        }
        $message = json_encode($message);
        // push失败,close
        if ($this->_serv->push($fd, $message) == false) {
            $this->close($fd);
        }
    }

    public function start()
    {
        $this->_serv->start();
    }
}

$server = new CommentServer;
$server->start();


应用案例

安装workerman

curl -Ss http://www.workerman.net/check.php | php 检查安装需要的扩展  没有就按照上面的编译安装即可
把workerman下载复制到任何目录  然后编写入口文件运行即可

workerman的运行不依赖任何的容器
测试:创建文件 webserver.php
<?php
use Workerman\Worker;
require_once './Workerman/Autoloader.php';

// 创建一个Worker监听2346端口,使用websocket协议通讯
$ws_worker = new Worker("websocket://0.0.0.0:2346");

// 启动4个进程对外提供服务
$ws_worker->count = 4;

// 当收到客户端发来的数据后返回hello $data给客户端
$ws_worker->onMessage = function($connection, $data)
{
    // 向客户端发送hello $data
    $connection->send('hello ' . $data);
};

// 运行worker
Worker::runAll();
cli 写直接运行该文件  我已经开启守护进程了 好了现在你打开你的浏览器  控制台直接用websocket对象进程链接把

应用案例

为swoole+workerman websocket压力测试

用的ab工具  没有自己装
ab -n1000000 -c100 -k http://127.0.0.1:9502/
ab -n1000000 -c100 -k http://127.0.0.1:2346/
性能都差不多
不过后期swoole更好一些的表现
可能php还是动态的有消耗吧

安装boost库为安装最新版的mysql做准备

	
 ./bootstrap.sh --prefix=/usr/local/boost  安装目录自己设定
 ./b2 install
 ok
 

安装mysql

官网下载最新版的mysql
解压
进入mysql目录
执行
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/usr/local/mysql/data -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DMYSQL_TCP_PORT=3306 -DMYSQL_USER=mysql -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_MEMORY_STORAGE_ENGINE=1 -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/usr/local/boost
make && make install
cd /usr/local/mysql
chown -R mysql .
chgrp -R mysql .
cd /usr/local/mysql/bin
./mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
密码生成:BmMYtbmau0<7

将默认生成的my.cnf备份
mv /etc/my.cnf /etc/my.cnf.bak

cd /usr/local/mysql/support-files
拷贝配置文件模板为新的mysql配置文件
cp my-default.cnf /etc/my.cnf

配置my.cnf
vim /etc/my.cnf
[mysqld]

character_set_server=utf8
init_connect='SET NAMES utf8'

[client]
default-character-set=utf8

配置mysql服务开机自动启动
拷贝启动文件到/etc/init.d/下并重命令为mysqld
cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql
增加执行权限
chmod 755 /etc/init.d/mysql

检查自启动项列表中没有mysql这个,如果没有就添加mysql
设置开机启动

chkconfig --list mysql
chkconfig --add mysql

设置MySQL自动启动
chkconfig mysql on

service mysql start
service mysql restart
service mysql stop
测试
mysql -uroot -p

设置全局
echo -e '\nexport PATH=/usr/local/mysql/bin:$PATH\n' >> /etc/profile && source /etc/profile

netstat -an | grep 3306 发现运行良好

现在基本的都安装完了
有想安装其他软件的给我留言
没事的时候会补上

安装mongodb扩展

官网下载mongodb
tar xvf 解压 
cd 进入解压目录 bin目录
执行./mongod命令
创建远程用户
use admin
db.createUser(
  {
    user: "live",
    pwd: "123456",
    roles: [ { role: "root", db: "admin" } ]
  }
)

//删除
db.system.users.remove({user:"live"});

创建完成 关闭mongod

然后创建mongod配置文件,位置随便
内容大概如下:
bind_ip = 0.0.0.0
port = 27017
dbpath=/home/mongodb/db
auth=true
重启启动链接

php编译扩展
php绝对路径/phpize  执行
cd mongo扩展目录
phpize
./configure --with-php-config=/usr/local/php/bin/php-config   php绝对路径
make && make install

守护进程模式 ./bin/mongod -f /etc/mongo.db --fork --logpath=/home/mongodb/mongodb.log

php自动加载类

public function loadClass($className)
{
	$classMap = $this->classMap();

	if (isset($classMap[$className])) {
		// 包含内核文件
		$file = $classMap[$className];
	} elseif (strpos($className, '\\') !== false) {
		// 包含应用(application目录)文件
		$file = APP_PATH . str_replace('\\', '/', $className) . '.php';
		if (!is_file($file)) {
			return;
		}
	} else {
		return;
	}

	include $file;

	// 这里可以加入判断,如果名为$className的类、接口或者性状不存在,则在调试模式下抛出错误
}

// 内核文件命名空间映射关系
protected function classMap()
{
	return [
		'fastphp\base\Controller' => CORE_PATH . '/base/Controller.php',
		'fastphp\base\Model' => CORE_PATH . '/base/Model.php',
		'fastphp\base\View' => CORE_PATH . '/base/View.php',
		'fastphp\db\Db' => CORE_PATH . '/db/Db.php',
		'fastphp\db\Sql' => CORE_PATH . '/db/Sql.php',
	];
}


函数式自动加载
function __autoload($name){
	$name = strtolower($name);//转成小写
	if(file_exists("./action/{$name}.class.php")){
		require("./action/{$name}.class.php");
	}elseif(file_exists("./model/{$name}.class.php")){
		require("./model/{$name}.class.php");
	}elseif(file_exists("./ORG/{$name}.class.php")){
		require("./ORG/{$name}.class.php");
	}elseif(file_exists("./libs/".ucfirst($name).".class.php")){
		require("./libs/".ucfirst($name).".class.php");
	}elseif(file_exists("./libs/sysplugins/{$name}.php")){
		require("./libs/sysplugins/{$name}.php");
	}else{
		die("@@%%$$###没有定义{$name}类!!!");
	}
}
	

个人随笔

安静的等待,你若盛开,蝴蝶自来

承接高质量 网站开发 app开发 如有需要请点击About联系

@承鹏辉