木叶三忍的博客

web开发

Menu Close

Category: PHP (page 1 of 4)

使用php+nodejs+socket.io+redis搭建简单聊天室

两年前研究了一下socket.io和nodejs构建聊天室,并实现了简单的聊天室,由于太懒,一直没有更新博文,前几天鼓起勇气又从头到尾搭建了一次,并已将代码放到github。

源码:https://github.com/konohanaruto/ChatRoom

Laravel中关于阿里云短信接口的使用

由于个人项目中需要用到短信验证码,所以购买了阿里云的短信服务,之前是阿里大于,不过到现在已经不再是之前的接口了,下面介绍laravel中怎么使用它。
* 方式一:使用composer安装sdk
* 方式二:手动引入官方sdk

这里只介绍方式二,也是我目前使用的方案,不过使用composer去安装前辈们已经做好的包确实方便,但有时候也不得不说有一种那个什么。因为不是所有的三方接口都能那么有幸有人做成包共享的,虽然说可以自己做成依赖包或者自己搭建一个本地git仓库,存放公司内部共享类包,然后composer去管理依赖。嗯,好像扯远了,回到正题。

下载SDK

https://help.aliyun.com/document_detail/55451.html 它的结构如下:

这里我只引入api_sdk,也就是发短信用的,目录结构介绍,请自行查看文档! 进入到api_sdk,发现它的结构:

它的composer文件内容如下:

{
    "name": "aliyuncs/aliyun-dysms-php-sdk",
    "description": "Aliyun SMS SDK for PHP",
    "keywords": [ "aliyun", "SMS", "sdk" ],
    "type": "library",
    "license": "Apache-2.0",
    "homepage": "https://www.aliyun.com/product/sms",
    "authors": [
        {
            "name": "Aliyuncs",
            "homepage": "https://www.aliyun.com"
        }
    ],
    "require": {
        "php": ">=5.5.0"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.0"
    },
    "minimum-stability": "stable",
    "autoload": {
        "psr-4": {
            "Aliyun\\": "lib/",
            "Aliyun\\Test\\": "tests/"
        }
    }
}

我并不需要那个tests目录,所以核心是lib目录,结构如下:

在app目录下,新建一个Libraries目录 当然,你放在哪里都可以,名字也可以随意,例如: app/Konohanaruto/Libraries 然后,我在这个目录下创建了一个Aliyun的目录: app/Konohanaruto/Libraries/Aliyun 接着,复制api_sdk/lib目录下的Api和Core目录,到我们的app/Konohanaruto/Libraries/Aliyun目录:

再然后,编辑我们laravel项目的composer.json文件,在classmap中,我们加入我们包的路径:

//省略...
"autoload": {
        "classmap": [
            "database",
            "app/Konohanaruto/Libraries"
        ],
        "psr-4": {
            "App\\": "app/",
            "Tests\\": "tests/"
        }
    },
//省略...

最后,运行如下命令: composer dumpautoload 我们在项目中就可以如同官方demo文件中一般使用,我这样就轻松使用了它:

总结 可能会遇到各种可能出错的情况,我在调用中,出现了一个错误,如下:

InvalidTimeStamp.Expired Specified time stamp or date value is expired. HTTP Status: 400 RequestID: 7DA7D916-3419-47E9-BFDE-C2D04480D517

按照官方的FAQ发现是我开发机的时区问题,而且时间也不对,后来把开发机的时间调准确就没问题了。

Memcached管理脚本

创建并设置脚本权限:

[root@distribute01 ~]# touch /etc/init.d/memached && chmod +x /etc/init.d/memcached

编辑/etc/init.d/memcached,并添加如下内容:

#! /bin/sh
#
# chkconfig: - 55 45
# description:  The memcached daemon is a network memory cache service.
# processname: memcached
# config: /etc/sysconfig/memcached
# pidfile: /var/run/memcached/memcached.pid

# Standard LSB functions
#. /lib/lsb/init-functions

# Source function library.
. /etc/init.d/functions

PORT=11211
USER=nobody
MAXCONN=1024
CACHESIZE=64
LISTEN="$(ifconfig | grep -A 1 'eth0' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1)"
OPTIONS="-l $LISTEN"
MEMCACHED_BIN="/usr/local/memcached-1.5.1/bin/memcached"

if [ -f /etc/sysconfig/memcached ];then 
        . /etc/sysconfig/memcached
fi

# Check that networking is up.
. /etc/sysconfig/network

if [ "$NETWORKING" = "no" ]
then
        exit 0
fi

RETVAL=0
prog="memcached"

