part8.all

一、redis搭建哨兵原理和集群实现

1、redis哨兵模式

1.1搭建哨兵的原理

sentinel的中文含义是哨兵、守卫。也就是说既然主从模式中,当master节点挂了以后,slave节点不能主动选举一个master节点出来,那么我就安排一个或多个sentinel来做这件事,当sentinel发现master节点挂了以后,sentinel就会从slave中重新选举一个master。

对哨兵模式的理解
①sentinel模式是建立在主从模式的基础上,如果只有一个Redis节点,sentinel就没有任何意义。

②当master节点挂了以后,sentinel会在slave中选择一个做为master,并修改它们的配置文件,其他slave的配置文件也会被修改,比如slaveof属性会指向新的master。

③当master节点重新启动后,它将不再是master,而是作为slave接收新的master节点的同步数据

④sentinel因为也是一个进程有挂掉的可能,所以sentinel也会启动多个形成一个sentinel集群。

⑤当主从模式配置密码时,sentinel也会同步将配置信息修改到配置文件中,不需要担心。

⑥一个sentinel或sentinel集群可以管理多个主从Redis。

⑦sentinel最好不要和Redis部署在同一台机器,不然Redis的服务器挂了以后,sentinel也挂了。

⑧sentinel监控的Redis集群都会定义一个master名字,这个名字代表Redis集群的master Redis。

当使用sentinel模式的时候,客户端就不要直接连接Redis,而是连接sentinel的ip和port,由sentinel来提供具体的可提供服务的Redis实现,这样当master节点挂掉以后,sentinel就会感知并将新的master节点提供给使用者。

Sentinel本身也支持集群,只使用单个sentinel进程来监控redis集群是不可靠的,当sentinel进程宕掉后,sentinel本身也有单点问题。所以有必要将sentinel集群,这样有几个好处:

  1. 如果只有一个sentinel进程,如果这个进程运行出错,或者是网络堵塞,那么将无法实现redis集群的主备切换(单点问题)。
  2. 如果有多个sentinel,redis的客户端可以随意地连接任意一个sentinel来获得关于redis集群中的信息。
  3. sentinel集群自身也需要多数机制,也就是2个sentinel进程时,挂掉一个另一个就不可用了。

注意:Redis Sentinel中的Sentinel节点个数应该为大于等于3且最好为奇数

1.2搭建哨兵模式

实验环境(测试环境将sentinel和redis上放一起以减少机器运行,生产环境不放一起)
192.168.217.135 主服务器和sentinel
192.168.217.134 从服务器1和sentinel
192.168.217.133 从服务器2和sentinel

1.2.1配置节点文件

#在所有节点执行
[root@redis-master/slave ~]#vim /etc/redis.conf
bind 0.0.0.0
masterauth "123456"
requirepass "123456"

#在所有从节点执行
[root@redis-slave]#echo "replicaof 192.168.217.135 6379" >> /etc/redis.conf

#修改所有节点的哨兵配置
[root@redis-slave1 ~]#cp /usr/local/src/redis-6.2.5/sentinel.conf /apps/redis/etc/sentinel.conf
[root@redis-slave1 ~]#chown -R redis.redis /apps/redis/etc/sentinel.conf 
[root@redis-slave1 ~]#vim /apps/redis/etc/sentinel.conf
port 26379
daemonize no
pidfile "redis-sentinel.pid"
logfile "sentinel_26379.log"
dir "/apps/redis/log"
sentinel monitor mymaster 192.168.217.135 6379 
sentinel auth-pass mymaster 123456
sentinel down-after-milliseconds mymaster 3000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes

[root@redis-slave1 ~]# grep "^[a-Z]" /apps/redis/etc/sentinel.conf
bind 0.0.0.0
port 26379
daemonize yes
pidfile "redis-sentinel.pid"
logfile "sentinel_26379.log"
dir "/apps/redis/log"
sentinel monitor mymaster 192.168.217.135 6379 
sentinel down-after-milliseconds mymaster 30000
sentinel auth-pass mymaster 123456
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes


[root@redis-slave1 ~]#scp /apps/redis/etc/sentinel.conf 192.168.217.135:/apps/redis/etc/
[root@redis-slave1 ~]#scp /apps/redis/etc/sentinel.conf 192.168.217.133:/apps/redis/etc/

1.2.2启动哨兵

[root@redis-master/slave ~]#/apps/redis/bin/redis-sentinel /apps/redis/etc/sentinel.conf --sentinel

正常启动日志
[root@redis-slave1 ~]#tail -f /apps/redis/log/sentinel_26379.log
6112:X 03 Nov 2022 04:49:15.391 # Configuration loaded
6112:X 03 Nov 2022 04:49:15.391 * Increased maximum number of open files to 10032 (it was originally set to 1024).
6112:X 03 Nov 2022 04:49:15.391 * monotonic clock: POSIX clock_gettime
6112:X 03 Nov 2022 04:49:15.391 * Running mode=sentinel, port=26379.
6112:X 03 Nov 2022 04:49:15.392 # Sentinel ID is e0cfac02ac91ffb0f8295802e75bd4feb5f92e15
6112:X 03 Nov 2022 04:49:15.392 # +monitor master mymaster 192.168.217.135 6379 quorum 2
6112:X 03 Nov 2022 04:49:15.393 * +slave slave 192.168.217.134:6379 192.168.217.134 6379 @ mymaster 192.168.217.135 6379
6112:X 03 Nov 2022 04:49:15.394 * +slave slave 192.168.217.133:6379 192.168.217.133 6379 @ mymaster 192.168.217.135 6379
6112:X 03 Nov 2022 04:49:17.276 * +sentinel sentinel 45a593894e173d503d5793659326c287e7e5a8db 192.168.217.135 26379 @ mymaster 192.168.217.135 6379
6112:X 03 Nov 2022 04:49:24.170 * +sentinel sentinel 8b7bdf988967d4ec0be32e33230a91c9d57d3ed6 192.168.217.133 26379 @ mymaster 192.168.217.135 6379

