阅读视图

发现新文章,点击刷新页面。
☑️ ☆

Java动态代理总结

动态代理技术在Java中有着非常广泛的应用,我再阅读Spring源码的时候多次碰到对这种技术的使用,在这篇文章做个总结。

代理模式

首先介绍下什么是代理模式:代理模式给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。

在代理模式中,我们把角色分为 3 种:

Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;
RealSubject(真实主题角色):真正实现业务逻辑的类;
Proxy(代理主题角色):用来代理和封装真实主题;

后面在编写Java动态代理的代码时,我们也会看到代码与这三个角色的对应关系。

如果根据字节码的创建时机来分类,可以将代理模式分为静态代理和动态代理:

  • 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
  • 而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件

静态代理

我们先通过实例来了解下静态代理,理解静态代理的缺点,再来了解动态代理

编写一个接口 Run,以及该接口的一个实现类 Runner。

public interface Run {
	public void run();
}
public class Runner implements Run {
	@Override
	public void run() {
		System.out.println("I am running");
	}
}

然后我们使用静态代理对 Runner 进行功能增强,在调用run之前记录一些日志。写一个代理类 RunnerProxy ,代理类需要实现 Run

public class RunnerProxy implements Run {
	private Run run;

	public RunnerProxy(Run run) {
		this.run = run;
	}

	@Override
	public void run() {
		System.out.println("before run");
		run.run();
		System.out.println("after run");
	}
}

具体使用方式如下:

public class ProxyTest {
	public static void main(String[] args) {
		Run run = new Runner();
		RunnerProxy runnerProxy = new RunnerProxy(run);
		runnerProxy.run();
	}
}

打印结果如下:

before run
I am running
after run

通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。

静态代理的缺点
虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。

1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:

  • 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
  • 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类

2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。

动态代理

动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用,在这里我们借助JDK自带的动态代理和CGLIB动态代理两种技术来实现。

JDK动态代理

JDK动态代理主要涉及两个类:java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler

我们依照上面代理的三个角色分别来写编码。

Subject(抽象主题角色),对应Run接口:

public interface Run {
	public void run();
}

RealSubject(真实主题角色),对应Runner类:

public class Runner implements Run {
	@Override
	public void run() {
		System.out.println("I am running");
	}
}


Proxy(代理主题角色),对应ProxyHandler:

public class ProxyHandler implements InvocationHandler {
	private Object object;

	public ProxyHandler(Object object) {
		this.object = object;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("before " + method.getName());
		Object object = method.invoke(this.object, args);
		System.out.println("after " + method.getName());
		System.out.println("--------------------------------");
		return object;
	}
}

具体使用方式如下:

import java.lang.reflect.Proxy;

public class ProxyTest {
	public static void main(String[] args) {
		Run run = new Runner();
		Jump jump = new Jumper();
		ProxyHandler handler = new ProxyHandler(run);
		Run proxyRunner = (Run) Proxy.newProxyInstance(run.getClass().getClassLoader(), run.getClass().getInterfaces(), handler);
		proxyRunner.run();
	}
}

运行结果:

before run
I am running
after run

CGLIB动态代理

首先maven引入CGLIB包。

正常编写类Jumper,不需要实现接口

public class Jumper {
	public void jump() {
		System.out.println("I am jumping");
	}
}

编写一个JumperLogger,继承MethodInterceptor,用于方法的拦截回调:

public class JumperLogger implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(object, objects);  
        after();
        return result;
    }
    private void before() {
        System.out.println(String.format("log start time [%s] ", new Date()));
    }
    private void after() {
        System.out.println(String.format("log end time [%s] ", new Date()));
    }
}

具体使用方式如下:

public class ProxyTest {
	public static void main(String[] args) {
		JumperLogger jumperLogger = new JumperLogger();
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(Jumper.class);  
		enhancer.setCallbacks(new Callback[]{jumperLogger});  
		Jumper proxy = (Jumper) enhancer.create(); 
		proxy.jump();
	}
}

运行结果如下:

log start time [Fri Apr 14 17:55:33 CST 2023] 
I am jumping
log end time [Fri Apr 14 17:55:33 CST 2023] 

CGLIB 创建动态代理类的模式是:

  1. 查找目标类上的所有非final 的public类型的方法定义;
  2. 将这些方法的定义转换成字节码;
  3. 将组成的字节码转换成相应的代理的class对象;
  4. 实现 MethodInterceptor接口,用来处理对代理类上所有方法的请求

JDK动态代理与CGLIB动态代理对比

JDK动态代理:基于Java反射机制实现,必须要实现了接口的业务类才能用这种办法生成代理对象。

cglib动态代理:基于ASM机制实现,通过生成业务类的子类作为代理类。