start () {
        echo -n $"Starting $prog: "
        # insure that /var/run/memcached has proper permissions
    if [ "`stat -c %U /var/run/memcached`" != "$USER" ]; then
        chown $USER /var/run/memcached
    fi

        daemon $MEMCACHED_BIN -d -p $PORT -u $USER  -m $CACHESIZE -c $MAXCONN -P /var/run/memcached/memcached.pid $OPTIONS
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && touch /var/lock/subsys/memcached
}
stop () {
        echo -n $"Stopping $prog: "
        killproc $MEMCACHED_BIN -QUIT
        RETVAL=$?
        echo
        if [ $RETVAL -eq 0 ] ; then
            rm -f /var/lock/subsys/memcached
            rm -f /var/run/memcached.pid
        fi
}

restart () {
        stop
        start
}


# See how we were called.
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status memcached
        ;;
  restart|reload|force-reload)
        restart
        ;;
  condrestart)
        [ -f /var/lock/subsys/memcached ] && restart || :
        ;;
  *)
        echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
        exit 1
esac

exit $?

Redis启动脚本

创建/etc/init.d/redis-server,内容如下:

#!/bin/sh
#
# redis - this script starts and stops the redis-server daemon
#
# chkconfig:   - 85 15 
# description:  Redis is a persistent key-value database
# processname: redis-server
# config:      /etc/redis/redis.conf
# config:      /etc/sysconfig/redis
# pidfile:     /var/run/redis.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0

redis="/usr/local/redis-4.0.1/src/redis-server"
prog=$(basename $redis)

REDIS_CONF_FILE="/etc/redis/redis.conf"

[ -f /etc/sysconfig/redis ] && . /etc/sysconfig/redis

lockfile=/var/lock/subsys/redis

start() {
    [ -x $redis ] || exit 5
    [ -f $REDIS_CONF_FILE ] || exit 6
    echo -n $"Starting $prog: "
    daemon $redis $REDIS_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}

stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}

restart() {
    stop
    start
}

reload() {
    echo -n $"Reloading $prog: "
    killproc $redis -HUP
    RETVAL=$?
    echo
}

force_reload() {
    restart
}

rh_status() {
    status $prog
}

rh_status_q() {
    rh_status >/dev/null 2>&1
}

case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart|configtest)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
        exit 2
esac

上方脚本内容中,redis程序文件和配置文件根据个人需要修改即可。

修改权限

[root@distribute01 ~]# chmod +x /etc/init.d/redis-server

redis配置文件

假如我们创建了配置文件在/etc/redis/redis.conf,如果需要后台启动或者绑定ip和端口,如下:

bind 127.0.0.1
port 6379
# redis默认是no,这里我们改为yes,让它后台运行
daemonize yes

Mongo-Connector启动脚本

创建一个/etc/init.d/mongo-connector.start文件,内容如下:

# path to mongo-connector
mc="/usr/bin/mongo-connector"
# options to pass to mongo-connector
mongo_host="$(ifconfig | grep -A 1 'eth0' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1)"
mongo_port=27017
elastic_host="$(ifconfig | grep -A 1 'eth0' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1)"
elastic_port=9200
log_file=/var/log/mongodb/mongo-connector.log
mongo_dbname=laravel_project_db
mongo_collection=logs
mc_options="-m $mongo_host:$mongo_port -t http://$elastic_host:$elastic_port -d elastic2_doc_manager -n $mongo_dbname.$mongo_collection"
# log path
mc_log="/var/log/mongodb/mongo-connector.log"

pidfile=/var/run/mongo-connector.pid

nohup $mc $mc_options > $mc_log 2>&1 &
echo $! > $pidfile

创建一个/etc/init.d/mongo-connector文件,内容如下:

#!/bin/bash
#
# mongo-connector               Start Mongo Connector
#
# chkconfig: 345 90 25
# description: Mongo Connector replicates data from MongoDB to external
#              database systems.

### BEGIN INIT INFO
# Provides: mongo-connector
# Default-Start: 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start up Mongo Connector
# Description: Mongo Connector replicates data from MongoDB to external
#              database systems.
### END INIT INFO

# path to wrapper script
wrapper=/etc/init.d/mongo-connector.start

# source function library
. /etc/rc.d/init.d/functions

RETVAL=0
lockfile=/var/run/mongo-connector.lock
pidfile=/var/run/mongo-connector.pid

start()
{
    [ -x $mc ] || exit 5

    action "starting mongo-connector: " daemon --pidfile $pidfile $wrapper
    RETVAL=$?
    if [ $RETVAL -eq 0 ]; then
        echo "done."
    else
        echo "failed. Please check exit code and logs for more information"
    fi
    return $RETVAL
}

stop()
{
    action "stopping mongo-connector: " killproc -p $pidfile
    RETVAL=$?
    if [ $RETVAL -eq 0 ]; then
        echo "done."
        rm -f $lockfile
    else
        echo "failed. Please check exit code and logs for more information"
    fi
    return $RETVAL
}

restart() {
    $0 stop
    $0 start
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        status -p $pidfile mongo-connector
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart|status}"
        RETVAL=2
esac
exit $RETVAL

设置权限:

