解决mac系统(10.11.5+ and macOS Sierra)读取 samba 共享速度慢的问题

困扰了几个星期的问题终于找到原因了,之前一直误以为是NSA的 smb 共享服务器速度慢的问题,万万没想到,原来是Mac捣的鬼。我读取 samba 共享的速度始终只有10-12MB/s之间,还特地用有线试了还是这个速度,后来无意间找到 https://dpron.com/os-x-10-11-5-slow-smb/ 这篇文章才发现李鬼,在 10.11.5+ 以及最新的 10.12 的 Sierra 都有这个问题。按文章里说的改过后,瞬间从10MB/s 跑到了 60MB/s 的速度(我那个 cubieTruck 的服务器的极限了)。

简单的说就是,如果 mac 是客户端,那么给 /etc/nsmb.conf 文件(默认不存在)加入以下内容:

[default]
signing_required=no

一行命令执行:

printf "[default]\nsigning_required=no\n" | sudo tee /etc/nsmb.conf >/dev/null

如果是mac作为服务器,则执行:

sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server SigningRequired -bool FALSE

sudo /usr/libexec/smb-sync-preferences

笔记:新MacOS Sierra 系统使用brew安装php、mysql(mariadb)、nginx等开发环境,也适合小白用户

安装brew

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装完毕后,把源改成中科大的

cd "$(brew --repo)"
git remote set-url origin https://mirrors.ustc.edu.cn/brew.git

cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git

cd "$(brew --repo)"/Library/Taps/caskroom/homebrew-cask
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-cask.git

# 2选1
# for bash user
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.bash_profile
source ~/.bash_profile
# for zsh user
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.zshrc
source ~/.zshrc

安装 MySQL(MariaDB)

brew install mariadb

# 设置下,否则mysql_的命令不会出现
# ls /usr/local/Cellar/mariadb/ 看下是不是10.2.8,如果是别的版本记得更换下下面的路径
echo 'export PATH="$PATH:/usr/local/Cellar/mariadb/10.2.8/bin"' >> ~/.bash_profile

修改完必要的 /usr/local/etc/my.cnf 配置后(其实本地测试没什么好修改的,我就用的默认的)执行 mysql_install_db 命令初始化数据库(如果 /usr/local/var/mysql/ 目录已经有了,可以忽略这个)

启动服务器:

brew services start mariadb

下次开机会自动启动,看所有服务列表:

brew services list

如果要修改MySQL管理员密码,可以使用

mysql_secure_installation

命令(先把服务器启动起来)

安装PHP

先执行下面

brew install homebrew/php/php-install

这个命令,然后

brew search php

你会发现就有好多不同版本的 php 了,比如你要装 php7.1 版本的话,可以

brew search php71

,然后选择你需要的扩展模块安装就可以了,比如:

brew install homebrew/php/php71 homebrew/php/php71-redis homebrew/php/php71-swoole homebrew/php/php71-yaml

你可能没找到 mysql,php-fpm 等这些扩展,这个是 homebrew/php/php71 里就包含了的。

启动 php-fpm

在启动 php-fpm 前建议修改下配置

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

将 group 改成 staff,也可以将 user 改成自己的账户名。这样做可能会存在一点点安全风险,但是如果你用到php需要写一些文件的话,就会方便很多,否则连自己操作这个文件都没有权限,略麻烦。

启动的话非常简单,此时使用命令

brew services list

会看到 php71 是 stopped 的,执行

brew services start php71

就可以了。

安装nginx

执行执行

brew install nginx

就可以快速的安装好了,brew 安装好的 nginx 默认值监听 8080 端口的,这显然太麻烦了,果断改到80端口,但是由于 Mac 系统的限制,低于1000的端口不能由用户启动,所以需要再配置下。

执行

sudo vim /Library/LaunchDaemons/homebrew.mxcl.nginx.plist

输出密码,后把下面的内容写进去

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>homebrew.mxcl.nginx</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/opt/nginx/bin/nginx</string>
        <string>-g</string>
        <string>daemon off;</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/usr/local</string>
  </dict>
</plist>

保存,然后执行下面代码加载 nginx

sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.nginx.plist

这样重启nginx

sudo launchctl stop homebrew.mxcl.nginx
sudo launchctl start homebrew.mxcl.nginx

看看有没有启动成功:

ps -ef | grep nginx

,如果有进程就表示启动成功了。

小建议,brew 安装的 nginx 默认是8个线程,个人觉得测试有点浪费了,所以可以自己设置下,

vim /usr/local/etc/nginx/nginx.conf

将 worker_processes 改成你要的值(我的是4)

