Linux CentOS 6.5 yum安装MongoDB的操作

安装mongodb-3.6.4版本

执行命令

wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.6.4.tgz

如果出现错误,则在root下更新wget,执行

yum upgrade wget

再去执行以上wget命令即可。

下载完成后,进行解压

tar -zxvf mongodb-linux-x86_64-rhel62-3.6.4.tgz

如果嫌解压后文件名称过长,可以进行重命名

mv mongodb-linux-x86_64-rhel62-3.6.4 mongodb

进入到mongodb目录下

cd mongodb

创建db和日志目录

mkdir data
mkdir -p data/db
mkdir -p data/logs

在logs目录下创建mongodb.log文件

touch mongodb.log

在data目录下创建mongodb.conf文件

cd mongodb/data
vi mongodb.conf
port=8087

dbpath=/opt/mongodb/mongodb/data/db

logpath=/opt/mongodb/mongodb/data/logs/mongodb.log

fork=true

logappend=true

启动

在mongodb目录下执行

./bin/mongod --config /opt/mongodb/mongodb/data/mongodb.conf

可以看到以下内容:

未分类

进入到mongodb进行操作

./bin/mongo

无法连接 127.0.01:27017,经过分析,是防火墙端口没有开放。

未分类

可以看到有很多警告信息,没有关系,因为接下来要创建用户,mongodb默认情况下没有用户,需要创建,授权。

> use admin
switched to db admin
> db.system.users.find();

没有任何输出,这时则创建用户,我创建的是一个超级用户

db.createUser(
... {
... user:"root",
... pwd:"554466",
... roles:[{role:"root",db:"admin"}]
... }
... )
Successfully added user: {
    "user" : "root",
    "roles" : [
        {
            "role" : "root",
            "db" : "admin"
        }
    ]

}

然后关闭mongodb,执行命令db.shutdownServer();

exit;退出mongodb客户端,重新编辑配置文件vi data/mongodb.conf

加入一行auth=true,保存退出,再次启动mongodb,此时就不会出现警告信息,进入客户端,进行用户验证。

未分类

Linux(centos 6.X)环境下LVS-NAT模式高可用负载均衡集群系统快速配置

本文简单记录下Linux环境下lvs-nat模式(基础调度器路由转发)负载均衡简单配置,揭开这个神秘东西的面纱,让你五分钟钟搞定配置LVS-NAT。

  • 环境配置: 三台centos 6.5
  • 调度器: DIP:192.168.1.11 VIP:192.168.1.110
  • web服务器: RIP:192.168.1.9 RIP:192.168.1.10

一、前期服务器环境搭建

由于是之前kvm克隆了dr模式下的服务器,这里和dr下边的IP和服务器环境信息是一样的。只不过是web服务器取消了arp禁响应和VIP配置。

(1) 配置lamp环境,这里不做演示。
(2)关闭selinux、调度器和所有web服务器上的iptables
(3)设置时间同步,保证服务器时间一致(后期nfs或数据同步用到,包括session同步需要)

二、配置调度器

1. 开启IP转发

vi /etc/sysctl.conf
net.ipv4.ip_forward = 1

sysctl -p可以查看是否开启。

2. 调度器安装ipvsadm和keepalived

首先安装依赖包:

yum -y install gcc make openssl-devel openssl net-snmp net-snmp-devel popt popt-devel

安装ipvs和keepalived:

yum install ipvsadm  keepalived  -y
chkconfig ipvsadm on
chkconfig keepalived on

修改keepalived.conf配置文件配置服务器IP信息:

! Configuration File for keepalived

global_defs {   
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state MASTER             #备用机器这里需要更改成BACKUP
    interface eth0           
    virtual_router_id 51     
    priority 100             #备机优先级设置低于100的数值。数值越高优先级越高。
    advert_int 1             
    authentication {         
        auth_type PASS       
        auth_pass 1111       
    }
    virtual_ipaddress {      
        192.168.1.110        
    }
}

virtual_server 192.168.1.110 80 {
    delay_loop 6       
    lb_algo rr        
    lb_kind NAT        
    nat_mask 255.255.255.0
    persistence_timeout 50  
    protocol TCP            

    real_server 192.168.1.9 80 {       
        weight 3                       
        TCP_CHECK {  
            connect_timeout 3          
            nb_get_retry 3
            delay_before_retry 3
            connect_port 80
        }
    }
    real_server 192.168.1.10 80 {
        weight 3
        TCP_CHECK {
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
            connect_port 80
        }
    }

}