[root@distribute01 ~]# chmod +x /etc/init.d/mongo-connector.start
[root@distribute01 ~]# chmod +x /etc/init.d/mongo-connector

添加到系统服务:

[root@distribute01 ~]# chkconfig --add /etc/init.d/mongo-connector

测试:

[root@distribute01 ~]# service mongo-connector start

使用runuser命令以指定用户身份后台运行Elasticsearch

由于elasticsearch不能用root用户启动,否则会报错,所以在当前登录用户是root的情况下,我们可以不切换用户的情况下让它去以指定用户身份去运行elasticsearch,这里我以elasticsearch作为运行的用户,命令如下:

[root@distribute01 ~]# runuser -l elasticsearch -c 'elasticsearch -d'

关于elasticsearch启动的常见错误

问题一:

max file descriptors [12000] for elasticsearch process is too low, increase to at least [65536]

永久生效方案:

登录root用户,编辑/etc/security/limits.conf,添加如下内容:

* soft nofile 8192

* hard nofile 131072

* soft nproc 2048

* hard nproc 4096

*代表的是对所有用户生效

临时方案:

登录root用户,运行:

[root@distribute01 ~]# ulimit -n 8192

问题二:

max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

解决方案:

登录root用户,运行:

[root@distribute01 ~]# echo 262144 > /proc/sys/vm/max_map_count

两种错误相应配置修改后,可以退出再重新登录,另外,elasticsearch如果想后台启动,只需要加上-d参数即可!

MongoDB启动脚本

每次命令行启动mongodb很不方便,这里我google了一个启动脚本,适当的修改了一番,用起来很方便,这里分享给大家!只需要把配置修改成你实际需要的,即可使用了!

创建/etc/init.d/mongod文件,添加如下:

#!/bin/sh
#
# mongod - Startup script for mongod
#
# chkconfig: - 85 15
# description: Mongodb database.
# processname: mongod
# Source function library
  
. /etc/rc.d/init.d/functions
# things from mongod.conf get there by mongod reading it

# OPTIONS
# 你的可执行的mongod程序文件
mongod="/usr/local/mongodb-3.2.16/bin/mongod"
# 锁文件,默认它会存放在数据目录下
lockfile="/data/db/mongodb/mongod.lock"
# 当前的系统ip地址,由于我开发都是装的linux虚拟机,断电会自动变换ip,很烦,所以才这样自动获取了
current_ip="$(ifconfig | grep -A 1 'eth0' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1)"
# 监听的端口
port=27017
# 数据目录
data_path="/data/db/mongodb"
# 日志存放目录
log_path="/var/log/mongodb/mongod.log"
# 如果是32位系统,需要指定--storageEngine=mmapv1选项,64位的可以忽略这一项,如果需要配置config,自己加一项即可,我这里没加
# --logappend表示日志会追加写入文件,不用担心日志丢失问题
OPTIONS="--storageEngine=mmapv1 --nojournal --bind_ip=$current_ip --port=$port --dbpath=$data_path --logpath=$log_path --logappend &"

start()
{
 echo -n $"Starting mongod: "
 daemon $mongod $OPTIONS
 RETVAL=$?
 echo
 [ $RETVAL -eq 0 ] && touch $lockfile
}
  
stop()
{
 echo -n $"Stopping mongod: "
 killproc $mongod -QUIT
 RETVAL=$?
 echo
 [ $RETVAL -eq 0 ] && rm -f $lockfile
}
  
restart () {
    stop
    start
}
ulimit -n 12000
RETVAL=0
  
case "$1" in
 start)
  start
  ;;
 stop)
  stop
  ;;
 restart|reload|force-reload)
  restart
  ;;
 condrestart)
  [ -f $lockfile ] && restart || :
  ;;
 status)
  status $mongod
  RETVAL=$?
  ;;
 *)
  echo "Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
  RETVAL=1
esac
exit $RETVAL

添加执行权限

[root@distribute01 ~]# chmod +x /etc/init.d/mongod

开机自启动

[root@distribute01 ~]# chkconfig --add mongod
[root@distribute01 ~]# chkconfig mongod on

测试

Laravel5.4使用MongoDB作为默认的日志存储方式

本次环境介绍:

  • Laravel5.4
  • Mongodb3.2.16

上次说过我想实现项目日志监控和搜索系统,通过MongoDB+Elasticsearch做数据同步来实现,上篇文章已经在架构和配置层面实现了,这里介绍Laravel项目中如何将Log从默认的文件存储改为Mongodb存储。

安装jenssegers/laravel-mongodb

[root@lnmp laravel-project]# composer require jenssegers/mongodb -vvv