JDK Proxy 的优势:

  • 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。
  • 平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。
  • 代码实现简单。

基于类似 cglib 框架的优势:

  • 无需实现接口,达到代理类无侵入
  • 只操作我们关心的类,而不必为其他相关类增加工作量。
  • 高性能

Java动态代理总结最先出现在牧尘的网络日志

☑️ ⭐

Model资源-记录

记录下Stable Diffusion模型下载位置。

https://rentry.co/sdmodels(目前觉得比较全)
https://cyberes.github.io/stable-diffusion-models/(SD模型)
https://publicprompts.art/(App Icon Generator,比较有趣想资源)
https://huggingface.co/ (在网站中检索)
http://aimodel.subrecovery.top
https://docs.qq.com/doc/DY0lFeWZuVXRCdUJU
http://www.123114514.xyz/models/ckpt(挺多私炉的)

Model资源-记录最先出现在牧尘的网络日志

☑️ ⭐

常用命令设置和取消代理

工作中可能需要对有些命令设置代理, 我把最近使用的命令进行了总结,后面遇到其他命令也会持续进行更新。

git设置和取消代理

git设置代理

git config --global http.proxy http://127.0.0.1:1080
git config --global https.proxy https://127.0.0.1:1080

git取消代理

git config --global --unset http.proxy
git config --global --unset https.proxy

npm设置和取消代理

npm设置代理

npm config set proxy http://server:port
npm config set https-proxy http://server:port

如果代理需要认证的话可以这样来设置。

npm config set proxy http://username:password@server:port
npm config set https-proxy http://username:pawword@server:port

npm取消代理

npm config delete proxy
npm config delete https-proxy

如果代理不支持https的话需要修改npm存放package的网站地址。

npm config set registry 'https://registry.npm.taobao.org

也可以通过安装cnpm的方式解决

npm install -g cnpm --registry=https://registry.npm.taobao.org

常用命令设置和取消代理最先出现在牧尘的网络日志

☑️ ⭐

NAS折腾记录:外网访问

很多朋友买回NAS后很关心的一个问题就是如何在外网访问。这里我把自己认为比较好的方法罗列出来并一一实现,我们可以根据自己的实际情况选择使用。

外网访问方法汇总

如图,汇总了我目前认为在外网访问家中NAS比较好用的方法,首推的方法当然还是开通公网IP。

开通公网IP之后启用DDNS,然后我们就可以直接使用路由器端口映射到NAS,或者使用Traefik、npm代理都可以。

如果实在无法开通公网IP的话,也可以使用frp进行内网穿透,内网穿透需要有一个拥有公网IP的服务器做一个跳板,有个人服务器的话最好,没有的话,可以使用第三方的免费frp服务。

下面我对这些方法逐个进行介绍。

可以开通公网IP

首先介绍有公网IP的情况,有公网IP的话,需要配置下DDNS,我在之前的NAS折腾记录:打通网络那篇文章有过相关的介绍,不清楚怎么配置DDNS的可以去参考下。

我在这里假设我们的DDNS域名是*.deamlyn.cn,也就是dreamlyn.cn的所有子域名都指向家中的网络。

路由器端口映射到NAS

配置好DDNS后,外网访问NAS的最简单的办法就是直接使用路由器端口映射到NAS。

我以我家的小米路由器为例进行介绍,在路由器界面点击高级设置,然后选择端口转发,在里面添加一条规则,其中名字随便写,这里输入NAS,协议选TCP,端口输入5000,内部IP地址就输入NAS的IP地址,我家的是192.168.31.206,内部端口输入5000,点击添加就可以了。

之后我们就可以使用home.dreamlyn.cn:5000访问NAS 了。

这种方法固然简单,但是如果在NAS上面安装了很多的服务,每个服务都有不同的端口号,那么我们很容易记错不同服务的端口,所以比较好的方式是使用反向代理,我们只用路由器对traefik或者npm等反向代理服务进行端口映射,而对其他服务则使用traefik等进行反向代理即可。

这样的话,对于不同的服务,我们可以使用不同的子域名进行访问。比如访问nas,就使用nas.dreamlyn.cn,访问portainer,就使用portainer.dreamlyn.cn.

路由器端口映射Traefik

traefik的安装我在NAS折腾记录:安装Traefik这篇文章进行了详细的介绍,这里就不再赘述,我们接着它直接介绍具体使用方式。在nas中的容器相关目录下,找到traefik/conf.d,在里面添加文件nas.yml文件。

文件内容如下:

http:
  services:
    nas:
      loadBalancer:
        servers:
        - url: "http://192.168.31.206:5000"
  routers:
    nas: 
      entrypoints: web
      service: nas
      rule: Host(`nas.dreamlyn.cn`)