配置完以后启动keepalived:

service keepalived start

现在通过ipvsadm查看ip信息:

[root@natlb ~]# ipvsadm  -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.1.110:http rr persistent 50
  -> 192.168.0.9:http             Masq    3      0          0         
  -> 192.168.0.10:http            Masq    3      0          0  

三、真实服务器配置

这个无需做特殊配置,只是把网关设置成VIP就可以了。设置以后可以用route查看网关。

好了。配置完成。测试下论坛访问,依旧是1.9和1.10轮流提供web访问。

拓展部分:

如果需要进行nfs系统配置,请参考《Linux下网络文件系统NFS的配置实现数据共享》
如果要进行mysql主从配置,请参考《mysql数据库如何设置互为主从》
DR模式配置过程,请参考《Linux(centos 6.X)环境下LVS-DR模式负载均衡集群系统快速配置》

补充LVS添加url检测防止假死:

real_server 192.168.1.120 80  {
     weight  50
     HTTP_GET {
        url {
            path   /ok.php
            status_code 200
        }
        connect_timeout  10
        nb_get_retry 3
        delay_before_retry 3
     }
}

Linux文本处理命令grep

1. 查找单个关键字

举例:查找Baiduspider访问日志,并输出行号。

# grep -n Baiduspider /usr/local/nginx1.14/logs/access.log

3371:180.76.15.137 - - [31/Dec/2018:05:05:46 +0800] "GET /asset/detail/show/RG9ja2VyIENFIDE4LjA5IOS9v+eUqERvY2tlcmZpbGXlronoo4 HTTP/1.1" 200 4748 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
4570:180.76.15.11 - - [08/Jan/2019:16:57:04 +0800] "GET /asset/detail/show/RG9ja2VyIENFIDE4LjA5IOS9v+eUqERvY2tlcmZpbGXlronoo4 HTTP/1.1" 200 7754 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
4693:180.76.15.20 - - [09/Jan/2019:05:29:54 +0800] "GET /asset/detail/show/Q2VudE9TNy41IE15U1FMOC4wLjEzIFJQTea6kOeggee8luivke HTTP/1.1" 200 16691 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
4924:180.76.15.160 - - [10/Jan/2019:22:30:28 +0800] "GET /asset/detail/show/SlF1ZXJ5My4zLjEg6YCJ5oup5Zmo HTTP/1.1" 200 10049 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
...

2. 查找多个关键字

举例:查找Baiduspider、bingbot、Googlebot访问日志,并输出行号。

# grep -n -E 'Baiduspider|bingbot|Googlebot' /usr/local/nginx1.14/logs/access.log

5620:203.208.60.0 - - [14/Jan/2019:10:26:09 +0800] "GET / HTTP/1.1" 200 17232 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
5639:180.76.15.21 - - [14/Jan/2019:16:39:35 +0800] "GET / HTTP/1.1" 200 17232 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
5888:157.55.39.40 - - [17/Jan/2019:13:45:05 +0800] "GET / HTTP/1.1" 200 17232 "-" "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"
...

3. 多文件查找关键字

举例:查找10.100.78.12服务器多天的监控日志。

# grep 10.100.78.12 2018-12-10.bjyz.network.monitor.log 2018-12-11.bjyz.network.monitor.log

2018-12-10.bjyz.network.monitor.log:[2018-12-10]|[13:10:00]|10.100.78.12|active
2018-12-11.bjyz.network.monitor.log:[2018-12-11]|[13:10:00]|10.100.78.12|down

4. 递归查找关键字

举例:递归查找10.100.78.12服务器多天的监控日志。

grep -r 10.100.78.12

2018-12-10.bjyz.network.monitor.log:[2018-12-10]|[13:10:00]|10.100.78.12|active
2018-12-11.bjyz.network.monitor.log:[2018-12-11]|[13:10:00]|10.100.78.12|down
2018-12-12.bjyz.network.monitor.log:[2018-12-12]|[13:10:00]|10.100.78.12|active

5. 查找指定进程

举例:查找java进程。

# ps -ef | grep java

6. 查找进程个数

举例:查找java进程个数。

# ps -ef | grep -c java

在 Linux 中打扮你的冬季 Bash 提示符

