家里网络总被广告骚扰?这个Go项目帮你搞定
Blocky是一个6389星标的轻量级DNS代理,用Go打造,单二进制部署,支持广告拦截、客户端分组、多种加密协议。本文深入解析其链式架构设计,附带完整的树莓派部署方案。

作为一名被各类广告弹窗折磨了多年的开发者,我深刻体会到家庭网络管理的痛点:孩子看视频的片头广告、智能电视的推荐弹窗、甚至某些网站的恶意跳转。每次想解决,要么得在每个设备上装插件,要么得买个昂贵的"智能路由器"。
直到我遇到了Blocky——这个6389星标的Go语言项目,给了我一个全新的思路:为什么不直接在网络层面解决问题?
痛点:为什么需要网络级广告拦截
传统广告拦截方案的局限性很明显:
- 浏览器插件只能管浏览器,管不了电视和手机App
- 每买新设备都得重新配置
- 有些设备(比如IoT设备)压根不支持安装插件
而网络级的解决方案,思路就完全不同了:所有设备的域名解析请求都要经过路由器,在这个环节做拦截,一劳永逸。
Blocky做的就是这件事。它是一个轻量级的DNS代理服务器,所有发往互联网的域名查询都会先经过它,它会根据配置的规则决定是否放行。广告域名?直接返回空地址;正常网站?转发给上游DNS服务器。
架构设计:链式解析器的精妙之处
从源码层面看,Blocky的架构设计非常"Go风格"——简洁、模块化、职责单一。它的核心是一个Resolver链,每个DNS请求会依次经过多个处理节点:
客户端请求 → CachingResolver → BlockingResolver → ConditionalResolver → UpstreamResolver → 互联网
这种设计让我想起了责任链模式,但实现更加轻量。每个Resolver只关注自己的职责:
CachingResolver:检查本地缓存,命中则直接返回BlockingResolver:根据黑名单/白名单决定是否拦截ConditionalResolver:根据域名匹配条件转发到不同上游UpstreamResolver:最终转发到上游DNS服务器
这种链式架构的好处是组合灵活。你可以根据需要启用或禁用某些Resolver,而不会影响整体逻辑。比如在测试环境可以关闭BlockingResolver,在生产环境全部启用。
核心配置:看代码说话
别的项目可能给你一堆复杂的配置文件,Blocky就一个YAML文件搞定。来看看基础配置:
yaml
## 上游DNS服务器配置
upstream:
default:
- 1.1.1.1 # Cloudflare
- 8.8.8.8 # Google DNS
- https://dns.google/dns-query # DoH加密查询
## 缓存策略
caching:
minTime: 6h # 最小缓存时间
maxTime: 24h # 最大缓存时间
prefetching: true # 启用预取,热门域名提前缓存
## 广告拦截规则
blocking:
clientGroupsBlock:
default: # 默认组所有设备
- ads
- tracking
kids: # 儿童设备组(需配合clientLookup)
- ads
- tracking
- adult # 额外拦截成人内容
## 拦截列表来源
lists:
ads:
- https://someblocklist.com/ads.txt
tracking:
- https://someblocklist.com/tracking.txt
adult:
- https://someblocklist.com/adult.txt
这段配置有几个值得注意的设计细节:
1. 上游服务器的多样性:你可以同时配置传统的UDP DNS和现代的DoH(DNS over HTTPS)。Blocky会自动随机选择上游服务器,这样既能分散查询请求,又能提升隐私保护——没有单一服务商能完整记录你的查询历史。
2. 缓存预取机制:prefetching: true 这个选项很关键。它会监控即将过期的缓存记录,在过期前主动刷新。这意味着你常访问的网站,域名解析几乎是即时的。
3. 客户端分组:可以给不同设备分配不同策略。比如孩子的iPad加入kids组,自动启用成人内容过滤;智能家居设备加入白名单组,避免被误拦截。
实战部署:树莓派上的5分钟安装
我选择在树莓派上部署Blocky,原因很简单:它支持ARM架构,内存占用极低,24小时开机也不心疼电费。
使用Docker方式安装是最简单的:
bash
## 创建配置目录
mkdir -p ~/blocky && cd ~/blocky
## 下载配置文件模板
curl -o config.yml https://raw.githubusercontent.com/0xERR0R/blocky/main/config.yml
## 编辑配置文件(根据你的需求修改)
nano config.yml
## 启动Blocky容器
docker run --rm -d \
--name blocky \
--restart always \
-p 53:53/tcp -p 53:53/udp \
-p 8080:8080 \
-v ./config.yml:/app/config.yml \
-v ./data:/app/data \
spx01/blocky
这里有几个关键点:
--restart always:确保树莓派重启后自动恢复服务-p 53:53/tcp -p 53:53/udp:开放标准DNS端口-p 8080:8080:开放管理接口和Prometheus指标端点-v ./data:/app/data:持久化日志和缓存数据容器重启不丢失
启动完成后,登录你的路由器,将全局DNS服务器地址改为树莓派的局域网IP。至此,家里所有设备的查询请求都会经过Blocky处理。
高级玩法:条件转发和查询日志
如果你家里有多个网段,或者想记录所有查询日志,可以用更高级的配置:
yaml
## 条件转发:内网域名转发到本地DNS服务器
conditional:
mapping:
fritz.box: 192.168.178.1 # 路由器管理的域名
home.lan: 192.168.1.1 # 自定义局域网
## 客户端识别:通过IP或MAC地址识别设备并分组
clientLookup:
upstream: 192.168.178.1
## 查询日志:存储到数据库便于分析
queryLog:
type: mysql
target: mysql://user:password@tcp(db:3306)/blocky
logRetention: 7d
## Prometheus监控指标(默认开启)
prometheus:
enable: true
path: /metrics
这个配置有几个实用场景:
- 内网域名解析:配置
fritz.box或home.lan后,访问内网设备的主机名可以直接解析,无需每台设备配置hosts文件 - 设备级策略:通过
clientLookup可以自动识别设备类型(基于IP或DHCP分配的固定地址),应用不同策略 - 查询日志:记录所有查询请求,可以发现哪些设备在频繁查询广告域名,或者排查某些网站无法访问的原因
避坑指南:这几个地方要注意
部署过程中我遇到了一些问题,分享出来帮助大家少走弯路:
问题1:容器启动后无法解析域名
## 检查防火墙规则
sudo iptables -L -n | grep 53
## 树莓派可能需要开启端口转发
sudo sysctl -w net.ipv4.ip_forward=1
问题2:某些网站访问变慢
这通常是因为上游DNS服务器选择不当。建议至少配置两个不同服务商的DNS,比如Cloudflare + Google,必要时还可以添加国内的114DNS或阿里DNS作为备用。
问题3:Smart TV无法联网
某些智能电视会硬编码使用8.8.8.8等公共DNS,绕过路由器设置。解决方法是在路由器上配置强制重定向规则,将所有53端口流量重定向到Blocky。
个人评价:为什么我认为它值得学习
作为一个常年和复杂架构打交道的开发者,Blocky让我看到了简单设计的魅力:
1. 功能聚焦:只做一件事——做好轻量级DNS代理。没有强行集成Web管理界面(虽然有第三方项目),没有用户系统,这种克制很难得。
2. 配置即代码:单一配置文件,可以版本控制,修改配置=修改文件。这种理念在容器化时代反而更加合适。
3. 无状态设计:不需要数据库,没有临时文件,单二进制部署。这意味着部署、备份、迁移都极其简单。
当然,它也有不足:对不熟悉命令行的用户不太友好。虽然有完整的API,但不是人人都会用curl。如果能有一个可选的轻量级管理界面,可能会更受欢迎。
总结
用了一周后,家里的广告弹窗明显减少,孩子的iPad也不会跳出奇怪的推荐了。更重要的是,我掌握了家庭网络的"命门"——所有域名的生杀大权都握在自己手里。
如果你符合以下任何一种情况:
- 有多台设备需要统一管理
- 想保护孩子上网安全
- 对隐私有要求
- 想学习网络编程和DNS协议
- 需要一个轻量级的网络监控工具
那么Blocky绝对值得一试。这个项目也给了我一个启发:作为开发者,我们不一定非要解决高深的问题,把网络基础服务这种"小事"做到极致,同样能获得大量用户的认可。
6389个star,就是最好的证明。