这段代码中services下面的nas表示这个服务名为nas,下面的url表示反向代理的地址。

routers下面的nas表示有个名为nas的路由,入口点为web,也就是80端口,路由对应的服务为nas,规则是nas.dreamlyn.cn,这样我们使用nas.dreamlyn.cn访问时,就可以了。

这里我们顺便看一眼,我在路由器中端口映射的时候 ,使用外网的880映射到了,traefik的80端口。

然后我们不需要重启traefik,就可以用nas.dreamlyn.cn:880来访问nas了。

路由器端口映射npm

npm是nginx proxy manager的简称,如果在nas里的服务很少用docker安装的话,也推荐使用。这里我介绍下具体的安装和使用方法。

在nas上合适的位置创建目录,并创建如下文件。

其中docker-compose文件如下:

version: '2.1'
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    ports:
      - '680:80'  
      - '681:81' 
      - '6443:443' 
    volumes:
      - ./data:/data # 把数据存放在在当前文件夹下的 data 文件夹中
      - ./letsencrypt:/etc/letsencrypt
networks:
  default:
    external:
      name: docker_default

这里介绍下npm的几个端口,80和443分别为npm接收的http和https请求,我们将他们映射到NAS的680和6443端口。

81为npm的可视化界面,映射为681端口。

然后在npm目录下执行如下命令:

# 创建docker网络,如果已经有docker_default网络,则不需要。
docker network create -d bridge --attachable=true docker_default
# 启动npm
docker-compose up -d
# 查看启动日志
docker-compose logs -f

启动没有问题的话,就可以通过192.168.31.206:681来进入npm的管理界面。

登陆的初始账号密码是admin@example.com和changeme。

我们输入正确的账号密码登录。

首次登陆会提示我们修改账号密码,我们根据自己的情况修改下。

之后点击proxy hosts –> Add Proxy Host。

其中domain names填写访问的域名,这里输入nas.dreamlyn.cn;Scheme 输入http;Forward Hostname / IP 输入192.168.31.206;Forward Port 输入 5000。

点击 save就可以了。

下面我们还需要在路由器中为npm开通端口映射。

我以我家小米路由器为例进行介绍,打开路由器管理界面,找到端口转发,点击添加规则

名称这里我填npm;协议选择TCP;外部端口填680;内部IP地址填192.168.31.206,也就是npm所在NAS主机的IP地址;内部端口填写npm的680端口;点击添加。

然后我们就可以通过nas.dreamlyn.cn:680来访问nas了。

无法开通公网IP

在某些特殊情况下,我们无法开通公网IP,这时我们就需要使用一些折中的办法来访问家中的NAS了,下面分别介绍使用个人CentOS服务器和使用免费frp服务两种方式来实现。

在Centos上搭建frp服务

当我们有一台暴漏公网IP的Centos服务器时,可以在服务器端安装frps服务。

下载frp并解压

到GitHub查看最新版本:https://github.com/fatedier/frp/releases

如果访问不了github,我在这儿也给出0.45.0版下载地址

操作命令如下:

# 下载frp可执行包
wget https://github.com/fatedier/frp/releases/download/v0.45.0/frp_0.45.0_linux_amd64.tar.gz
# git无法访问的话,用下面的地址
wget https://minio.dreamlyn.cn:9000/blog/attachment/frp_0.45.0_linux_amd64.tar.gz
# 解压
tar zxf frp_0.45.0_linux_amd64.tar.gz

配置frps

# 进入解压缩后的目录
cd frp_0.45.0_linux_amd64/
# 修改配置
vim frps.ini 

配置文件如下:

[common]
# fpr客户端和frp服务器通信的地址
bind_port = 7000
# 默认http和https监听端口
vhost_http_port = 80
vhost_https_port = 443
# 针对不同的服务使用[]隔开,服务名自己可以随便定,跟客户端能对应上就行。
[nas_http]
type = http
custom_domains = nas.dreamlyn.cn
auth_token = 123456
# 如果没有域名的话,用下面这个可以直接使用ip:5000来访问nas
[nas_tcp]
listen_port = 5000
auth_token = 123456

启动frps

# 启动frps
./frps -c frps.ini 

设置开机自启

使用vim创建并编辑 /lib/systemd/system/frps.service,设置如下

[Unit]
Description=Frp Server Service
After=network.target

[Service]
Type=simple
# 假设fpr存放地址为/usr/local/
ExecStart=/usr/local/frp_0.45.0_linux_amd64/frps -c /usr/local/frp_0.45.0_linux_amd64/frps.ini 
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
StandardOutput=syslog
StandardError=inherit