你的 Linux 终端可能支持 Unicode,那么为何不利用它在提示符中添加季节性的图标呢?

欢迎再次来到 Linux 命令行玩具日历的另一篇。如果这是你第一次访问该系列,你甚至可能会问自己什么是命令行玩具?我们对此比较随意:它会是终端上有任何有趣的消遣,对于任何节日主题相关的还有额外的加分。

也许你以前见过其中的一些,也许你没有。不管怎样,我们希望你玩得开心。

今天的玩具非常简单:它是你的 Bash 提示符。你的 Bash 提示符?是的!我们还有几个星期的假期可以盯着它看,在北半球冬天还会再多几周,所以为什么不玩玩它。

目前你的 Bash 提示符号可能是一个简单的美元符号( $),或者更有可能是一个更长的东西。如果你不确定你的 Bash 提示符是什么,你可以在环境变量 $PS1 中找到它。要查看它,请输入:

echo $PS1

对于我而言,它返回:

[u@h W]$

uhW 分别是用户名、主机名和工作目录的特殊字符。你还可以使用其他一些符号。为了帮助构建你的 Bash 提示符,你可以使用 EzPrompt,这是一个 PS1 配置的在线生成器,它包含了许多选项,包括日期和时间、Git 状态等。

你可能还有其他变量来组成 Bash 提示符。对我来说,$PS2 包含了我命令提示符的结束括号。有关详细信息,请参阅 这篇文章。

要更改提示符,只需在终端中设置环境变量,如下所示:

$ PS1='u is cold: '
jehb is cold:

要永久设置它,请使用你喜欢的文本编辑器将相同的代码添加到 /etc/bashrc 中。

那么这些与冬季化有什么关系呢?好吧,你很有可能有现代一下的机器,你的终端支持 Unicode,所以你不仅限于标准的 ASCII 字符集。你可以使用任何符合 Unicode 规范的 emoji,包括雪花 ❄、雪人 ☃ 或一对滑雪板。你有很多冬季 emoji 可供选择。

  • 圣诞树
  • 外套
  • 鹿手套
  • 圣诞夫人
  • 圣诞老人
  • 围巾
  • 滑雪者
  • 滑雪板
  • 雪花
  • 雪人
  • 没有雪的雪人
  • 包装好的礼物

选择你最喜欢的,享受冬天的欢乐。有趣的事实:现代文件系统也支持文件名中的 Unicode 字符,这意味着技术上你可以将你下个程序命名为 ❄❄❄❄❄.py。只是说说,不要这么做。

解决Linux环境下执行脚本时报错:/bin/bash^M: 坏的解释器: 没有那个文件或目录

一、问题描述

我在Windows 10 系统下编辑了一个发送消息到企业微信的shell脚本文件,然后copy到了远程的Linux服务器,当运行的时候报错了。如下所示:

未分类

root@ubuntu116:/data/gitlabData/auto_back_shell# ./qiyewechat-notifier.sh 
-bash: ./qiyewechat-notifier.sh: /bin/bash^M: 坏的解释器: 没有那个文件或目录
root@ubuntu116:/data/gitlabData/auto_back_shell# 

二、错误原因

这个文件在Windows 下编辑过,在Windows下每一行结尾是nr,而Linux下则是n,所以才会有 多出来的r。

三、修改错误

使用指令sed -i 's/r$//' xxxxxxx.sh,上面的指令会把 xxxxxxx.sh 中的r 替换成空白!
实操一下:

未分类

root@ubuntu116:/data/gitlabData/auto_back_shell# sed -i 's/r$//' qiyewechat-notifier.sh 
您在 /var/mail/root 中有新邮件
root@ubuntu116:/data/gitlabData/auto_back_shell# ./qiyewechat-notifier.sh -h 
./qiyewechat-notifier.sh: 非法选项 -- h
Usage:
  qiyewechat.sh [-u USER] [-t TITLE] [-c CONTENT] [-d DETAIL] [-p PICTURE]
Description:
    USER, 用户.
    TITLE, 标题.
    CONTENT, 内容.
    DETAIL, 细节.
    PICTURE, 图片.
root@ubuntu116:/data/gitlabData/auto_back_shell# 

如上所示,执行了下面的脚本之后,

sed -i 's/r$//' qiyewechat-notifier.sh

qiyewechat-notifier.sh就可以正常运行了!

