热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

varnishwebcache服务

varnish介绍缓存开源解决方案:varnish:是一个轻量级的Cache和高性能的反向代理软件,通常为httpd提供缓存。官方站点: http:www.varnish-cach

技术分享图片

varnish介绍

缓存开源解决方案:

   - varnish
         - 充分利用epoll机制(能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率),并发量大,单连接资源较轻
     - squid
         - 在较大并发下,稳点性好,老当益壮

varnish:是一个轻量级的Cache和高性能的反向代理软件,通常为httpd提供缓存。

官方站点: http://www.varnish-cache.org/

varnish架构:
技术分享图片

varnish拥有俩套配置文件;一套配置文件用于varnish自身进程的参数配置,另一套用于定义缓存规则;定义缓存规则需要使用灵活的语言来定义,这就是VCL(varnish语言);应用时需要将VCL编写的规则送给VCC编译后才能运行,所以安装varnish需要依赖gcc编译器。

     - Manager Process
         - 管理进程,相当于nginx的主控进程,不处理用户请求
     - Cacher Process
         - 线程Storage:完成缓存存储管理
         - 线程Log/Stats:日志记录----->存入共享内存Shared Memory Log中
         - 线程Worker threads:真正处理用户请求,通过线程池来定义,最大并发(线程池*线程池最大并发)
     - shared memory log
         - varnishlog:读取日志文件,保存在磁盘中
         - varnishstat:读取统计数据,计数器    
     - VCL配置接口:varnish配置语言
     - varnishadm:让varnish加载新配置文件
     - VCC Process:varnish的c编译器

安装(centos 7中varnish被收入epel仓库)

  yum -y install varnish

程序环境

 配置文件:
     - /etc/varnish/varnish.params(/etc/sysconfig/varnishd):配置varnish服务进程的工作特性(监听地址和端口,缓存机制等)
     - /etc/varnish/default.vcl:配置各Child/Cache线程的工作特性
 主程序:
     - /usr/sbin/varnishd
 CLI interface- /usr/bin/varnishadmin:通过此管理工具,完成与Manager Process的交互,进而控制varnish的工作特性
 Share Memory Log交互工具:
     - /usr/bin/varnishhist:日志历史
     - /usr/binvarnishlog:记录详细log(请求报文首部,响应报文首部等)
     - /usr/bin/varnishcsa:格式化记录日志
     - /usr/bin/varnishstat:日志统计
     - /usr/bin/varnishtop:日志排序分析
 测试工具程序:
     - /usr/bin/varnishtest
 VCL配置文件重载程序:
     - /usr/sbin/varnish_reload_vcl:此程序会编译配置文件
 Systemd Unit File:
     - /usr/lib/systemd/system/varnish.service:varnish服务
     - /usr/lib/systemd/system/varnishlog.service:原始记录日志(保存磁在盘上)
     - /usr/lib/systemd/system/varnishncsa.service:ncsa格式日志(保存磁在盘上)

arnish的缓存存储机制(Storage Types):

 - malloc[,size]:内存存储,[,size]用于定义空间大小,重启后所有缓存项失效
 - file[,path[,size[,granularity]]]:文件存储,黑盒,重启后所有缓存项失效
 - persistent,path,size:文件存储,黑盒,重启后所有缓存项有效(试验阶段)

varnish的程序选项