Problem 1
    - jenssegers/mongodb v3.2.0 requires mongodb/mongodb ^1.0.0 -> satisfiable by mongodb/mongodb[1.0.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.1.0, 1.1.1, 1.1.2].
    - jenssegers/mongodb v3.2.1 requires mongodb/mongodb ^1.0.0 -> satisfiable by mongodb/mongodb[1.0.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.1.0, 1.1.1, 1.1.2].
    - jenssegers/mongodb v3.2.2 requires mongodb/mongodb ^1.0.0 -> satisfiable by mongodb/mongodb[1.0.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.1.0, 1.1.1, 1.1.2].
    - mongodb/mongodb 1.1.2 requires ext-mongodb ^1.2.0 -> the requested PHP extension mongodb is missing from your system.
    - mongodb/mongodb 1.1.1 requires ext-mongodb ^1.2.0 -> the requested PHP extension mongodb is missing from your system.
    - mongodb/mongodb 1.1.0 requires ext-mongodb ^1.2.0 -> the requested PHP extension mongodb is missing from your system.
    - mongodb/mongodb 1.0.5 requires ext-mongodb ^1.1.0 -> the requested PHP extension mongodb is missing from your system.
    - mongodb/mongodb 1.0.4 requires ext-mongodb ^1.1.0 -> the requested PHP extension mongodb is missing from your system.
    - mongodb/mongodb 1.0.3 requires ext-mongodb ^1.1.0 -> the requested PHP extension mongodb is missing from your system.
    - mongodb/mongodb 1.0.2 requires ext-mongodb ^1.1.0 -> the requested PHP extension mongodb is missing from your system.
    - mongodb/mongodb 1.0.1 requires ext-mongodb ^1.1.0 -> the requested PHP extension mongodb is missing from your system.
    - mongodb/mongodb 1.0.0 requires ext-mongodb ^1.1.0 -> the requested PHP extension mongodb is missing from your system.
    - Installation request for jenssegers/mongodb ^3.2 -> satisfiable by jenssegers/mongodb[v3.2.0, v3.2.1, v3.2.2].

  To enable extensions, verify that they are enabled in your .ini files:
    - /etc/php.ini
  You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.

提示我们需要Mongodb扩展,而且提供了可选的版本,我这里就挑选安装mongodb1.1.2吧,我们从pecl下载即可,注意,我现在安装的是Mongodb扩展,而不是mongo扩展!链接:https://pecl.php.net/package/mongodb

安装Mongodb扩展

[root@lnmp laravel-project]# cd /var/soft
[root@lnmp laravel-project]# wget https://pecl.php.net/get/mongodb-1.1.2.tgz
[root@lnmp soft]# tar xf mongodb-1.1.2.tgz 
[root@lnmp soft]# cd mongodb-1.1.2
[root@lnmp mongodb-1.1.2]# phpize
Configuring for:
PHP Api Version:         20131106
Zend Module Api No:      20131226
Zend Extension Api No:   220131226
[root@lnmp mongodb-1.1.2]# ./configure --with-php-config=/usr/local/php/bin/php-config
[root@lnmp mongodb-1.1.2]# make && make install
// 省略...
Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/
[root@lnmp mongodb-1.1.2]# ll /usr/local/php/lib/php/extensions/no-debug-non-zts-20131226
-rwxr-xr-x 1 root root 2495692 Sep 14 20:28 mongodb.so

接下来编辑/etc/php.ini,添加如下一行:

[root@lnmp mongodb-1.1.2]# extension=mongodb.so

接着重启php-fpm,运行:

[root@lnmp mongodb-1.1.2]# service php-fpm restart

现在,重新切换到你的laravel项目目录,再次运行安装命令:

[root@lnmp laravel-project]# composer require jenssegers/mongodb -vvv

OK!现在安装一切正常,我们成功的安装了jenssegers/mongodb包!

编辑Laravel项目的config/app.php,在键名为”providers”的数组中添加:

Jenssegers\Mongodb\MongodbServiceProvider::class,

创建测试路由

1. 创建一个logging的路由,指到HomeController下的logging方法,作为我们的测试路由。当然,如果HomeController和logging方法如果不存在,你需要自己新建这样的文件,打开routes/web.php,加入如下行:

Route::get('logging', 'HomeController@logging');

2. HomeController内容如下:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller
{
    public function logging()
    {
        echo 'logging测试!';
    }
}

3. 浏览器访问测试,是否能正常看到我们的输出内容!

MongoDB配置加入.env

编辑.env文件,加入如下内容:

# Mongodb连接
MONGO_DB_HOST=192.168.2.116
MONGO_DB_PORT=27017
MONGO_DB_DATABASE=laravel_project_db
MONGO_DB_USERNAME=
MONGO_DB_PASSWORD=

此时默认db操作还是Mysql,我们只是新增了一个mongodb的配置而已!不过要注意,如果单独在一个机器上部署的mongodb,我就是这样的,所以你在启动mongodb时要特别注意,要使用能访问的ip,而不是默认的监听127.0.0.1,否则无法支持其它机器连接!

MongDB配置加入config/database.php

