varnish

  1. 云栖社区>
  2. 博客>
  3. 正文

varnish

技术小阿哥 2017-11-28 14:26:00 浏览1311
展开阅读全文

第二篇 varnish

一、http缓存机制详解

作为替代squid缓存工具的存在。

连接接口:cli、telnet、web方式


1、 web常见架构模型

        c/s架构(http协议):

                user agent:elinks curl、ab、http_load、spider、网络爬虫

        b/s架构browser/server(只需要客户端浏览器即可完成很多需要第三方软件才可以完成工作。比如在线linux、在线office、网页游戏等就是b/s架构)

     

2、缓存分类

        私有缓存:

                    存在于客户端浏览器,通常关闭浏览器后删除

        公共缓存:

                    正向代理服务器:接近于客户端(客户端局域网),代理用于访问网站,通常是客户端的网关地址,也可以只设置浏览器的代理功能。透明代理(trunning proxy)需要将客户机的网关指向代理服务器,然后代理服务器通过自身设置将客户端请求转发给实际web服务器的情况。

            WEBRESOURCEb05473ad16f40cc9ef71cfc54b188

                    反向代理服务器:接近于web服务器端,为客户端请求提供解析、负载均衡、缓存功能。当反向代理服务器接近客户端的时候,就已经是CDN的技术范围了,这里就需要智能DNS技术啦。而CDN实质实质上就是指缓存服务器之间的缓存共享,以及同级别缓存之间的自动路由功能,这种依据内容进行的路由就是内容分发网络CDN啦,而实现这种技术缓存服务器之间要通过CDP协议进行通信和路由。

 3、缓存时效机制

    由于原始服务器上的数据可能会变化,所以会有缓存时效的情况。最简单的缓存控制时效是在服务器端自行完成的。http1.0的管理手段很简单,主要是通过expire来定义。并且在缓存过期后重新发起请求,而不管这个缓存是否还有效。

    而1.1增加了缓存检测机制,取消了exprie机制,客户端通过发送条件式请求来判断缓存是否还有效,减少了重传次数。

    cache control

        max-age这种相对时长来解决了不同客户机和服务器之间存在时差的问题。

        Etag/if-none-match:通过生成随机ID号给客户端,来解决频繁的数据更新的情况。

HTTP首部控制缓存失效功能:

    1、expires:提供指定某web对象的过期时间,常用于纯静态内容缓存。

    2、cache-control:用于定义所有的缓存机制都必须遵循的缓存指示,并且它设置的时间会覆盖expire设置的时间,如public、private、no-cache

            etag:响应首部,用于在响应首部报文中为某web资源定义版本标识符

            last-modified:响应首部,用于回应客户端关于last-modified-since或者if-none-match首部的请求,通知客户端其请求的web对象最近的修改时间。

            if-modified-since:条件式请求首部,如果在此首部指定的时间后其请求的web内容发生了更改,则服务器响应更改后的内容。否则响应304.

            if-none-match:条件式请求首部,web服务器为其web内容定义了etag首部,客户端请求时能获取并保存这个首部的值;而后在后续的请求中通过if-none-match首部附加其认可的标签列表并让服务器检查器原始内容是否有可以和此列表中的某个标签匹配的标签,如果有,则相应304,没有就返回原始内容。

            vary:响应首部,原始服务器根据请求来源的不同响应的可能会有所不同的首部,最常见的是vary:accept-encoding,用于通知缓存机制其内容看起来可能不同于用户请求时accept-encoding-header首部格式

WEBRESOURCE089209894c85d0addd5a188dfe78f

WEBRESOURCEacb5107d279909d411df6003a00a9

WEBRESOURCEcd90a32f7c93412ce011c67f730ab

WEBRESOURCE3a4d6e5dc6964cfb2513b9ed51817WEBRESOURCE3b30eeca85c5082275a5a96121266WEBRESOURCEb8a4bf2470630281e06e7bcb11e6e

常见的缓存服务器:

    squid:适用于重量级大并发的环境,稳定性强

    varnish:灵活小巧,适用于轻量级环境

二、varnish