四、附录

qiyewechat-notifier.sh的部分代码如下所示:

未分类

#!/bin/bash

#用法提示
usage() {
    echo "Usage:"
    echo "  qiyewechat.sh [-u USER] [-t TITLE] [-c CONTENT] [-d DETAIL] [-p PICTURE]"
    echo "Description:"
    echo "    USER, 用户."
    echo "    TITLE, 标题."
    echo "    CONTENT, 内容."
    echo "    DETAIL, 细节."
    echo "    PICTURE, 图片."
    exit -1
}


# 获取脚本执行时的选项
while getopts u:t:c:d:p: option
do
   case "${option}"  in
                u) USER=${OPTARG};;
                t) TITLE=${OPTARG};;
                c) CONTENT=${OPTARG};;
                d) DETAIL=${OPTARG};;
                p) PICTURE=${OPTARG};;
                h) usage;;
                ?) usage;;
   esac
   echo $option
   echo $OPTARG

done

Linux 防火墙 阻止ssh暴力测试

Fedora CentOS 系统下有效

安装fail2ban服务

dnf install fail2ban -y

设置开机启动

systemctl enable fail2ban

启动服务

systemctl start fail2ban

编辑文件

vim /etc/fail2ban/jail.conf

...
...
...

[ssh]
enabled = true
filter   = sshd
logpath = /var/log/secure
maxretry = 1
findtime = 300
bantime = 86400
ignoreip = 192.168.1.1/24,192.168.0.1/24

重启fail2ban

systemctl restart fail2ban

Linux 惊群效应之 Nginx 解决方案

前言

因为项目涉及到 Nginx 一些公共模块的使用,而且也想对惊群效应有个深入的了解,在整理了网上资料以及实践后,记录成文章以便大家复习巩固。

结论

  • 不管还是多进程还是多线程,都存在惊群效应,本篇文章使用多进程分析。
  • 在 Linux2.6 版本之后,已经解决了系统调用 accept 的惊群效应(前提是没有使用 select、poll、epoll 等事件机制)。
  • 目前 Linux 已经部分解决了 epoll 的惊群效应(epoll 在 fork 之前),Linux2.6 是没有解决的。
  • Epoll 在 fork 之后创建仍然存在惊群效应,Nginx 使用自己实现的互斥锁解决惊群效应。

惊群效应是什么

惊群效应(thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应。

惊群效应消耗了什么

  • Linux 内核对用户进程(线程)频繁地做无效的调度、上下文切换等使系统性能大打折扣。上下文切换(context switch)过高会导致 CPU 像个搬运工,频繁地在寄存器和运行队列之间奔波,更多的时间花在了进程(线程)切换,而不是在真正工作的进程(线程)上面。直接的消耗包括 CPU 寄存器要保存和加载(例如程序计数器)、系统调度器的代码需要执行。间接的消耗在于多核 cache 之间的共享数据。
  • 为了确保只有一个进程(线程)得到资源,需要对资源操作进行加锁保护,加大了系统的开销。目前一些常见的服务器软件有的是通过锁机制解决的,比如 Nginx(它的锁机制是默认开启的,可以关闭);还有些认为惊群对系统性能影响不大,没有去处理,比如 Lighttpd。

Linux 解决方案之 Accept

Linux 2.6 版本之前,监听同一个 socket 的进程会挂在同一个等待队列上,当请求到来时,会唤醒所有等待的进程。

Linux 2.6 版本之后,通过引入一个标记位 WQ_FLAG_EXCLUSIVE,解决掉了 accept 惊群效应。

具体分析会在代码注释里面,accept代码实现片段如下:

// 当accept的时候,如果没有连接则会一直阻塞(没有设置非阻塞)
// 其阻塞函数就是:inet_csk_accept(accept的原型函数)  
struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
{
    ...  
    // 等待连接 
    error = inet_csk_wait_for_connect(sk, timeo); 
    ...  
}

static int inet_csk_wait_for_connect(struct sock *sk, long timeo)
{
    ...
    for (;;) {  
        // 只有一个进程会被唤醒。
        // 非exclusive的元素会加在等待队列前头,exclusive的元素会加在所有非exclusive元素的后头。
        prepare_to_wait_exclusive(sk_sleep(sk), &wait,TASK_INTERRUPTIBLE);  
    }  
    ...
}

void prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)  
{  
    unsigned long flags;  
    // 设置等待队列的flag为EXCLUSIVE,设置这个就是表示一次只会有一个进程被唤醒,我们等会就会看到这个标记的作用。  
    // 注意这个标志,唤醒的阶段会使用这个标志。
    wait->flags |= WQ_FLAG_EXCLUSIVE;  
    spin_lock_irqsave(&q->lock, flags);  
    if (list_empty(&wait->task_list))  
        // 加入等待队列  
        __add_wait_queue_tail(q, wait);  
    set_current_state(state);  
    spin_unlock_irqrestore(&q->lock, flags);  
}