1.2.3模拟主服务器down

[root@redis-master ~]#systemctl stop redis.service

日志变化
[root@redis-slave2 ~]#tail -f /apps/redis/log/sentinel_26379.log
***
6251:X 03 Nov 2022 04:55:01.298 # +sdown master mymaster 192.168.217.135 6379
6251:X 03 Nov 2022 04:55:01.378 # +new-epoch 1
6251:X 03 Nov 2022 04:55:01.379 # +vote-for-leader 45a593894e173d503d5793659326c287e7e5a8db 1
6251:X 03 Nov 2022 04:55:02.176 # +config-update-from sentinel 45a593894e173d503d5793659326c287e7e5a8db 192.168.217.135 26379 @ mymaster 192.168.217.135 6379
6251:X 03 Nov 2022 04:55:02.176 # +switch-master mymaster 192.168.217.135 6379 192.168.217.133 6379
6251:X 03 Nov 2022 04:55:02.176 * +slave slave 192.168.217.134:6379 192.168.217.134 6379 @ mymaster 192.168.217.133 6379
6251:X 03 Nov 2022 04:55:02.176 * +slave slave 192.168.217.135:6379 192.168.217.135 6379 @ mymaster 192.168.217.133 6379

2、搭建集群

2.1集群架构

image.png

注意:①每个Redis 节点采用相同的相同的Redis版本、相同的密码、硬件配置
②所有Redis服务器必须没有任何数据

实验环境(测试环境采用多实例方法)
三台机器(开放6379,6380端口实现多实例)
192.168.217.135 node1:6379/6380
192.168.217.134 node2:6379/6380
192.168.217.133 node3:6379/6380

2.2启用redis cluster配置

[root@redis-node1 ~]#vim /apps/redis/etc/redis.conf 
[root@redis-node1 ~]# grep "^[a-Z]" /apps/redis/etc/redis.conf 
bind 0.0.0.0 -::1
protected-mode yes
port 6379
daemonize no
pidfile /apps/redis/run/redis_6379.pid
logfile /apps/redis/log/redis-6379.log
dbfilename dump6379.rdbs
dir /apps/redis/data/
masterauth 123456
requirepass 123456
appendonly yes
appendfilename "appendonly.aof"
cluster-enabled yes
cluster-config-file nodes-6379.conf

#实现多实例,在同一节点生成6379和6380的conf和service服务
[root@redis-node1 ~]#cp -p /apps/redis/etc/redis.conf /apps/redis/etc/redis6380.conf 
[root@redis-node1 ~]#sed -i -e 's/6379/6380/' -e 's/dbfilename dump6379\.rdb/dbfilename dump6380.rdb/' -e 's/appendfilename "appendonly6379\.aof"/appendfilename "appendonly6380.aof"/' /apps/redis/etc/redis6380.conf 

[root@redis-node1 ~]#cp /lib/systemd/system/redis.service /lib/systemd/system/redis6380.service 
[root@redis-node1 ~]#sed -i -e 's/redis.conf/redis6380.conf/' /lib/systemd/system/redis6380.service
[root@redis-node1 ~]#systemctl daemon-reload 
[root@redis-node1 ~]#systemctl enable --now redis6380.service  redis.service

#准备redis-trib.rb工具
root@redis-node1 ~]#find / -name redis-trib.rb
/usr/local/src/redis-6.2.5/src/redis-trib.rb
[root@redis-node1 ~]#cp /usr/local/src/redis-6.2.5/src/redis-trib.rb /usr/bin/
[root@redis-node1 ~]#redis-trib.rb  #依赖ruby
/usr/bin/env: ‘ruby’: No such file or directory
[root@redis-node1 ~]#yum install rubygems -y

#将生成的多实例文件复制到node2和node3
[root@redis-node1 ~]#scp /apps/redis/etc/redis.conf 192.168.217.134:/apps/redis/etc/redis.conf
[root@redis-node1 ~]#scp /apps/redis/etc/redis6380.conf 192.168.217.134:/apps/redis/etc/redis6380.conf
[root@redis-node1 ~]#scp /lib/systemd/system/redis6380.service 192.168.217.134:/lib/systemd/system/redis6380.service

[root@redis-node1 ~]#scp /apps/redis/etc/redis.conf 192.168.217.133:/apps/redis/etc/redis.conf
[root@redis-node1 ~]#scp /apps/redis/etc/redis6380.conf 192.168.217.133:/apps/redis/etc/redis6380.conf
[root@redis-node1 ~]#scp /lib/systemd/system/redis6380.service 192.168.217.133:/lib/systemd/system/redis6380.service

2.3创建集群

[root@redis-node1 ~]#redis-cli --cluster create 192.168.217.135:6379 192.168.217.134:6379 192.168.217.133:6379 192.168.217.135:6380 192.168.217.134:6380 192.168.217.133:6380 -a 123456 --cluster-replicas 1
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.217.134:6380 to 192.168.217.135:6379
Adding replica 192.168.217.133:6380 to 192.168.217.134:6379
Adding replica 192.168.217.135:6380 to 192.168.217.133:6379
M: 0220317b886f2fd4b761fc623caada4ca7396989 192.168.217.135:6379
   slots:[0-5460] (5461 slots) master
