CDN实现varnish缓存

实验环境:

server5:varnish机(ip:172.25.254.5)
server2:real server机(ip:172.25.254.2)
server3:real server机(ip:172.25.254.3)

server5配置

一、安装varnish相关软件

[root@server5 ~]# yum install -y varnish-libs-3.0.5-1.el6.x86_64.rpm 
[root@server5 ~]# yum install -y varnish-3.0.5-1.el6.x86_64.rpm 

二、修改varnish配置文件

vim /etc/sysconfig/varnish 

修改varnish的端口

未分类

vim /etc/varnish/default.vcl

backend default {
  .host = "172.25.20.2";
  .port = "80";
}

三、修改内核显示,保证流量大时,服务正常运行

[root@server5 ~]# vim /etc/security/limits.conf 

未分类

四、varnish主机配置

修改varnish配置文件

backend web1 {   ##访问web1时,指向172.25.20.2
  .host = "172.25.254.2";
  .port = "80";
}

backend web2 {   ##访问web1时,指向172.25.20.3
  .host = "172.25.254.3";
  .port = "80";
}

sub vcl_recv {   ##www.westos.org和westos.org指向同一地址,节省varnish空间
         ##实质上,域名不同,varnish缓存地址不同
if (req.http.host ~ "^(www.)?westos.org") {
set req.http.host = "www.westos.org";
set req.backend = web1;
} elsif (req.http.host ~ "^bbs.westos.org") {
set req.backend = web2;
} else {error 404 "westos cache";
}
}

sub vcl_deliver {   ##第一次访问时MISS,后续访问HIT,varnish默认清楚时间120s
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT from westos cache";
}
else {
set resp.http.X-Cache = "MISS from westos cache";
}
return (deliver);
}

测试:
在物理机做域名解析:

172.25.254.5    www.westos.org  bbs.westos.org  westos.org

未分类

未分类

五、varnish负载均衡设置

server5

1、修改配置文件

backend web1 {
  .host = "172.25.254.2";
  .port = "80";
}

backend web2 {
  .host = "172.25.254.3";
  .port = "80";
}
director lb round-robin {     ##轮询操作
{ .backend = web1; }
{ .backend = web2; }
}
sub vcl_recv {
if (req.http.host ~ "^(www.)?westos.org") {
set req.http.host = "www.westos.org";
set req.backend = lb;       ##调用轮询
return (pass);
} elsif (req.http.host ~ "^bbs.westos.org") {
set req.backend = web2;      ##直接访问,不参与轮询
} else {error 404 "westos cache";   
}
}

server3开启httpd虚拟主机设置

/etc/httpd/cong/httpd.conf

未分类

重新启动server3的httpd服务

测试:

物理机访问:

未分类

六、varnish推送管理

目的:当web有更新时,varish的cache实现同步更新
安装httpd,php,unzip
修改httpd端口8080
默认发布目录/var/www/html下解压 bansys.zip
修改varnish配置文件 /etc/varnish/default.vcl
acl westos {
"127.0.0.1";
"172.25.254.0"/24;   指定 254 网段的可以推送
}

backend web1 {
.host = "172.25.254.2";   指定服务器
.port = "80";
}

backend web2 {
.host = "172.25.254.3";   指定服务器
.port = "80";
}
director lb round-robin {       轮巡
{       .backend = web1;        }
{       .backend = web2;        }
}
sub vcl_recv {

if (req.request == "BAN") {     如果输入错误  则 报错
if (!client.ip ~ westos) {
error 405 "Not allowed.";
}
ban("req.url ~ " + req.url);
error 200 "ban added";
}

if (req.http.host ~ "^(www.)?westos.org") {
set req.http.host = "www.westos.org";
set req.backend = lb;    引用 lb 函数  参加轮训
} elsif (req.http.host ~ "^bbs.westos.org") {
set req.backend = web2;
} else {error 404 "westos cache";
}
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT from westos cache";
}                 命中缓存
else {
set resp.http.X-Cache = "MISS from westos cache";
}      未命中缓存
return (deliver);
}

访问 172.25.254.5:8080 即可进行推送
varnishadm ban.url /index.html
清除 index.html 页面缓存
varnishadm ban.url /admin/$
清除 admin 目录缓存

zabbix主机自动发现和监控

在主机较多的时候,配置主机自动发现并加入监控可以代替手动的添加主机,减轻工作量,自动发现由服务端主动发起,Zabbix Server开启发现进程,定时扫描局域网中IP服务器、设备。可以根据需要,在对主机安装系统的时候就安装配置并启动zabbix-agent服务,这样的话只要主机系统安装完成,便会自动加入监控

1、在客户机上安装zabbix-agent并配置

2、zabbix-server自动发现和监控

2.1 创建自动发现规则

依次点击:配置——>自动发现,选择Local network或创建自动发现规则

填写server端所扫描的主机ip地址所在范围,例如数据库服务器所用的范围为192.168.1.51-59

修改适合的时间延迟(延迟太短如果主机太多会导致有的主机扫描不到)

检查项不必修改,默认即可

设备唯一性准则选择ip地址

最后勾选已启用、点击更新

未分类

2.2 创建发现动作

依次点击:配置——>动作,选择事件源为自动发现,点击Auto discovery. Linux servers.——>操作

添加新的动作操作,当主机自动发现并添加监控的时候自动给管理员发送邮件提示,选择要发送邮件的用户,并去掉勾选消息内容,这样收到的邮件才有内容,点击更新后启用该动作

未分类

未分类

2.3 配置邮件通知

配置发件人邮箱,依次点击:管理——>报警媒介类型——>Email,填写邮箱服务器的地址以及邮件用户及客户端授权码,并勾选已启用

未分类

配置收件人邮箱,依次点击:管理——>用户,点击要收到邮件的用户,点击报警媒介——>添加,填写邮件地址,启用时间和报警类型根据需要选择,点击添加——>更新

未分类

3、等待自动发现并添加监控

点击监测中——>自动发现,等待发现主机

未分类