[Install]
WantedBy=multi-user.target

之后就可以使用如下命令控制frp服务

启动服务 systemctl start frps
开机自启动 systemctl enable frps
重启服务 systemctl restart frps
停止服务 systemctl stop frps
查看日志与状态 systemctl status frps

使用NAS创建frp客户端

我采用docker-compose的方式安装frp。

在nas上合适的位置创建目录,并创建如下文件。

docker-compose.yml文件如下

---
version: "2.1"
services:
  natfrp:
    image: oldiy/frpc
    container_name: natfrp
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
    volumes:
      - ./configs:/frp
    restart: unless-stopped
networks:
  default:
    external:
      name: docker_default

frpc.ini文件如下

# 对应Centos搭建的frp服务
[common]
server_addr = 服务器IP地址
server_port = 7000
auth_token = 123456

[nas_http]
type = http
local_ip = 192.168.31.206
local_port = 5000
custom_domains = nas.dreamlyn.cn
[nas_tcp]
local_ip = 192.168.31.206
local_port = 5000
remote_port = 5000

启动docker容器

cd natfrp
docker-compose up -d
docker-compose logs -f

OK,可以了,我们可以使用nas.dreamlyn.cn或者服务器IP:5000访问家中的NAS了。

使用第三方免费frp服务

当我们没有任何可以暴漏公网IP的服务器时,就需要使用第三方的免费frp服务。

先来说下什么是第三方免费frp服务吧,它的存在使我们可以在没有公网IP服务器、并且运营商早已封杀80和443端口的情况下,爆露出我们的服务,笔者这个博客的搭建也是采用的这种形式。

在此推荐使用SAKURA FRP,隧道限速10M足够使用,每月有5G的免费流量,签到的话还送流量,基本用不完。

第一步先注册,然后到管理界面点击 服务->隧道列表

然后点击创建隧道然后选择节点,这里的隧道可以理解为从公网IP到自己本地服务的一条线路,我们随便选一个,这里我选择成都电信1。

然后如图设置隧道类型、本地IP,本地端口和绑定域名等,随后点击创建。

创建成功后,我们把鼠标放在如图所示位置,会出现 cn-cd-dx-1.natfrp.cloud 的域名,我们需要到域名管理后台将 nas.dreamlyn.cn 的cname记录设置为 cn-cd-dx-1.natfrp.cloud。

如图是我在dnspod的管理端添加cname记录。

第三方frp的客户端配置

对应frp客户端的安装方式与上文介绍的frp客户端安装方式一模一样,区别在于配置的不同。

我们在SakuraFrp管理端点击配置文件。

在弹出的对话框中点击复制配置。

然后如图,打开之前的frpc.ini文件,将内容改为,刚才复制的配置,随后启动容器就可以了。

然后我们既可以通过nas.dreamlyn.cn来对nas进行访问了。

NAS折腾记录:外网访问最先出现在牧尘的网络日志

☑️ ☆

NAS折腾记录:安装Traefik

我在家里的NAS上使用docker搭建了许多服务,这些服务都使用traefik进行代理,当这些服务需要使用HTTPS时,我们可以使用Traefik的Let’s Encrypt来自动获取证书,本篇文章主要介绍Traefik的安装以及如何使用traefik来自动获取证书。

安装Traefik

我在NAS上的服务大部分都是采用docker-compose的方式进行安装,而traefik的反向代理对docker原生支持,所以我采用traefik作为所有服务的入口。当我们使用不同的域名,比如:gitea.dreamlyn.cn或者给blog.dreamlyn.cn来访问网站时,traefik会根据不同的域名,将访问代理到不同的docker容器。有兴趣深入了解traefik的可以参考中文文档或者官方文档

traefik反向代理

对于traefik本身的安装,我也是采用了docker-compose的方式。

在nas上合适的位置创建目录,并创建如下文件。

其中docker-compose文件如下:

---
version: "2.1"
services:
  traefik:
    image: traefik:latest
    container_name: traefik
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./configs/traefik.yml:/etc/traefik/traefik.yml
      - ./configs/conf.d:/configs/conf.d:rw  
    ports:
      - "80:80"
      - "443:443"
    labels:
      - "traefik.enable=true"
      #设置http
      - "traefik.http.routers.traefik.entrypoints=web"
      - "traefik.http.routers.traefik.service=traefik"
      - "traefik.http.routers.traefik.rule=Host(`traefik.dreamlyn.cn`)"
      #设定端口号,traefik本身有个dashboard,端口为8080
      - "traefik.http.services.traefik.loadbalancer.server.port=8080"
    restart: always
networks:
  default:
    external:
      name: docker_default

traefik配置文件如下:

entryPoints:
  web:
    address: :80
  websecure:
    address: :443
# 两种Traefik的配置方式,
providers:
  #docker配置方式
  #可以在使用docker-compose安装wordpress的时候直接配置好wordpress的相关配置,而不需要修改traefik的任何东西。
  docker: 
    exposedByDefault: false
    defaultRule: "Host(`{{trimPrefix `/` .Name}}.dreamlyn.cn`)" 
  #file配置方式,反向代理非docker时使用,配置文件统一放在/configs/conf.d目录里
  file:
    watch: true
    directory: /configs/conf.d
api:
  debug: true
  dashboard: true
  insecure: true
#log:
#  level: DEBUG

此时在traefik目录下执行如下命令:

# 创建docker网络,如果已经有docker_default网络,则不需要。
docker network create -d bridge --attachable=true docker_default
# 启动traefik
docker-compose up -d
# 查看启动日志
docker-compose logs -f

如果启动没有问题,那么就可以使用http://traefik.dreamlyn.cn:880来访问traefik的dashboard了。

使用DNS Challenge

当我们的服务需要使用HTTPS时,我们可以使用traefik的Let’s Encrypt来自动获取证书。Traefik有三种方式来使用Let’s Encrypt:TLS Challenge、HTTP Challenge和DNS Challenge。

其中TLS Challenge和HTTP Challenge两种方式较为简单,但是需要服务的访问端口为80或者443,而我们在家中的网络一般都是屏蔽了80和443端口的,所以需要使用较为复杂的DNS Challenge。

Let's Encrypt是一种自动获取HTTPS证书的方式,也就是我们设置好之后,不需要再去手动申请证书,然后各种配置了。

使用DNS Challenge有几个前置条件:

  1. 确保域名的服务商在Traefik支持的域名服务商列表中(不在列表中时,请参考这篇文章对域名服务商进行修改)。
  2. 确保使用DNS Challenge申请的域名使用的是A记录,并且正确解析到traefik服务上。(在NAS折腾记录:打通网络篇,我将DDNS的主机域名直接设置为了*.dreamlyn.cn就是这个原因)

那么接下来就根据上面的原理一步步实现DNS Challenge。

DNS设置

DNS Challenge自动获取HTTPS证书的一个过程是验证域名的归属权,这个过程是Traefik通过调用域名服务商的API自动进行的(Traefik支持的域名服务商参考这里),而Traefik调用API的时候,是需要SecretId和SecretKey的,接下来具体操作。

到 https://doc.traefik.io/traefik/https/acme/#providers 查看Traefik是否支持自己所购买的域名服务商,如果不支持也没关系,参考这篇文章,将域名服务商修改为DNSPOD(被腾讯收购后改为了Tencent Cloud DNS)。

在DNSPOD的控制台中,如下图所示点击API密钥中。

新建密钥。

修改docker-compose.yml

如图,在相应位置创建acme.json。需要使用命令chmod 600 acme.json将acme.json文件权限调整为600

修改docker-compose.yml,确保下图红框部分正确配置。

最终代码如下:

---
version: "2.1"
services:
  traefik:
    image: traefik
    container_name: traefik
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
      # 因为家中的服务端口不是443,在使用Let’s Encrypt时只能用DNS Challenge的方式
      # 前期如果不需要HTTPS,可以忽略
      - "TENCENTCLOUD_SECRET_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
      - "TENCENTCLOUD_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxx"

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./configs/traefik.yml:/etc/traefik/traefik.yml
      # 如果不用HTTPS,可以忽略acem.json
      # 用的话,需要使用 chmod 600 acme.json 命令修改文件的权限
      - ./configs/acme.json:/configs/acme.json:rw
      - ./configs/conf.d:/configs/conf.d:rw  
      - ./configs/certs:/configs/certs  
    ports:
      - "80:80"
      - "443:443"
    labels:
      - "traefik.enable=true"
      #设置http
      - "traefik.http.routers.traefik.entrypoints=web"
      - "traefik.http.routers.traefik.service=traefik"
      - "traefik.http.routers.traefik.rule=Host(`traefik.dreamlyn.cn`)"
      #设定端口号,traefik本身有个dashboard,端口为8080
      - "traefik.http.services.traefik.loadbalancer.server.port=8080"
    restart: always
networks:
  default:
    external:
      name: docker_default

修改traefik.yml

修改traefik.yml文件,确保红框部分的正确配置。

最终代码如下:

entryPoints:
  web:
    address: :80
  websecure:
    address: :443
    # 不使用HTTPS的话,下面可以忽略
    http:
      tls: 
        certResolver: letsencrypt
        domains:
          - main: dreamlyn.cn
            sans:
              - "*.dreamlyn.cn"
