11月1日 缓存服务varnish

参考:https://www.cnblogs.com/cutemsyu/p/6051876.html

1、varnish程序环境

软件包:yum install varnish
/etc/varnish/varnish.params: 配置varnish服务进程的工作特性,例如监听的地址和端口,缓存机制;
/etc/varnish/default.vcl:配置各Child/Cache线程的缓存;
主程序:/usr/sbin/varnishd
/usr/share/doc/varnish-4.0.4/builtin.vcl ---系统内置的一些VCL
Systemd Unit File:/usr/lib/systemd/system/varnish.service
程序架构:
Manager进程
Cacher进程,包含多种类型的线程:
accept, worker, expiry, ...
也就是一个主的Manager进程和一个子Cacher进程,Cacher进程分成很多个线程来处理用户的请求
shared memory log:
统计数据:计数器;
日志区域:日志记录;
varnishlog, varnishncsa, varnishstat...
配置接口:VCL
Varnish Configuration Language,
vcl complier --> c complier --> shared object

2、varnish的缓存存储机制( Storage Types)

malloc[,size]内存存储,[,size]用于定义空间大小;重启后所有缓存项失效;
file[,path[,size[,granularity]]]磁盘文件存储,黑盒;重启后所有缓存项失效;
persistent,path,size文件存储,黑盒;重启后所有缓存项有效;实验阶段,目前还没有使用;

3、VCL

Varnish Configuration Language
VCL有多个状态引擎,状态之间存在相关性,但状态引擎彼此间互相隔离;每个状态引擎可使用return(x)指明关联至哪个下一级引擎;每个状态引擎对应于vcl文件中的一个配置段,即为subroutine

varnish.png

4、变量类型

QQ截图20171103193246.png
内建变量:
req.*:request,表示由客户端发来的请求报文相关;
req.http.*
req.http.User-Agent, req.http.Referer, ...
bereq.*:由varnish发往BE主机的httpd请求相关;
bereq.http.*
beresp.*:由BE主机响应给varnish的响应报文相关;
beresp.http.*
resp.*:由varnish响应给client相关;
obj.*:存储在缓存空间中的缓存对象的属性;只读;
常用变量:
bereq.*, req.*:
bereq.http.HEADERS
bereq.request:请求方法;
bereq.url:请求的url;
bereq.proto:请求的协议版本;
bereq.backend:指明要调用的后端主机;
req.http.Cookie:客户端的请求报文中Cookie首部的值; 
req.http.User-Agent ~ "chrome"
beresp.*, resp.*:
beresp.http.HEADERS
beresp.status:响应的状态码;
reresp.proto:协议版本;
beresp.backend.name:BE主机的主机名;
beresp.ttl:BE主机响应的内容的余下的可缓存时长;
obj.*
obj.hits:此对象从缓存中命中的次数;
obj.ttl:对象的ttl值,也就是有效期  
server.*
server.ip:varnish主机的IP;
server.hostname:varnish主机的Hostname;
client.*
client.ip:发请求至varnish主机的客户端IP;

5、/etc/varnish/varnish.params的配置

vim /etc/varnish/varnish.params
# Default address and port to bind to. Blank address means all IPv4
# and IPv6 interfaces, otherwise specify a host name, an IPv4 dotted
# quad, or an IPv6 address in brackets.
# VARNISH_LISTEN_ADDRESS=192.168.1.5    ---监听的地址,默认是本机的所有地址
VARNISH_LISTEN_PORT=6081    ---监听的端口

# Admin interface listen address and port    ---一个和varnishadm管理工具通讯的管理接口
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1---地址为本机,说明只能在本机上使用varnishadm命令管理缓存配置,这样比较安全
VARNISH_ADMIN_LISTEN_PORT=6082   ---监听的端口
varnish除了监听在6081端口之外,还监听在6082端口,varnishadm管理工具可以通过这个端口连接到varnish服务器,来管理varnish缓存的配置
# Shared secret file for admin interface   ---管理接口的共享秘钥文件
VARNISH_SECRET_FILE=/etc/varnish/secret   

# Backend storage specification, see Storage Types in the varnishd(5)
# man page for details.  
 RNISH_STORAGE="malloc,256M"   ---内存存储