发现主机成功后会自动在主机列表中出现发现的主机并添加了监控

未分类

同时,邮箱也收到了邮件

未分类

zabbix通过企业微信发送监控告警

很好的监控线上业务,需要有及时提醒的告警功能,以便及时处理问题,在运维的监控工作中,告警大概有这么几种,一是邮件,较为传统,往往会被忽略,因为现在垃圾邮件实在太多,二是短信,这个比较有及时性,但有时也会有垃圾短信,一般情况下,人们的习惯是收到短信,都会看一眼!所以相对邮件关注率要高一些,第三种是通过钉钉或者企业微信这种做监控告警,目前来说,还是比较及时和重要的,因为办公信息,大家都很重视,因为大家都是自驱型人才,下面的python脚本,用于调用企业微信自定义应用来发送告警消息(或者这样说不准备,暂且这么说吧):

#!/usr/bin/python
# --*-- coding:utf8 --*--
# Author: Jack.Z


import json
import sys
import simplejson
import requests


def get_token(cid, secret):
    gettoken_url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=' + cid + '&corpsecret=' + secret
    try:
        req = requests.get(gettoken_url)
    except requests.HTTPError as e:
        print e.errno
        sys.exit()

    return json.loads(req.text)['access_token']


def send_data(token, account, subject_name, context):
    send_url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + token
    send_values = {
        "touser": account,          # 企业号中的用户帐号,在zabbix用户Media中配置,如果配置不正常,将按部门发送。
        "toparty": "2",             # 企业号中的部门id。
        "msgtype": "text",          # 消息类型。
        "agentid": "1000002",       # 企业号中的应用id。
        "text": {
            "content": subject_name + 'n' + context
        },
        "safe": "0"
    }

    data = simplejson.dumps(send_values, ensure_ascii=False).encode('utf-8')
    try:
        req = requests.post(send_url, data)
    except requests.HTTPError as e:
        print e.errno
        sys.exit()

    print req.text


if __name__ == '__main__':
    user = str(sys.argv[1])
    subject = str(sys.argv[2])
    content = str(sys.argv[3])

    corp_id = '*************'
    corp_secret = '******************'

    access_token = get_token(corp_id, corp_secret)
    send_data(token=access_token, account=user, subject_name=subject, context=content)

前提条件是: 你得有企业微信,然后呢,还要自建应用, 然后在zabbix中做相关设置,下面是我们在实际测试中的信息提示载图:

未分类

Mysql中-Xtrabackup备份和恢复应用

关于Xtrabackup(又或innobackupex)的介绍,详细参考官方文档https://www.percona.com/doc/percona-xtrabackup/2.1/index.html

Xtrabackup安装指南

文件准备

[root@wuxiang11 ~]# cd percona-xtrabackup/
[root@wuxiang11 percona-xtrabackup]# ls
libev-4.15-1.el6.rf.x86_64.rpm  percona-release-0.1-4.noarch.rpm  percona-xtrabackup-24-2.4.4-1.el7.x86_64.rpm

开始安装依赖文件

[root@wuxiang11 percona-xtrabackup]# rpm -ivh libev-4.15-1.el6.rf.x86_64.rpm 
warning: libev-4.15-1.el6.rf.x86_64.rpm: Header V3 DSA/SHA1 Signature, key ID 6b8d79e6: NOKEY
Preparing...                ########################################### [100%]
   1:libev                  ########################################### [100%]

安装-01

[root@wuxiang11 percona-xtrabackup]# rpm -ivH percona-release-0.1-4.noarch.rpm
Preparing packages for installation...
percona-release-0.1-4
[root@wuxiang11 percona-xtrabackup]# yum list | grep percona

马上就安装完成

[root@wuxiang11 percona-xtrabackup]# yum install percona-xtrabackup-24

Loaded plugins: fastestmirror, security
Setting up Install Process
Loading mirror speeds from cached hostfile
Resolving Dependencies
--> Running transaction check
---> Package percona-xtrabackup-24.x86_64 0:2.4.11-1.el6 will be installed
--> Processing Dependency: perl(DBD::mysql) for package: percona-xtrabackup-24-2.4.11-1.el6.x86_64
--> Running transaction check
---> Package perl-DBD-MySQL.x86_64 0:4.013-3.el6 will be installed
--> Processing Dependency: perl(DBI::Const::GetInfoType) for package: perl-DBD-MySQL-4.013-3.el6.x86_64
--> Processing Dependency: perl(DBI) for package: perl-DBD-MySQL-4.013-3.el6.x86_64
--> Processing Dependency: libmysqlclient.so.16(libmysqlclient_16)(64bit) for package: perl-DBD-MySQL-4.013-3.el6.x86_64
--> Processing Dependency: libmysqlclient.so.16()(64bit) for package: perl-DBD-MySQL-4.013-3.el6.x86_64
--> Running transaction check
---> Package Percona-Server-shared-51.x86_64 0:5.1.73-rel14.12.625.rhel6 will be installed
---> Package perl-DBI.x86_64 0:1.609-4.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

===============================================================================================================================================================================================
 Package                                           Arch                            Version                                               Repository                                       Size
===============================================================================================================================================================================================
Installing:
 percona-xtrabackup-24                             x86_64                          2.4.11-1.el6                                          percona-release-x86_64                          8.1 M
Installing for dependencies:
 Percona-Server-shared-51                          x86_64                          5.1.73-rel14.12.625.rhel6                             percona-release-x86_64                          2.1 M
 perl-DBD-MySQL                                    x86_64                          4.013-3.el6                                           centos6.7_64                                    134 k
 perl-DBI                                          x86_64                          1.609-4.el6                                           centos6.7_64                                    705 k

Transaction Summary
===============================================================================================================================================================================================
Install       4 Package(s)