- 程序选项:/etc/varnish/varnish.params文件;
     - -a address[:port][,address[:port][...]:默认为6081端口;
     - -T address[:port]:默认为6082端口;
     - -s [name=]type[,options]:定义缓存存储机制;
     - -u user
     - -g group
     - -f config:VCL配置文件;
     - -F:运行于前台;
 - 运行时参数:/etc/varnish/varnish.params文件, DEAMON_OPTS
     - DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300"
     - -p param=value:设定运行参数及其值,可重复使用多次
     - -r param[,param...]:设定指定的参数为只读状态

重载vcl配置文件

# varnish_reload_vcl

varnishadm

# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 #登录管理程序
help [] 获取帮助
ping [] 测试服务器
auth 
quit 退出cli
banner
status 显示状态
start 启动
stop 停止
vcl.load   加载VCL配置文件
vcl.inline  
vcl.use  激活VCL配置文件
vcl.discard  删除VCL配置
vcl.list 列出VCL配置
param.show [-l] [] 列出当前运行的参数
param.set   运行参数临时调整
panic.show
panic.clear
storage.list 列出数据存储信息
vcl.show [-v]  列出VCL详细配置
backend.list [] 列出后端服务器
backend.set_health  
ban  <operator>  [&&   ]...
ban.list

配置文件

默认配置文件

RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/default.vcl  #指定加载VCL配置文件
VARNISH_LISTEN_ADDRESS=192.168.1.5  #服务监听的地址
VARNISH_LISTEN_PORT=6081  #默认监听端口
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1  #管理服务监听的地址
VARNISH_ADMIN_LISTEN_PORT=6082 #管理服务监听的端口
VARNISH_SECRET_FILE=/etc/varnish/secret  #连接秘钥
VARNISH_STORAGE="malloc,256M"  #用内存提供保存缓存,大小为256M
VARNISH_USER=varnish  #用户身份
VARNISH_GROUP=varnish #组身份
DAEMON_OPTS="-p thread_pool_min=5 -p thread_pool_max=500 -p thread_pool_timeout=300" #指定进程的运行参数

缓存流程图解
技术分享图片

- 一个请求进入,varnish收下这个请求,判断是否对这个请求做处理
    - 访问控制,直接拒绝
    - 请求方法不识别,直接找后端服务器响应
    - 正常请求,进入下一步
- 查询此请求方法是否能缓存
    - 不能缓存(如post,put),交给vcl_fetcg,由vcl_fetch投递给后端服务器响应
    - 能缓存,进入下一步
- vcl_hash基于hash查找缓存中是否有对应资源
    - 如果命中,从本地缓存中直接响应给客户端
    - 若未命中,通过vcl_fetch到后端服务器取回资源,然后先缓存,再响应给客户端

VCL

Varnish配置语言(VCL)是一种特定于域的语言,用于描述Varnish Cache的请求处理和文档缓存策略。加载新配置时,由Manager进程创建的VCC进程将VCL代码转换为C.此C代码通常由gcc共享对象编译。然后将共享对象加载到cacher进程中。

 VCL状态引擎切换
技术分享图片

- vcl_recv收到请求,查找vcl_hash
    - 若命中(传递值hit),交由vcl_hit
        - hit命中,直接从缓存中响应,交由vcl_deliver投递给客户端
        - vcl_hash -(hit)-> vcl_hit --> vcl_deliver
    - 未命中(传递值miss),交由vcl_miss
        - 交由vcl_backend_fetch请求后端服务器
        - vcl_hash -(miss)-> vcl_miss --> vcl_backend_fetch --> vcl_backend_response --> vcl_deliver
    - 若要删除缓存项(传递值purge),交由vcl_purge
        - 交由vcl_synh管理缓存,删除对应缓存
        - vcl_hash -(purge)-> vcl_purge --> vcl_synth
    - 若不能理解请求(传递值pipe),交由vcl_pipe,请求被直接送至后端服务器
        - vcl_hash -(pipe)-> vcl_pipe
    - 并发连接超出(传递值busy),进入waiting状态,会等待重新请求查询缓存
    - 传递值(pass,hit-for-pass),交由vcl_pass
    - vcl_hit和vcl_miss也能交由给pass
- 两个特殊引擎:
    - vcl_init:在处理任何请求之前要执行的vcl代码:主要用于初始化vMODS
    - vcl_fini:所有的请求都已经结束,在vcl配置被丢弃时调用,主要用于清理vMODS

vainish默认的VCL配置

默认VCL配置也叫做隐式规则,在配置文件中无法看到,即使我们修改了配置文件,默认配置规则也是在最后做处理。

varnish> vcl.show -v boot  #在客户端cli工具中查看
sub vcl_recv {
    if (req.method == "PRI") {  #如果客户端的请求方法是PRI,不支持SPDY或HTTP/2.0
        return (synth(405));  #则构建一个405的包响应给客户端
    }
    if (req.method != "GET" &&  #如果客户端的请求方法不是GET
      req.method != "HEAD" &&  #并且不是HEAD
      req.method != "PUT" &&  #并且不是PUT
      req.method != "POST" &&  #并且不是...
      req.method != "TRACE" &&
      req.method != "OPTIONS" &&
      req.method != "DELETE") {
        return (pipe);  #即,不是标准HTTP请求方法的交给pipe(管道)
    }
    if (req.method != "GET" && req.method != "HEAD") {  #请求方法不是GET和HEAD的
        return (pass);  #交给pass处理,也就是除了GAT和HEAD方法其他的无法缓存
    }
    if (req.http.Authorization || req.http.COOKIE) {  #http的请求首部包含Authorization(认证)或COOKIE,即个人专有信息
        return (pass);  #交给pass处理,因为这些带有个人信息的数据无法缓存
    }
    return (hash);  #以上的规则都没有做处理的请求交给hash做处理,剩下的是可以查询缓存的请求了
}

sub vcl_pipe
sub vcl_pass
sub vcl_hash
sub vcl_purge
sub vcl_hit
sub vcl_miss
sub vcl_deliver
sub vcl_synth
sub vcl_backend_fetch
sub vcl_backend_response
sub vcl_backend_error
sub vcl_init
sub vcl_fini

VCL语法格式

 - 文件开始要注明vcl版本号:vcl 4.0- //,#,/*,*/为注释
 - 子例行Subroutines使用sub关键字;例如sub_recv {...};
 - 不支持循环,但支持条件语法,支持內建变量(受限于引擎)
 - 使用一个keyword基于return函数终止当前状态引擎,并决定交给哪一个状态引擎
 - “域”专用配置,在一个状态引擎中的配置只对当前状态引擎有效

VCL Finite State MAchine

 - 每一个请求被单独处理
 - 请求和请求间任何时间都是隔离的
 - 各状态引擎有相关性,通过return连接
 - 內建VCL code一直有效,并附加在自建的代码之后(vcl.show -v boot)

三类主要语法

 - sub subroutine {
         ...
     }
 - if CONDITION {
         ...
     } else {    
         ...
     }
 - return(), hash_data()

内建函数

regsub(str,regex,sub):字符串为str,根据正则regex模式匹配,把匹配到的内容替换为sub,只替换一次
regsuball(str,regex,sub):和regsub相同,替换所有
ban(boolean expression):符合表达式的都清理
hash_data(input):对input做hash计算
synthetic(str)
hash_data():指明哈希计算的数据;减少差异,以提升命中率
regsub(str,regex,sub):把str中被regex第一次匹配到字符串替换为sub;主要用于URL Rewrite
regsuball(str,regex,sub):把str中被regex每一次匹配到字符串均替换为sub
return()
ban(expression)
ban_url(regex):Bans所有的其URL可以被此处的regex匹配到的缓存对象
synth(status,"STRING"):生成响应报文

Keywords

call subroutine:调用子例行程序
return(action):指明下一个动作
new
set:设定变量的值
unset:取消变量的值

布尔型表达式操作符

==,!=,~,>,>=,<,<=
     逻辑操作符:&&,||,!
     变量赋值:=

示例:obj.hits是内建变量,用于保存某缓存项从缓存中命中的次数

# vim /etc/varnish/varnish.params
VARNISH_LISTEN_PORT=6081 #监听端口默认是监听在本机的所有低智商的端口
# vim /etc/varnish/default.vcl
backend default {
    .host = "192.168.130.10"; #后端服务器的地址
    .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;
    }
}
# systemctl restart varnish  #谨慎重启varnish服务,会导致之前的缓存失效
#yum install httpd #在后端安装
#systemctl start http #启动服务
# echo X
-Cache > /var/www/html/index.html #在后端服务器上添加页面 # for i in {1..5};do curl -I -s 192.168.130.8:6081 | grep "X-Cache"; done #在客户端访问 X-Cache: MISS from192.168.130.8 X-Cache: HTT via 192.168.130.8 X-Cache: HTT via 192.168.130.8 X-Cache: HTT via 192.168.130.8 X-Cache: HTT via 192.168.130.8