M: 89718ba22faef64b365299a3c412ba837fe74947 192.168.217.134:6379
   slots:[5461-10922] (5462 slots) master
M: d847d2418416622767fb22b548492b9d5dc1b615 192.168.217.133:6379
   slots:[10923-16383] (5461 slots) master
S: e221a42fe139d8466a1707e77331dd2aa1da26dd 192.168.217.135:6380
   replicates d847d2418416622767fb22b548492b9d5dc1b615
S: 419a959943eae2373f6a7d95afed86cb6796cdfa 192.168.217.134:6380
   replicates 0220317b886f2fd4b761fc623caada4ca7396989
S: e145eb7842622d1b535521883ee39fb0689901c7 192.168.217.133:6380
   replicates 89718ba22faef64b365299a3c412ba837fe74947
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join

>>> Performing Cluster Check (using node 192.168.217.135:6379)
M: 0220317b886f2fd4b761fc623caada4ca7396989 192.168.217.135:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: d847d2418416622767fb22b548492b9d5dc1b615 192.168.217.133:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 419a959943eae2373f6a7d95afed86cb6796cdfa 192.168.217.134:6380
   slots: (0 slots) slave
   replicates 0220317b886f2fd4b761fc623caada4ca7396989
M: 89718ba22faef64b365299a3c412ba837fe74947 192.168.217.134:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: e221a42fe139d8466a1707e77331dd2aa1da26dd 192.168.217.135:6380
   slots: (0 slots) slave
   replicates d847d2418416622767fb22b548492b9d5dc1b615
S: e145eb7842622d1b535521883ee39fb0689901c7 192.168.217.133:6380
   slots: (0 slots) slave
   replicates 89718ba22faef64b365299a3c412ba837fe74947
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

redis-cluster使用的局限性
集群的优点:

  1. 多个master读写,解决了单点故障,写性能得到比较好的提升
  2. 有从节点作为备份

集群的缺点:

  1. 成本高,机器多,维护比较麻烦
  2. 不支持读写分离,因为从节点不提供读功能,从节点只能同步主节点的数据
  3. 客户端链接的时候,还是得找一下槽位,导致响应可能会慢
  4. 有些命令例如mget,keys *等遍历所有库的,不方便
  5. 集群cluster会和sentinel哨兵冲突,建议选择一个即可,一般来说哨兵+主从足以,一个项目一套,不要混用redis即可

像磐石系统:直接使用的哨兵来实现,1主2从,加上哨兵做高可用即可

二、LVS常用模型工作原理,负载策略

1、负载均衡LVS基本介绍

LB集群的架构和原理很简单,就是当用户的请求过来时,会直接分发到Director Server上,然后它把用户的请求根据设置好的调度算法,智能均衡地分发到后端真正服务器(real server)上。为了避免不同机器上用户请求得到的数据不一样,需要用到了共享存储,这样保证所有用户请求的数据是一样的。

LVS是 Linux Virtual Server 的简称,也就是Linux虚拟服务器。这是一个由章文嵩博士发起的一个开源项目,它的官方网是 http://www.linuxvirtualserver.org 现在 LVS 已经是 Linux 内核标准的一部分。使用 LVS 可以达到的技术目标是:通过 LVS 达到的负载均衡技术和 Linux 操作系统实现一个高性能高可用的 Linux 服务器集群,它具有良好的可靠性、可扩展性和可操作性。从而以低廉的成本实现最优的性能。LVS 是一个实现负载均衡集群的开源软件项目,LVS架构从逻辑上可分为调度层、Server集群层和共享存储。

2、LVS的基本工作原理

image.png

当用户向负载均衡调度器(Director Server)发起请求,调度器将请求发往至内核空间

PREROUTING链首先会接收到用户请求,判断目标IP确定是本机IP,将数据包发往INPUT链

IPVS是工作在INPUT链上的,当用户请求到达INPUT时,IPVS会将用户请求和自己已定义好的集群服务进行比对,如果用户请求的就是定义的集群服务,那么此时IPVS会强行修改数据包里的目标IP地址及端口,并将新的数据包发往POSTROUTING链

POSTROUTING链接收数据包后发现目标IP地址刚好是自己的后端服务器,那么此时通过选路,将数据包最终发送给后端的服务器

3、LVS的组成

LVS 由2部分程序组成,包括 ipvs 和 ipvsadm。

ipvs(ip virtual server):一段代码工作在内核空间,叫ipvs,是真正生效实现调度的代码。

ipvsadm:另外一段是工作在用户空间,叫ipvsadm,负责为ipvs内核框架编写规则,定义谁是集群服务,而谁是后端真实的服务器(Real Server)

4、LVS相关术语

名称 定义
DS Director Server 指的是前端负载均衡器节点
RS Real Server。后端真实的工作服务器
VIP Virtual IP 向外部直接面向用户请求,作为用户请求的目标的IP地址
DIP Director Server IP,主要用于和内部主机通讯的IP地址
RIP Real Server IP,后端服务器的IP地址
CIP Client IP,访问客户端的IP地址

5、LVS/NAT原理和特点

image.png

5.1 重点理解NAT方式的实现原理和数据包的改变