安装phpMyAdmin

本人比较懒,直接使用

brew install homebrew/php/phpmyadmin

安装了个 phpMyAdmin,然后将下列配置文件写入

/usr/local/etc/nginx/servers/phpmyadmin.conf

重启 nginx 即可

upstream php-handler {
    server 127.0.0.1:9000;
}
server {
    listen 80;
    server_name 127.0.0.1;
    root /usr/local/share/phpmyadmin/;
    charset utf-8;

    gzip off;
    proxy_buffering off;
    index index.html index.php;

    location ~ .php$ {
        fastcgi_pass php-handler;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        #fastcgi_buffering off;
        fastcgi_buffers 10 256k;
        fastcgi_buffer_size 512k;
        fastcgi_keep_conn on;
        #fastcgi_request_buffering off;
        client_max_body_size 128m;
        fastcgi_temp_file_write_size 512k;
        include fastcgi_params;
    }
}

另外,如果要用 phpMyAdmin 执行超长的任务需要注意,它默认是最大300秒的执行时限的,请修改

/usr/local/etc/phpmyadmin.config.inc.php

文件,在最后加入如下内容即可:

$cfg['ExecTimeLimit'] = 0;  # 或你认为的最大值

另外,关于php.ini的一些设置,

vim /usr/local/etc/php/7.1/php.ini

,可以根据自己的需要调节,比如:

max_execution_time 30  # 最大执行时间,可以根据自己的需要修改
memory_limit = 128M    # 内存限制
post_max_size = 8M     # 将会影响上传文件大小
date.timezone          # 默认时区,可以改成 PRC(中华人民共和国缩写),即 date.timezone = PRC

改好后,使用

brew services restart php71

重启 php-fpm 即可。

至此,php,nginx,mysql都已经安装好了。如果你是用PhpStorm的开发者,推荐我调的代码风格和配色,预览和下载地址:https://www.queyang.com/blog/archives/638

see

奉上自己的 PhpStorm 的黑色风格的配色和代码格式

二话不说先看配色,自己调的,整体风格是 Sublime 的御用配色样子,附上下载地址(内有说明) PhoStorm代码风格和配色

使用 xdebug 在 phpStorm 进行 php 本地和远程 swoole 调试设置

其实类似 xdebug 在 phpStorm 的调试文件一搜一大把,这里简单的总结下,主要是适合使用 swoole 开发的人。

本地调试的设置方法

第1步

在 Run 菜单中点击 Edit Configurations,然后点击最左侧 + 按钮,选择 PHP Script。

第2步

Configuration 的 File 参数就选择 swoole 的启动文件,Arguments 输入你这个启动文件的参数,没有的话就留空,名字自己设定

完成

然后点击保存,如果 phpStorm 提示找不到php命令的话你设置下路径就好了。

这个时候在窗口的右上角就会有一个可以调试的按钮,这个时候在要断点的位置打上打断,启动调试就可以了。


远程调试设置

当脚本在服务器上运行时,就需要用到远程调试了(也适用于本机调试)远程调试设置方法如下:

第1步

打开设置面板,找到 Languages & Frameworks -> PHP -> Servers。点击 + 号添加,Host 填需要调试的服务器的ip,端口填 xdebug 的 remote_port (ini中默认为 9000,这个值远程服务器启动命令上是可以设置的)

如果服务器上代码路径和本地路径不一致的话,需要勾选 “Use path mappings”,然后主项目路径后的 Absolute path on the server 填一下对应服务器的路径,设置好后保存。

第2步

设置时候继续设置,同本地是设置,在 Run 菜单中点击 Edit Configurations 点击添加按钮,选择 php remote debug。servers 就选择刚刚添加的那个。网上文档都有说 ide key 要填个 PHPSTORM 什么的,其实可以不用填,因为 swoole 起的服务器并不是传统的 web 服务器。

第3步

设置好了后,记得在swoole的启动命令上加上参数:

-dxdebug.remote_autostart=1 -dxdebug.remote_host=192.168.1.2 -dxdebug.remote_port=9100 -dxdebug.remote_enable=1

, 例如:

php -dxdebug.remote_autostart=1 -dxdebug.remote_host=192.168.1.2 -dxdebug.remote_port=9100 -dxdebug.remote_enable=1 server.php -vvv

其中 remote_host 是服务器 ip,remote_port 是 xdebug 的调试端口,千万别暴露给外网。当然,这些参数是可以在 php.ini 中直接设置的,如果设置好了,可以不带这些参数直接启动的。


参阅

一步到位,开启https + http2

今天头条上看到自己网站可以免费获取证书,于是尝试了下,最终一步到位全部成功。