Total download size: 11 M
Installed size: 12 M
Is this ok [y/N]: y
Downloading Packages:
(1/4): Percona-Server-shared-51-5.1.73-rel14.12.625.rhel6.x86_64.rpm                                                                                                    | 2.1 MB     00:02     
(2/4): percona-xtrabackup-24-2.4.11-1.el6.x86_64.rpm                                                                                                                    | 8.1 MB     00:01     
(3/4): perl-DBD-MySQL-4.013-3.el6.x86_64.rpm                                                                                                                            | 134 kB     00:00     
(4/4): perl-DBI-1.609-4.el6.x86_64.rpm                                                                                                                                  | 705 kB     00:00     
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                                                          2.5 MB/s |  11 MB     00:04     
warning: rpmts_HdrFromFdno: Header V4 DSA/SHA1 Signature, key ID cd2efd2a: NOKEY
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
Importing GPG key 0xCD2EFD2A:
 Userid : Percona MySQL Development Team <[email protected]>
 Package: percona-release-0.1-4.noarch (installed)
 From   : /etc/pki/rpm-gpg/RPM-GPG-KEY-Percona
Is this ok [y/N]: y
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Warning: RPMDB altered outside of yum.
** Found 3 pre-existing rpmdb problem(s), 'yum check' output follows:
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of libmysqlclient.so.16()(64bit)
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of libmysqlclient.so.16(libmysqlclient_16)(64bit)
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of mysql-libs
  Installing : perl-DBI-1.609-4.el6.x86_64                                                                                                                                                 1/4 
  Installing : Percona-Server-shared-51-5.1.73-rel14.12.625.rhel6.x86_64                                                                                                                   2/4 
  Installing : perl-DBD-MySQL-4.013-3.el6.x86_64                                                                                                                                           3/4 
  Installing : percona-xtrabackup-24-2.4.11-1.el6.x86_64                                                                                                                                   4/4 
  Verifying  : percona-xtrabackup-24-2.4.11-1.el6.x86_64                                                                                                                                   1/4 
  Verifying  : perl-DBD-MySQL-4.013-3.el6.x86_64                                                                                                                                           2/4 
  Verifying  : Percona-Server-shared-51-5.1.73-rel14.12.625.rhel6.x86_64                                                                                                                   3/4 
  Verifying  : perl-DBI-1.609-4.el6.x86_64                                                                                                                                                 4/4 

Installed:
  percona-xtrabackup-24.x86_64 0:2.4.11-1.el6                                                                                                                                                  

Dependency Installed:
  Percona-Server-shared-51.x86_64 0:5.1.73-rel14.12.625.rhel6                      perl-DBD-MySQL.x86_64 0:4.013-3.el6                      perl-DBI.x86_64 0:1.609-4.el6                     

Complete!

检验是否安装成功

[root@wuxiang11 percona-xtrabackup]# rpm -qa|grep xtrabackup
percona-xtrabackup-24-2.4.11-1.el6.x86_64
[root@wuxiang11 percona-xtrabackup]#

备份思路

Xtrabackup提供了全量备份和增量备份两种方式,全量就不解释了,增量是指其可以只备份指定位置后的新增数据。本文介绍的增量备份方法使用到了LSN(Log Sequence Number),可以从备份成功文件夹的xtrabackup_checkpoints文件中获取,其中to_lsn就是下次增量备份的起始位置。
结合两者,我们可以得到下图的备份思路。

未分类

解释如下:

由于全量备份和增量备份的命令参数不同,所以首先要判断是否需要全量备份。
如果是全量备份,那么便执行命令,备份成功后,记录xtrabackup_checkpoints文件中的to_lsn,以便后面进行增量备份
如果是增量备份,那么首先读取上次备份时记录下的to_lsn,如果读取失败,报错并结束。否则执行相应命令,备份成功后记录to_lsn。
编写一个简单的脚本:db-backup.sh,如下所示:

#!/bin/sh

# xtrabackup的相关配置
INNOBACKUPEX="innobackupex "
MY_CNF="/home/config/mysql/3307.backup.cnf"
MY_USER="xtrabackup"
MY_PASSWORD="xtrabackup"
MY_SOCKET="/home/socket/mysql/3307.sock"

# 远程备份机 文件名配置
REMOTE_HOST="dbbackup"
REMOTE_DIR="/home/backup/mysql/test"
LOCAL_LSN_FILE="~/.to_lsn_important"
DATE_NAME=`date +%Y-%m-%d-%H-%M-%S`
REMOTE_FILE=$DATE_NAME.tar.gz
LOCK_FILE="~/.mysql.backup.lock"
LOCAL_BACKUP_DIR="/home/backup/mysql/test/$DATE_NAME"

# 输出帮助信息
function usage()
{
    echo "Usage:"
    echo "-f db will be backuped fully with this parameter. If not , incrementally. "
}

#防止同时执行两个备份命令,发生冲突
if [ -f $LOCK_FILE ] ;then
    echo 'Mysql backup lockfile is locked!'
    exit 0
fi

full=0

while getopts "fh" arg #选项后面的冒号表示该选项需要参数
do
    case $arg in
        f)  
            full=1
            ;;
        h)  # 输出帮助信息
            usage
            exit 0
            ;;
    esac
done

echo "backup dir is $REMOTE_DIR/$REMOTE_FILE"

# backup up db to remote host
echo "start to backup db!"`date +%Y-%m-%d-%H-%M-%S`
if [ "$full"x = "1"x ] ;then
    # 全量备份
    echo '1' > $LOCK_FILE
    $INNOBACKUPEX --defaults-file=$MY_CNF --user=$MY_USER --password=$MY_PASSWORD --socket=$MY_SOCKET ./ --stream=tar | gzip | ssh $REMOTE_HOST "cat - > $REMOTE_DIR/FULL-$REMOTE_FILE"
    ssh $REMOTE_HOST "cd $REMOTE_DIR;rm -f xtrabackup_checkpoints;tar zxfi $REMOTE_DIR/FULL-$REMOTE_FILE xtrabackup_checkpoints "
    toLSN=$( ssh $REMOTE_HOST "cat $REMOTE_DIR/xtrabackup_checkpoints|grep to_lsn|awk -F= '{gsub(/ /,"",$2);print $2}'" )
    if [ $toLSN ] ;then
        echo $toLSN > $LOCAL_LSN_FILE
    else
        echo 'no lsn from remote host!please check!'
    fi