# 两种配置方式,
providers:
  #docker配置方式
  #比如:可以在使用docker-compose安装wordpress的时候直接配置好wordpress的相关配置,而不需要修改traefik的任何东西。
  docker: 
    exposedByDefault: false
    defaultRule: "Host(`{{trimPrefix `/` .Name}}.dreamlyn.cn`)" 
  #file配置方式,反向代理非docker时使用,比如群晖上的Plex,配置文件统一放在/configs/conf.d目录里
  file:
    watch: true
    directory: /configs/conf.d
#注意,letsencrypt在服务器无法开放80和443端口的情况下无法使用tls和http,暂时不用。
#后来发现DNS的服务商可以修改,故将花生壳的DNS服务商修改为DNSPod,可以使用DNS-01进行自动tls申请
certificatesResolvers:
  letsencrypt:
    acme:
      email: lyn@dreamlyn.cn
      storage: /configs/acme.json
      dnschallenge: 
        provider: tencentcloud
api:
  debug: true
  dashboard: true
  insecure: true
#log:
#  level: DEBUG

运行Traefik

在traefik目录下执行如下命令:

# 创建docker网络,如果已经有docker_default网络,则不需要。
docker network create -d bridge --attachable=true docker_default
# 启动traefik
docker-compose up -d
# 查看启动日志
docker-compose logs -f

如果日志没有问题的话,我们就可以通过http://traefik.dreamlyn.cn:880来访问了。

至此,Traefik的安装已经完成了。

NAS折腾记录:安装Traefik最先出现在牧尘的网络日志

☑️ ⭐

Traefik2将acme.json 分割成证书

我们在使用Let’s Encrypt进行自动证书获取时,是将TLS信息存放到acme.json中的。但是总有那么些原因要用到证书,比如我在同一个域名下,有另外一个服务没有使用traefik代理,这时候就需要将acme.json分割成证书。

在此,使用docker-compose安装certdump来对acme.json进行分割。

在traefik目录下,创建certdump.yml文件。

文件内容如下:

---
version: "2.1"
services:
  certdumper:
    image: humenius/traefik-certs-dumper:latest
    container_name: certdumper
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
    volumes:
      - ./configs:/traefik:ro
      - ./output:/output:rw
networks:
  default:
    external:
      name: docker_default

此时在traefik目录下执行如下命令:

# 创建docker网络
docker network create -d bridge --attachable=true docker_default
# 启动容器,分割acme.json
docker-compose -f certdump.yml up -d

容器启动后,将会在output文件夹下导出证书。

Traefik2将acme.json 分割成证书最先出现在牧尘的网络日志

☑️ ⭐

NAS折腾记录:打通网络

自从找联通的师傅把家里的光猫改成桥接模式并开通公网IP之后,笔者便在折腾NAS的道路上一去不复返。

我在NAS上搭建了工作中常用的服务,gitea、yApi、jenkins、outline等,搭建了这个博客网站(采用frp进行内网穿透,解决运营商屏蔽443端口的问题),还将自己的小程序(小程序搜索:认真谈恋爱)的服务端放在NAS上,整体来说为自己创造了不少价值。

最近打算把近期折腾NAS的过程记录下来,同时也算做个分享,本篇内容主要是说明网络部分,也就是如下图蓝色区块的搭建。

开通公网IP

这个是一切的起点,所有的服务,想在公网爆露出去,最好的办法就是开通公网IP,公网IP不是固定IP,经常有朋友把他们弄混。

2022年的时候师傅表示300M以上的带宽默认都是公网IP,后面政策会不会变还不清楚。

注意:开不了公网IP的话还有个折中办法,采用内网穿透的方式,在此推荐使用frp,frp的速度要比花生壳内网穿透快很多(但比公网IP方式慢)。关于frp的使用以及如何免费使用第三方frp可以参考这篇文章

光猫改桥接模式

之所以要把光猫改成桥接是因为,运营商送的光猫并不具备DDNS和端口转发功能,我们需要使用自己的路由器进行拨号,开启DDNS,同时进行端口转发。给装宽带的师傅打电话,他们远程就能改。

开启端口映射

在这里我主要的映射有:

  • NAS SSH,这样在外网可以用ssh连接NAS(我将NAS的SSH端口设置为了2200)。
  • OpenWRT,在NAS里装的旁路由,如果家中没有路由器的时候可以当作备选方案。
  • Minio,文件存储服务,因为需要公网访问9000和9001这两个端口,所以没有放到traefik后面。
  • Treafik,类似Nginx,但原生支持对docker的反向代理。8443→NAS 443,880→NAS 80.