系统为:centos7 + nginx

第一步,获取证书 ps: 免费的哦,而且不像自签名的那种证书浏览器会不认
很简单,先安装certbot命令:

yum install certbot

然后使用此命令生成证书:

certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com

这边说明下, /var/www/example 是你现在网站可以访问的根目录,在生成时它会创建一个 .well-known 文件夹去验证你是否有这个网站的权限,然后 -d 后面是证书匹配的域名,可以多个,example.com 请换成你的域名。
然后是一个引导窗口你就填个email然后确认基本上等待验证就能成功了(千万别在screen里运行,否则像我一开始一样全部是蓝屏看不到字)

生成证书后,证书在 /etc/letsencrypt/live/example.com/ 目录

server {
    listen      443 ssl http2;
    ssl         on;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_dhparam         /etc/nginx/ssl/dhparam.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4';
}

以上是nginx的https相关的基本配置(其它参数不写了,自行加上,以上参数说明请见参阅内容第3个,ssl_protocols 主要用的安全协议,ssl_ciphers 是禁用的不安全的协议,这里不赘述了)

这里需要自己生成个 dhparam.pem 文件,执行如下命令即可:

mkdir /etc/nginx/ssl/
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048

这是加强 HTTPS 安全性的,如果嫌麻烦,把 ssl_dhparam 参数删掉就可以了。

参阅:

  1. https://certbot.eff.org/
  2. https://ksmx.me/letsencrypt-ssl-https/
  3. https://aotu.io/notes/2016/08/16/nginx-https/

https 只要有证书启动起来是没什么问题的,如果对这些流程不是很熟悉可能需要找不少文章才能搞明白。
http2 则是需要 nginx 1.9.5+ 和 openssl 1.0.2+ 才支持,我一开始用的是系统自带的 nginx,版本才1.6,后来升级了 openssl 到最新版在自己装了个nginx repo 安装是新版本了,可是死活还是启动不了 http2,然后我这样猥琐的解决了:

nginx -V

先得到所有的 configure arguments: 参数。然后下载 openssl 和 nginx 对应版本的源码包,然后增加一个参数“–with-openssl=/root/openssl-1.0.2j” 这个路径就是对应 openssl 源码的目录,然后在nginx里重新编译安装,然后就可以了

注意,letsencrypt 的证书的有效期是3个月,届时务必要更新下,否则会导致网站出现安全提醒,更新证书非常简单,只要相同的命令重新运行下就可以,然后重启下 nginx

笔记:canvas 支持retina显示解决模糊的问题

之所以模糊是在浏览器上生成的图片是1x的非矢量图,所以在retina屏幕上会出现模糊的问题(手机版的浏览器无此问题),以下是我整理的一段代码。

原理很简单,先算出 ratio 值(2,3等)然后根据 ratio 值对 canvas 的 width 和 height 调整,并且一定要对

canvas.getContext("2d")

获取的对象进行 scale(ratio, ratio) 相同的倍数,否则图片会被拉小。

function reviseRatio(canvas, width, height)
{
    var context = canvas.getContext("2d");
    var backingStoreRatio = context.webkitBackingStorePixelRatio ||
        context.mozBackingStorePixelRatio ||
        context.msBackingStorePixelRatio ||
        context.oBackingStorePixelRatio ||
        context.backingStorePixelRatio || 1;

    var devicePixelRatio = window.devicePixelRatio || 1;

    // canvas的实际渲染倍率
    var ratio = devicePixelRatio / backingStoreRatio;

    if (ratio > 1)
    {
         canvas.width = width * ratio;
         canvas.height = height * ratio;
         context.scale(ratio, ratio);
     }
    else
    {
        canvas.width = width;
        canvas.height = height;
    }
    canvas.style.width = width;
    canvas.style.height = height;
}

直接调用方法

reviseRatio(canvas, 100, 80);

即可修正参数。

其中, canvas 是需要修正的canvas的dom对象,100是显示的宽度,80是高度(会渲染出一个 200 * 160 的canvas的图片)

笔记:css 实现玻璃磨砂效果

.test{
    -webkit-filter: blur(3px);
    -moz-filter: blur(3px);
    -ms-filter: blur(3px);
    -o-filter: blur(3px);
    filter: blur(3px);
}

很简单,只要给个

filter: blur(3px);

就可以了,其中里面的数值是玻璃模糊程度

开源了一个好用又简单的大数据日志数据实时统计服务功能 Easy Total