else
    # 增量备份
    if [ -f $LOCAL_LSN_FILE ] ;then
        toLSN=`cat $LOCAL_LSN_FILE`
    fi

    if [ ! $toLSN ] ;then
        echo 'last LSN is not set !please check!'
        exit 0
    fi

    echo '1' > $LOCK_FILE
    mkdir -p $LOCAL_BACKUP_DIR
    echo "last to lsn is "$toLSN
    $INNOBACKUPEX --parallel=6 --defaults-file=$MY_CNF --user=$MY_USER --password=$MY_PASSWORD --socket=$MY_SOCKET --incremental --incremental-lsn=$toLSN $LOCAL_BACKUP_DIR 2>/tmp/innobackexLog
    toLSN=$( cd $LOCAL_BACKUP_DIR/*; cat xtrabackup_checkpoints|grep to_lsn|awk -F= '{gsub(/ /,"",$2);print $2}' )
    echo "new to lsn is "$toLSN;
    if [ $toLSN ] ;then
        echo $toLSN > $LOCAL_LSN_FILE
        cd $LOCAL_BACKUP_DIR/*;tar zc .|ssh $REMOTE_HOST "cat - > $REMOTE_DIR/$REMOTE_FILE"
        echo "save file to $REMOTE_HOST @ $REMOTE_DIR/$REMOTE_FILE"
    else
        echo 'no lsn from local backup file!delete remote backup file!'$LOCAL_BACKUP_DIR/$REMOTE_FILE
    fi
    echo "remove $LOCAL_BACKUP_DIR"
    rm -rf $LOCAL_BACKUP_DIR
fi

rm -f $LOCK_FILE
echo "end to backup db!"`date +%Y-%m-%d-%H-%M-%S`

将上述脚本配置好后,放到mysql主机上,执行 sh db-backup.sh -f进行全量备份,之后将sh backup.sh添加到crontab中每小时增量备份一次就可以了。

注意–parallel=6使用是有条件的,参考文档https://www.percona.com/doc/percona-xtrabackup/2.1/innobackupex/parallel_copy_ibk.html

恢复思路

一旦线上数据库发生宕机之类的灾难性事故,备份数据就要立马发挥作用了。打开备份文件夹一看,如上面的图,每小时一个压缩文件,时间久了,茫茫多,上百个压缩文件,简直是一定的,这必须得来个脚本自动化恢复数据才成。思路很简单,见下图:

未分类

解释如下:

  1. 先解压,没啥说的。
  2. 找到全量备份的数据,因为增量备份也是以此为基准进行的。上面备份的时候,全量备份文件命名以FULL开头便是为了此处便于查找。
  3. 先恢复全量备份数据,然后按照时间顺序恢复增量备份的数据。
  4. 将数据恢复到mysql中
  5. 启动mysql,连上看看有没问题。

简单的实现脚本,我们叫它restore.sh,如下所示

#!/bin/sh
BACK_DIR="/home/backup/mysql/test"
RESTORE_DIR="/home/tmpback/test"

# xtrabackup恢复时使用的配置文件可能与数据库配置文件不同
RESTORE_MY_CNF="/home/config/mysql/3307.backup.cnf"

# 启动mysql需要的配置文件,按需设定
MY_CNF="/home/config/mysql/3307.cnf"
MY_DATA_DIR="/home/data/mysql/3307"
MY_SOCK_DIR="/home/socket/mysql/"
MY_LOG_DIR="/home/logs/mysqld/3307"

#extract all tar file
fullFileName=""
for file in $( ls $BACK_DIR | grep "tar.gz" )
do
    fileName=${file%.tar.gz}
    full=${file%%-*}
    if [ "${full}"x = "FULL"x ] ;then
        fullFileName=$fileName
    fi
    DEST_DIR="$RESTORE_DIR/$fileName"
    if [ -d $DEST_DIR ] ;then
        echo "$DEST_DIR exists!"
    else
        mkdir -p $DEST_DIR
        echo "start to extract file $file to $DEST_DIR"
        tar zxif $BACK_DIR/$file -C $RESTORE_DIR/$fileName
        echo "end to extract file "$file
    fi
done

echo "full backup dir is "$fullFileName

echo "**start to repare full backup dir"

# 恢复全量数据
echo "innobackupex --apply-log --redo-only --use-memory=4G $RESTORE_DIR/$fullFileName"
innobackupex --apply-log --redo-only --use-memory=4G "$RESTORE_DIR/$fullFileName"

echo "**end to repare full backup dir"

# 恢复增量数据
for file in $( ls $RESTORE_DIR|grep -v "FULL" )
do
    echo "**start to repare $file"
    echo "innobackupex --apply-log --redo-only --use-memory=4G $RESTORE_DIR/$fullFileName --incremental-dir=$RESTORE_DIR/$file"
    innobackupex --apply-log --redo-only --use-memory=4G "$RESTORE_DIR/$fullFileName" --incremental-dir="$RESTORE_DIR/$file"

    echo "**end to repare $file"

done

echo "**start to copy back data to mysql"
rm -rf $MY_DATA_DIR
mkdir $MY_DATA_DIR
mkdir $MY_SOCK_DIR
mkdir $MY_LOG_DIR

#将数据恢复到mysql中
echo "innobackupex --defaults-file=$RESTORE_MY_CNF --copy-back $RESTORE_DIR/$fullFileName"
innobackupex --defaults-file=$RESTORE_MY_CNF --copy-back $RESTORE_DIR/$fullFileName
echo "**end to copy back data to mysql"

chown -R mysql:mysql $MY_DATA_DIR
chown -R mysql:mysql $MY_SOCK_DIR
chown -R mysql:mysql $MY_LOG_DIR

echo "All data has been restored! Try to start mysql now"

echo "mysqld_multi --defaults-file=$MY_CNF start 1"

使用方法很简单,配置好后,直接运行sh restore.sh就可以了。

WordPress 使用 wp_count_posts() 函数快速获取文章数量

还记得之前想要获取 WordPress 站点的所有文章总数统计网上的教程,以及子凡在某些地方的使用也都是直接使用 SQL 语句直接查询数据来做总数统计,现在相信还真的是费时费力,原因就是不知道 WordPress 还有 wp_count_posts()这样一个函数,具体是哪个版本出来就懒得去看了,反正随时都保持 WordPress 最新版。

未分类

其实在很多的 WordPress 主题上都会有一个统计或者文章归档的页面,就需要统计一些 WordPress 站点上已经发布多少文章了,以此来展示自己的站点或者给自己一个统计,当然我们在开发某些特别的功能或者逻辑计算时也可能会用到,子凡昨天就开发一个插件的时候就多次用到了,不想再去写 SQL 来统计,费时费力并且 WordPress 本身就有的功能为什么就不好好利用呢,果断就想起了 wp_count_posts 函数。

wp_count_posts 函数是在 WordPress 中用来统计文章数量的函数,可以统计的类型有文章(post)和页面(page)。并且能够直接统计出文章各个不同状态的数据,使用起来非常的便捷,两行代码即可搞定。

//获取文章数量
$postcount = wp_count_posts();
//获取页面数量
$pagecount = wp_count_posts('page');

默认是获取的 post 文章类型的,当然如果是自定义类型应该也是可以支持的,这个子凡目前没有测试,不过相信 WordPress 是绝对考虑到了兼容性的,值得注意的是,子凡上面说过了是两行代码,所以上面的例子并不能直接输出,因为 wp_count_posts 函数返回的是一个对象数据,如下。

// WordPress wp_count_posts 函数返回值
stdClass Object
(
    [publish] => 11 //已发布
    [future] => 0   //定时发布
    [draft] => 0    //草稿
    [pending] => 0  //待审
    [private] => 0  //私有
    [trash] => 0    //垃圾箱
    [auto-draft] => 34  //自动草稿
    [inherit] => 0  //修订版本
    [request-pending] => 0
    [request-confirmed] => 0
    [request-failed] => 0
    [request-completed] => 0
)

所以,获取已发布状态文章的完整统计代码如下:

//WordPress 已发布文章数量
$count_posts = wp_count_posts();  
$publish_posts = $count_posts->publish;

其它的就举一反三,相信也都能看得懂了,子凡就不再过多的赘述。最后在补充一点,有时候我们可能喜欢代码的简单性,所以在写法上面就比较大胆,没有怎么考虑兼容性,一行代码就能搞定,所以在不考虑兼容性的情况下还有以下的一种写法:

//WordPress 已发布文章数量,不兼容 PHP5.4 以前
$publish_posts = wp_count_posts()->publish;

子凡比较喜欢用吧,不过使用这种写法请一定保证你的 PHP 版本在 5.4 以上即可正常运行。

Tomcat单机多实例部署及管理

未分类

单台机器部署多个 Tomcat, 每个Tomcat部署独立服务,Tomcat之间启停互不影响

不要问我为什么有这个需求, 复制粘贴就是干

0x00 单机多实例概述

未分类

大概的目录结构:

  • CATALINA_HOME 为Tomcat应用程序运行程序及所需依赖
  • CATALINA_BASE 即我们即将部署的程序

手动更改如下工作目录:

这是我自己个儿整的初始目录结构 download

war_apps                #手动创建用来存放下载好的war包文件
tomcat                  #CATALINA_HOME
├── bin
├── INIT_APPS_FILE      #CATALINA_BASE
│   ├── conf
│   ├── logs
│   ├── temp
│   ├── webapps
│   └── work
└── lib

然后呢正常的思路就是配置每个APP的server.xml端口:

- Server Port:该端口用于监听关闭tomcat的shutdown命令,默认为8005
- Connector Port:该端口用于监听HTTP的请求,默认为8080
- AJP Port:该端口用于监听AJP( Apache JServ Protocol )协议上的请求,通常用于整合Apache Server等其他HTTP服务器,默认为8009
- Redirect Port:重定向端口,出现在Connector配置中,如果该Connector仅支持非SSL的普通http请求,那么该端口会把 https 的请求转发到这个Redirect Port指定的端口,默认为8443;

应用太多你难道要一个个手动改? no no no! 上脚本

0x01 自动部署管理配置

需要把应用war包传到可下载位置,来用于应用分发,我这里是直接传到了阿里云的oss桶里

1.添加tomcat更新启停控制脚本

需要安装unzip yum -y install unzip

manage.sh需要放在这里,也可以自定义改代码

war_apps                #手动创建用来存放下载好的war包文件
tomcat                  #CATALINA_HOME
├── bin
├── manage.sh
├── INIT_APPS_FILE      #CATALINA_BASE
│   ├── conf
│   ├── logs
│   ├── temp
│   ├── webapps
│   └── work
└── lib

蓝色块需要根据实际情况自定义, 应用名称, 和war包下载地址不需要写,后面的python总控制台会自动分配

manage.sh

#!/bin/sh
# Author: Kionf
# description: 启动tomcat多实例.
# PATH=/opt/op/java/jdk1.8.0_172/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
# 应用名称
app=
# war包下载地址
war_url=
soft_dir="/opt/op"
download_war_file="${soft_dir}/war_apps/${app}.war"
export CATALINA_BASE="${soft_dir}/tomcat/$app"
export CATALINA_HOME="${soft_dir}/tomcat"
export JVM_OPTIONS="-Xms528m -Xmx812m -Xmn328m"
check(){
    PID=`ps aux|grep java|grep -w ${CATALINA_BASE}|awk '{print $2}'`
    if [ -n "$PID" ];then
        echo -e "33[94m $app is running PID:$PID"
        running=`netstat -ntlp|grep $PID|grep 127.0.0.1`
        if [ -n "$running" ];then
            echo -e "33[92m $app is provide services33[0m "
        else
            echo -e "33[93m $app is running but not provide services33[0m"
        fi
        return 0
    else
        echo -e "33[91m $app is dead33[0m "
        return 1
    fi
}
start() {
    check
    if [ $? -eq 1 ];then
        echo -e "33[94m Start $app 33[0m"
        $CATALINA_HOME/bin/startup.sh >/dev/null 2>&1
    fi
}
stop() {
    check
    if [ $? -eq 0 ];then
        echo -e "33[94m Stop $app33[0m"
        $CATALINA_HOME/bin/shutdown.sh >/dev/null 2>&1
        kill -9 $PID
    fi
}
update() {
    echo "下载文件"
    wget ${war_url} -O ${download_war_file} > /dev/null 2>&1
    if [ $? -eq 0 ];then
        cd ${CATALINA_BASE}/webapps/*/; unzip -q -o ${download_war_file} >/dev/null 2>&1
    fi
}
log() {
    tailf ${CATALINA_BASE}/logs/catalina.out
}
if [ $# != "0" ];then
    case "$1" in
        start)
            start
            ;;
        stop)
            stop
            ;;
        restart)
            stop
            start 
            ;;
        status)
            check
            ;;
        upgrade)
            stop
            update
            start
            ;;
        log)
            log
            ;;
        *)
            echo $"Usage: $0 {start|stop|restart|status|upgrade|log}"
            exit 1
            ;;
    esac