编辑config/database.php,在connections数组中,添加一个新的mongodb driver:

'mongodb' => [
    'driver'   => 'mongodb',
    // 我的mongodb在另一台linux虚拟机上,所以我填的并不是localhost
    'host'     => env('MONGO_DB_HOST', '192.168.2.116'),
    'port'     => env('MONGO_DB_PORT', 27017),
    'database' => env('MONGO_DB_DATABASE', 'laravel_project_db'),
    //'username' => env('DB_USERNAME'),
    //'password' => env('DB_PASSWORD'),
    'options'  => [
        'database' => 'laravel_project_db'
    ]
],

测试laravel-mongodb是否正常

回到我们的HomeController中,在logging方法中测试,HomeController内容如下:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class HomeController extends Controller
{
    public function logging()
    {
        $status = DB::connection('mongodb');
        var_dump($status);
    }
}

浏览器输出了如下内容:

object(Jenssegers\Mongodb\Connection)#220 (18) {
  ["db":protected]=>
  object(MongoDB\Database)#228 (6) {
    ["databaseName"]=>
    string(18) "laravel_project_db"
    ["manager"]=>
    object(MongoDB\Driver\Manager)#227 (3) {
      ["request_id"]=>
      int(1804289383)
      ["uri"]=>
      string(48) "mongodb://192.168.2.116:27017/laravel_project_db"
      ["cluster"]=>
      array(1) {
        [0]=>
        array(11) {
          ["host"]=>
          string(13) "192.168.2.116"
          ["port"]=>
          int(27017)
          // 省略...

到此,说明我们包的安装,配置已经准备完毕,现在laravel-mongodb能正常工作!接下来我们将会进入正题!

Laravel修改默认的Log存储方式为MongoDB

方式一:

让我们来看看文档的一段描述:

Custom Monolog Configuration

If you would like to have complete control over how Monolog is configured for your application, you may use the application's configureMonologUsing method. You should place a call to this method in your bootstrap/app.php file right before the $app variable is returned by the file:

$app->configureMonologUsing(function ($monolog) {
    $monolog->pushHandler(...);
});

return $app;

它让我们在bootstrap/app.php的return $app之前,加入那个代码块,好的,我根据手册以及借助google,发现最终代码块如下:

// 前面的省略...

// 我指定了数据库和collection
// laravel_project_db:Mongodb的数据库名称
// logs:数据库下的collection名称
$app->configureMonologUsing(function ($monolog) {
    $mongoHandler = new \Monolog\Handler\MongoDBHandler(
        new MongoClient('mongodb://192.168.2.116:27017'), 'laravel_project_db', 'logs'
    );
    
    $monolog->pushHandler($mongoHandler);
});

return $app;

可能会问,我们在.env已经配置了mongodb,这里为什么再写一次,可以用env之类的函数直接取值吗,很遗憾,在bootstrap/app.php这个文件中,无法使用helper之类的函数!

方式二(推荐)

编辑app/providers/AppServiceProvider.php文件,如下:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Log;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        $monolog = Log::getMonolog();
        $mongoHost = env('MONGO_DB_HOST');
        $mongoPort = env('MONGO_DB_PORT');
        $mongoDsn = 'mongodb://' . $mongoHost . ':' . $mongoPort;
        $mongoHandler = new \Monolog\Handler\MongoDBHandler(new \MongoClient($mongoDsn), 'laravel_project_db', 'logs');
        $monolog->pushHandler($mongoHandler);
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

你会发现,方式二由于是ServiceProvider中,所以可以访问到配置项,这样更加灵活!如果你不想直接修改AppServiceProvider文件,你可以新建一个自定义的ServiceProvider文件,然后在config/app.php中的providers数组中,引入你的自定义Provider即可!

打开vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php这个文件,它的内容大致如下:

namespace Monolog\Handler;

use Monolog\Logger;
use Monolog\Formatter\NormalizerFormatter;

/**
 * Logs to a MongoDB database.
 *
 * usage example:
 *
 *   $log = new Logger('application');
 *   $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod");
 *   $log->pushHandler($mongodb);
 *
 * @author Thomas Tourlourat <thomas@tourlourat.com>
 */
class MongoDBHandler extends AbstractProcessingHandler
{
    protected $mongoCollection;

    public function __construct($mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true)
    {
        if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo || $mongo instanceof \MongoDB\Client)) {
            throw new \InvalidArgumentException('MongoClient, Mongo or MongoDB\Client instance required');
        }

        $this->mongoCollection = $mongo->selectCollection($database, $collection);

        parent::__construct($level, $bubble);
    }
    // 省略...
}

它的构造方法允许三个类型的mongo类扩展,否则会报错!我有想过在这个文件最上方去引入一些帮助类,使它能够使用自带的env之类的函数,但是这样就感觉破坏了这个文件一般,可能我有点强迫症,好吧!这里就先这样写了,我们怎么去让PHP有MongoClient这个类呢,答案是:装扩展!