注意:如果家中没有路由器或者路由器不支持端口映射,也可以用NAS安装旁路由来实现拨号,具体可以参考这篇文章

购买域名

别的地方的钱都可以省,但是买域名的钱还是出一下吧,价格不贵,而且能极大的提升各项服务的体验。

我的域名是在花生壳买的,同时将域名的服务商改成了DNSPod,修改方式参考这篇文章

域名添加A记录,其中主机域名设为*,记录值随便设置,可以先设置为1.1.1.1。

开启DDNS

DDNS其实就是动态的调整DNS服务器中的A记录,实现的前提就是域名服务商提供API来修改域名的A记录,我们在需要使用DDNS的地方通过脚本来获取可能动态会变动的公网IP,然后通过API告诉域名服务商修改A记录。

因此,实现DDNS需要做两件事:第一,获取DNSPod的API Token;第二,在本地用程序获取公网IP地址并使用DNSPod API更新A记录。

获取DNSPod Token

在DNSPOD的控制台中,如下图所示点击API密钥中。

点击DNSPod Token并创建密钥,记录下ID和Token。

动态更新DNS

我在威联通上通过docker-compose来实现DDNS。

在nas上合适的位置创建目录,并创建如下文件。

其中docker-compose文件如下:

---
version: "2.1"
services:
  ddns-go:
    image: jeessy/ddns-go
    container_name: ddns-go
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
    network_mode: "host"
    restart: unless-stopped

在ddns-go目录下执行命令docker-compose up -d命令来启动ddns-go,使用docker-compose logs -f来查看启动日志,如果启动没有问题,那么就可以使用浏览器访问http://192.168.31.206:9876来访问。ddns-go的配置界面如下:DNS服务商选择Dnspod;ID和Token设置为上面记录的值;Domains设置为*.dreamlyn.cn。

啰嗦一句,最开始的时候,我是将DDNS的域名设置为home.dreamlyn.cn,并将*.dreamlyn.cn的cname设置为home.dreamlyn.cn。但这样设置的话,后面在使用traefik进行DNS Challenge时会存在问题,不得已只好将DDNS直接设置为*.dreamlyn.cn。

在其他配置中,设置登录用户名、密码并保存。

查看右侧的日志并登录DNSPod的后台看*.dreamlyn.cn的A记录是否变更,如变更,则说明DDNS安装成功。

至此,我们便打通了从 外网到NAS的线路,也就是说访问*.dreamlyn.cn:880时会映射到NAS的80端口,访问*.dreamlyn.cn:8443则会映射到NAS的443端口。

NAS折腾记录:打通网络最先出现在牧尘的网络日志

☑️ ☆

使用NAS安装旁路由

试想以下这样的场景,当我们了解了DDNS、端口转发等功能,却发现家里的路由器并不支持;或者当家里的路由器对于拦截广告等功能已经不堪重负时,我们应该怎么办?

最好的方式当然是换一台路由器,但是当我们手边刚好有一台NAS的时候,不妨使用NAS搭建旁路由(软路由)的方式来解决这些问题。

NAS安装旁路由的两种方式

网上流传比较多的有两种方式:

通过NAS自带的虚拟机安装
通过Docker安装。

笔者在当时安装的时候,刚开始是使用Docker的方式,中间也对网络也进行了各种设置,但是让我崩溃的是,安装完后对笔者的某些应用有限制。不得已只好放弃,转而采用虚拟机进行安装。建议在有NAS的时候采用虚拟机的方式安装,而在无法提供虚拟机的时候再采用Docker的方式。

安装虚拟机VMM

在这里使用群晖NAS进行安装,其他NAS流程是类似的。

VMM是虚拟机程序Virtual Machine Manager的简称,群晖套件中心就有,打开套件中心,搜索Virtual Machine Manager,第一个就是,点击安装

安装vmm虚拟机

主板开启虚拟化

进入群晖系统–>控制面板–>网络–>选中“局域网”–>点击“管理”,在下拉菜单中选择“Open vSwitch 设置”

开启主板虚拟化

在弹出界面中选中“启用”

下载OpenWRT

有强迫症,必须下载最新版本的话,可以到恩山无限论坛的openwrt专区下载,注册可能会需要1块钱。

恩山无限论坛

我这儿也给出了2023-01-06日的备份:

精简版下载

大而全版下载

建议不要折腾,直接下载大而全版本。

安装OpenWRT

解压刚才下载的文件得到.img后缀的文件,然后打开群晖内的Virtual Machine Manager,选择映像–>新增–>从PC上传文件并上传刚才解压出来的img文件,之后点击下一步。

添加虚拟机映像

勾选存储空间并点击应用

选择映像存储空间

状态处显示良好,表明此次上传没有问题。

上传映像