else
    start
    log
fi

2.添加总控制台脚本

我就放在了/usr/local/bin下, chmod +x /usr/local/bin/tomcat_manager 蓝色部分需要根据自己需求更改 (支持python2)

/usr/local/bin/tomcat_manager

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/23 15:18
# @Author  : Kionf
# @FileName: tomcat_manager.py
#
#   初始配置并管理单例tomcat多应用
#
import os
import subprocess
import sys
import shutil
# PATH = os.getcwd()
PATH = "/opt/op/tomcat"         #Tomcat目录
shell_script = os.path.join(PATH, "manage.sh")          #更新启停管理脚本
INIT_FILES = os.path.join(PATH, "INIT_APPS_FILE")
bucket = "https://oss.aliyuncs.com/tomcat_app/"         # webapp下载地址
config_data = {
    # 应用名: [ServerPort, ConnectPort, AJPPort, RedirectPort, ]
    'Application': ['8201', '8101', '8301', '8401', 'BaseApplication.war'],
    'BaseNotify_Service': ['8202', '8102', '8302', '8402', 'BaseNotify_Service.war'],
    'BaseUserCenter_Service': ['8203', '8103', '8303', '8403', 'BaseUserCenter_Service.war'],
}
def customize_print(msg, stat=0):
    if stat == 1:
        print("33[91m [ERROR]:  %s 33[0m" % msg)
    elif stat == 0:
        print("33[92m [INFO]:  %s 33[0m" % msg)
    elif stat == 2:
        print("33[93m [DEBUG]:  %s 33[0m" % msg)