(a). 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP
(b). PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
(c). IPVS比对数据包请求的服务是否为集群服务,若是,修改数据包的目标IP地址为后端服务器IP,然后将数据包发至POSTROUTING链。 此时报文的源IP为CIP,目标IP为RIP
(d). POSTROUTING链通过选路,将数据包发送给Real Server
(e). Real Server比对发现目标为自己的IP,开始构建响应报文发回给Director Server。 此时报文的源IP为RIP,目标IP为CIP
(f). Director Server在响应客户端前,此时会将源IP地址修改为自己的VIP地址,然后响应给客户端。 此时报文的源IP为VIP,目标IP为CIP

5.2 LVS-NAT模型的特性

RS应该使用私有地址,RS的网关必须指向DIP

DIP和RIP必须在同一个网段内

请求和响应报文都需要经过Director Server,高负载场景中,Director Server易成为性能瓶颈

支持端口映射

RS可以使用任意操作系统

缺陷:对Director Server压力会比较大,请求和响应都需经过director server

6、LVS/DR原理和特点

image.png

6.1重点将请求报文的目标MAC地址设定为挑选出的RS的MAC地址

(a) 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP
(b) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
(c) IPVS比对数据包请求的服务是否为集群服务,若是,将请求报文中的源MAC地址修改为DIP的MAC地址,将目标MAC地址修改RIP的MAC地址,然后将数据包发至POSTROUTING链。 此时的源IP和目的IP均未修改,仅修改了源MAC地址为DIP的MAC地址,目标MAC地址为RIP的MAC地址
(d) 由于DS和RS在同一个网络中,所以是通过二层来传输。POSTROUTING链检查目标MAC地址为RIP的MAC地址,那么此时数据包将会发至Real Server。
(e) RS发现请求报文的MAC地址是自己的MAC地址,就接收此报文。处理完成之后,将响应报文通过lo接口传送给eth0网卡然后向外发出。 此时的源IP地址为VIP,目标IP为CIP
(f) 响应报文最终送达至客户端

6.2 LVS-DR模型的特性

特点1:保证前端路由将目标地址为VIP报文统统发给Director Server,而不是RS

RS可以使用私有地址;也可以是公网地址,如果使用公网地址,此时可以通过互联网对RIP进行直接访问

RS跟Director Server必须在同一个物理网络中

所有的请求报文经由Director Server,但响应报文必须不能进过Director Server

不支持地址转换,也不支持端口映射

RS可以是大多数常见的操作系统

RS的网关绝不允许指向DIP(因为我们不允许他经过director)

RS上的lo接口配置VIP的IP地址

缺陷:RS和DS必须在同一机房中

6.3 特点1的解决方案:

在前端路由器做静态地址路由绑定,将对于VIP的地址仅路由到Director Server

存在问题:用户未必有路由操作权限,因为有可能是运营商提供的,所以这个方法未必实用

arptables:在arp的层次上实现在ARP解析时做防火墙规则,过滤RS响应ARP请求。这是由iptables提供的

修改RS上内核参数(arp_ignore和arp_announce)将RS上的VIP配置在lo接口的别名上,并限制其不能响应对VIP地址解析请求。

7、LVS/Tun原理和特点

image.png

7.1 在原有的IP报文外再次封装多一层IP首部,内部IP首部(源地址为CIP,目标IIP为VIP),外层IP首部(源地址为DIP,目标IP为RIP)

(a) 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP 。
(b) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
© IPVS比对数据包请求的服务是否为集群服务,若是,在请求报文的首部再次封装一层IP报文,封装源IP为为DIP,目标IP为RIP。然后发至POSTROUTING链。 此时源IP为DIP,目标IP为RIP
(d) POSTROUTING链根据最新封装的IP报文,将数据包发至RS(因为在外层封装多了一层IP首部,所以可以理解为此时通过隧道传输)。 此时源IP为DIP,目标IP为RIP
(e) RS接收到报文后发现是自己的IP地址,就将报文接收下来,拆除掉最外层的IP后,会发现里面还有一层IP首部,而且目标是自己的lo接口VIP,那么此时RS开始处理此请求,处理完成之后,通过lo接口送给eth0网卡,然后向外传递。 此时的源IP地址为VIP,目标IP为CIP
(f) 响应报文最终送达至客户端

7.2 LVS-Tun模型特性

RIP、VIP、DIP全是公网地址

RS的网关不会也不可能指向DIP

所有的请求报文经由Director Server,但响应报文必须不能进过Director Server

不支持端口映射

RS的系统必须支持隧道

其实企业中最常用的是 DR 实现方式,而 NAT 配置上比较简单和方便。

8、LVS的十种调度算法:

静态(fixed):

  • rr:round robin 轮调

  • wrr: weight rr 加权的轮调

  • sh: source hash 源地址哈希,对客户端IP地址做哈希,director会维护一个session hash table,结果就是来自同一个客户端的请求都会被调度到一台后端服务器。

  • dh: destination hash 目标url等hash,适用于有cache server场景,对于同一请求发送到同一cache server.

动态(Dynamic):

  • lc: least connection 最少连接

    overhead = active(活动连接数)*256 + inactive 选最小的

  • wlc: weight lc 加权的最小连接

    Overhead= (active*256 + inactive) / weight 选最小的

  • sed: shortest expected delay 最小期望延迟

    overhead = (active + 1)*256 / weight

  • nq: never queue 永不排队,开始就先一人发一个,然后再按sed算法调度

  • lblc: locality based least connection基于本地的最小连接

    动态的dh,大概相当于dh + lc

  • lblcr : lblc with replication 带复制的lblc

    director维护一个proxy server table,请求进来时选择活动连接最少的

三、通过LVS DR任意实现1-2种场景

1、LVS-DR模式单网段案例