唤醒阻塞的 accept 代码片段如下:

// 当有tcp连接完成,就会从半连接队列拷贝socket到连接队列,这个时候我们就可以唤醒阻塞的accept了。
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
    ...
    // 关注此函数
    if (tcp_child_process(sk, nsk, skb)) { 
        rsk = nsk;  
        goto reset;  
    }
    ...
}

int tcp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb)
{
    ...
    // Wakeup parent, send SIGIO 唤醒父进程
    if (state == TCP_SYN_RECV && child->sk_state != state)  
        // 调用sk_data_ready通知父进程
        // 查阅资料我们知道tcp中这个函数对应是sock_def_readable
        // 而sock_def_readable会调用wake_up_interruptible_sync_poll来唤醒队列
        parent->sk_data_ready(parent, 0);  
    }
    ...
}

void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, void *key)  
{  
    ...  
    // 关注此函数
    __wake_up_common(q, mode, nr_exclusive, wake_flags, key);  
    spin_unlock_irqrestore(&q->lock, flags);  
    ...  
} 

static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, int wake_flags, void *key)
{
    ...
    // 传进来的nr_exclusive是1
    // 所以flags & WQ_FLAG_EXCLUSIVE为真的时候,执行一次,就会跳出循环
    // 我们记得accept的时候,加到等待队列的元素就是WQ_FLAG_EXCLUSIVE的
    list_for_each_entry_safe(curr, next, &q->task_list, task_list) {  
        unsigned flags = curr->flags;  
        if (curr->func(curr, mode, wake_flags, key) 
        && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
        break; 
    }
    ...
}

Linux 解决方案之 Epoll

在使用 select、poll、epoll、kqueue 等 IO 复用时,多进程(线程)处理链接更加复杂。
在讨论 epoll 的惊群效应时候,需要分为两种情况:

  • epoll_create 在 fork 之前创建
  • epoll_create 在 fork 之后创建

epoll_create 在 fork 之前创建

与 accept 惊群的原因类似,当有事件发生时,等待同一个文件描述符的所有进程(线程)都将被唤醒,而且解决思路和 accept 一致。

为什么需要全部唤醒?因为内核不知道,你是否在等待文件描述符来调用 accept() 函数,还是做其他事情(信号处理,定时事件)。

此种情况惊群效应已经被解决。

epoll_create 在 fork 之后创建

epoll_create 在 fork 之前创建的话,所有进程共享一个 epoll 红黑数。
如果我们只需要处理 accept 事件的话,貌似世界一片美好了。但是 epoll 并不是只处理 accept 事件,accept 后续的读写事件都需要处理,还有定时或者信号事件。

当连接到来时,我们需要选择一个进程来 accept,这个时候,任何一个 accept 都是可以的。当连接建立以后,后续的读写事件,却与进程有了关联。一个请求与 a 进程建立连接后,后续的读写也应该由 a 进程来做。

当读写事件发生时,应该通知哪个进程呢?Epoll 并不知道,因此,事件有可能错误通知另一个进程,这是不对的。所以一般在每个进程(线程)里面会再次创建一个 epoll 事件循环机制,每个进程的读写事件只注册在自己进程的 epoll 种。

我们知道 epoll 对惊群效应的修复,是建立在共享在同一个 epoll 结构上的。epoll_create 在 fork 之后执行,每个进程有单独的 epoll 红黑树,等待队列,ready 事件列表。因此,惊群效应再次出现了。有时候唤醒所有进程,有时候唤醒部分进程,可能是因为事件已经被某些进程处理掉了,因此不用在通知另外还未通知到的进程了。

Nginx 解决方案之锁的设计

首先我们要知道在用户空间进程间锁实现的原理,起始原理很简单,就是能弄一个让所有进程共享的东西,比如 mmap 的内存,比如文件,然后通过这个东西来控制进程的互斥。

Nginx 中使用的锁是自己来实现的,这里锁的实现分为两种情况,一种是支持原子操作的情况,也就是由 NGX_HAVE_ATOMIC_OPS 这个宏来进行控制的,一种是不支持原子操作,这是是使用文件锁来实现。

锁结构体

如果支持原子操作,则我们可以直接使用 mmap,然后 lock 就保存 mmap 的内存区域的地址
如果不支持原子操作,则我们使用文件锁来实现,这里 fd 表示进程间共享的文件句柄,name 表示文件名

typedef struct {  
#if (NGX_HAVE_ATOMIC_OPS)  
    ngx_atomic_t  *lock;  
#else  
    ngx_fd_t       fd;  
    u_char        *name;  
#endif  
} ngx_shmtx_t;

原子锁创建

// 如果支持原子操作的话,非常简单,就是将共享内存的地址付给loc这个域
ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name)  
{  
    mtx->lock = addr;  


   return NGX_OK;  
} 