RANSH_STORAGE="file,/data/cache/varnish.bin,2G"  ---磁盘文件存储,重启后所有缓存项失效
# User and group for the varnishd worker processes
VARNISH_USER=varnish
VARNISH_GROUP=varnish

6、 搭建一个缓存服务,并测试缓存是否命中

Paste_Image.png
1、在后端搭建好httpd服务
2、在varnish的设置
vim /etc/varnish/varnish.params 
VARNISH_LISTEN_PORT=80    ---将监听的端口改为80,这样在网站上访问的时候就不用输入端口号了
systemctl start varnish 
vim /etc/varnish/default.vcl
backend default {
    .host = "192.168.74.133";
    .port = "80";
}
sub vcl_deliver {    ---表示在投递给客户端的时候,给客户端的响应报文头部中给出是否命中
        if (obj.hits>0) {
                set resp.http.x-cache = "HIT via" + server.ip;
        }else {
                set resp.http.x-cache = "MISS from" + server.ip;
}
}
 varnish_reload_vcl   ---重新载入default.vcl配置文件
测试
在火狐浏览器上测试
http://172.18.21.107/test1.html   ---第一次访问时响应报文的头部显示没有命中,第二次访问的时候显示命中了
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082   ---可以利用这个管理工具来管理varnish的各种配置

7、设置对某资源的请求不缓存

vim /etc/varnish/default.vcl
sub vcl_recv {     ---表示如果请求的uri中是以下面两个字符串开头的就不缓存
        if (req.url ~ "(?i)^/(login|admin)") {   ---(?i)表示忽略大小写
                return(pass);
        }
}
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
vcl.load test1 default.vcl  ---将test1这个配置载入配置文件中
vcl.use test1    ---用这个配置
vcl.list    ---查看配置
vcl.show test1   ---查看配置文件是否载入配置
测试
http://172.18.21.107/admin/index.html   ---发现不会缓存

8、对于特定类型的资源,例如公开的图片等,取消其私有标识,并强行设定其可以由varnish缓存

vim /etc/varnish/default.vcl
sub vcl_backend_response {      
        if (beresp.http.cache-control !~ "s-maxage") {  ---如果后端服务器响应给缓存服务器的响应报文中不包含s-maxage
                if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") {  ---如果缓存服务器请求后端web服务器的url中包括以这些点结尾的文件
                        unset beresp.http.set-cookie;  ---不设定响应的cookie
                        set beresp.ttl = 3600s;   ---设定缓存时长为3600s
                }
        }
}
vcl.load test2 default.vcl
vcl.use test2
vcl.discard test1  ---删除某个配置
backend.list   ---查看后端主机

测试
http://172.18.21.107/simplelock.jpg  ---发现缓存

9、后端服务器可以看到真正的客户端地址

vim /etc/varnish/default.vcl
sub vcl_recv { 
        if (req.restarts == 0 ) {    ---如果客户端从新发请求的次数为0,也就是客户端没有重新发起请求
                if (req.http.X-Forwarded-For) {  ---如果请求的报文中含有首部X-Forwarded-For
                set req.http.X-Forwarded-For = req.http.X-Fordwarded-For + "," + client.   ---就设定首部的值是client
ip;
        }else{
                set req.http.X-Fordwarded-For = client.ip;  ---如果请求的报文中没有首部X-Forwarded-For就直接设定客户端地址
                }
        }
}
vcl.load test3 default.vcl
vcl.use test3
在后端的web服务器上的设置
vim /etc/httpd/conf/httpd.conf 
 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{X-Forwarded-For}i" combined
service httpd reload
测试
curl 172.18.21.107
tail -f /var/log/httpd/access_log  ---在后端日志上监控可以看到客户端地址

10、缓存对象的修剪

1、purge方法
vim /etc/varnish/default.vcl
if (req.method == "PURGE") {    ---加到sub vcl_recv语句块里
                return(purge);
        }
sub vcl_purge {      ---此项可以不加,因为在默认的VCL里已经定义,cat /usr/share/doc/varnish-4.0.4/builtin.vcl可以看到
                    return (synth(200,"Purged"));
                }    ---此项的作用的是上面定义的返回purge后进行的操作,就返回一个合成的响应码200,"Purged"