变量类型
技术分享图片
技术分享图片
技术分享图片

示例: 强制对某类资源的请求不检查缓存

# vim /etc/varnish/default.vcl
sub vcl_recv {
    if (req.url ~ "(?i)^/(login|admin)") {  #"?i"表示忽略大小写,匹配到url中带有login或admin的不查询缓存
        return(pass);
    }
}
# varnish_reload_vcl
# for i in {1..5};do curl -I -s 192.168.130.8:6081/login | grep "X-Cache"; done #在客户端访问/login全部MISS
X-Cache: MISS from192.168.130.8
X-Cache: MISS from192.168.130.8
X-Cache: MISS from192.168.130.8
X-Cache: MISS from192.168.130.8
X-Cache: MISS from192.168.130.8
# for i in {1..5};do curl -I -s 192.168.130.8:6081/admin | grep "X-Cache"; done #在客户端访问/admin全部MISS
X-Cache: MISS from192.168.130.8
X-Cache: MISS from192.168.130.8
X-Cache: MISS from192.168.130.8
X-Cache: MISS from192.168.130.8
X-Cache: MISS from192.168.130.8
# for i in {1..5};do curl -I -s 192.168.130.8:6081/ | grep "X-Cache"; done #在客户端访问其他页面正常缓存查询
X-Cache: MISS from192.168.130.8
X-Cache: HTT via 192.168.130.8
X-Cache: HTT via 192.168.130.8
X-Cache: HTT via 192.168.130.8
X-Cache: HTT via 192.168.130.8

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