原子锁获取

TryLock,它是非阻塞的,也就是说它会尝试的获得锁,如果没有获得的话,它会直接返回错误。
Lock,它也会尝试获得锁,而当没有获得他不会立即返回,而是开始进入循环然后不停的去获得锁,知道获得。不过 Nginx 这里还有用到一个技巧,就是每次都会让当前的进程放到 CPU 的运行队列的最后一位,也就是自动放弃 CPU。

原子锁实现
如果系统库支持的情况,此时直接调用OSAtomicCompareAndSwap32Barrier,即 CAS。

#define ngx_atomic_cmp_set(lock, old, new)                                   
    OSAtomicCompareAndSwap32Barrier(old, new, (int32_t *) lock) 

如果系统库不支持这个指令的话,Nginx 自己还用汇编实现了一个。

static ngx_inline ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,  
    ngx_atomic_uint_t set)  
{  
    u_char  res;  

    __asm__ volatile (  

         NGX_SMP_LOCK  
    "    cmpxchgl  %3, %1;   "  
    "    sete      %0;       "  

    : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");  

    return res;  
}

原子锁释放

Unlock 比较简单,和当前进程 id 比较,如果相等,就把 lock 改为 0,说明放弃这个锁。

#define ngx_shmtx_unlock(mtx) (void) ngx_atomic_cmp_set((mtx)->lock, ngx_pid, 0)  

Nginx 解决方案之惊群效应

变量分析

 // 如果使用了 master worker,并且 worker 个数大于 1,并且配置文件里面有设置使用 accept_mutex. 的话,设置
 ngx_use_accept_mutex  
 if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) 
 {  
        ngx_use_accept_mutex = 1;  
        // 下面这两个变量后面会解释。  
        ngx_accept_mutex_held = 0;  
        ngx_accept_mutex_delay = ecf->accept_mutex_delay;  
 } else {  
        ngx_use_accept_mutex = 0;  
 }

ngx_use_accept_mutex 这个变量,如果有这个变量,说明 Nginx 有必要使用 accept 互斥体,这个变量的初始化在 ngx_event_process_init 中。
ngx_accept_mutex_held 表示当前是否已经持有锁。
ngx_accept_mutex_delay 表示当获得锁失败后,再次去请求锁的间隔时间,这个时间可以在配置文件中设置的。

ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;

ngx_accept_disabled,这个变量是一个阈值,如果大于 0,说明当前的进程处理的连接过多。

是否使用锁

// 如果有使用mutex,则才会进行处理。  
if (ngx_use_accept_mutex) 
{  
    // 如果大于0,则跳过下面的锁的处理,并减一。  
    if (ngx_accept_disabled > 0) {  
        ngx_accept_disabled--; 
    } else {  
        // 试着获得锁,如果出错则返回。  
        if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {  
            return;  
        }  
        // 如果ngx_accept_mutex_held为1,则说明已经获得锁,此时设置flag,这个flag后面会解释。
        if (ngx_accept_mutex_held) {  
            flags |= NGX_POST_EVENTS;  
        } else {  
            // 否则,设置timer,也就是定时器。接下来会解释这段。  
            if (timer == NGX_TIMER_INFINITE  
                 || timer > ngx_accept_mutex_delay) {  
                timer = ngx_accept_mutex_delay;  
            }  
        }  
    }  
}