class TomcatAppsManage:
    def __init__(self, webapp):
        self.name = webapp
        self.app_config = config_data[self.name]
        self.manage_shell_file = os.path.join(PATH, self.name, self.name)
        self.server_port, self.conn_port, self.ajp_port, self.redirect_port, self.app_war_name = self.app_config
        self.app_download_url = bucket + self.app_war_name
        self.config_file = os.path.join(PATH, self.name, 'conf/server.xml')
        self.webapp_dir = os.path.join(PATH, self.name)
    def create_app(self):
        """
        创建app
        :return:
        """
        if not os.path.exists(self.webapp_dir):
            customize_print("创建APP: %s" % self.name)
            shutil.copytree(INIT_FILES, self.webapp_dir)
            os.mkdir(os.path.join(self.webapp_dir, "webapps", self.name.lower()))
    def config_app_port(self):
        customize_print("正在修改APP:%s 配置" % self.name)
        change_port = {
            'ServerPort': "sed -i s'#Server port="[0-9]*"#Server port="" + self.server_port + ""#'g " + self.config_file,
            'ConnPort': "sed -i s'#Connector port="[0-9]*" protocol="HTTP/1.1"#Connector port="" + self.conn_port + "" protocol="HTTP/1.1"#'g " + self.config_file,
            'RedirectPort': "sed -i s'#redirectPort="[0-9]*"#redirectPort="" + self.redirect_port + ""#'g " + self.config_file,
            'AjpPort': "sed -i s'#Connector port="[0-9]*" protocol="AJP/1.3"#Connector port="" + self.ajp_port + "" protocol="AJP/1.3"#'g " + self.config_file,
        }
        for port in change_port.keys():
            # customize_print("修改 %s 端口" % port)
            os.system(change_port[port])
    def config_app_manage_shell(self):
        customize_print("%s 添加管理脚本" % self.name)
        copy_shell_script = 'cp -f ' + shell_script + ' ' + self.manage_shell_file
        os.system(copy_shell_script)
        config_script_app_name = "sed -i 's/app=/app="" + self.name + ""/' " + self.manage_shell_file
        os.system(config_script_app_name)
        config_script_war_url = "sed -i 's#war_url=#war_url="" + self.app_download_url + ""#' " + self.manage_shell_file
        os.system(config_script_war_url)
    def status_app(self):
        """
        :return: 0提供服务,1停止,2未提供服务
        """
        try:
            result = subprocess.check_output(['sh', self.manage_shell_file, 'status'])
        except subprocess.CalledProcessError as e:
            result = e.output
        if 'run' in result:
            if 'is provide services' in result:
                customize_print("应用 %s 成功启动并提供服务" % self.name)
                return 0
            elif 'but' in result:
                customize_print("应用 %s 进程存在但未提供服务" % self.name, 2)
                return 2
        else:
            customize_print("应用 %s 以停止" % self.name, 1)
            return 1
    def manage(self, operate):
        os.system('sh %s %s' % (self.manage_shell_file, operate))

    def init(self):
        self.create_app()
        self.config_app_port()
        self.config_app_manage_shell()
        self.manage("upgrade")
        self.manage("stop")
    def restart(self):
        self.manage("stop")
        self.manage("start")
    def start(self):
        self.manage("start")
    def stop(self):
        self.manage("stop")
    def log(self):
        self.manage("log")
    def upgrade(self):
        self.lock_config_file()
        self.manage("upgrade")
    def lock_config_file(self):
        cmd = 'find ' + self.webapp_dir + ' -name db*properties -o -name config_base_*|xargs chattr +i >/dev/null 2>&1'
        customize_print("锁配置文件", 2)
        os.system(cmd)
    def unlock_config_file(self):
        cmd = 'find ' + self.webapp_dir + ' -name db*properties -o -name config_base_*|xargs chattr -i >/dev/null 2>&1'
        customize_print("解锁配置文件", 2)
        os.system(cmd)