vcl.load test5 default.vcl    ---加载配置到配置文件中
vcl.use test5  ---使用这个配置
在客户端curl -X PURGE 172.18.21.107/simplelock.jpg   ---X选项用于指明客户端请求的方法是purge
[root@centos6 network-scripts]#curl -X PURGE 172.18.21.107/simplelock.jpg -I
HTTP/1.1 200 Purged    ---可以看到这个合成的响应码
Date: Thu, 02 Nov 2017 11:21:34 GMT
Server: Varnish
X-Varnish: 163953
Content-Type: text/html; charset=utf-8
Retry-After: 5
Content-Length: 241
Connection: keep-alive
测试
http://172.18.21.107/simplelock.jpg    ---purge之后就没有缓存了
添加此类请求的访问控制法则
也就是acl控制列表,运行哪些客户端purge
示例
vim /etc/varnish/default.vcl
vcl 4.0;
acl purges {     ---在vcl 4.0下面定义一个acl
        "127.0.0.0"/8;
        "172.18.0.0"/16;
}
if (req.method == "PURGE") {    ---加到sub vcl_recv语句块里
                if (!client.ip ~ purges) {    ---如果客户端的地址不满足上面acl中定义的地址
                return(synth(405,"purging not allowed for " + client.ip));   ---就返回一个合成的状态码
                }
                return(purge);
        }
vcl.load test7 default.vcl   ---这些命令都是在另外一个窗口通过varnishadm管理工具实现的
use test7
测试curl -X PURGE 172.18.21.107/readblock.jpg    ----只能本机和这个网段的进行修剪
2、Banning
示例
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
ban req.url ~ (?i).(jpg|jpeg)$     ---用命令行的方式ban掉一类缓存

总结:用purge修剪单个的缓存,用ban修剪一类缓存

11 、如何设定使用多个后端主机

vim /etc/varnish/default.vcl
backend default {
    .host = "192.168.74.133";
    .port = "80";
}
backend appsrv {
        .host = "192.168.74.129";
        .port = "80";
}
sub vcl_recv {
  if (req.url ~ "(?i)\.php$") {   
                set req.backend_hint = appsrv;
        }else{
                set req.backend_hint = default;
        }
}
vcl.load test8 default.vcl
vcl.use test8
backend.list     ---可以看到有两个后端主机
200        
Backend name                   Refs   Admin      Probe
default(192.168.74.133,,80)    8      probe      Healthy (no probe)
appsrv(192.168.74.129,,80)     1      probe      Healthy (no probe)
测试
http://172.18.21.107/index.php  

12、实现负载均衡

vim /etc/varnish/default.vcl
vcl 4.0;
import directors;     ---要实现负载均衡要先导入这个模块
backend srv1 {
    .host = "192.168.74.133";
    .port = "80";
}
backend srv2 {
        .host = "192.168.74.129";
        .port = "80";
}
sub vcl_init {    ---初始化后端服务器组
        new srvs = directors.round_robin();   --定义一个服务器组为srvs,并设置调度算法为轮询,不支持权重,varnish有两种调度算法轮询和随机random
        srvs.add_backend(srv1);
        srvs.add_backend(srv2);   ---添加两个后端服务器到这个组中
}
sub vcl_recv {   
        set req.backend_hint = srvs.backend();    ---请求的后端主机是定义的服务器组
  if (req.url ~ "(?i)^/(login|admin)") {     ---为了看出轮询效果,定义一个不缓存的
                return(pass);
        }
}
vcl.load test9 default.vcl
vcl.use test9
测试
http://172.18.21.107/admin/index.html   ---可以发现轮询

13、基于cookie的session stick

sub vcl_init {
       new h = directors.hash(); ---定义一个服务器组为h,调度算法为哈希
       h.add_backend(srv1, 1);   
       h.add_backend(srv2, ,2);  ---添加两个服务器到组中,后面的1和2为权重,轮询算法不支持权重
                }
sub vcl_recv {
       set req.backend_hint = h.backend(req.http.cookie);   ----req.http.cookie表示请求报文中含有cookie的就调度到指定的后端服务器
                }       