NGX_POST_EVENTS 标记,设置了这个标记就说明当 socket 有数据被唤醒时,我们并不会马上 accept 或者说读取,而是将这个事件保存起来,然后当我们释放锁之后,才会进行 accept 或者读取这个句柄。

// 如果ngx_posted_accept_events不为NULL,则说明有accept event需要nginx处理。  
if (ngx_posted_accept_events) {  
        ngx_event_process_posted(cycle, &ngx_posted_accept_events);  
}

如果没有设置 NGX_POST_EVENTS 标记的话,Nginx 会立即 Accept 或者读取句柄

定时器,这里如果 Nginx 没有获得锁,并不会马上再去获得锁,而是设置定时器,然后在 epoll 休眠(如果没有其他的东西唤醒)。此时如果有连接到达,当前休眠进程会被提前唤醒,然后立即 accept。否则,休眠 ngx_accept_mutex_delay时间,然后继续 tryLock。

获取锁来解决惊群

ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle)  
{  
    // 尝试获得锁  
    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {  
        // 如果本来已经获得锁,则直接返回Ok  
        if (ngx_accept_mutex_held  
            && ngx_accept_events == 0  
            && !(ngx_event_flags & NGX_USE_RTSIG_EVENT))  
        {  
            return NGX_OK;  
        }  

        // 到达这里,说明重新获得锁成功,因此需要打开被关闭的listening句柄。  
        if (ngx_enable_accept_events(cycle) == NGX_ERROR) {  
            ngx_shmtx_unlock(&ngx_accept_mutex);  
            return NGX_ERROR;  
        }  

        ngx_accept_events = 0;  
        // 设置获得锁的标记。  
        ngx_accept_mutex_held = 1;  

        return NGX_OK;  
    }  

    // 如果我们前面已经获得了锁,然后这次获得锁失败
    // 则说明当前的listen句柄已经被其他的进程锁监听
    // 因此此时需要从epoll中移出调已经注册的listen句柄
    // 这样就很好的控制了子进程的负载均衡  
    if (ngx_accept_mutex_held) {  
        if (ngx_disable_accept_events(cycle) == NGX_ERROR) {  
            return NGX_ERROR;  
        }  
        // 设置锁的持有为0.  
        ngx_accept_mutex_held = 0;  
    }  

    return NGX_OK;  
} 

如上代码,当一个连接来的时候,此时每个进程的 epoll 事件列表里面都是有该 fd 的。抢到该连接的进程先释放锁,在 accept。没有抢到的进程把该 fd 从事件列表里面移除,不必再调用 accept,造成资源浪费。

同时由于锁的控制(以及获得锁的定时器),每个进程都能相对公平的 accept 句柄,也就是比较好的解决了子进程负载均衡。

tcpdump 基于mac地址抓取数据包

1、刚刚接触tcpdump时,常用tcpdump -i eth1 host 192.168.1.1 这个命令基于ip地址抓取数据包信息。

tcpdump -i eth1(接口名称) host 192.168.1.1(计算机IP地址)

2、在分析客户的网络中,经常会用到设备中自带的tcpdump软件,再配合PC端的wireshark软件来简单检查分析客户的网络情况。

这时候经常用到的tcpdump参数为:

tcpdump -i eth1 -nn(不做地址解析) -s0(抓取数据包长度不限制) -v(显示详细信息,需要显示更详细信息,可再加两个) -e (列出链路层头部) -c 20 (抓取指定个数的数据包,比如此处写20个,则为抓取20个包就停止)

如果不加-n参数的话,抓取的数据包会显示主机名或者域名信息,端口也会显示为相关的服务,如抓80端口,会显示为http

如果不加-s0参数的话,默认只抓取一部分(68字节),则数据包在wireshark中打开,会显示数据包不完整

3、在分析dhcp数据包的交互(IP地址下发),arp攻击等问题时,会涉及到链路层头部的抓取,也就是mac地址。抓取命令为

tcpdump -i eth1 ether src 6c:41:6a:ac:11:42 -c 10

在接口eth1上,抓取源mac地址为6c:41:6a:ac:01:42的数据包,个数为10

Linux安装MySQL

1、下载MySQL服务,MySQL下载地址 https://dev.mysql.com/downloads/mysql/

未分类