为PHP安装Mongo扩展

看看官方对该类的描述,建议我们使用MongoDB\Driver\Manage:

The MongoClient class

This extension that defines this class is deprecated. Instead, the MongoDB extension should be used. Alternatives to this class include: MongoDB\Driver\Manager

然而jenssegers的包并不支持它!所以我们只能选择安装不被推荐的MongoClient了!

下载mongo1.6.0

[root@lnmp mongodb-1.1.2]# cd /var/soft
[root@lnmp soft]# wget https://pecl.php.net/get/mongo-1.6.0.tgz
[root@lnmp soft]# tar xf mongo-1.6.0.tgz 
[root@lnmp soft]# cd mongo-1.6.0
[root@lnmp mongo-1.6.0]# phpize
Configuring for:
PHP Api Version:         20131106
Zend Module Api No:      20131226
Zend Extension Api No:   220131226
[root@lnmp mongo-1.6.0]# ./configure --with-php-config=/usr/local/php/bin/php-config 
[root@lnmp mongo-1.6.0]# make && make install

编辑/etc/php.ini,加入一行:

extension=mongo.so

然后,重启php-fpm:

[root@lnmp mongo-1.6.0]# service php-fpm restart

最后查看phpinfo:

好的,我们的Mongo客户端扩展已经安装并加载完成!

回到我们的Laravel框架

我们增加一个新的路由:

Route::get('storeLog', 'HomeController@storeLog');

添加新的storeLog方法并修改之前的logging方法,HomeController如下:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Log;

class HomeController extends Controller
{
    public function logging()
    {
        // 我取了所有logs这个collection(相当于Mysql的表)中的所有日志
        $logs = DB::connection('mongodb')->collection('logs')->get()->toArray();
        var_dump($logs);
    }
    
    public function storeLog()
    {
        // 使用默认的Log facade去保存日志,现在它会自动写入到Mongodb
        Log::info(md5(uniqid()));
    }
}

测试写入和查询:

先访问storeLog,然后在访问logging,如果我们之前没有安装MongoClient,在访问logging时,网页会无法响应的,这里要注意你上面的Mongo扩展成功安装了!最终我访问了storeLog两次,然后访问logging结果如下:

array(2) {
  [0]=>
  array(8) {
    ["_id"]=>
    object(MongoDB\BSON\ObjectID)#243 (1) {
      ["oid"]=>
      string(24) "59bc7076dc2a9a640f8b4567"
    }
    ["message"]=>
    string(32) "51f34708318c93485010ad65f8d84db5"
    ["context"]=>
    array(0) {
    }
    ["level"]=>
    int(200)
    ["level_name"]=>
    string(4) "INFO"
    ["channel"]=>
    string(5) "local"
    ["datetime"]=>
    string(19) "2017-09-16 00:29:42"
    ["extra"]=>
    array(0) {
    }
  }
  [1]=>
  array(8) {
    ["_id"]=>
    object(MongoDB\BSON\ObjectID)#241 (1) {
      ["oid"]=>
      string(24) "59bc7085dc2a9a5e0f8b4567"
    }
    ["message"]=>
    string(32) "988d2b9bba91012b622ae652a1030a77"
    ["context"]=>
    array(0) {
    }
    ["level"]=>
    int(200)
    ["level_name"]=>
    string(4) "INFO"
    ["channel"]=>
    string(5) "local"
    ["datetime"]=>
    string(19) "2017-09-16 00:29:57"
    ["extra"]=>
    array(0) {
    }
  }
}

mongo-connector实现MongoDB和Elasticsearch数据同步

环境要求

  • CentOS(内存至少512M)
  • curl
  • Java和JRE
  • Python
  • pip
  • MongoDB
  • Elasticsearch
  • mongo-connector
  • elastic2-doc-manager

上方大部分环境在我之前的博文中已经介绍完毕,可自行查看我之前的文章,这里只介绍剩下的三个环境配置:

  • python
  • python-pip
  • mongo-connector
  • elastic2-doc-manager

查看当前python状态

[root@lnmp soft]# yum info python
Loaded plugins: security
Installed Packages
Name        : python
Arch        : x86_64
Version     : 2.6.6
Release     : 52.el6
Size        : 78 k
Repo        : installed
# 省略...

可见我当前版本是python2.6.6,并且是已经被安装了。

查看当前python-pip状态:

[root@lnmp soft]# yum info python-pip
Loaded plugins: security
Installed Packages
Name        : python-pip
Arch        : noarch
Version     : 7.1.0
Release     : 1.el6
Size        : 6.6 M
Repo        : installed
# 省略...

同样python-pip在我的环境中也是被安装的,这里就不多说了。

使用pip安装mongo-connector

[root@lnmp soft]# pip install mongo-connector