14、定义健康状态检测

vim /etc/varnish/default.vcl
probe check {    --定义一个检查方法为check
        .url = "/index.html";    ---检查的uri
        .window = 5;  ---定义一个窗口,基于最近的多少次检查来判断其健康状态; 
        .threshold = 4;   ---定义一个门槛,也就是上面定义的检查次数中有4次成功的才认为后端服务器是健康的
        .interval = 2s;   ---监控间隔
        .timeout = 1s;   ---超时时长
}
backend srv1 {
    .host = "192.168.74.133";
    .port = "80";
        .probe = check;
}
backend srv2 {
        .host = "192.168.74.129";
        .port = "80";
        .probe = check;    ---在两个后端服务器组中调用这个检测方法
}
vcl.load test10 default.vcl
vcl.use test10
backend.list    ---这是没有设定健康状态检查之前的状态
200        
Backend name                   Refs   Admin      Probe
default(192.168.74.133,,80)    2      probe      Healthy (no probe)
srv1(192.168.74.133,,80)       1      probe      Healthy (no probe)
srv2(192.168.74.129,,80)       1      probe      Healthy (no probe)
backend.list    ---设定之后我们发现Healthy 5/5
200        
Backend name                   Refs   Admin      Probe
default(192.168.74.133,,80)    2      probe      Healthy (no probe)
srv1(192.168.74.133,,80)       2      probe      Healthy 5/5
srv2(192.168.74.129,,80)       2      probe      Healthy 5/5
手动设定BE主机的状态:
sick:管理down; 
healthy:管理up;
auto:probe auto;
示例
backend.set_health srv1 sick    ---设置为down状态,可以用于灰度发布时使用,一批一批上线
200        
backend.list
200        
Backend name                   Refs   Admin      Probe
default(192.168.74.133,,80)    2      probe      Healthy (no probe)
srv1(192.168.74.133,,80)       2      sick       Healthy 5/5
srv2(192.168.74.129,,80)       2      probe      Healthy 5/5
backend.set_health srv1 healthy    ---设置为健康状态,即使这台主机真的坏了,也会往这台主机调度,最好不要这样设置
200        
backend.list
200        
Backend name                   Refs   Admin      Probe
default(192.168.74.133,,80)    2      probe      Healthy (no probe)
srv1(192.168.74.133,,80)       2      healthy    Healthy 5/5
srv2(192.168.74.129,,80)       2      probe      Healthy 5/5
backend.set_health srv1 auto  ---设定为自动状态,也就是根据健康状态检测情况决定
200        
backend.list
200        
Backend name                   Refs   Admin      Probe
default(192.168.74.133,,80)    2      probe      Healthy (no probe)
srv1(192.168.74.133,,80)       2      probe      Healthy 5/5
srv2(192.168.74.129,,80)       2      probe      Healthy 5/5
设置后端主机的健康检测的其他方法:
.connect_timeout = 0.5s;  指的是tcp连接建立的超时时长
.first_byte_timeout = 20s; 指的是tcp连接建立之后开始传输数据的第一个字节的超时时长
.between_bytes_timeout = 5s;传输一个字节之后再传输一个字节,两个字节之间的传输超时时间超过5秒定义为这台主机时不健康的
.max_connections = 50; 当前服务器的最大并发连接数等于50的时候定义为不健康的,这个生成中可能会用到

15、线程相关的参数设置

使用线程池机制管理线程;在线程池内部,其每一个请求由一个线程来处理; 其worker线程的最大数决定了varnish的并发响应能力;
thread_pools:线程池的数量. 最好小于或等于CPU核心数量;
thread_pool_max:每线程池的最大线程数;
thread_pool_min:每个线程池的最小线程数,额外意义为“最大空闲线程数”;
最大并发连接数 = thread_pools * thread_pool_max
thread_pool_timeout:线程空闲时间,超过阈值则摧毁线程
thread_pool_add_delay:创建一个新线程的延迟时间,默认值为0s
thread_pool_destroy_delay:摧毁一个线程的延迟时间,默认值为2s;
如何修改varnish处于运行状态时的这些参数

varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
param.show -l   ---可以查看所有参数
param.set thread_pool 4  ---设置线程池数量为4个
param.show thread_pools   ---查看一下
param.set thread_pool_max 3000   ---设置每个池子中的最大线程
param.set thread_pool_max 3000
此时最大并发为12000

要想永久修改执行如下设置

vim /etc/varnish/varnish.params 
DAEMON_OPTS="-p thread_pool_min=100 -p thread_pool_max=3000 -p thread_pool_timeout=300 -p thread_pools=4 -r thread_pools,thread_pool_max,thread_pool_min"---用-r选项指明这些参数是只读的,不能进行修改
systemctl restart varnish
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
param.show thread.pools
param.set thread_pools 2    ---发现不能设置,已经被写保护了
107        
parameter "thread_pools" is protected.

16、varnish日志

varnishlog   ---可以显示新生成的日志
varnishtop  ---动态的显示日志信息
varnishncsa --以combine的格式显示日志信息,这种格式是http日志的格式
varnishstat ---显示统计日志
varnishstat -l  --显示所有的字段名称列表
[root@centos7 varnish]#varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss     ---可以显示命中的次数和未命中的次数,从而计算命中率,-f表示字段field,-1是只显示一次,不然会像top命令一样
MAIN.cache_hit               6         0.01 Cache hits
MAIN.cache_miss              3         0.00 Cache misses
varnishstat -1 -f MAIN    ---可以显示某个字段
[root@centos7 varnish]#varnishtop -1  ---一次性的显示过去的参数
[root@centos7 varnish]#varnishtop -i RespHeader   ---动态的显示某个指定的标签,-I支持正则表达式
list length 16                                               centos7.magedu.com

     1.17 RespHeader     Accept-Ranges: bytes
     1.12 RespHeader     Age: 0
     1.12 RespHeader     Via: 1.1 varnish-v4
     1.12 RespHeader     Connection: keep-alive
     1.12 RespHeader     x-cache: MISS from172.18.21.107
     0.67 RespHeader     X-Varnish: 9
[root@centos7 varnish]#varnishtop -x  RespHeader, RespStatus  ---表示除了RespHeader、RespStatus 的内容都显示出来,-X支持正则表达式
[root@centos7 varnish]#varnishtop -I  Resp.*   ---正则表达式匹配的是后面的内容,小写的时候匹配的是前面的标签
list length 3                                                centos7.magedu.com

     0.56 Timestamp  Resp: 1509634477.875729 0.001115 0.000280
     0.50 Timestamp  Resp: 1509634477.516917 0.002058 0.000363
     0.50 Timestamp  Resp: 1509634477.706995 0.001768 0.000230

如何永久的记录日志

[root@centos7 varnish]#systemctl start varnishncsa.service
[root@centos7 varnish]#cd /var/log/varnish/
[root@centos7 varnish]#ls
varnishncsa.log
[root@centos7 varnish]#tail varnishncsa.log 
[root@centos7 varnish]#tail varnishncsa.log -f 
172.18.254.200 - - [02/Nov/2017:23:02:09 +0800] "GET http://172.18.21.107/ HTTP/1.1" 200 32 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"

要根据缓存服务器的负载情况决定是否启动日志功能

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

推荐阅读更多精彩内容

  • 1.介绍 运维日常: 2.Web Page Cache: varnish2.0,3.0处理过程 varnish4....
    尛尛大尹阅读 3,352评论 0 0
  • 缓存的基础知识 1、程序本身具有局部性 时间局部性过去访问到的数据,也有可能被两次访问 空间局部性一个数据被访问到...
    魏镇坪阅读 2,005评论 1 3
  • 官方网址:各版本之间差距较大,其中第四版本被epel源收录 概括: 对于网站而言,缓存为王;缓存是对网站加速必不可...
    油菜花的ID阅读 1,822评论 1 2
  • 如果我能重回18岁的盛夏,我想,我会勇敢牵起他的手,谈一场无所畏惧的爱情。故事有点长,慢慢看就好。 1 “我们分手...
    祝从容阅读 1,078评论 22 25
  • 我、你、他各个关系的发展历程,也就是 一元、二元、三元关系; 一元关系以忠诚为核心,对自己的忠诚; 是指一个人只看...
    玥烨阅读 188评论 0 0