实验环境(五台机器)
-Client : eth0主机模式:192.168.36.100 GW:192.168.36.200
-ROUTER : eth0主机模式:192.168.36.200 / eth1 NAT:192.168.217.134
-LVS : eth0主机模式:192.168.36.200 / eth1 NAT:192.168.217.134
-RS1 : eth0 NAT: 192.168.217.133 GW:192.168.217.134
-RS2 : eth0 NAT: 192.168.217.132 GW:192.168.217.134

1.1、路由器设置

#设置转发权限
[root@Router ~]#echo 'net.ipv4.ip_forward=1' >>/etc/sysctl.conf
[root@Router ~]#sysctl -p

1.2、后端RealSever设置

#RS1开启vip
[root@RS1 ~]#echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@RS1 ~]#echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@RS1 ~]#echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
[root@RS1 ~]#echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
#此为临时生效,想永久生效可将其写入/etc/sysctl.conf文件里
net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.lo.arp_ignore=1
net.ipv4.conf.lo.arp_announce=2
#注意:必须先设置,然后才能添加VIP

#限制响应级别:arp_ignore
0:默认值,表示可使用本地任意接口上配置的任意地址进行响应
1:仅在请求的目标IP配置在本地主机的接收到请求报文的接口上时,才给予响应
#限制通告级别:arp_announce
0:默认值,把本机所有接口的所有信息向每个接口的网络进行通告
1:尽量避免将接口信息向非直接连接网络进行通告
2:必须避免将接口信息向非本网络进行通告
[root@RS1 ~]#ifconfig lo:1 192.168.217.100/32 # 添加回环ip

#RS2设置
[root@RS2 ~]#echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@RS2 ~]#echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@RS2 ~]#echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
[root@RS2 ~]#echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
[root@RS2 ~]#ifconfig lo:1 192.168.217.100/32

1.3、LVS设置

#设置路由规则,实现LVS 规则
[root@LVS-server ~]#yum -y install ipvsadm
[root@LVS-server ~]#ifconfig lo:1 192.168.217.100/32 # 添加回环ip
[root@LVS-server ~]#ipvsadm -A -t 192.168.217.100:80 -s rr 
[root@LVS-server ~]#ipvsadm -a -t 192.168.217.100:80 -r 192.168.217.133:80 -g
[root@LVS-server ~]#ipvsadm -a -t 192.168.217.100:80 -r 192.168.217.132:80 -g
[root@LVS-server ~]#ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.217.100:80 rr
  -> 192.168.217.132:80           Route   1      0          0         
  -> 192.168.217.133:80           Route   1      0          0 

1.4、测试访问

[root@client ~]#curl 192.168.217.100
This is RS2
[root@client ~]#curl 192.168.217.100
This is RS1
[root@client ~]#curl 192.168.217.100
This is RS2
[root@client ~]#curl 192.168.217.100
This is RS1

四、web http协议通信过程,相关技术术语总结

1、http协议通信过程

image.png

2、相关技术术语总结

2.1、WEB开发语言

http:Hyper Text Transfer Protocol 应用层协议,默认端口: 80/tcp

html:Hyper Text Markup Language 超文本标记语言,编程语言,主要负责实现页面的结构

CSS:Cascading Style Sheet 层叠样式表, 定义了如何显示(装扮) HTML 元素,比如:字体大小和颜色属性等。样式通常保存在外部的 .css 文件中,用于存放一些HTML文件的公共属性,从而通过仅编辑一个简单的 CSS 文档,可以同时改变站点中所有页面的布局和外观。

Js:javascript,实现网页的动画效果,但实属于静态资源

2.2、MIME

MIME : Multipurpose Internet Mail Extensions 多用途互联网邮件扩展

文件 /etc/mime.types ,来自于mailcap包

MIME格式:type/subtype txt html jpg bmp

2.3、URI和URL

URI: Uniform Resource Identifier 统一资源标识,分为URL 和 URN

URN:Uniform Resource Naming,统一资源命名

示例: P2P下载使用的磁力链接是URN的一种实现 magnet:?xt=urn:btih:660557A6890EF888666

URL:Uniform Resorce Locator,统一资源定位符,用于描述某服务器某特定资源位置

两者区别:URN如同一个人的名称,而URL代表一个人的住址。换言之,URN定义某事物的身份,而

URL提供查找该事物的方法。URN仅用于命名,而不指定地址。

URL组成


image.png
scheme:方案,访问服务器以获取资源时要使用哪种协议
user:用户,某些方案访问资源时需要的用户名
password:密码,用户对应的密码,中间用:分隔
Host:主机,资源宿主服务器的主机名或IP地址
port:端口,资源宿主服务器正在监听的端口号,很多方案有默认端口号
path:路径,服务器资源的本地名,由一个/将其与前面的URL组件分隔
params:参数,指定输入的参数,参数为名/值对,多个参数,用;分隔
query:查询,传递参数给程序,如数据库,用?分隔,多个查询用&分隔
frag:片段,一小片或一部分资源的名字,此组件在客户端使用,用#分隔

五、总结网络IO模型和nginx架构

1、网络IO模型

阻塞型、非阻塞型、复用型、信号驱动型、异步

1.1、阻塞型I/O模型(blocking IO)

image.png

①阻塞IO模型是最简单的I/O模型,用户线程在内核进行IO操作时被阻塞

②用户线程通过系统调用read发起I/O读操作,由用户空间转到内核空间。内核等到数据包到达后,然后将

接收的数据拷贝到用户空间,完成read操作