sub vcl_backend_response {
    if (beresp.http.cache-control !~ "s-maxage") {
        if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|css|js)$") {
            unset beresp.http.Set-COOKIE;
            set beresp.ttl = 3600s;
        }
    }
}

示例:在报文首部添加真正的客户端IP,使得后端server可以记录真正客户端来源

# vim /etc/varnish/default.vcl
sub vcl_recv {
    if (req.restarts == 0) {  #匹配没有被重写的URL请求,即第一次请求
        if (req.http.X-Forwarded-For) {  #变量存在并且有值则为真
            set req.http.X-Forwarded-For = req.http.X-Forwarded-For + "," + client.ip;  #将真正的client.ip添加到此变量中,用","隔开
        } else {
            set req.http.X-Forwarded-For = client.ip;  #如果变量不存在或值为空,则直接将client.ip赋值与
        }
    }
}
# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
varnish> vcl.load conf1 /etc/varnish/default.vcl
varnish> vcl.use conf1
varnish> vcl.list   
available       0 boot
available       0 reload_2018-07-14T09:55:58
active          0 conf1  #当前正在使用的配置
[root@web ~]# vim /etc/httpd/conf/httpd.conf
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
[root@web ~]# systemctl restart httpd
[root@client ~]# for i in {1..5}; do curl -I -s http://192.168.130.8:6081/admin |grep "X-Cache"; done #在客户端访问
X-Cache: MISS from192.168.130.8
X-Cache: HTT via 192.168.130.8
X-Cache: HTT via 192.168.130.8
X-Cache: HTT via 192.168.130.8
X-Cache: HTT via 192.168.130.8
[root@web ~]# tail /var/log/httpd/access_log 
192.168.130.8 - - [15/Jul/2018:15:25:03 +0800] "HEAD /login HTTP/1.1" 404 - "-" "curl/7.29.0"
192.168.130.8 - - [15/Jul/2018:15:25:03 +0800] "HEAD /login HTTP/1.1" 404 - "-" "curl/7.29.0"
192.168.130.8 - - [15/Jul/2018:15:25:03 +0800] "HEAD /login HTTP/1.1" 404 - "-" "curl/7.29.0"
192.168.130.8 - - [15/Jul/2018:15:25:13 +0800] "HEAD /admin HTTP/1.1" 404 - "-" "curl/7.29.0"
192.168.130.8 - - [15/Jul/2018:15:25:13 +0800] "HEAD /admin HTTP/1.1" 404 - "-" "curl/7.29.0"
192.168.130.8 - - [15/Jul/2018:15:25:13 +0800] "HEAD /admin HTTP/1.1" 404 - "-" "curl/7.29.0"
192.168.130.8 - - [15/Jul/2018:15:25:13 +0800] "HEAD /admin HTTP/1.1" 404 - "-" "curl/7.29.0"
192.168.130.8 - - [15/Jul/2018:15:25:13 +0800] "HEAD /admin HTTP/1.1" 404 - "-" "curl/7.29.0"
192.168.130.8 - - [15/Jul/2018:15:27:09 +0800] "GET / HTTP/1.1" 200 8 "-" "curl/7.29.0"
192.168.130.7,192.168.130.7 - - [15/Jul/2018:15:58:58 +0800] "GET /admin HTTP/1.1" 404 203 "-" "curl/7.29.0"#拿到了真正客户端IP,而不是之前的varnish服务器的IP