def dash_board(apps, operate):
    """
    主管理程序,调用
    :param operate: 应用操作
    :param apps: apps 为list
    """
    for app in apps:
        app_obj = TomcatAppsManage(app)
        main_dict = {
            "init": app_obj.init,
            "shell": app_obj.config_app_manage_shell,
            "status": app_obj.status_app,
            "start": app_obj.start,
            "stop": app_obj.stop,
            "restart": app_obj.restart,
            "upgrade": app_obj.upgrade,
            "log": app_obj.log,
            "lock": app_obj.lock_config_file,
            "unlock": app_obj.unlock_config_file,
        }
        try:
            main_dict[operate]()
        except KeyError as e:
            customize_print(help_msg)
help_msg = """
使用方法:
    1 log
    all status
    管理应用编号 操作
操作:
    lock        锁配置文件
    unlock      解锁配置文件
    init        配置tomcat监听端口
    shell       配置webapp控制脚本
    status,start,restart, log,upgrade,stop 应用操作
"""
def main():
    app_list = []
    for index, app_name in enumerate(config_data, 1):
        print "33[94m %s:  %s 33[0m" % (index, app_name)
        app_list.append(app_name)
    choice = raw_input("输入要管理的服务:  ")
    try:
        app_index = choice.split()[0]
        operate = choice.split()[1]
        if app_index.isdigit():
            app_index = int(app_index)
            if len(app_list) >= app_index > 0:
                app_name = app_list[app_index - 1]
                dash_board(app_name.split(), operate)
        elif app_index == "all":
            dash_board(app_list, operate)
    except ValueError and IndexError:
        customize_print("参数输入错误", 1)
        customize_print(help_msg)
if __name__ == '__main__':
    try:
        dash_board(sys.argv[1].split(), sys.argv[2])
    except IndexError:
        try:
            while True:
                main()
        except KeyboardInterrupt:
            customize_print("Bye!")

0x02 开始初始化部署

执行tomcat_manager

未分类

all init 自动初始化部署所有项目, 其他具体使用方法见帮助信息

Tomcat 部署项目的三种方法

1、下载 Tomcat 服务器

①、官网下载地址:http://tomcat.apache.org/

②、tomcat 8.0 64位百度云下载地址:http://pan.baidu.com/s/1slbKPsx 密码:ewui

③、tomcat 8.0 32位百度云下载地址:http://pan.baidu.com/s/1o8G28rS 密码:k11n

2、启动并部署 Tomcat 服务器

①、解压 tomcat 安装包到一个非中文目录下
②、配置环境变量。JAVA_HOME(指向 JDK 安装的根目录)
③、双击 apache-tomcat-6.0.16bin 目录下的 startup.bat,启动服务器(如果一闪而过,那就是没有配置 JAVA_HOME 的环境变量)
④、在浏览器中输入 http://localhost:8080

注意:Tomcat 启动不了的时候注意配置 JAVA_HOME:C:Program FilesJavajdk1.6.0_43这是安装 JDK的根目录

3、Tomcat 的目录结构

未分类

4、部署项目的第一种方法(项目直接放入 webapps 目录中)

1、将编写并编译好的web项目(注意要是编译好的,如果是 eclipse,可以将项目打成 war 包放入),放入到 webapps 中

未分类

2、启动tomcat服务器(双击 apache-tomcat-6.0.16bin 目录下的 startup.bat,启动服务器)

未分类

3、在浏览器输入:http://localhost:8080/项目名/访问的文件名

未分类   

5、部署项目的第二种方法(修改 conf/server.xml 文件 )

①、打开tomcat下conf/server.xml,在 标签之间输入项目配置信息

<Context path="/WebProject" docBase="D:/WebProject" reloadable="true" />

path:浏览器访问时的路径名

docBase:web项目的WebRoot所在的路径,注意是WebRoot的路径,不是项目的路径。其实也就是编译后的项目

reloadble:设定项目有改动时,tomcat是否重新加载该项目

②、双击 startup.bat,启动 tomcat 服务器,然后在浏览器输入访问的项目名称路径

未分类  

注意:如果你配置的 path=”/xx”,那么访问的时候就是这样:

未分类

6、部署项目的第三种方法(apache-tomcat-7.0.52confCatalinalocalhost )

①、进入到 apache-tomcat-7.0.52confCatalinalocalhost 目录,新建一个 项目名.xml 文件

未分类

②、在 那个新建的 xml 文件中,增加下面配置语句(和上面的是一样的,但是不需要 path 配置,加上也没什么用)

<Context  docBase="D:/WebProject" reloadable="true" />

未分类

③、在浏览器输入路径:localhost:8080/xml文件名/访问的文件名

未分类

总结:

①、第一种方法比较普通,但是我们需要将编译好的项目重新 copy 到 webapps 目录下,多出了两步操作

②、第二种方法直接在 server.xml 文件中配置,但是从 tomcat5.0版本开始后,server.xml 文件作为 tomcat 启动的主要配置文件,一旦 tomcat 启动后,便不会再读取这个文件,因此无法再 tomcat 服务启动后发布 web 项目

③、第三种方法是最好的,每个项目分开配置,tomcat 将以confCatalinalocalhost 目录下的 xml 文件的文件名作为 web 应用的上下文路径,而不再理会 中配置的 path 路径,因此在配置的时候,可以不写 path。

通常我们使用第三种方法