③用户需要等待read将数据读取到buffer后,才继续处理接收的数据。整个I/O请求的过程中,用户线程是

被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够

优点:程序简单,在阻塞等待数据期间进程/线程挂起,基本不会占用 CPU 资源

缺点:每个连接需要独立的进程/线程单独处理,当并发请求量大时为了维护程序,内存、线程切换开销

较大,apache 的prefork使用的是这种模式。

1.2、非阻塞型I/O模型(nonblocking IO)

image.png

用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起IO请求,直到数据

到达后,才真正读取到数据,继续执行。即 “轮询”机制存在两个问题:如果有大量文件描述符都要等,

那么就得一个一个的read。这会带来大量的Context Switch(read是系统调用,每调用一次就得在用户

态和核心态切换一次)。轮询的时间不好把握。这里是要猜多久之后数据才能到。等待时间设的太长,

程序响应延迟就过大;设的太短,就会造成过于频繁的重试,干耗CPU而已,是比较浪费CPU的方式,一

般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。

1.3、I/O多路复用型(I/O multiplexing)

image.png

①多路复用IO指一个线程可以同时(实际是交替实现,即并发完成)监控和处理多个文件描述符对应各自

的IO,即复用同一个线程

②一个线程之所以能实现同时处理多个IO,是因为这个线程调用了内核中的SELECT,POLL或EPOLL等系统

调用,从而实现多路复用IO

③它的基本原理就是select/poll/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数

据到达了,就通知用户进程。

④当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,

当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从

kernel拷贝到用户进程。

优点:可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述

符一个线程),这样可以大大节省系统资源

缺点:当连接数较少时效率相比多线程+阻塞 I/O 模型效率较低,可能延迟更大,因为单个连接处

理需要 2 次系统调用,占用时间会有增加

IO多路复用适用如下场合

  • 当客户端处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用

  • 当一个客户端同时处理多个套接字时,此情况可能的但很少出现

  • 当一个服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用

  • 当一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用

  • 当一个服务器要处理多个服务或多个协议,一般要使用I/O复用

1.4、信号驱动式I/O模型(signal-driven IO)

image.png

信号驱动I/O的意思就是进程现在不用傻等着,也不用去轮询。而是让内核在数据就绪时,发送信号通知

进程。

调用的步骤:通过系统调用 sigaction ,并注册一个信号处理的回调函数,该调用会立即返回,然后

主程序可以继续向下执行,当有I/O操作准备就绪,即内核数据就绪时,内核会为该进程产生一个 SIGIO

信号,并回调注册的信号回调函数,这样就可以在信号回调函数中系统调用 recvfrom 获取数据,将用户

进程所需要的数据从内核空间拷贝到用户空间

此模型的优势在于等待数据报到达期间进程不被阻塞。用户主程序可以继续执行,只要等待来自信号处

理函数的通知。

在信号驱动式 I/O 模型中,应用程序使用套接口进行信号驱动 I/O,并安装一个信号处理函数,进程继

续运行并不阻塞

当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据。

优点:线程并没有在等待数据时被阻塞,内核直接返回调用接收信号,不影响进程继续处理其他请求因此

可以提高资源的利用率

缺点:信号 I/O 在大量 IO 操作时可能会因为信号队列溢出导致没法通知

1.5、异步I/O模型(asynchronous IO)

image.png

执行过程:用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO两个阶段,进程都是非阻塞的。

信号驱动IO当内核通知触发信号处理程序时,信号处理程序还需要阻塞在从内核空间缓冲区拷贝数据到

用户空间缓冲区这个阶段,而异步IO直接是在第二个阶段完成后,内核直接通知用户线程可以进行后续

操作了

优点:异步 I/O 能够充分利用 DMA 特性,让 I/O 操作与计算重叠

缺点:要实现真正的异步 I/O,操作系统需要做大量的工作。目前 Windows 下通过 IOCP 实现了真正的

异步 I/O,在 Linux 系统下,Linux 2.6才引入,目前 AIO 并不完善,因此在 Linux 下实现高并发网络编

程时以 IO 复用模型模式+多线程任务的架构基本可以满足需求

六、nginx总结核心配置和优化

1、Nginx的配置文件的组成部分

①主配置文件:nginx.conf