同memcached的最大区别

    varnish是开源的反向代理服务器,但最大应用还是缓存功能,所以从根本上讲工作在web服务器之前提供缓存功能的服务器都具备反向代理的功能,就像nginx、haproxy都具有缓存功能,但是它们的主要作用却是反向代理和负载均衡。

    所以和memcached的最大区别就是varnish工作在网桥模式下,而memcached工作在旁路模式。


1、特点

    缓存可以工作在内存中,也可以工作在磁盘(ssd固态)上,但是并不永久保存数据

    支持VCL,varnish configureation language专用的编程语言。所以vcl编写的代码需要编译成c代码,所以需要gcc程序。

    日志默认保存在内存中,并轮转替换使用。

2、软件架构

    management:主进程

                提供了接口:cli interface

                                 telnet interface

                                 web interface

                管理子进程

                初始化工作

                VCL编译及检测功能

                                        配置哪些对象可以缓存

                                       上游服务器有哪些

                                        算法及负载均衡

    child/cache:子进程

               日志管理(内存管理)

                命令行接口

                缓存管理及hash表

                接受请求

                后端web通信

                缓存对象失效管理

                工作线程管理

3、varnish的状态引擎:

    用于检测上游web服务器中的响应报文和客户端的请求报文内容,并依次判断作何操作的引擎,通常是在几个状态上设置规则并进行检测处理机制。

    基本工作流程:

            varnish开始处理一个请求时,首先分析http请求自身,比如从首部获取请求方法,验证其是否是一个合法的请求等。当这些基本的分析结束后就西药做出第一个决策,就是varnish是否从缓存中查找请求的资源,这个决定的实现需要VCL来完成,简单说,要由VCL-recv方法来完成。如果管理员没有自定义VCL-recv函数,varnish将会执行默认的vcl-recv函数,但是官方建议用默认的vcl-recv函数。然后返回到主引擎,并有主引擎决定请求的走向。

子例程通过return()将执行结果返回给varinish主进程,并有主进程判断下一步操作发往何处。

9种状态:

(1)vcl-recv:

(2)vcl-hash:直接查缓存

(3)vcl-pipe:不查缓存创建专用通道

(4)vcl-pass:不查缓存交给fetch直接到后端查找

(5)vcl-hit:vcl-hash查询结果,缓存命中

(6)vcl-miss:vcl-hash查询结果,未命中

(7)vcl-fetch:未命中后由fetch交到后端查询

(8)vcl-deliver:后端查询结果回送给客户端

(9)vcl-error:直接创建错误响应报文并返回给客户端

引擎讲解

    VCL-RECV:varnish完成对请求报文的解码为基本数据结构后要执行的第一个子例程

    (1)修改客户端数据以减少缓存对象的差异

    (2)基于客户端数据选用缓存策略

    (3)为web应用程序执行URL重写规则

    (4)挑选合适的后端web服务器

操作指令

    (1)pass:绕过缓存,不从缓存中查询内容或者不讲内容缓存存储起来

    (2)pipe:不对客户端进行检查或做出任何任何操作,而是在客户端和后端web之间建立专用“隧道”。

    (3)error:又varnish自己合成一个响应报文,一般是错误类消息、重定向信息。

    (4):lookup:在缓存中查找用户请求的数据,如果没有,后续操作可能会将请求的对象缓存下来。

VCL-RECV的安全缓存策略:

    (1)仅处理可以识别的http方法,并且只缓存get或head方法

    (2)不缓存任何用户特有的数据

三、VCL语法

    1、//、#、/*comment*/用于注释

    2、sub $name 定义函数

    3、不支持循环、有内置变量

    4、使用终止语句,没有返回值

    5、域专用

    6、操作符:=(赋值)、==(等值比较)、~(匹配模式)、!(取反)、&&(逻辑与)、||(逻辑或)