上传完映像之后,我们点击网络–>新增,为网络命名为ETH1,勾选连接网线的接口(一般局域网1就是),随后应用。

设置虚拟机网络

之后我们点击虚拟机,导入刚才的镜像。

新建虚拟机

从硬盘映像导入,选择刚才上传的映像,然后点击下一步

将映像导入虚拟机

选择存储空间后继续下一步

选择虚拟机存储空间

为虚拟机命名,选择CPU和内存容量,继续下一步。

选择虚拟机CPU和内存容量

设置虚拟盘大小,其他默认,下一步。

设置虚拟机存储盘

网络选择ETH1并设置,在里面的型号中选择e1000。(不选也可以使用,但网络为半双工的,改为e1000后,网络变成全双工)。

设置虚拟机网路

自动启动选择是,其他默认,下一步

其他设置

设置管理员权限,只勾选admin,下一步

虚拟机访问权限

勾选创建后启动虚拟机,随后应用。

完成设置

至此,虚拟机已经安装成功了。但是openwrt的默认ip地址是192.168.1.1,而我家里的路由器IP地址是192.168.31.1,需要将openwrt的IP地址设置成192.168.31.X,在此我设置成192.168.31.2(需要和主路由一个网段)。

如下图,选择旁路由后点击连接,在弹出的标签页进入openwrt的终端。

链接虚拟机

输入命令修改ip地址

vi etc/config/network 

VI编辑器简单实用方式:按 i 键进行修改,修改完后按Esc退出,接着输入命令 :wq ,保存刚才的修改

修改OpenWRT的IP地址

之后输入service network restart命令重启网络就可以了。

待重启之后,在浏览器输入刚才修改过的IP,就可以进入Openwrt的登陆界面了,OpenWRT的默认账号密码是root:password,进入系统后可以修改密码。

登录OpenWRT

配置OpenWRT

安装完成后,要想正常使用还需要再配置一下,找到左侧菜单 网络–>接口,选择LAN的接口进行修改

配置OpenWRT网络接口

这里主要设置IPv4地址、IPv4网关和自定义DNS服务器。

配置OpenWRT网络接口

同时在下方勾选忽略此接口,来禁用OpenWRT的DHCP功能,保存并应用就可以了。

禁用DHCP

OpenWRT常用功能

拨号上网

默认情况下,运营商提供的光猫是不支持DDNS和端口转发等功能的,那么我们就需要使用OpenWRT来进行拨号上网,并在里面进行DDNS和端口转发等功能。

首先来看下网络组成情况。

我们需要将光猫改成桥接模式(可以找维修师傅远程处理即可),然后在OpenWRT里进行如下操作:

点击网络–>接口–>LAN–>修改–>物理设置,取消桥接接口选项。

在网络–>接口中添加新接口,如图所示进行设置,点击提交。

修改WAN口,在基础设置中输入拨号账号和密码即可。

PC上网时,将默认网关设置成旁路由的IP地址(我这里是192.168.31.2)。

端口转发

点击网络–>防火墙–>端口转发来进行端口转发的设置。

设置DDNS

首先我们要了解DDNS的原理。

如图为PC正常上网的流程,PC根据域名到DNS服务器请求IP地址,DNS服务器根据A 记录查找IP地址并返回给PC,PC使用IP地址请求web服务。(当然这些流程对用户来说都是透明的)

了解了这个流程我们再来说DDNS,DDNS其实就是动态的调整DNS服务器中的A记录,实现的前提就是域名服务商提供API来修改域名的A记录,我们在需要使用DDNS的地方通过脚本来获取可能动态会变动的公网IP,然后通过API告诉域名服务商修改A记录

我以DNSPOD为例进行演示,在DNSPOD的控制台找到域名解析,并添加一条A记录,这里我设置主机记录为home,IP地址先随便填写.

在DNSPOD的控制台点击我的账号并选择我的密钥,查看对应的SecretId和SecretKey .

点击服务–>动态DDNS–>端口转发来进行端口转发的设置,在下方输入框输入DNSPOD并点击添加.

勾选启用,查询主机名填写刚才添加记录的域名,DDNS服务提供商,选择dnspod.cn,域名填写刚才添加记录的域名,用户名和密码对应SecretId和SecretKey,然后点击保存并应用.

当下图红线处出现域名和IP对应关系时,即为配置成功.

其他功能

OpenWRT是搭载在Linux系统之上的,里面的服务还有很多,比如frp内网穿透、DNS过滤器、广告过滤大师、Docker等,在此就不再一一列举了,感兴趣的可以在评论区留言共同交流,当然也可以从右下角添加我的个人微信.

使用NAS安装旁路由最先出现在牧尘的网络日志

❌