2、选择对应的系统下载之后,通过共享文件的方式,传到服务器/usr/local/目录下。然后解压

tar zxvf mysql-5.6.42-linux-glibc2.12-x86_64.tar.gz -C ./

3、安装依赖

yum -y install perl perl-devel autoconf libaio

4、复制解压后的mysql目录到系统的本地软件目录

cp -r mysql-5.6.33-linux-glibc2.5-x86_64 /usr/local/mysql

5、添加系统mysql组和mysql用户

groupadd mysql
useradd -r -g mysql -s /bin/false mysql

6、进入安装mysql软件目录,修改目录拥有者为mysql用户

cd mysql/
chown -R mysql:mysql ./

7、安装数据库,在/usr/local/mysql目录下执行下面命令

./scripts/mysql_install_db --user=mysql

8、修改当前目录拥有者为root用户

chown -R root:root ./

9、修改当前data目录拥有者为mysql用户

chown -R mysql:mysql data

10、添加mysql服务开机自启动,把启动脚本放到开机初始化目录。

cp support-files/mysql.server /etc/init.d/mysql
# 赋予可执行权限
chmod +x /etc/init.d/mysql
# 添加服务
chkconfig --add mysql 

11、查看服务列表

chkconfig --list

未分类

如果看到mysql的服务,并且3,4,5都是on的话则成功。如果是off,则执行

chkconfig --level 345 mysql on

12、先创建缺少的文件夹,再启动MySQL服务

#创建缺少的文件夹
mkdir /var/log/mariadb
#启动服务
service mysql start

如果在启动服务的时候出现下面这个问题:

未分类

这时候,需要创建一个日志文件:

touch /var/log/mariadb/mariadb.log

创建日志文件之后再启动,就OK了。

未分类

13、把mysql客户端放到默认路径

ln -s /usr/local/mysql/bin/mysql /usr/local/bin/mysql

14、登录MySQL

#默认root用户没有密码
mysql -uroot -p

这时候会出现一个错误:ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

解决方法:打开/etc/my.cnf,看看里面配置的socket位置是什么目录。“socket=/var/lib/mysql/mysql.sock”;路径和“/tmp/mysql.sock”不一致。
建立一个软连接:ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock

15、设置root用户密码

SET PASSWORD FOR 'root'@'localhost' = PASSWORD('root');

未分类

结语:到这里,MySQL服务就算安装完成了。

Linux下rsync 数据镜像备份 client / server 模式

rsync特性:

  • 可以镜像保存整个目录树和文件系统
  • 可以增量同步数据,文件传输效率高,因而同步时间很短。
  • 可以保持原有文件的权限、时间等属性。
  • 加密传输数据,保证了数据的安全性

两种模式:

  • client / server
  • client / client

安装rsync

yum install rsync

查看rsync版本

rpm -qa rsync
rsync-3.1.2-4.el7.x86_64

查看rsync安装位置

rpm -ql rsync

未分类

在服务器端:

编辑rsync配置文件

vim /etc/rsyncd

内容如下

uid = nobody
gid = nobody
use chroot = no
max connections = 10
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log

[rsync_module_1]
path = /rsync_data_1
comment = rsync_data for sharing to client server
ignore errors
read only = true
list = false
uid = root
gid = root
auth users = rsync_bak
secrets file = /etc/rsync_server.pass

未分类

按照配置文件内容,创建/etc/rsync_server.pass 文件,且权限为600
内容

rsync_bak:rsyncpwd
chmod 600 /etc/rsync_server.pass

未分类

根据配置文件创建相应的目录

mkdir -p /rsync_data_1

启动rsync服务和验证

/usr/bin/rsync --daemon
ps -ef | grep rsync
lsof -c rsync
netstat -antlp | grep rsync

未分类

未分类

客户端

确保安装了rsync组件

创建客户端rsync的密码文件,其密码和服务器端一致,且文件权限为600

vim /etc/rsync_client.pass
cat /etc/rsync_client.pass
rsyncpwd
chmod 600 /etc/rsync_client.pass

客户端指定/创建rsync的目录

mkdir -p /rsync_data

从服务器端拉取数据

/usr/bin/rsync -vzrtopg --delete --progress rsync_bak@192.168.199.185::rsync_module_1 /rsync_data --password-file=/etc/rsync_client.pass

验证是否成功从服务器端拉取到数据

ll /rsync_data/

未分类