VMOD扩展 #

一、VMOD概述 #

1.1 什么是VMOD #

VMOD (Varnish Module) 是Varnish的扩展模块系统,允许使用C语言编写扩展功能,为VCL提供额外的函数和能力。

1.2 VMOD优势 #

优势 说明
高性能 C语言实现,性能优异
可扩展 添加VCL不具备的功能
模块化 按需加载,灵活配置
社区支持 丰富的第三方模块

二、安装VMOD #

2.1 安装varnish-modules #

bash
# Ubuntu/Debian
sudo apt install varnish-modules

# CentOS/RHEL
sudo yum install varnish-modules

# Fedora
sudo dnf install varnish-modules

2.2 编译安装 #

bash
# 安装依赖
sudo apt install varnish-dev python3-docutils

# 克隆仓库
git clone https://github.com/varnish/varnish-modules.git
cd varnish-modules

# 编译安装
./configure
make
sudo make install

2.3 验证安装 #

bash
# 查看已安装的VMOD
ls /usr/lib/varnish/vmods/

# 输出示例
libvmod_bodyaccess.so
libvmod_cookie.so
libvmod_header.so
libvmod_saintmode.so
libvmod_std.so
libvmod_tcp.so
libvmod_unix.so
libvmod_vsthrottle.so

三、常用VMOD模块 #

3.1 std模块 #

vcl
import std;

sub vcl_recv {
    # 时间戳
    std.timestamp("request_start");
    
    # 日志
    std.log("Processing request: " + req.url);
    
    # syslog
    std.syslog(6, "Info: " + req.url);
    
    # 字符串转换
    set req.http.X-Lower = std.tolower(req.http.Host);
    set req.http.X-Upper = std.toupper(req.http.Host);
    
    # 类型转换
    set req.http.X-Int = std.integer(req.http.X-Value, 0);
    set req.http.X-Duration = std.duration(req.http.X-TTL, 60s);
    
    # 健康检查
    if (std.healthy(req.backend_hint)) {
        # 后端健康
    }
    
    # 回滚
    std.rollback(req);
    
    # 收集头
    std.collect(req.http.Cookie);
}

3.2 cookie模块 #

vcl
import cookie;

sub vcl_recv {
    # 解析Cookie
    cookie.parse(req.http.Cookie);
    
    # 获取Cookie值
    set req.http.X-Session = cookie.get("session_id");
    
    # 设置Cookie
    cookie.set("visited", "true");
    
    # 删除Cookie
    cookie.delete("tracking_id");
    
    # 重新构建Cookie字符串
    set req.http.Cookie = cookie.get_string();
}

3.3 header模块 #

vcl
import header;

sub vcl_deliver {
    # 添加头
    header.append(resp.http.X-Forwarded-For, client.ip);
    
    # 复制头
    header.copy(resp.http.X-Original-Host, req.http.Host);
    
    # 删除头
    header.unset(resp.http.X-Powered-By);
    
    # 正则替换
    header.regsub(resp.http.Server, "Apache", "MyServer");
}

3.4 vsthrottle模块 #

vcl
import vsthrottle;

sub vcl_recv {
    # 限流
    if (vsthrottle.is_denied("ip_" + client.ip, 100, 1s)) {
        return (synth(429, "Too Many Requests"));
    }
}

3.5 bodyaccess模块 #

vcl
import bodyaccess;

sub vcl_recv {
    # 限制请求体大小
    if (req.method == "POST") {
        if (req.http.Content-Length > 1048576) {
            return (synth(413, "Payload Too Large"));
        }
    }
}

3.6 tcp模块 #

vcl
import tcp;

sub vcl_recv {
    # 设置TCP选项
    tcp.set_socket_pace(1000);
    
    # 获取连接信息
    set req.http.X-Local-Port = tcp.local_port();
}

3.7 unix模块 #

vcl
import unix;

sub vcl_init {
    # 使用Unix socket连接后端
    new u = unix.socket("/var/run/backend.sock");
}

3.8 saintmode模块 #

vcl
import saintmode;

sub vcl_init {
    # 创建带熔断的后端
    new sm = saintmode.saintmode(backend1, 5);
}

sub vcl_recv {
    set req.backend_hint = sm.backend();
}

四、第三方VMOD #

4.1 vmod-dynamic #

动态后端管理:

vcl
import dynamic;

sub vcl_init {
    new d = dynamic.director(
        port = "8080",
        ttl = 10m
    );
}

sub vcl_recv {
    set req.backend_hint = d.backend("server1.example.com");
}

4.2 vmod-geoip #

地理位置识别:

vcl
import geoip;

sub vcl_recv {
    # 获取国家代码
    set req.http.X-Country = geoip.country_code(client.ip);
    
    # 获取城市
    set req.http.X-City = geoip.city(client.ip);
    
    # 地理路由
    if (geoip.country_code(client.ip) == "CN") {
        set req.backend_hint = china_backend;
    }
}

4.3 vmod-curl #

HTTP客户端:

vcl
import curl;

sub vcl_recv {
    # 发送HTTP请求
    new req = curl.request("GET", "http://api.example.com/check");
    req.set_header("Authorization", "Bearer token");
    
    if (req.perform() == 0) {
        set req.http.X-Check-Result = req.get_body();
    }
}