tomcat与jvm的关系分析

首先,我们来看几个概念:

1. 什么是jvm

我们从操作系统的层面来理解,jvm其实就是操作系统中的一个进程。既然是一个进程,那么我们很容易的可以通过任务管理器来查看。假设此时我们启动myeclipse(myeclipse其实就是用java语言编写的一个软件,他的运行必然会启动一个jvm,我们可以把myeclipse理解成我们自己写的一个简单的java版的helloworld程序)。查看任务管理器的截图如下:

未分类

2. 什么是tomcat

tomcat其实是一个用java语言开发的免费开源的web服务器(因为是java语言开发,这就是为什么使用tomcat前要配置好jdk,因为jdk里面有jvm,而运行java应用需要jvm)。此时再次查看任务管理器会发现多了一个javaw.exe

看了两者之间的概念之后,相信我们都清楚了两者之间的关系。

现在还有一个问题:

同一个tomcat下的java ee项目使用的是不是同一个jvm?答案是是的。(使用的都是启动tomcat的jvm)这个可以通过启动不同的web应用来自己判断。

如果运行的是普通的java se程序,使用的是不是同一个jvm呢?答案是否。这个可以自己运行程序判断。(可以写一个很简单的while死循环,便于查看)。

tcpdump在Ubuntu和CentOS下的安装和使用

tcpdump安装

在ubuntu下安装

sudo apt-get install tcpdump

在CentOS下安装

yum install tcpdump

tcpdump使用

安装好以后,运行tcpdump -help查看帮助如下所示:

未分类

1、监视指定网络接口的数据包(本机网卡名为ens33)

tcpdump -i ens33

2、监视指定主机的数据包,例如:抓取所有192.168.1.11主机发送和接收的数据包

tcpdump -i ens33 host 192.168.1.11

3、抓取192.168.1.11主机发送的数据包

tcpdump -i ens33 src host 192.168.1.11

4、抓取192.168.1.11主机接收到的数据包

tcpdump -i ens33 dst 192.168.1.11

5、抓取指定端口的数据包:

tcpdump -i ens33 udp port 12345

6、抓取回环网口的包:

tcpdump -i ens33 -i lo

CentOS 7设置Samba共享目录

一、安装Samba服务

yum -y install samba
# 查看yum源中Samba版本
yum list | grep samba
# 查看samba的安装情况
rpm -qa | grep samba

Samba服务器安装完之后, 会生成配置文件目录/etc/samba, /etc/samba/smb.conf是samba的核心配置文件.

二、启动Samba服务

Samba服务安装完成之后有两种方法启动:

service smb start/stop/restart/status
# 或者
systemctl start/stop/restart/status smb.service

# 设置smb服务开机启动
systemctl enable smb.service

三、开放Samba服务使用到的端口号

Samba服务会用到如下的一些端口号:

  • 137(UDP): NetBIOS名字服务
  • 138(UDP): NetBIOS数据报服务
  • 139(TCP): 文件和打印共享
  • 389(TCP): 用于LDAP
  • 445(TCP): NetBIOS服务在windows 2000及以后使用此端口
  • 901(TCP): 用于SWAT, 网页管理Samba

如果不想关闭防火墙的话, 就要在CentOS中放开Samba使用到的TCP端口号

firewall-cmd --zone=public -add-port=139/tcp --permanent
firewall-cmd --zone=public -add-port=389/tcp --permanent
firewall-cmd --zone=public -add-port=445/tcp --permanent
firewall-cmd --zone=public -add-port=901/tcp --permanent

firewall-cmd --reload

# 查看已经放开的端口号
firewall-cmd --list-all

四、配置Samba服务

1、配置匿名访问, 任何人都可以访问的共享目录

1) 创建共享目录

mkdir /opt/shares

# 因为需要设置匿名用户可以上传下载文件, 所以需要给shares目录授予nobody权限
chown -R nobody:nobody /opt/shares

2) 修改/etc/samba/smb.conf文件

cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
vi /etc/samba/smb.conf

修改配置如下:

# See smb.conf.example for a more detailed config file or
# read the smb.conf manpage.
# Run 'testparm' to verify the config is correct after
# you modified it.

[global]
        workgroup = SAMBA
        security = user
        map to guest = Bad User
        log file = /var/log/samba/log.%m


[public]
        comment = Public Stuff
        path = /opt/shares
        public = yes
        read only = No

其中 path就是上边设置的共享目录, read only 表示是否有写权限

3) 修改完配置文件之后重启samba服务

systemctl restart smb.service

4) 测试smb.conf配置是否正确

# 使用testparm命令
testparm

5) 至此就配置完成, 可以从Windows下访问samba的共享目录.

2、配置指定用户可以访问的共享目录

设置共享目录, 只允许指定用户组的用户访问

1) 添加工作组cnki和用户share

groupadd cnki
# useradd -g 组名 用户名
useradd -g cnki share
# 设置用户share的密码
passwd share

#删除用户
userdel -r 用户名

2) 把要访问的账户添加到samba的账户中

光添加系统账户还不够, 需要把已经存在的系统账户添加到samba中才可以访问共享目录

# smbpasswd 参数: -a: 添加 -x: 删除 -d: 禁用 -e: 启用
smbpasswd -a share

3) 创建共享目录

mkdir /opt/shares1

# chown -R 用户名:组名 目录
chown -R share:cnki /opt/shares1

4) 设置samba服务

修改配置文件/etc/samba/smb.conf如下

# See smb.conf.example for a more detailed config file or
# read the smb.conf manpage.
# Run 'testparm' to verify the config is correct after
# you modified it.

[global]
        workgroup = SAMBA
        security = user
        map to guest = Bad User
        log file = /var/log/samba/log.%m

[shares]
        comment = CNKI
        path = /opt/shares1
        # 表示用户组
        valid users = @cnki
        read only = No

5) 重启smb服务

systemctl restart smb.service
# 检查smb.conf文件是否配置正确
testparm

6) 至此配置完成, 可以在Windows平台下通过用户名share/share来访问共享目录了.