因为我们公司游戏的日志越来越多,普通的数据光插入数据就会让数据库挂掉,后来我们用 Hadoop 集群来处理日志,但是问题是需要越来越多的服务器,为了解决这个问题,用了半年时间开发了一个简单好用的实时统计服务器:EasyTotal。此服务还在测试,感兴趣的同学可以去点赞或贡献代码,项目地址 https://github.com/xindong/easy-total。在我们线上测试环境(监听了20多条统计的SQL语句)单机每分钟处理300万+的日志量cpu负载在 3 – 5之间,可持续处理1000万/分钟的日志量,峰值可达1300万/分钟,这样的性能堪称无敌,因为这样的数据光插入 10 台机器组成的 Elasticsearch 集群用不了多久集群就要挂了。

下面是项目介绍:

EasyTotal 是一个通过监听预先添加好的SQL统计查询语句,对汇入的数据进行实时统计处理后不间断的将统计结果导出的服务解决方案,它解决了日志数据量非常巨大的情况下,数据库无法承载巨大的插入和查询请求的问题,并且可以满足业务统计的需求。程序的网络层是采用c写的swoole扩展进行处理,具有极高的性能,网络处理能力和 nginx 相当,处理数据模块采用 php 开发则可以方便团队根据自己的需求进行二次开发。

支持常用的运算统计功能,比如 count, sum, max, min, avg, first, last, dist,支持 group by、where 等,后续将会增加 join 的功能。

特点:

  • 实时处理,定时汇出统计结果;
  • 对巨大的日志量进行清洗汇总成1条或多条输出,可以成万倍的缩小数据体量,可在汇总结果中进行二次统计;
  • 特别适用于对大量log的汇入统计,免去了先入库再统计这种传统方式对系统造成的负担;
  • 分布式水平扩展,支持随时增删统计服务器;
  • 不需要特别的技术、使用简单;
  • 可以二次开发;

使用场景

当需要对业务数据进行统计分析时,传统的做法是把数据或日志导入到数据库后进行统计,但是随着数据量的增长,数据库压力越来越大甚至插入数据都成问题更不用说是进行数据统计了,此时只能对数据进行分库、分片等处理或者是用 hadoop、spark 等离线统计,然后就需要分布式架构,这在技术角度上来说是可行的做法,但这带来的问题就是:

  • 需要很多服务器,增加巨额托管费用;
  • 需要能够hold住这些服务器和技术的高级开发及运维人员;
  • 架构调整导致的研发难度和周期增加;

EasyTotal 正是为了解决这些问题而诞生,可以用极少的服务器以及简单的技术就能处理巨大的数据并满足业务统计的需求,对你来说一切都是那么的easy。

上两张管理界面:

admin01

admin02

[笔记] centos 里使用 lvm 挂载多个硬盘

首先安装命令:

yum install -y lvm2

使用

parted -l

查看当前硬盘的信息,出来的结果类似:

警告: 无法以读写方式打开 /dev/sr0 (只读文件系统)。/dev/sr0 已按照只读方式打开。
错误: /dev/sr0: unrecognised disk label
Model: QEMU QEMU DVD-ROM (scsi)
Disk /dev/sr0: 432kB
Sector size (logical/physical): 2048B/2048B
Partition Table: unknown
Disk Flags:

Model: Virtio Block Device (virtblk)
Disk /dev/vda: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  标志
 1      1049kB  21.5GB  21.5GB  primary  xfs          启动


错误: /dev/vdb: unrecognised disk label
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 537GB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:

错误: /dev/vdc: unrecognised disk label
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 537GB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:

错误: /dev/vdd: unrecognised disk label
Model: Virtio Block Device (virtblk)
Disk /dev/vdd: 537GB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags:

使用

pvcreate /dev/vdb /dev/vdc /dev/vdd

创建,注意,这里面的”/dev/vdb”, “/dev/vdc”, “/dev/vdd” 就是前面看到信息的内容。

输出如下表示成功:

  Physical volume "/dev/vdb" successfully created
  Physical volume "/dev/vdc" successfully created
  Physical volume "/dev/vdd" successfully created

执行

pvscan

输出:

  PV /dev/vdd         lvm2 [500.00 GiB]
  PV /dev/vdb         lvm2 [500.00 GiB]
  PV /dev/vdc         lvm2 [500.00 GiB]
  Total: 3 [1.46 TiB] / in use: 0 [0   ] / in no VG: 3 [1.46 TiB]

执行

vgcreate sata /dev/vdb /dev/vdc /dev/vdd

输出:

Volume group "sata" successfully created

PS:vgcreate 后的 sata 是一个名称,可以自己起,后面的3个参数是之前对应的盘符路径,按实际情况写。

执行

vgdisplay