我使用了aliyun的yum镜像,如果你没有配置额外的yum源,出现找不到可用包,可以像我一样,增加一个阿里云的yum源,查看:添加阿里云yum源方式

elastic2-doc-manager的安装

因为我的elasticsearch是5.0的版本,所以配合的我需要安装elastic2-doc-manager[elastic5]版本,具体版本对应关系,查看这里https://github.com/mongodb-labs/elastic2-doc-manager

安装elastic2-doc-manager[elastic5],我们可以使用pip安装:

[root@lnmp soft]# pip install elastic2-doc-manager[elastic5]

配置MongoDB

日志存储目录

[root@lnmp soft]# mkdir -pv /var/log/mongodb

数据目录

[root@lnmp soft]# mkdir -pv /data/db/mongodb

数据保存目录,mongodb默认是/data/db,请确保该目录存在,并且有相应权限。

创建mongodb配置文件

这里创建了/etc/mongodb.conf文件,放在哪里可以随意,因为我们将会将路径当作启动mongodb的选项而已,将如下内容写入到你的mongodb.conf文件中:

storage:
  dbPath: /data/db/mongodb
  journal:
    enabled: true
 
# 日志存储位置
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
 
# 网络和端口
net:
  port: 27017
  bindIp: 127.0.0.1

# 复制配置
replication:
  replSetName: rs0
  oplogSizeMB: 100

启动mongodb

[root@lnmp soft]# mongod --config /etc/mongodb.conf 
[root@lnmp soft]# ps aux | grep mongod
root      2133  0.0  0.0   4420   772 pts/0    S+   12:48   0:00 grep mongod

发现并未成功启动,我们看看日志,打开/var/log/mongodb/mongod.log,信息如下:

old lock file: /data/db/mongodb/mongod.lock.  probably means unclean shutdown,
but there are no journal files to recover.
this is likely human error or filesystem corruption.
please make sure that your journal directory is mounted.

说明是我上次未正常关闭mongodb造成的,因为我习惯用kill -9去杀掉进程了,解决方案目前我是直接清空数据目录,也就是/data/db/mongodb,后面我会介绍mongodb的脚本启动方式和正常关闭方式,这里只是介绍数据同步,就不去测试了,我们这里先清空数据目录,执行:

[root@lnmp soft]# rm -rf /data/db/mongodb/*

再次执行:

[root@lnmp soft]# mongod --config /etc/mongodb.conf

这次启动正常,不过这个窗口也就不能编辑和关闭了,如果想继续使用窗口,可以后台运行,命令如下:

# 执行后敲两下回车,可看到标准输入又开始工作
[root@lnmp soft]# nohup mongod --config /etc/mongodb.conf &

测试你是否正常运行了mongod:

[root@lnmp soft]# ps aux | grep mongod
root      2136  2.9  5.2 442308 54088 pts/0    Sl+  12:54   0:00 mongod --storageEngine=mmapv1 --config /etc/mongodb.conf
500       2157  0.0  0.0   4420   768 pts/1    S+   12:55   0:00 grep mongod

我们也可以执行:

[root@lnmp soft]# netstat -tnlp | grep mongod
tcp        0      0 127.0.0.1:27017             0.0.0.0:*                   LISTEN      2136/mongod

以上两种都可以说明启动成功。

启动mongo客户端

运行:

[root@lnmp soft]# mongo

我们输入初始化命令:

rs.initiate()

噢!报错如下:

{
	"info2" : "no configuration specified. Using a default configuration for the set",
	"me" : "lnmp:27017",
	"ok" : 0,
	"errmsg" : "No host described in new configuration 1 for replica set rs0 maps to this node",
	"code" : 93
}

我们缺少一点东西而已,我们添加本机到members就行,在终端粘贴如下内容:

config = {
    _id : "rs0",
     members : [
         {_id : 0, host : "127.0.0.1:27017"}
     ]
}

然后,再次运行:

rs.initiate(config)

运行成功,如下:

> rs.initiate(config)
{ "ok" : 1 }

此时,命令提示符成为如下:

rs0:OTHER>

然后,运行:

rs0:OTHER> rs.status
function () {
    return db._adminCommand("replSetGetStatus");
}

发现,这次命令提示符如下:

rs0:PRIMARY>

因为只有一个成员,所以它被识别成为了主库,也就是Primary!

我们来往mongodb里面插入一些测试数据,数据库名和collection我就随意了,但是后面同步会再次用到,如果不想出错,就和我保持一样吧,嗯~

切换到数据库

use connectortest

创建collection

db.createCollection("syncthis");

循环插入一些数据,复制粘贴如下内容:

for (var i = 1; i <= 10; i++) {
   db.syncthis.insert( { value : i } )
}

查看我们插入的测试数据:

rs0:PRIMARY> db.syncthis.find()
{ "_id" : ObjectId("59b6c4ff796518572652aec7"), "value" : 1 }
{ "_id" : ObjectId("59b6c4ff796518572652aec8"), "value" : 2 }
{ "_id" : ObjectId("59b6c4ff796518572652aec9"), "value" : 3 }
{ "_id" : ObjectId("59b6c4ff796518572652aeca"), "value" : 4 }
{ "_id" : ObjectId("59b6c4ff796518572652aecb"), "value" : 5 }
{ "_id" : ObjectId("59b6c4ff796518572652aecc"), "value" : 6 }
{ "_id" : ObjectId("59b6c4ff796518572652aecd"), "value" : 7 }
{ "_id" : ObjectId("59b6c4ff796518572652aece"), "value" : 8 }
{ "_id" : ObjectId("59b6c4ff796518572652aecf"), "value" : 9 }
{ "_id" : ObjectId("59b6c4ff796518572652aed0"), "value" : 10 }

此mongo客户端窗口别关,我们再登录开启另一个窗口,用于接下来的步骤!

启动elasticsearch

新开的窗口请使用elasticsearch用户登录,上篇文章我专门强调过,这里再不作叙述!

启动mongo-connector

[root@lnmp soft]# mongo-connector -m localhost:27017 -t http://localhost:9200 -o /var/log/mongodb/mongo-connector.log -d elastic2_doc_manager -n connectortest.syncthis

-m:指定mongodb的host和port

-t:目标程序的host和port也就是我们此次实验的elasticsearch

-o:输出日志,这里我放到/var/log/mongodb下了,之前我们创建过这个目录了

-d:指定目标文档类型,这里我们使用的是elastic2_doc_manager

-n:被复制的数据库和collection名称

出现如下信息,并且窗口不可输入,即证明启动成功:

Logging to /root/mongo-connector.log.

OK!请再次打开登录一个新的窗口!

测试elasticsearch数据

[root@lnmp soft]# curl http://localhost:9200/connectortest/_search/?pretty=1

{
  "took" : 207,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 10,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aec7",
        "_score" : 1.0,
        "_source" : {
          "value" : 1.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aeca",
        "_score" : 1.0,
        "_source" : {
          "value" : 4.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aecb",
        "_score" : 1.0,
        "_source" : {
          "value" : 5.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aec9",
        "_score" : 1.0,
        "_source" : {
          "value" : 3.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aed0",
        "_score" : 1.0,
        "_source" : {
          "value" : 10.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aec8",
        "_score" : 1.0,
        "_source" : {
          "value" : 2.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aecd",
        "_score" : 1.0,
        "_source" : {
          "value" : 7.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aece",
        "_score" : 1.0,
        "_source" : {
          "value" : 8.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aecc",
        "_score" : 1.0,
        "_source" : {
          "value" : 6.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aecf",
        "_score" : 1.0,
        "_source" : {
          "value" : 9.0
        }
      }
    ]
  }
}

可以发现,这就是我们插入的数据,不错,我们回到mongo客户端窗口,再插入一条内容:

rs0:PRIMARY> db.syncthis.insert( { value : 66666 } )
WriteResult({ "nInserted" : 1 })

好的,我们再回到最后开启的窗口,运行:

[root@lnmp soft]# curl localhost:9200/connectortest/_count/?pretty=1
{
  "count" : 11,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  }
}

数据成了11条了,好的,再次运行之前的命令:

[root@lnmp soft]# curl http://localhost:9200/connectortest/_search/?pretty=1

{
  "took" : 25,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 11,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aec7",
        "_score" : 1.0,
        "_source" : {
          "value" : 1.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aeca",
        "_score" : 1.0,
        "_source" : {
          "value" : 4.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aecb",
        "_score" : 1.0,
        "_source" : {
          "value" : 5.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aec9",
        "_score" : 1.0,
        "_source" : {
          "value" : 3.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aed0",
        "_score" : 1.0,
        "_source" : {
          "value" : 10.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aec8",
        "_score" : 1.0,
        "_source" : {
          "value" : 2.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aecd",
        "_score" : 1.0,
        "_source" : {
          "value" : 7.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aece",
        "_score" : 1.0,
        "_source" : {
          "value" : 8.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6ca3e796518572652aed1",
        "_score" : 1.0,
        "_source" : {
          "value" : 66666.0
        }
      },
      {
        "_index" : "connectortest",
        "_type" : "syncthis",
        "_id" : "59b6c4ff796518572652aecc",
        "_score" : 1.0,
        "_source" : {
          "value" : 6.0
        }
      }
    ]
  }
}

可以发现,我们最后插入的66666已经自动被同步了,到这里就结束了,有了同步环境,下次我们将介绍laravel中将日志写入mongodb,并且由于数据会自动同步到elasticsearch,我们可以做日志和bug的搜索和分析,听起来很不错,right? 我将会在不久后实现。

© 2019 木叶三忍的博客. All rights reserved.

Theme by Anders Norén.