②子配置文件: include conf.d/*.conf

③fastcgi, uwsgi,scgi 等协议相关的配置文件

④mime.types:支持的mime类型,MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,MIME消息能包含文本、图像、音频、视频以及其他应用程序专用的数据,是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

MIME参考文档:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_Types

2、nginx 配置文件格式说明

配置文件由指令与指令块构成
每条指令以;分号结尾,指令与值之间以空格符号分隔
可以将多条指令放在同一行,用分号分隔即可,但可读性差,不推荐
指令块以{ }大括号将多条指令组织在一起,且可以嵌套指令块
include语句允许组合多个配置文件以提升可维护性
使用#符号添加注释,提高可读性
使用$符号使用变量
部分指令的参数支持正则表达式

3、主配置文件结构:四部分

directive value [value2 ...];
注意
(1) 指令必须以分号结尾
(2) 支持使用配置变量
 内建变量:由Nginx模块引入,可直接引用
 自定义变量:由用户使用set命令定义,格式: set variable_name value;
 引用变量:$variable_name
主配置文件结构:四部分
main block:主配置段,即全局配置段,对http,mail都有效
#事件驱动相关的配置
event {
 ...
}   
#http/https 协议相关配置段
http {
 ...
}          
#默认配置文件不包括下面两个块
#mail 协议相关配置段
mail {
 ...
}    
#stream 服务器相关配置段
stream {
 ...
}

4、全局配置中的必备配置和优化

user nginx nginx; #启动Nginx工作进程的用户和组
worker_processes [number | auto]; #启动Nginx工作进程的数量,一般设为和CPU核心数相同
worker_cpu_affinity 00000001 00000010 00000100 00001000 | auto ; #将Nginx工作进程绑定到指定的CPU核心,默认Nginx是不进行进程绑定的,绑定并不是意味着当前nginx进程独占以一核心CPU,但是可以保证此进程不会运行在其他核心上,这就极大减少了nginx的工作进程在不同的cpu核心上的来回跳转,减少了CPU对进程的资源分配与回收以及内存管理等,因此可以有效的提升nginx服务器的性能。
CPU MASK: 00000001:0号CPU
          00000010:1号CPU
  10000000:7号CPU
#示例:
worker_cpu_affinity 0001 0010 0100 1000;第0号---第3号CPU
worker_cpu_affinity 0101 1010;

#错误日志记录配置,语法:error_log file [debug | info | notice | warn | error | crit | alert | emerg]
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log /apps/nginx/logs/error.log error; 

#pid文件保存路径
pid       /apps/nginx/logs/nginx.pid;

worker_priority 0; #工作进程优先级,-20~20(19)
worker_rlimit_nofile 65536; #所有worker进程能打开的文件数量上限,包括:Nginx的所有连接(例如与代理服务器的连接等),而不仅仅是与客户端的连接,另一个考虑因素是实际的并发连接数不能超过系统级别的最大打开文件数的限制.最好与ulimit -n 或者limits.conf的值保持一致,

daemon off;  #前台运行Nginx服务用于测试、或者以容器运行时,需要设为off
master_process off|on; #是否开启Nginx的master-worker工作模式,仅用于开发调试场景,默认为on

events {
   worker_connections  65536;  #设置单个工作进程的最大并发连接数
   use epoll; #使用epoll事件驱动,Nginx支持众多的事件驱动,比如:select、poll、epoll,只能设置在events模块中设置。
   accept_mutex on; #on为同一时刻一个请求轮流由worker进程处理,而防止被同时唤醒所有worker,避免多个睡眠进程被唤醒的设置,默认为off,新请求会唤醒所有worker进程,此过程也称为"惊 群",因此nginx刚安装完以后要进行适当的优化。建议设置为on
   multi_accept on; #on时Nginx服务器的每个工作进程可以同时接受多个新的网络连接,此指令默认为off,即默认为一个工作进程只能一次接受一个新的网络连接,打开后几个同时接受多个。建议设置为on
}

5、http 协议配置中的必备配置和优化

http {
   include       mime.types; #导入支持的文件类型,是相对于/apps/nginx/conf的目录
   default_type application/octet-stream; #除mime.types中文件类型外,设置其它文件默认类型,访问其它类型时会提示下载不匹配的类型文件
   
#日志配置部分
    #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    #                 '$status $body_bytes_sent "$http_referer" '
    #                 '"$http_user_agent" "$http_x_forwarded_for"';
    #access_log logs/access.log main;
    
#自定义优化参数
   sendfile       on; 
    #tcp_nopush     on; #在开启了sendfile的情况下,合并请求后统一发送给客户端,必须开启sendfile
    #tcp_nodelay   off; #在开启了keepalived模式下的连接是否启用TCP_NODELAY选项,当为off时,延迟0.2s发送,默认On时,不延迟发送,立即发送用户响应报文。
    #keepalive_timeout 0;
   keepalive_timeout  65 65; #设置会话保持时间,第二个值为响应首部:keep�Alived:timeout=65,可以和第一个值不同
    #gzip on; #开启文件压缩
    
   server {
       listen       80; #设置监听地址和端口
       server_name localhost; #设置server name,可以以空格隔开写多个并支持正则表达式,如:*.magedu.com www.magedu.* ~^www\d+\.magedu\.com$ default_server 
        #charset koi8-r; #设置编码格式,默认是俄语格式,建议改为utf-8
        #access_log logs/host.access.log main;
       location / {
           root   html;
           index index.html index.htm;
       }
       
        #error_page 404             /404.html;
        # redirect server error pages to the static page /50x.html
        #
       error_page   500 502 503 504 /50x.html; #定义错误页面
       location = /50x.html {
           root   html;
       }
       
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ { #以http的方式转发php请求到指定web服务器
        #   proxy_pass   http://127.0.0.1;
        #}
        
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ { #以fastcgi的方式转发php请求到php处理
        #   root           html;
        #   fastcgi_pass   127.0.0.1:9000;
        #   fastcgi_index index.php;
        #   fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
        #   include       fastcgi_params;
        #}
        
          # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht { #拒绝web形式访问指定文件,如很多的网站都是通过.htaccess文件来改变自己的重定向等功能。
        #   deny all;
        #}
       location ~ /passwd.html {
           deny all;
       }
   }
   
    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server { #自定义虚拟server
    #   listen       8000;
    #   listen       somename:8080;
    #   server_name somename alias another.alias;
    
    #   location / { 
    #       root   html;
    #       index index.html index.htm; #指定默认网页文件,此指令由
ngx_http_index_module模块提供
    #   }
    #}
    
    # HTTPS server
    #
    #server { #https服务器配置
    #   listen       443 ssl;
    #   server_name localhost;
    
    #   ssl_certificate     cert.pem;
    #   ssl_certificate_key cert.key;
    
    #   ssl_session_cache   shared:SSL:1m;
    #   ssl_session_timeout 5m;
    
    #   ssl_ciphers HIGH:!aNULL:!MD5;
    #   ssl_prefer_server_ciphers on;
    
    #   location / {
    #       root   html;
    #       index index.html index.htm;
    #   }
    #}

七、使用脚本完成一键编译安装nginx任意版本

#!/bin/bash
SRC_DIR=/usr/local/src
NGINX_URL=http://nginx.org/download/
NGINX_FILE=nginx-1.20.2
TAR=.tar.gz
NGINX_INSTALL_DIR=/apps/nginx
CPUS=`lscpu |awk '/^CPU\(s\)/{print $2}'`
. /etc/os-release

os_type () {
   awk -F'[ "]' '/^NAME/{print $2}' /etc/os-release
}

os_version () {
   awk -F'"' '/^VERSION_ID/{print $2}' /etc/os-release
}

check () {
    [ -e ${NGINX_INSTALL_DIR} ] && { color "nginx 已安装,请卸载后再安装" 1; exit; }
    cd  ${SRC_DIR}
    if [  -e ${NGINX_FILE}${TAR} ];then
        color "相关文件已准备好" 0
    else
        color '开始下载 nginx 源码包' 0
        wget ${NGINX_URL}${NGINX_FILE}${TAR} 
        [ $? -ne 0 ] && { color "下载 ${NGINX_FILE}${TAR}文件失败" 1; exit; } 
    fi
} 

install () {
    color "开始安装 nginx" 0
    if id nginx  &> /dev/null;then
        color "nginx 用户已存在" 1 
    else
        useradd -s /sbin/nologin -r  nginx
        color "创建 nginx 用户" 0 
    fi
    color "开始安装 nginx 依赖包" 0
    if [ $ID == "centos" ] ;then
        if [[ $VERSION_ID =~ ^7 ]];then
            yum -y -q  install make gcc pcre-devel openssl-devel zlib-devel perl-ExtUtils-Embed
        elif [[ $VERSION_ID =~ ^8 ]];then
            yum -y -q install make gcc-c++ libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel perl-ExtUtils-Embed 
        else 
            color '不支持此系统!'  1
            exit
        fi
    elif [ $ID == "rocky"  ];then
        yum -y -q install make gcc-c++ libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel perl-ExtUtils-Embed 
    else
        apt update &> /dev/null
        apt -y install make gcc libpcre3 libpcre3-dev openssl libssl-dev zlib1g-dev &> /dev/null
    fi
    cd $SRC_DIR
    tar xf ${NGINX_FILE}${TAR}
    NGINX_DIR=`echo ${NGINX_FILE}${TAR}| sed -nr 's/^(.*[0-9]).*/\1/p'`
    cd ${NGINX_DIR}
    ./configure --prefix=${NGINX_INSTALL_DIR} --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module 
    make -j $CPUS && make install 
    [ $? -eq 0 ] && color "nginx 编译安装成功" 0 ||  { color "nginx 编译安装失败,退出!" 1 ;exit; }
    echo "PATH=${NGINX_INSTALL_DIR}/sbin:${PATH}" > /etc/profile.d/nginx.sh
    cat > /lib/systemd/system/nginx.service <<EOF
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=${NGINX_INSTALL_DIR}/logs/nginx.pid
ExecStartPre=/bin/rm -f ${NGINX_INSTALL_DIR}/logs/nginx.pid
ExecStartPre=${NGINX_INSTALL_DIR}/sbin/nginx -t
ExecStart=${NGINX_INSTALL_DIR}/sbin/nginx
ExecReload=/bin/kill -s HUP \$MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
LimitNOFILE=100000