看下结果:

  --- Volume group ---
  VG Name               sata
  System ID
  Format                lvm2
  Metadata Areas        3
  Metadata Sequence No  1
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                3
  Act PV                3
  VG Size               1.46 TiB
  PE Size               4.00 MiB
  Total PE              383997
  Alloc PE / Size       0 / 0
  Free  PE / Size       383997 / 1.46 TiB
  VG UUID               C0PTEh-erdY-7gh3-fTAb-mwsE-3fMk-J8Tg5e

然后执行

lvcreate -l 383997 -n data sata

创建一个整个大盘,如果不要一次性全部创建,则可以用“L”参数。
其中 data 表示一个名称, sata 是之前创建的 VG Name,会输出:

  Logical volume "data" created.

表示创建成功。

执行

lvscan

输出如下:

  ACTIVE            '/dev/sata/data' [1.46 TiB] inherit

执行

mkfs.xfs /dev/sata/data

创建 XFS 格式的盘到 /dev/sata/data 上,输出如下:

meta-data=/dev/sata/data         isize=256    agcount=4, agsize=98303232 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=0        finobt=0
data     =                       bsize=4096   blocks=393212928, imaxpct=5
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=0
log      =internal log           bsize=4096   blocks=191998, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

执行

blkid /dev/sata/data

查看新盘的UUID,复制下来下面要用,比如我的是:

/dev/sata/data: UUID="e54fa05f-dc2f-460b-a573-e2817adf9f7b" TYPE="xfs"

编辑文件

vim /etc/fstab

, 加入内容:

UUID=e54fa05f-dc2f-460b-a573-e2817adf9f7b /data                   xfs     noatime,nobarrier 0 0

其中

/data

表示你要挂载的目录,加上 noatime 可以提高盘的性能(如果根目录的没有noatime,也可以加上,加上后记得执行

mount -o remount /

生效),完整内容如下:

UUID=6001f930-2d2b-4e57-b55b-3fde13756c7d /                       xfs     defaults,noatime  0 0
UUID=e54fa05f-dc2f-460b-a573-e2817adf9f7b /data                   xfs     noatime,nobarrier 0 0

注意,UUID应该对应自己的盘符名称。

执行

mkdir /data
mount -a

这样就把盘挂到了 /data 目录了,可

df -lh

看下:

文件系统               容量  已用  可用 已用% 挂载点
/dev/vda1               20G  2.4G   18G   12% /
devtmpfs               3.9G     0  3.9G    0% /dev
tmpfs                  3.9G     0  3.9G    0% /dev/shm
tmpfs                  3.9G  8.4M  3.9G    1% /run
tmpfs                  3.9G     0  3.9G    0% /sys/fs/cgroup
tmpfs                  783M     0  783M    0% /run/user/0
/dev/mapper/sata-data  1.5T   33M  1.5T    1% /data

这样就把3块500G的硬盘挂载到1个1.5T的 /data 目录了

利用 Grafana influxDB fluentd pidstat 对指定进程进行监控

有图有真相,先来看几张成果图:

QQ20160407-1@2x

QQ20160407-0@2x

我先介绍下这些东西都是做什么的

  • Grafana 是一个非常漂亮的可以自定义的监控web服务,如上图
  • InfluxDB 是一个高效的时间数据库,擅长用于记录按时间推进的数据,这里的数据都存在它里面
  • Fluentd 是一个数据(日志)接受和分发的服务,可以指定任意输入源后经过适当的处理后再分发到其它的接收端,这里收集数据都是通过它进入到 InfluxDB 的
  • pidstat 直接安装 sysstat 就有了,可以查看进程相关数据
【安装】

这里不赘述,只简单的说下(其实都很简单)

 

确定以上程序都安装好后,接下来就是进行配置了,Grafana 和 InfluxDB 实际上也没什么要配置的,装好后直接启动吧,启动命令

systemctl start influxd
systemctl start grafana-server

开启开机自动启动:

systemctl enable influxd.service
systemctl enable grafana-server.service
【InfluxDB 设置】

启动后,就可以通过 http://serverip:8083/ 访问在线的InfluxDB管理功能了。
在Query栏里输入:

CREATE USER "grafana" WITH PASSWORD 'grafana'
CREATE DATABASE grafana WITH DURATION 2d REPLICATION 5

第一行是创建一个 grafana 的帐号和密码
第二行是创建一个 grafana 库,并且默认保留数据2d是2天(m表示月)的意思,可以自行设置,REPLICATION 5 是分5个片,如果想永久保留把 DURATION 2d 去掉就可以了,这些后期都可以修改

【Grafana 设置】