4.4 vmod-redis #

Redis集成:

vcl
import redis;

sub vcl_init {
    new r = redis.client("127.0.0.1", 6379);
}

sub vcl_recv {
    # 获取缓存
    set req.http.X-Cache-Key = r.get("cache_key:" + req.url);
    
    # 设置缓存
    r.set("cache_key:" + req.url, "value", 3600);
}

4.5 vmod-jwt #

JWT处理:

vcl
import jwt;

sub vcl_recv {
    # 验证JWT
    if (req.http.Authorization ~ "Bearer ") {
        set req.http.X-Token = regsub(req.http.Authorization, "^Bearer ", "");
        
        if (jwt.verify(req.http.X-Token, "secret-key")) {
            set req.http.X-User-ID = jwt.get_claim("user_id");
        } else {
            return (synth(401, "Invalid Token"));
        }
    }
}

五、自定义VMOD开发 #

5.1 VMOD结构 #

text
vmod_example/
├── src/
│   ├── vmod_example.c    # 源代码
│   └── vmod_example.vcc  # VCL接口定义
├── autogen.sh
├── configure.ac
└── Makefile.am

5.2 VCC接口定义 #

c
# vmod_example.vcc

$Module example 3 "Example VMOD"

$Function STR hello(STRING name)
$Function INT add(INT a, INT b)
$Function VOID log(STRING message)

5.3 C实现 #

c
// vmod_example.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vmod_example.h"

VCL_STRING vmod_hello(VRT_CTX, VCL_STRING name) {
    char *result;
    size_t len;
    
    if (name == NULL) {
        name = "World";
    }
    
    len = strlen("Hello, ") + strlen(name) + strlen("!");
    result = malloc(len + 1);
    snprintf(result, len + 1, "Hello, %s!", name);
    
    return result;
}

VCL_INT vmod_add(VRT_CTX, VCL_INT a, VCL_INT b) {
    return a + b;
}

VCL_VOID vmod_log(VRT_CTX, VCL_STRING message) {
    if (message != NULL) {
        VSL(ctx->vsl, SLT_VCL_Log, "%s", message);
    }
}

5.4 编译安装 #

bash
# 生成配置脚本
./autogen.sh

# 配置
./configure VARNISHSRC=/path/to/varnish/source

# 编译
make

# 安装
sudo make install

5.5 使用自定义VMOD #

vcl
import example;

sub vcl_recv {
    # 调用自定义函数
    set req.http.X-Greeting = example.hello("Varnish");
    
    # 计算函数
    set req.http.X-Sum = example.add(10, 20);
    
    # 日志函数
    example.log("Processing request");
}

六、VMOD最佳实践 #

6.1 性能考虑 #

c
// 避免频繁内存分配
// 使用工作区内存

VCL_STRING vmod_example(VRT_CTX, VCL_STRING input) {
    // 使用Varnish工作区内存
    char *result = WS_Printf(ctx->ws, "Result: %s", input);
    return result;
}

6.2 错误处理 #

c
VCL_STRING vmod_safe_operation(VRT_CTX, VCL_STRING input) {
    if (input == NULL) {
        VSL(ctx->vsl, SLT_VCL_Error, "Input is NULL");
        return NULL;
    }
    
    // 安全操作
    return result;
}

6.3 线程安全 #

c
// 使用线程局部存储或互斥锁

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

VCL_VOID vmod_thread_safe(VRT_CTX) {
    pthread_mutex_lock(&lock);
    // 临界区操作
    pthread_mutex_unlock(&lock);
}

七、VMOD调试 #

7.1 启用调试 #

bash
# 启动Varnish调试模式
varnishd -d -f /etc/varnish/default.vcl

# 查看VMOD加载日志
varnishlog -q "VCL_Log"

7.2 测试VMOD #

bash
# 使用varnishtest
varnishtest test.vtc

测试脚本示例:

vtc
# test.vtc

varnish v1 -vcl {
    import example;
    
    backend default { .host = "127.0.0.1"; }
    
    sub vcl_recv {
        set req.http.X-Test = example.hello("Test");
    }
} -start

client c1 {
    txreq
    rxresp
    expect resp.http.X-Test == "Hello, Test!"
} -run

八、常用VMOD列表 #

VMOD 功能 来源
std 标准工具函数 官方
cookie Cookie处理 varnish-modules
header 头操作 varnish-modules
vsthrottle 限流 varnish-modules
bodyaccess 请求体访问 varnish-modules
dynamic 动态后端 第三方
geoip 地理位置识别 第三方
redis Redis集成 第三方
jwt JWT处理 第三方
curl HTTP客户端 第三方

九、总结 #

本章我们学习了:

  1. VMOD概述:概念、优势
  2. 安装VMOD:包管理器、编译安装
  3. 常用模块:std、cookie、header、vsthrottle
  4. 第三方VMOD:dynamic、geoip、redis、jwt
  5. 自定义开发:VCC定义、C实现、编译安装
  6. 最佳实践:性能、错误处理、线程安全
  7. 调试测试:调试模式、varnishtest

掌握VMOD扩展后,让我们进入下一章,学习日志分析!

最后更新:2026-03-28