[Install]
WantedBy=multi-user.target
EOF
    systemctl daemon-reload
    systemctl enable --now nginx &> /dev/null 
    systemctl is-active nginx &> /dev/null ||  { color "nginx 启动失败,退出!" 1 ; exit; }
    color "nginx 安装完成" 0
}

check
install

八、任意编译一个第3方nginx模块,并使用

nginx-module-vts模块实现流量监控
nginx-module-vts模块Github文档链接

image.png

[root@Rocky ~]#cd /usr/local/src
[root@Rocky src]#wget https://github.com/vozlt/nginx-module-vts/archive/refs/tags/v0.2.1.tar.gz
[root@Rocky src]#tar xf v0.2.1.tar.gz
[root@Rocky src]#ls
nginx-1.22.1  nginx-1.22.1.tar.gz  nginx-module-vts-0.2.1  v0.2.1.tar.gz
[root@Rocky src]#cd nginx-1.22.1/
[root@Rocky nginx-1.22.1]#nginx -V    #查看Nginx已安装的模块
nginx version: nginx/1.23.2
built by gcc 8.5.0 20210514 (Red Hat 8.5.0-10) (GCC) 
built with OpenSSL 1.1.1k  FIPS 25 Mar 2021
TLS SNI support enabled
configure arguments: --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module
#添加新模块--add-module=/usr/local/src/nginx-module-vts-0.2.1
[root@Rocky nginx-1.22.1]#./configure --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module --add-module=/usr/local/src/nginx-module-vts-0.2.1
[root@Rocky nginx-1.22.1]#make -j 2 && make install

http {
  ......
  vhost_traffic_status_zone;             #添加此行
  ......
  server {
     ......
     location /status {
           vhost_traffic_status_display;
           vhost_traffic_status_display_format html;
     }
  ......
  }
}
:wq
[root@Rocky nginx-1.20.2]#systemctl restart nginx

添加监控模块前


image.png

添加监控模块后


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容