http://serverip:3000/ 是 Grafana 的管理界面。

进入 Grafana 页面(默认帐号是 admin/admin)点击 Data Sources 连接,在上面点击 Add new 连接,type选择 InfluxDB0.9.x(虽然我装的是0.12但是只有这个,选这个可以用)URL 填 http://serverip:8086/ 这个 8086 端口就是 influxdb 的默认http接口端口。注意,Access 要选择 proxy。
下面的 InfluxDB Details 全部填 grafana 就好了。

【Fluentd 设置】

InfluxDB 的源添加好了,现在开始收集数据吧。收集数据我用到了 fluentd 进行分发。默认装好后就有

td-agent

命令了,此时,需要安装2个插件:

td-agent-gem install fluent-plugin-influxdb
td-agent-gem install fluent-plugin-rewrite-tag-filter

安装成功后,将下面我修改的influx插件文件放在 /etc/td-agent/plugin/out_influx.rb 文件中(是用官方的改的,解决了些细节问题)

# encoding: UTF-8
require 'date'
require 'influxdb'

class Fluent::InfluxOutput < Fluent::BufferedOutput
  Fluent::Plugin.register_output('influx', self)

  include Fluent::HandleTagNameMixin

  config_param :host, :string,  :default => 'localhost',
               :desc => "The IP or domain of influxDB."
  config_param :port, :integer,  :default => 8086,
               :desc => "The HTTP port of influxDB."
  config_param :dbname, :string,  :default => 'fluentd',
               :desc => <<-DESC
The database name of influxDB.
You should create the database and grant permissions at first.
DESC
  config_param :user, :string,  :default => 'root',
               :desc => "The DB user of influxDB, should be created manually."
  config_param :password, :string,  :default => 'root', :secret => true,
               :desc => "The password of the user."
  config_param :time_precision, :string, :default => 's',
               :desc => <<-DESC
The time precision of timestamp.
You should specify either hour (h), minutes (m), second (s),
millisecond (ms), microsecond (u), or nanosecond (n).
DESC
  config_param :use_ssl, :bool, :default => false,
               :desc => "Use SSL when connecting to influxDB."
  config_param :tag_keys, :array, :default => [],
               :desc => "The names of the keys to use as influxDB tags."
  config_param :sequence_tag, :string, :default => nil,
               :desc => <<-DESC
The name of the tag whose value is incremented for the consecutive simultaneous
events and reset to zero for a new event with the different timestamp.
DESC
  config_param :uniqu, :bool, :default => true


  def initialize
    super
    @seq = 0
  end

  def configure(conf)
    super
    @uniqu = conf['uniqu']
    @influxdb = InfluxDB::Client.new @dbname, host: @host,
                                              port: @port,
                                              username: @user,
                                              password: @password,
                                              async: false,
                                              time_precision: "n",
                                              # time_precision: @time_precision,
                                              use_ssl: @use_ssl
  end

  def start
    super
  end

  FORMATTED_RESULT_FOR_INVALID_RECORD = ''.freeze

  def format(tag, time, record)
    # TODO: Use tag based chunk separation for more reliability
    if record.empty? || record.has_value?(nil)
      FORMATTED_RESULT_FOR_INVALID_RECORD
    else
      [tag, time, record].to_msgpack
    end
  end

  def shutdown
    super
  end

  def write(chunk)
    points = []

    # 自动调回0
    if @seq > 1000000
      @seq = 0
    end

    chunk.msgpack_each do |tag, time, record|
      timestamp = record.delete('time') || time

      # 使用参数里的毫秒
      if record["microtime"] && record["microtime"] > timestamp
        timestamp = record["microtime"]
      end

      if @uniqu
        timestamp = (timestamp * 1000000000).to_i
      else
        @seq += 1
        # 这里强制 time_precision 参数设定成 n, 所以乘以1000000000
        timestamp = (timestamp * 1000000000).to_i + @seq
      end

      if tag_keys.empty?
        values = record
        tags = {}
      else
        values = {}
        tags = {}
        record.each_pair do |k, v|
          if @tag_keys.include?(k)
            tags[k] = v
          else
            values[k] = v
          end
        end
      end

      point = {
        :timestamp => timestamp,
        :series    => tag,
        :values    => values,
        :tags      => tags,
      }
      points << point
    end

    @influxdb.write_points(points)
  end
end

插件保存后,添加一个 td-agent(fluentd)的配置,将以下文件放在

/etc/td-agent/monitor-collect.conf

文件中