示例:访问控制,拒绝curl客户端的访问

sub vcl_recv {
    if(req.http.User-Agent ~ "curl") {
        return(synth(403));
    }
}

缓存对象的修剪:purge

 一般在发布新版的内容时需要将缓存清零,然后重新加载新的缓存。

1) 能执行purge操作

sub vcl_purge {
    return (synth(200,"Purged"));
}

2) 何时执行purge操作

sub vcl_recv {
    if (req.method == "PURGE") {
        return(purge);
    }
    ...
}

示例:清除指定缓存

# vim /etc/varnish/default.vcl
acl purgers {
    "127.0.0.0"/8;
    "192.168.0.0"/24;
}
sub vcl_recv {
    if (req.method == "PURGE") {
        if (!client.ip ~ purgers) {
            return(synth(405,"Purging not allowed for " + client.ip));
        }
        return(purge);
    }
}
varnish> vcl.load conf3 /etc/varnish/default.vcl
varnish> vcl.use conf3
[root@client ~]# curl -I http://192.168.130.8:6081/
HTTP/1.1 200 OK
Date: Sun, 15 Jul 2018 08:53:15 GMT
Server: Apache/2.4.6 (CentOS)
Last-Modified: Sun, 15 Jul 2018 07:58:29 GMT
ETag: "8-57105141f79f4"
Content-Length: 8
Content-Type: text/html; charset=UTF-8
X-Varnish: 32833 61
Age: 12
Via: 1.1 varnish-v4
X-Cache: HTT via 192.168.130.8 #从缓存中获取
Connection: keep-alive
[root@client ~]# curl -I -X "PURGE"  http://192.168.130.8:6081/ #清除缓存
HTTP/1.1 405 Purging not allowed for 192.168.130.7 #客户端IP 
Date: Sun, 15 Jul 2018 08:53:22 GMT
Server: Varnish
X-Varnish: 32831
Content-Type: text/html; charset=utf-8
Retry-After: 5
Content-Length: 333
Connection: keep-alive
[root@client ~]# curl -I http://192.168.130.8:6081/ 
HTTP/1.1 200 OK
Date: Sun, 15 Jul 2018 08:56:23 GMT
Server: Apache/2.4.6 (CentOS)
Last-Modified: Sun, 15 Jul 2018 07:58:29 GMT
ETag: "8-57105141f79f4"
Content-Length: 8
Content-Type: text/html; charset=UTF-8
X-Varnish: 63
Age: 0
Via: 1.1 varnish-v4
X-Cache: MISS from192.168.130.8 #不从缓存中获取
Connection: keep-alive

缓存对象的修剪:Banning

1)varnishadm: ban

varnish> ban req.url ~ (?i)^/Javascripts

2)在配置文件中定义,使用ban()函数

sub vcl_recv {
    if (req.method == "BAN") {
        ban("req.http.host == " + req.http.host + " && req.url == " + req.url);  #将规则拼接起来传递给ban函数
        return(synth(200, "Ban added"));
    }
}
# curl -I -X "BAN" http://192.168.130.8:6081/Javascripts/

多个后端主机实现调度功能

1、动静分离示例:

backend default {
    .host = "172.20.81.10";
    .port = "80";
}
backend appsrv {
    .host = "172.20.81.11";
    .port = "80";
}
sub vcl_recv {
    if (req.url ~ "(?i)\.php$") {
        set req.backend_hint = appsrv;
    } else {
        set req.backend_hint = default;
    }
}

2、轮询调度

import directors;
backend srv1 {
    .host = "192.168.130.10";
    .port = "80";
}
backend srv2 {
    .host = "192.168.130.11";
    .port = "80";
}
sub vcl_init {
    new websrvs = directors.round_robin();  #round_robin()调度算法,不支持加权
    websrvs.add_backend(srv1);
    websrvs.add_backend(srv2);
}
sub vcl_recv {
    set req.backend_hint = websrvs.backend();
}

3、基于COOKIE的session sticky

sub vcl_init {
    new h = directors.hash();
    h.add_backend(one, 1);
    h.add_backend(two, 1);
}
sub vcl_recv {
    set req.backend_hint = h.backend(req.http.COOKIE);
}

4、随机调度,支持权重

sub vcl_init {
    new websrvs = directors.random();
    websrvs.add_backend(srv1, 1);
    websrvs.add_backend(srv2, 2);
}

5、后端健康检查

.probe:定义健康状态检测方法;
.url:检测时要请求的URL,默认为”/";
.request:发出的具体请求;
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.dongfei.tech"
"Connection: close"
.window:基于最近的多少次检查来判断其健康状态;
.threshold:最近.window中定义的这么次检查中至有.threshhold定义的次数是成功的;
.interval:检测频度;
.timeout:超时时长;
.expected_response:期望的响应码,默认为200;
import directors;
probe http_chk {
        .url = "/index.html";
        .interval = 2s;
        .timeout = 2s;
        .window = 10;  #最近10次检查
        .threshold = 7;  #有7次成功则为健康主机
}
backend srv1 {
        .host = "192.168.130.10";
        .port = "80";
        .probe = http_chk;
}
backend srv2 {
        .host = "192.168.130.11";
        .port = "80";
        .probe = http_chk;
}
sub vcl_init {
        new websrvs = directors.random();
        websrvs.add_backend(srv1, 1);
        websrvs.add_backend(srv2, 2);
}
sub vcl_recv {
        set req.backend_hint = websrvs.backend();
}
varnish> backend.list #查看后端主机健康状态信息     
Backend name                   Refs   Admin      Probe
srv1(192.168.0.9,,80)          3      probe      Healthy 10/10
srv2(192.168.0.10,,80)         3      probe      Healthy 10/10
varnish> backend.set_health srv1 sick|healthy|auto  #手动标记主机状态 down|up|probe

设置后端的主机属性:

backend BE_NAME {
    ...
    .connect_timeout = 0.5s;  #连接超时时间
    .first_byte_timeout = 20s;  #第一个字节20s不响应则为超时
    .between_bytes_timeout = 5s;  #第一个字节和第二个字节间隔超时时间
    .max_connections = 50;  #最大连接数
}

varnish的运行时参数

最大并发连接数 = thread_pools * thread_pool_max

thread_pools:工作线程数,最好小于或等于CPU核心数量
thread_pool_max:每线程池的最大线程数
thread_pool_min:最大空闲线程数
thread_pool_timeout:空闲超过多长时间被清除
thread_pool_add_delay:生成线程之前等待的时间
thread_pool_destroy_delay:清除超出最大空闲线程数的线程之前等待的时间

日志管理

virnish的日志默认存储在80M的内存空间中,如果日志记录超出了则覆盖前边的日志,服务器重启后丢失;需要更改配置使其永久保存到磁盘

# varnishstat -1 -f MAIN  #指定查看MAIN段的信息
# varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss  #显示指定参数的当前统计数据
MAIN.cache_hit              47         0.00 Cache hits
MAIN.cache_miss             89         0.01 Cache misses

将日志永久保存到:/var/log/varnish/varnish.log

# systemctl start varnishlog.service

varnish web cache服务


推荐阅读
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
author-avatar
江山代有人2502914563
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有