四、VCL内置函数

    1、regsub(str,regex,sub)、regsuball(str,regex,sub)基于正则表示式搜索指定的字符串并替换为指定的字符串。regsuball可以将str中能够被regex匹配到的字符串统统替换成sub,而regsub只替换一次

    2、return():当某VCL域运行结束时将控制权返回给varnish,并指示varnish如何进行后续的动作,返回的指令包括(lookup、pass、pipe、hit_for_pass、fetch、deliver、hash等)

    3、ban(expresssion)

    4、purge:从缓存中挑出对某对象及相关变种一并删除。

    5、return(restart):重新运行整个VCL,即重新从vcl-recv开始进行处理,每一次重启都会增加req.restarts变量的值,max_restarts用来限制最大启动次数。

    6、hash_data(str)

    7、ban_url(regex):bans所有其URL能够由regex匹配的缓存对象

五、varnish的后端存储(缓存存储)

    -s选项指定存储类型

    file:使用特定的文件存储全部的缓存数据,并通过mmap()系统调用将其映射到内存

    malloc:使用malloc()库调用在varnish启动时向操作系统申请指定大小的内存来存储缓存对象

    persistent:和file功能相同,可以持久存储数据,测试阶段

缓存命中率:

文档命中率

字节命中率

六、varnish安装

官方网站:http://varnish-cache.org

[root@node6.dtedu.com ~]# yum install varnish


==============================================================================================================================================================================================================================================

 Package                                                      Arch                                                   Version                                                       Repository                                            Size

==============================================================================================================================================================================================================================================

Installing:

 varnish                                                      x86_64                                                 2.1.5-6.el6                                                   epel                                                 264 k

Installing for dependencies:

 jemalloc                                                     x86_64                                                 3.6.0-1.el6                                                   epel                                                 100 k

 varnish-libs                                                 x86_64                                                 2.1.5-6.el6                                                   epel                                                  90 k


Transaction Summary

==============================================================================================================================================================================================================================================

Install       3 Package(s)


安装目录内容:

/etc/rc.d/init.d/varnishlog //实现日志存储必须开启此服务

/etc/rc.d/init.d/varnish //varnish启动服务

/etc/varnish //配置文件目录

/var/log/varnish //日志存储位置

/etc/sysconfig/avrnish //varnish自身软件工作特性

/etc/varnish/default.vcl //缓存,引擎配置文件


配置文件基本管理

/etc/sysconfig/varnish

NFILES=131072 最大连接数56535*2

MEMLOCK=82000 varnish内存使用空间大小

RELOAD_VCL=1 不用重启就可重新编译并载入VCL

NPROCS="unlimited"

VARNISH_VCL_CONF=/etc/varnish/default.vcl 默认启动的vcl配置文件

VARNISH_LISTEN_PORT=6081 默认监听端口,建议是80,对于后端的服务

VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 表示本地监听

VARNISH_ADMIN_LISTEN_PORT=6082 管理端口

VARNISH_SECRET_FILE=/etc/varnish/secret 认证秘钥文件

VARNISH_MIN_THREADS=1 最小线程数

VARNISH_MAX_THREADS=1000 最大线程数

VARNISH_THREAD_TIMEOUT=120 线程超时时间

VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin 以文件方式缓存数据

VARNISH_STORAGE_SIZE=1G 文件缓存大小

VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}” 以文件方式缓存数据


#VARNISH_malloc_STORAGE_SIZE=1G 内存缓存大小

#VARNISH_STORAGE=“malloc,${VARNISH_malloc_STORAGE_SIZE}” 以内存方式缓存数据

VARNISH_TTL=120

DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \

             -f ${VARNISH_VCL_CONF} \

             -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \

             -t ${VARNISH_TTL} \

             -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \

             -u varnish -g varnish \

             -S ${VARNISH_SECRET_FILE} \

             -s ${VARNISH_STORAGE}"


登录管理后台:

[root@node6.dtedu.com ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082


七、配置varnish及VCL

1、创建后端服务器(default.vcl)

2、重新加载vcl配置文件,并使用新的编译文件

vcl.load default1 /etc/varnish/default.vcl 重新加载

200 13      

VCL compiled.

vcl.list 查看当前可用的配置vcl

200 50      

active          1 boot

available       0 default1


vcl.use default1 使用指定的vcl文件

200 0       


八、varnish变量

屏幕快照 2017-08-24 上午10.17.56.png

客户端发送过来的到本地varnishi的请求及本地发往后端web的请求:

req.*

req.request:请求的方法

req.url:请求的url地址

req.proto:请求的协议版本

req.backend:指定响应请求的后端web服务器

req.backend.healthy:后端web服务器健康状态检查

req.http.header:自定义首部信息,header可以替换成自己需要的首部。

req.can_gzip:是否使用gzip编码

req.restarts:统计请求重启次数,避免死循环。

varnish自己向后端web服务器发送的请求之前:

bereq.*

bereq.request:请求类型

bereq.url:请求的URL

bereq.http.header

bereq.connect_timeout:等待后端服务器连接超时时间(秒)

bereq.first_byte_timeout:

varnishi从后端web服务器获得响应,并未处理之前:

beresp.*

向后端web服务器取得数据后,本地varnish操作方法:

obj.*

obj.hits:表示缓存在varnishi中的数据被命中次数,0表示没有命中过。

对客户端应答及web服务器对本地varnishi的响应:

resp.*

屏幕快照 2017-08-24 上午10.51.53.png

实例一、

目的:

检查客户端请求是否被varnishi缓存命中的方法

方法:

自定义在/etc/varnishi/下创建vcl。并应用

[root@node6.dtedu.com ~]# vim /etc/varnish/hit.vcl


backend backendserver{

        .host = "10.40.0.229";

        .port = "80";

}



sub vcl_fetch{

        if (req.request == "GET" && req.url  ~"\.(html|jpeg|jpg)$"){

                set beresp.ttl = 3600s;

        }

}


sub vcl_deliver{

        if (obj.hits > 0 ){

        set resp.http.X_CACHE = "HIT";

}       else {

        set resp.http.X_CACHE = "MISS";

}       return (deliver);

}


[root@node6.dtedu.com ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

200 199     

-----------------------------

Varnish Cache CLI 1.0

-----------------------------

Linux,2.6.32-573.el6.x86_64,x86_64,-sfile,-hcritbit


Type 'help' for command list.

Type 'quit' to close CLI session.


vcl.load hit /etc/varnish/hit.vcl 加载新建的vcl文件

200 13      

VCL compiled.

vcl.use hit 应用新的vcl实例

200 0       


vcl.list 查看当前使用的实例

200 45      

available       2 boot

active          1 hit


屏幕快照 2017-08-24 下午2.55.10.png

实验二、

目的:

指定所有已htm结尾的url路径都不从缓存中查找而是到后台去查找。

方法:


backend backendserver{

        .host = "10.40.0.229";

        .port = "80";

}


sub vcl_recv {

set req.http.X-forword-for = client.ip; 设置记录客户端地真实址,用于日志记录,需要在后端web服务器上的httpd.conf文件中修改“LogFormat”参数为“%{X-forward-for}i”,但是client.ip在演示时不可用,需要进一步了解。

        if (req.url ~ "\.html$"){

        return (pass);

}

}


sub vcl_fetch{

        if (req.request == "GET" && req.url  ~"\.(html|jpeg|jpg)$"){

                set beresp.ttl = 3600s;

        }

}


sub vcl_deliver{

        if (obj.hits > 0 ){

        set resp.http.X_CACHE = "HIT";

}       else {

        set resp.http.X_CACHE = "MISS";

}       return (deliver);

}


实验三、

目的:

通过varnish实现网站动静分离功能、需要变量req.backend进行指定。

[root@node6.dtedu.com ~]# vim /etc/varnish/hit.vcl


backend backendhttp{

        .host = "10.40.0.229";

        .port = "80";

}


backend backendphp{

        .host = "10.40.0.228";

        .port = "80";

}


sub vcl_recv {

        if (req.url ~ "\.html$"){

        return (pass);

}

        if (req.url ~"\.html$"){

        set req.backend = backendhttp;

        }else {

        set req.backend = backendphp;

        }

}


sub vcl_fetch{

        if (req.request == "GET" && req.url  ~"\.(html|jpeg|jpg)$"){

                set beresp.ttl = 3600s;

        }

}


sub vcl_deliver{

        if (obj.hits > 0 ){

        set resp.http.X_CACHE = "HIT";

}       else {

        set resp.http.X_CACHE = "MISS";

}       return (deliver);

}

~       

实验四

目的:

对后端web服务器进行健康状态检查。

方法:

参数:

.url :指定检测的网页地址。

.timeout:定义超时时间。

.request:明确指明请求的方法

.window:采集后端服务器数据的次数,默认8次

.initial:探测次数,默认3次

.interval:探测时间间隔,默认5秒

.threshold:采集成功次数,默认3次

通过probe进行定义,可以将probe直接定义在backend里面,也可以先定义probe一个实例名,然后再在backend里面进行调用。

a、

backend backendhttp{

        .host = "10.40.0.229";

        .port = "80”;

.probe = {

.url = ‘/test.jpg’;

.timeout = 0.3s;

.windows = 8;

.threshold = 3;

.initial = 3;

}

}

b、

probe healthcheck {

.url = ‘/test.jpg’;

.timeout = 0.3s;

.windows = 8;

.threshold = 3;

.initial = 3;

}


backend backendhttp{

.host = "10.40.0.229";

        .port = "80”;

.probe = healthcheck;

}

实验五

目的:varnish对后端web服务器进行负载均衡。

方法:首先通过backend进行声明,然后通过director 进行引用(方式有2种)。

director使用的调度算法包括round-robin和random两种

round-robin是轮询算法

random是随机从可用后端服务器上进行挑选,需要权重值调整优先级。

random在之后又衍生出了client和hash两种算法,其中client使用client.identify作为挑选条件,这样就可以使相同的client因子访问相同的后端web服务器,,默认是client.ip可以修改自定义标识符。而hash挑选方法是对同一个URL的请求发送到同一个后端主机,常用于多级缓存场景中。

可以通过fallback来定义备用服务器。

backend backendhttp{

        .host = "10.40.0.229";

        .port = "80";

}


backend backendphp{

        .host = "10.40.0.228";

        .port = "80";

}


director web1 round-robin{

#       .retries = 2;

        {

        .backend = backendhttp;

#       .weight = 1;

        }

        {

        .backend = backendphp;

#       .weight = 1;

        }

}

sub vcl_recv {

        if (req.url ~"\.html$"){

        return(pass);

        }

        set req.backend = web1;

}


sub vcl_fetch{

        if (req.request == "GET" && req.url  ~"\.(html|jpeg|jpg)$"){

                set beresp.ttl = 3600s;

        }

}


sub vcl_deliver{

        if (obj.hits > 0 ){

        set resp.http.X_CACHE = "HIT";

}       else {

        set resp.http.X_CACHE = "MISS";

}       return (deliver);

}


实验六

目的:

清理varnish过期缓存,设置访问控制列表acl。

方法:

实验七

目的:

设置网页防止盗链问题

方法:

检查referer是否是可允许的,或者是否是“站内链接”。

实验八

目的:

工具说明param.show和param.set。分别用来显示和设置varnish参数。

方法:

param.show

200 2411    

acceptor_sleep_decay       0.900000 []

acceptor_sleep_incr        0.001000 [s]

acceptor_sleep_max         0.050000 [s]

auto_restart               on [bool]

ban_lurker_sleep           0.000000 [s]

between_bytes_timeout      60.000000 [s]

cache_vbe_conns            off [bool]

cc_command                 "exec cc -fpic -shared -Wl,-x -o %o %s"

cli_buffer                 8192 [bytes]

cli_timeout                10 [seconds]

clock_skew                 10 [s]

connect_timeout            0.400000 [s]

critbit_cooloff            180.000000 [s]

default_grace              10 [seconds]

default_ttl                120 [seconds]

diag_bitmap                0x0 [bitmap]

err_ttl                    0 [seconds]

esi_syntax                 0 [bitmap]

expiry_sleep               1.000000 [seconds]

fetch_chunksize            128 [kilobytes]

first_byte_timeout         60.000000 [s]

group                      varnish (302)

http_headers               64 [header lines]

http_range_support         off [bool]

listen_address             :80

listen_depth               1024 [connections] 排队队列长度

log_hashstring             off [bool]

log_local_address          off [bool]

lru_interval               2 [seconds] 清理过期缓存时间间隔

max_esi_includes           5 [includes]

max_restarts               4 [restarts]

overflow_max               100 [%]

ping_interval              3 [seconds]

pipe_timeout               60 [seconds]

prefer_ipv6                off [bool]

purge_dups                 on [bool]

rush_exponent              3 [requests per request]

saintmode_threshold        10 [objects]

send_timeout               600 [seconds]

sess_timeout               5 [seconds]

sess_workspace             65536 [bytes]

session_linger             50 [ms]

session_max                100000 [sessions]

shm_reclen                 255 [bytes]

shm_workspace              8192 [bytes]

syslog_cli_traffic         on [bool]

thread_pool_add_delay      20 [milliseconds]

thread_pool_add_threshold  2 [requests]

thread_pool_fail_delay     200 [milliseconds]

thread_pool_max            1000 [threads]

thread_pool_min            1 [threads]

thread_pool_purge_delay    1000 [milliseconds]

thread_pool_stack          unlimited [bytes]

thread_pool_timeout        120 [seconds]

thread_pools               2 [pools]

thread_stats_rate          10 [requests]

user                       varnish (302)

vcl_trace                  off [bool]

waiter                     default (epoll, poll)


查看帮助信息:param.show -l

查看varnish连接状态信息:

[root@node6.dtedu.com ~]# varnishstat 


1+08:40:28                                                                                                                                                                                                                    node6.dtedu.com

Hitrate ratio:        1        1        1

Hitrate avg:     0.2576   0.2576   0.2576


          58         0.00         0.00 Client connections accepted 客户端连接个数

          66         0.00         0.00 Client requests received 客户端请求个数

          17         0.00         0.00 Cache hits 命中个数

          49         0.00         0.00 Cache misses 未命中个数

          30         0.00         0.00 Backend conn. success 成功连接后端次数

          19         0.00         0.00 Backend conn. failures 失败次数

          30         0.00         0.00 Fetch with Length

          11          .            .   N struct sess_mem

           1          .            .   N struct objecthead

           2          .            .   N struct smf

           2          .            .   N large free smf

           2          .            .   N worker threads 当前活动线程个数

           2         0.00         0.00 N worker threads created 启动工作线程个数

       69799         0.00         0.59 N worker threads limited

           1          .            .   N backends

          30          .            .   N expired objects 过期对象个数

           4          .            .   N LRU moved objects 移除的缓存对象的次数

          55         0.00         0.00 Objects sent with write

          58         0.00         0.00 Total Sessions

          66         0.00         0.00 Total Requests

          30         0.00         0.00 Total fetch

       17423         0.00         0.15 Total header bytes

        8465         0.00         0.07 Total body bytes

          54         0.00         0.00 Session Closed

          20         0.00         0.00 Session Linger

          14         0.00         0.00 Session herd

       11964         0.00         0.10 SHM records 共享内存记录个数

        8983         0.00         0.08 SHM writes

          60         0.00         0.00 allocator requests

  1073741824          .            .   bytes free

          19         0.00         0.00 SMS allocator requests

        7961          .            .   SMS bytes allocated

        7961          .            .   SMS bytes freed

          30         0.00         0.00 Backend requests made

           1         0.00         0.00 N vcl total

           1         0.00         0.00 N vcl available

           1          .            .   N total active purges

           1         0.00         0.00 N new purges added

          66         0.00         0.00 HCB Lookups without lock

          49         0.00         0.00 HCB Lookups with lock

          49         0.00         0.00 HCB Inserts

      117628         1.00         1.00 Client uptime


启动和查看varnish日志信息

[root@node6.dtedu.com ~]# service varnishlog start 

Starting varnish logging daemon:                           [  OK  ]

[root@node6.dtedu.com ~]# varnishlog 

    0 CLI          - Rd ping

    0 CLI          - Wr 200 19 PONG 1503646793 1.0

    0 CLI          - Rd ping

    0 CLI          - Wr 200 19 PONG 1503646796 1.0

    0 CLI          - Rd ping

    0 CLI          - Wr 200 19 PONG 1503646799 1.0


CDN实现条件就是知道ip地址的归属,如何收集ip地址的归属,可以通过IANA来查询




本文转自 blackstome 51CTO博客,原文链接:http://blog.51cto.com/137783/1968804,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
技术小阿哥
+ 关注