# CPU
<source>
  type tcp
  port 5170
  tag collect.log
  source_host_key client
  types uid:integer,pid:integer,usr:float,system:float,guest:float,cpu:float,cpu_num:integer
  format /^(?<time>[^ ]+ [P|A]M)[ ]+(?<uid>\d+)?[ ]*(?<pid>\d+)[ ]+(?<usr>[^ ]+)[ ]+(?<system>[^ ]+)[ ]+(?<guest>[^ ]*)[ ]+(?<cpu>[^ ]*)[ ]+(?<cpu_num>[\d]+)[ ]+(?<command>.*)$/
</source>

# 内存
<source>
  type tcp
  port 5171
  tag collect.log
  source_host_key client
  types uid:integer,pid:integer,minflt:float,majflt:float,vsz:integer,rss:integer,mem:float
  format /^(?<time>[^ ]+ [P|A]M)[ ]+(?<uid>\d+)?[ ]*(?<pid>\d+)[ ]+(?<minflt>[^ ]+)[ ]+(?<majflt>[^ ]+)[ ]+(?<vsz>[^ ]+)[ ]+(?<rss>[^ ]+)[ ]+(?<mem>[^ ]+)[ ]+(?<command>.*)$/
</source>

# IO
<source>
  type tcp
  port 5172
  tag collect.log
  source_host_key client
  types uid:integer,pid:integer,kprd:float,kbwr:float,kbccwr:float
  format /^(?<time>[^ ]+ [P|A]M)[ ]+(?<uid>\d+)?[ ]*(?<pid>\d+)[ ]+(?<kprd>[^ ]+)[ ]+(?<kbwr>[^ ]+)[ ]+(?<kbccwr>[^ ]+)[ ]+(?<command>.*)$/
</source>


<match collect.log>
  type rewrite_tag_filter
  rewriterule1 command ^influxd$ monitor.influxd
  rewriterule2 command ^java$ monitor.elasticsearch
  rewriterule3 command ^td-agent$ monitor.td-agent
  rewriterule4 command ^td-agent-(.*)$ monitor.td-agent-$1
  rewriterule5 command ^([^.]+)$ monitor.$1
</match>


<match monitor.*>
  type influx
  host 10.1.37.3
  port 8086
  dbname grafana
  user influxdb
  password influxdb
  remove_tag_prefix monitor.
  flush_interval 1
  tag_keys ["pid", "parent_pid", "command", "client"]
  uniqu false

  retry_wait 3
  buffer_type memory
  buffer_queue_limit 10
  buffer_chunk_limit 1m
  retry_limit 5
</match>

然后使用

td-agent -c /etc/td-agent/monitor-collect.conf

启动即可,如果需要放后台启动可以加 -d 参数,如果需要记录log,则可以加参数 -o logpath.log 完整的如下:

td-agent -d -c /etc/td-agent/monitor-collect.conf -o /var/log/td-agent/monitor-collsole.log

它监听了3个tcp端口,分别用来收集cpu,内存,io的。

tail -f /var/log/td-agent/monitor-collect.log

看看是否成功启动了

【收集指定程序的数据】

成功启动后,接下来就是去需要收集进程的其它服务器上操作了,这边需要用到我写的另外一个bash文件,文件保存在

/usr/local/bin/monitor-collect.sh

文件里(如果是别的文件名,则请修改下代码里的 “monitor-collect.sh” 为同名文件),另外,注意修改第4行的IP为 Fluentd 运行的服务器IP

#!/bin/bash
# 监控收集脚本

# 请修改成自已实际机器的IP
IP=127.0.0.1

MYNAME=$1

if [ "$MYNAME" = "" ]; then
  echo "缺少1个参数,请加关键字"
  echo "参数为进程匹配grep -E 关键字。使用方法如下: "
  echo "    monitor-collect.sh influxd"
  echo "    monitor-collect.sh td-agent mysql  表示匹配 td-agent mysql"
  echo "    monitor-collect.sh -p 123,456      表示只读123,456这2个进程"
  exit
fi

if [ "$1" = "-p" ]; then
  if [ "$2" = "" ]; then
    echo "-p 参数后必须指定进程ID,多个用,隔开"
    exit
  fi
fi

PID1=0
PID2=0
PID3=0
HAVE_PID1=""
HAVE_PID2=""
HAVE_PID3=""
RUN=true

function TaskClean()
{
  echo "Now do task clean..."
  RUN=false
}

function TaskExit()
{
  if [ $PID1 > 0 ]; then
    echo "Now exit pidstat1: $PID1"
    kill $PID1
    echo "done."
  fi
  if [ $PID2 > 0 ]; then
    echo "Now exit pidstat2: $PID2"
    kill $PID2
    echo "done."
  fi
  if [ $PID3 > 0 ]; then
    echo "Now exit pidstat3: $PID3"
    kill $PID3
    echo "done."
  fi
}

trap 'TaskClean; exit' SIGINT
trap 'TaskExit; exit' EXIT

echo "collect script pid: $$";

LANG=en_US.UTF-8
while $RUN; do
  if [ "$1" = "-p" ]; then
    PIDS=$2
  else
    PIDS=$(ps -eo pid,ppid,user,pcpu,command | grep -v '/bin/sh -c' | grep -v 'monitor-collect.sh' | grep -E "$MYNAME" | grep -v 'grep' | gawk '{print $1}')
    PIDS=$(echo $PIDS | sed -e s'/ /,/g')
  fi

  if [ "$PIDS" != "" ]; then
    echo "found pids: $PIDS"

    # CPU
    if [ "$HAVE_PID1" = "" ]; then
      pidstat -p $PIDS 1 > /dev/tcp/$IP/5170 &
      PID1=$!
      echo "pidstat pid: $PID1"
    fi

    # 内存
    if [ "$HAVE_PID2" = "" ]; then
      pidstat -r -p $PIDS 1 > /dev/tcp/$IP/5171 &
      PID2=$!
      echo "pidstat pid: $PID2"
    fi

    # IO
    if [ "$HAVE_PID3" = "" ]; then
      pidstat -d -p $PIDS 1 > /dev/tcp/$IP/5172 &
      PID3=$!
      echo "pidstat pid: $PID3"
    fi

    # 检查进程是否退出
    while true; do
      HAVE_PID1=$(ps $PID1 | awk '{ print $1 }' | grep $PID1)
      HAVE_PID2=$(ps $PID2 | awk '{ print $1 }' | grep $PID2)
      HAVE_PID3=$(ps $PID3 | awk '{ print $1 }' | grep $PID3)

      if [ "$HAVE_PID1" = "" ]; then
        echo "pidstat1 is exit. now restart"
        break
      fi

      if [ "$HAVE_PID2" = "" ]; then
        echo "pidstat2 is exit. now restart"
        break
      fi

      if [ "$HAVE_PID3" = "" ]; then
        echo "pidstat3 is exit. now restart"
        break
      fi
      sleep 3
    done
  fi
  sleep 1
done

保存好后,记得增加可执行权限:

chmod +x /usr/local/bin/monitor-collect.sh

这个脚本的用法很简单,比如你现在需要收集 mysql 的数据,只要运行如下代码即可:

monitor-collect.sh mysql

如果需要放在后台运行,则这样运行

nohup monitor-collect.sh mysql &

如果要同时监控 mysql,php-fpm,mongodb 的,则可以这样

nohup monitor-collect.sh "mysql|php-fpm|mongod" &

也可以监听指定进程id:

nohup monitor-collect.sh -p 123,456 &

表示监听123 和 456 两个进程。

需要停掉收集程序,直接

ps -ef | grep monitor-collect.sh

得到进程id后直接kill即可。

【确保数据是否都推送到了 InfluxDB 里了】

可以在网页版里,也可以在 influx 终端里执行

use grafana
show measurements

类似出现下面的内容则表示收集来了对应的进程数据

name: measurements
------------------
name
elasticsearch
influxd
mongod

select * from mongod where time > now() - 10s

查看最新10秒的数据,如果有的话则推送过来了,如果没有,请查看 fluntd 的log。

【Grafana 添加监控项目】

Grafana这个工具对于新手来说还是很难用的(至少我一开始不知道怎么下手),所以这边就简单的普及下吧。

先添加一个Dashboard,然后按 ctrl + h 键可以调出/隐藏绿色的控制按钮,点那个绿色的小条条(这设计的真是无语)会出来一个菜单,然后选 Add Panel-> Graph,会出来一个对话框,General 就是常规设置,这个名字就好了。
Metrics里添加一个查询,From选择对应的库,比如 mysql(这里它会自动把有的列出来,很人性化),添加一个WHERE,选择client值是对应服务器IP
SLECEC 选择一个字段(比如cpu)
GROUP BY 选择按1s配置,如下图

QQ20160407-2@2x

如果是CPU项目,则在 Axes & Grid 标签里,Left Y 的 Unit 选择 percent(0-100)
在 Display Styles 标签里,Chart Options 参数可以选择 Bars,如下图:
QQ20160407-3@2x

修改好后,点右边的 “Back to dashboard”,我一开始都不知道这里点,晕呀。下次要修改,只要点击标题,就有菜单可以修改了。

所有修改完毕后,记得点上面的保存图标。