[{"content":"前言 本文记录了将一台搭载Debian系统的R2S设备配置为高性能旁路网关的过程。最终目标是实现一个可以为局域网内所有设备提供WireGuard VPN服务的透明网关，同时支持IPv4和IPv6，并具备智能DNS分流功能。\n最终方案的核心组件包括：\nDebian 12: 作为主操作系统。 WireGuard: 用于建立VPN连接。 uog: 一个流量混淆工具，用于将WireGuard的UDP流量伪装成其他协议（本文为HTTPS），以适应复杂的网络环境。 dnsproxy: AdGuard出品的高性能DNS代理，用于实现DNS-over-HTTPS (DoH)以及基于域名的DNS分流。 iptables/ip6tables: 用于配置网络地址转换（NAT），实现VPN共享。 准备工作 1. 安装系统与基础软件 首先，为R2S安装最新版本的Debian系统。您可以从FriendlyELEC的官方Wiki获取镜像和相关说明：\n官方镜像下载: NanoPi R2S - Official Image Debian系统使用指南: NanoPi R2S - Work with Debian Core 安装过程略过，系统启动后，请执行以下操作：\n# 更新系统并安装必要工具 sudo apt update \u0026amp;\u0026amp; sudo apt upgrade -y sudo apt install -y zsh wireguard-tools dnsutils # (可选) 更换为您喜欢的shell chsh -s /bin/zsh 2. 启用IP转发 作为旁路网关，设备需要能够转发网络包。编辑 /etc/sysctl.conf 文件：\nsudo vim /etc/sysctl.conf 找到并取消以下两行的注释（如果它们被注释了的话），确保值为 1：\nnet.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 保存文件后，应用设置：\nsudo sysctl -p 核心服务配置 我们将使用 systemd 来管理 dnsproxy 和 uog 这两个核心服务，以确保它们能开机自启并保持稳定运行。建议将二进制文件存放在 /home/pi/bin 目录下。\nmkdir -p /home/pi/bin 1. 配置 dnsproxy dnsproxy 负责处理所有DNS请求。它的关键任务是：\n将用于 uog 连接的特定域名解析请求，转发给主路由，避免DNS污染和路由循环。 将其余所有DNS请求通过DoH发送，保护隐私。 下载 dnsproxy:\n从 dnsproxy的GitHub Releases页面 下载最新的 linux-arm64 版本，解压后将 dnsproxy 二进制文件放入 /home/pi/bin。\n创建 systemd 服务:\n创建 /etc/systemd/system/dnsproxy.service 文件：\nsudo vim /etc/systemd/system/dnsproxy.service 写入以下内容：\n[Unit] Description=dnsproxy After=network-online.target network.target Wants=network-online.target [Service] Restart=always ExecStart=/home/pi/bin/dnsproxy -u \u0026#34;[/*.your_domain.com/]192.168.31.1:53\u0026#34; -u https://dns0.eu/dns-query -u https://open.dns0.eu/dns-query --http3 --cache --cache-min-ttl=600 --edns --edns-addr=\u0026lt;your_public_ip_for_cdn\u0026gt; ExecReload=/bin/kill $MAINPID KillMode=mixed KillSignal=SIGTERM TimeoutStopSec=5s PrivateTmp=true PrivateDevices=true ProtectHome=false ProtectSystem=full [Install] WantedBy=multi-user.target 配置说明:\n请将 your_domain.com 替换为您 uog 服务实际使用的域名。 192.168.31.1 是我局域网的主路由地址，请根据您的实际情况修改。 \u0026lt;your_public_ip_for_cdn\u0026gt; 是一个用于优化CDN解析的公网IP地址，您可以填写一个您所在地区的IP，或者直接移除 --edns 和 --edns-addr 参数。 启动 dnsproxy:\nsudo systemctl daemon-reload sudo systemctl enable dnsproxy --now 2. 配置 uog uog 负责将WireGuard的流量伪装后转发到真实的VPN服务器。\n下载 uog:\n从 uog的GitHub Releases页面 下载最新的 linux-arm64 版本，将 uog 二进制文件放入 /home/pi/bin。\n创建启动脚本:\n为了方便管理，我们创建一个包装脚本 /home/pi/uog.sh：\nvim /home/pi/uog.sh 内容如下：\n#!/bin/bash /home/pi/bin/uog 0.0.0.0:2928 https://uog.your_domain.com:443 \u0026lt;your_password\u0026gt; 0.0.0.0:2928: uog 的本地监听地址和端口，WireGuard将连接到这里。 https://uog.your_domain.com:443: uog 远端服务器的地址，请替换为您的实际地址。 \u0026lt;your_password\u0026gt;: 连接密码。 给脚本添加执行权限：\nchmod +x /home/pi/uog.sh 创建 systemd 服务:\n创建 /etc/systemd/system/uog.service 文件：\nsudo vim /etc/systemd/system/uog.service 写入以下内容：\n[Unit] Description=uog client After=network-online.target network.target Wants=network-online.target [Service] Restart=always ExecStart=/home/pi/uog.sh ExecReload=/bin/kill $MAINPID KillMode=mixed KillSignal=SIGTERM TimeoutStopSec=5s PrivateTmp=true PrivateDevices=true ProtectHome=false ProtectSystem=full [Install] WantedBy=multi-user.target 启动 uog:\nsudo systemctl daemon-reload sudo systemctl enable uog --now 配置 WireGuard 这是所有配置的核心，它将所有组件串联起来。\n1. 创建动态路由脚本 为了防止 uog 的流量被WireGuard错误地捕获，我们需要在WireGuard启动时，动态地为 uog 服务器的IP添加一条静态路由，让它直连主路由。\n创建目录：\nmkdir -p /home/pi/wg 创建 wgup.sh 脚本：\nvim /home/pi/wg/wgup.sh #!/bin/bash STATE_FILE=\u0026#34;/home/pi/wg/uog_server.ip\u0026#34; DEST_IP=$(host -t A uog.your_domain.com | awk \u0026#39;/has address/ {print $4}\u0026#39;) if [ -n \u0026#34;$DEST_IP\u0026#34; ]; then # 为 uog 服务器 IP 添加静态路由，强制经由主路由 ip route add $DEST_IP/32 via 192.168.31.1 dev eth0 echo \u0026#34;$DEST_IP\u0026#34; \u0026gt; \u0026#34;$STATE_FILE\u0026#34; echo \u0026#34;Route added for uog.your_domain.com -\u0026gt; $DEST_IP\u0026#34; fi 创建 wgdown.sh 脚本：\nvim /home/pi/wg/wgdown.sh #!/bin/bash STATE_FILE=\u0026#34;/home/pi/wg/uog_server.ip\u0026#34; if [ -f \u0026#34;$STATE_FILE\u0026#34; ]; then DEST_IP=$(cat \u0026#34;$STATE_FILE\u0026#34;) # 删除对应的静态路由 ip route del $DEST_IP/32 via 192.168.31.1 dev eth0 rm \u0026#34;$STATE_FILE\u0026#34; echo \u0026#34;Route removed for uog.your_domain.com -\u0026gt; $DEST_IP\u0026#34; fi 给脚本添加执行权限：\nchmod +x /home/pi/wg/wgup.sh /home/pi/wg/wgdown.sh 记得将脚本中的 uog.your_domain.com 和 192.168.31.1 替换为您的配置。\n2. 编写 wg0.conf 创建 /etc/wireguard/wg0.conf 文件：\nsudo vim /etc/wireguard/wg0.conf 写入以下配置：\n[Interface] PrivateKey = \u0026lt;your_wireguard_private_key\u0026gt; Address = 192.168.9.2/24, fd00:9:28::28/128 DNS = 192.168.9.2 MTU = 1280 # IPv4 NAT and Forwarding Rules PostUp = iptables -A FORWARD -i eth0 -j ACCEPT; iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE PostDown = iptables -D FORWARD -i eth0 -j ACCEPT; iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE # IPv6 NAT and Forwarding Rules PostUp = ip6tables -A FORWARD -i eth0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o wg0 -j MASQUERADE PostDown = ip6tables -D FORWARD -i eth0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o wg0 -j MASQUERADE # Dynamic route for uog server PostUp = /home/pi/wg/wgup.sh PostDown = /home/pi/wg/wgdown.sh # (Optional) Add static routes for IPs that should bypass the VPN # PostUp = ip route add 114.114.114.114/32 via 192.168.31.1 dev eth0 # PostDown = ip route del 114.114.114.114/32 via 192.168.31.1 dev eth0 [Peer] PublicKey = \u0026lt;your_wireguard_server_public_key\u0026gt; AllowedIPs = ::/0, 0.0.0.0/5, 8.0.0.0/7, 11.0.0.0/8, 12.0.0.0/6, 16.0.0.0/4, 32.0.0.0/3, 64.0.0.0/2, 128.0.0.0/3, 160.0.0.0/5, 168.0.0.0/6, 172.0.0.0/12, 172.32.0.0/11, 172.64.0.0/10, 172.128.0.0/9, 173.0.0.0/8, 174.0.0.0/7, 176.0.0.0/4, 192.0.0.0/9, 192.128.0.0/11, 192.160.0.0/13, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14, 192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/8, 194.0.0.0/7, 196.0.0.0/6, 200.0.0.0/5, 208.0.0.0/4, 192.168.9.0/24 Endpoint = 127.0.0.1:2928 PersistentKeepalive = 25 配置说明:\nPrivateKey 和 PublicKey: 替换为您自己的密钥。 Address: R2S在VPN网络中的地址，192.168.9.2 也是它作为DNS服务器的地址。 DNS: 将DNS指向设备自身，由 dnsproxy 提供服务。 PostUp/PostDown: 核心部分，负责添加/删除防火墙NAT规则和执行动态路由脚本。eth0 是我的R2S的LAN口，请根据您的设备接口名称修改。 AllowedIPs: 这是实现国内/外流量分流的关键。这个列表排除了大部分私有地址，使得访问这些地址的流量直连，其余流量走VPN。您可以根据自己的需求调整。 Endpoint: 关键点，指向本地 uog 服务的监听地址。 防火墙规则 (iptables/ip6tables) 详解:\nPostUp 中的两对 iptables 和 ip6tables 命令是实现VPN共享的关键，它们的作用如下：\niptables -A FORWARD -i eth0 -j ACCEPT:\n这条规则被添加到 FORWARD 链中，用于处理需要被转发的数据包。 -i eth0 指定了规则应用于从 eth0 (LAN口) 进入的数据包。 -j ACCEPT 表示接受并允许这些数据包被转发。 作用: 允许来自局域网设备的数据包通过R2S。 iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE:\n-t nat 表示操作 nat 表，用于网络地址转换。 -A POSTROUTING 规则被添加到 POSTROUTING 链，在数据包即将离开本机时进行处理。 -o wg0 指定了规则应用于即将从 wg0 (WireGuard接口) 出去的数据包。 -j MASQUERADE (伪装) 是一个特殊的NAT操作。它会自动将所有来自局域网的数据包的源IP地址，修改为 wg0 接口的IP地址。 作用: 这就是“共享VPN”的核心。它将所有局域网设备的流量“伪装”成来自R2S本身，这样VPN服务器看到的就是一个客户端，而当返回流量到达R2S时，它能根据记录自动将流量转发回正确的局域网设备。 ip6tables 的命令与 iptables 完全相同，只是它们分别用于处理IPv6和IPv4的流量。而 PostDown 中的命令则使用 -D (Delete) 来移除这些规则，确保在VPN断开后网络恢复原状。\n3. 启动 WireGuard 现在，可以启动WireGuard服务了：\nsudo systemctl enable wg-quick@wg0 --now 检查服务状态：\nsudo wg sudo systemctl status wg-quick@wg0 如果一切正常，您应该能看到与Peer的握手信息。\n验证 将局域网内的一台设备的网关和DNS服务器地址都设置为R2S的IP地址（例如 192.168.31.64，请根据您的R2S的实际LAN口IP修改）。\n然后在该设备上：\n访问 ip.sb 或类似网站，检查公网IP是否为VPN服务器的IP。 尝试访问国内网站，确认速度正常。 ping 局域网内其他设备，确认内网通信正常。 至此，您的R2S旁路网关就搭建完成了。\n","permalink":"https://rikaaa0928.github.io/posts/r2s-gateway-with-debian/","summary":"前言 本文记录了将一台搭载Debian系统的R2S设备配置为高性能旁路网关的过程。最终目标是实现一个可以为局域网内所有设备提供WireGuard VPN服务的透明网关，同时支持IPv4和IPv6，并具备智能DNS分流功能。\n最终方案的核心组件包括：\nDebian 12: 作为主操作系统。 WireGuard: 用于建立VPN连接。 uog: 一个流量混淆工具，用于将WireGuard的UDP流量伪装成其他协议（本文为HTTPS），以适应复杂的网络环境。 dnsproxy: AdGuard出品的高性能DNS代理，用于实现DNS-over-HTTPS (DoH)以及基于域名的DNS分流。 iptables/ip6tables: 用于配置网络地址转换（NAT），实现VPN共享。 准备工作 1. 安装系统与基础软件 首先，为R2S安装最新版本的Debian系统。您可以从FriendlyELEC的官方Wiki获取镜像和相关说明：\n官方镜像下载: NanoPi R2S - Official Image Debian系统使用指南: NanoPi R2S - Work with Debian Core 安装过程略过，系统启动后，请执行以下操作：\n# 更新系统并安装必要工具 sudo apt update \u0026amp;\u0026amp; sudo apt upgrade -y sudo apt install -y zsh wireguard-tools dnsutils # (可选) 更换为您喜欢的shell chsh -s /bin/zsh 2. 启用IP转发 作为旁路网关，设备需要能够转发网络包。编辑 /etc/sysctl.conf 文件：\nsudo vim /etc/sysctl.conf 找到并取消以下两行的注释（如果它们被注释了的话），确保值为 1：\nnet.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 保存文件后，应用设置：","title":"将R2S打造成支持IPv6的WireGuard透明网关"},{"content":"================================================================= = CHAPTER 1 RECAP = Use the h,j,k,l keys to move the cursor.\nType : to enter Command mode.\nThe q / quit and q! / quit! commands will exit Helix. The former fails when there are unsaved changes. The latter discards them. The w / write command will save the file. The wq / write-quit command will do both. Type d to delete the character at the cursor.\nType i to enter Insert mode and type text. Press Escape to return to Normal mode.\n================================================================= = CHAPTER 2 RECAP = Type a to append to the selection.\nType I to enter Insert mode at the first non-whitespace character at the start of a line.\nType A to enter Insert mode at the end of a line.\nUse o and O to open lines below and above the cursor respectively.\n================================================================= = CHAPTER 3 RECAP = Type w to select forward until the next word.\nType e to select to the end of the current word. Type b to select backward to the start of the current word. Use uppercase counterparts, W,E,B, to traverse WORDS. Type d to delete the entire selection.\nType c to delete the selection and enter Insert mode. Type a number before a motion to repeat it that many times.\nType v to enter Select mode, where all motions extend the selection.\nType x to select the entire current line. Type x again to select the next line.\nType semicolon ( ; ) to collapse selection.\n================================================================= = CHAPTER 4 RECAP = Type u to undo. Type U to redo.\nType y to yank (copy) text and p to paste.\nUse Space + y and Space + p to yank / paste on the system clipboard. Type / to search forward in file, and ? to search backwards.\nUse n and N to cycle through search matches. ================================================================= = CHAPTER 5 RECAP = Type C to duplicate the cursor to the next suitable line and Alt-C for previous suitable line.\nType s to select all instances of a regex pattern inside the current selection.\nType \u0026amp; to align selections.\nPress Alt-s to split the selection into lines.\n================================================================= = CHAPTER 6 RECAP = Type f / F to extend selection up to \u0026amp; including a character.\nType t / T to extend selection until a character. Type r to replace selected characters.\nType . to repeat the last insertion.\nPress Alt-. to repeat the last f / t selection. ================================================================= = CHAPTER 7 RECAP = Type R to replace the selection with yanked text.\nType J to join lines in selection.\nType \u0026gt; and \u0026lt; to indent / unindent lines.\nPress Ctrl-a to increment the selected number.\nPress Ctrl-x to decrement the selected number. ================================================================= = CHAPTER 8 RECAP = Type \u0026quot; to select a different register.\nType Q to start and stop recording a macro to a register, the default being @.\nType q to replay a macro from @ or the selected register.\n================================================================= = CHAPTER 9 RECAP = Type * to set the search register to the primary selection.\nType n / N in Select mode to add selections on each search match.\nPress Ctrl-s to save position to the jumplist.\nPress Ctrl-i and Ctrl-o to go forward and backward in the jumplist. Type gw to enable 2-character labels, and any 2 characters to jump to the corresponding label, or ESC to drop the labels.\n","permalink":"https://rikaaa0928.github.io/posts/helix-keymap/","summary":"================================================================= = CHAPTER 1 RECAP = Use the h,j,k,l keys to move the cursor.\nType : to enter Command mode.\nThe q / quit and q! / quit! commands will exit Helix. The former fails when there are unsaved changes. The latter discards them. The w / write command will save the file. The wq / write-quit command will do both. Type d to delete the character at the cursor.","title":"Helix Keymap"},{"content":"tokio中，返回的future无脑实现Send，因为tokio异步框架并不能保证单个异步任务绑定某个真实线程执行，实现Send表示这个future可以安全的跨线程执行。\nSync表示并发安全，需要跨异步任务调用的必须是Sync，可以套Mutex实现非Sync转Sync，再套Arc实现clone。\n// Sync 类型:\nArc // 当 T: Sync\nMutex // 当 T: Send\nRwLock // 当 T: Send\nAtomicBool\nAtomicI32\nVec // 当 T: Sync\nString\n\u0026amp;T // 当 T: Sync\n// 非 Sync 类型:\nRefCell // 内部可变性不是线程安全的\nCell // 同上\nRc // 引用计数不是线程安全的\n*mut T // 裸指针\n","permalink":"https://rikaaa0928.github.io/posts/rust-tokio-learn-send-sync/","summary":"tokio中，返回的future无脑实现Send，因为tokio异步框架并不能保证单个异步任务绑定某个真实线程执行，实现Send表示这个future可以安全的跨线程执行。\nSync表示并发安全，需要跨异步任务调用的必须是Sync，可以套Mutex实现非Sync转Sync，再套Arc实现clone。\n// Sync 类型:\nArc // 当 T: Sync\nMutex // 当 T: Send\nRwLock // 当 T: Send\nAtomicBool\nAtomicI32\nVec // 当 T: Sync\nString\n\u0026amp;T // 当 T: Sync\n// 非 Sync 类型:\nRefCell // 内部可变性不是线程安全的\nCell // 同上\nRc // 引用计数不是线程安全的\n*mut T // 裸指针","title":"Rust tokio: Send与Sync"},{"content":"from ipaddress import ip_network start = \u0026#39;0.0.0.0/0\u0026#39; exclude = [\u0026#39;1.1.1.1/32\u0026#39;, \u0026#39;10.0.0.0/8\u0026#39;] result = [ip_network(start)] for x in exclude: n = ip_network(x) new = [] for y in result: if y.overlaps(n): new.extend(y.address_exclude(n)) else: new.append(y) result = new print(\u0026#39;,\u0026#39;.join(str(x) for x in sorted(result))) ","permalink":"https://rikaaa0928.github.io/posts/gen-exclude-ip/","summary":"from ipaddress import ip_network start = \u0026#39;0.0.0.0/0\u0026#39; exclude = [\u0026#39;1.1.1.1/32\u0026#39;, \u0026#39;10.0.0.0/8\u0026#39;] result = [ip_network(start)] for x in exclude: n = ip_network(x) new = [] for y in result: if y.overlaps(n): new.extend(y.address_exclude(n)) else: new.append(y) result = new print(\u0026#39;,\u0026#39;.join(str(x) for x in sorted(result))) ","title":"生成排除置顶ip段后的新ip段"},{"content":"mut a: \u0026amp;mut i32 mut a表示a可变，可以被重新绑定\n\u0026amp;表示(不可变)引用 \u0026amp;mut表示可变引用，即*a可变，可以被重新绑定\n测试验证代码 fn change_i_mut_ref(i:\u0026amp;mut i32){ *i=2; // 给引用绑定新值，i 不可变，*i 指向的变量可变 } // fn change_i_mut_ref_err(i:\u0026amp;mut i32){ // i=xxxx; // i 不可变，不可重新绑定，等于什么都不行 // } fn change_mut_i(mut i:i32){ i=4; } fn change_mut_i_ref(mut i:\u0026amp;i32){ i=\u0026amp;5; } fn change_mut_i_mut_ref_change_i(mut i:\u0026amp;mut i32){ // i=\u0026amp;5; //会报错，因为 \u0026amp;5 的类型是 \u0026amp;i32, \u0026amp;mut 5 的类型是 \u0026amp;mut i32 // i=\u0026amp;mut 5; //一样会报错，因为 \u0026amp;mut 5 生命周期仅在函数内，函数返回后，i引用的值会被回收 } fn change_mut_i_mut_ref_change_ref(mut i:\u0026amp;mut i32){ *i=7; } #[tokio::test] async fn my_test1()-\u0026gt; crate::Result\u0026lt;()\u0026gt; { let mut i=1i32; println!(\u0026#34;{}\u0026#34;,i); change_i_mut_ref(\u0026amp;mut i);// 这里需要传 \u0026amp;mut i, \u0026amp;i的类型是 \u0026amp;i32, 不是 \u0026amp;mut i32，同时i需要是mut i，不可变变量不可转化为可变引用 println!(\u0026#34;{}\u0026#34;,i); change_mut_i(i);//修改失败 println!(\u0026#34;{}\u0026#34;,i);//修改失败 change_mut_i_ref(\u0026amp;mut \u0026amp;i); println!(\u0026#34;{}\u0026#34;,i);//修改失败 change_mut_i_mut_ref_change_i(\u0026amp;mut i); println!(\u0026#34;{}\u0026#34;,i);//修改失败 change_mut_i_mut_ref_change_ref(\u0026amp;mut i); println!(\u0026#34;{}\u0026#34;,i);//修改成功，和change_i_mut_ref等效 Ok(()) } ","permalink":"https://rikaaa0928.github.io/posts/rust-learn-mut-ref/","summary":"mut a: \u0026amp;mut i32 mut a表示a可变，可以被重新绑定\n\u0026amp;表示(不可变)引用 \u0026amp;mut表示可变引用，即*a可变，可以被重新绑定\n测试验证代码 fn change_i_mut_ref(i:\u0026amp;mut i32){ *i=2; // 给引用绑定新值，i 不可变，*i 指向的变量可变 } // fn change_i_mut_ref_err(i:\u0026amp;mut i32){ // i=xxxx; // i 不可变，不可重新绑定，等于什么都不行 // } fn change_mut_i(mut i:i32){ i=4; } fn change_mut_i_ref(mut i:\u0026amp;i32){ i=\u0026amp;5; } fn change_mut_i_mut_ref_change_i(mut i:\u0026amp;mut i32){ // i=\u0026amp;5; //会报错，因为 \u0026amp;5 的类型是 \u0026amp;i32, \u0026amp;mut 5 的类型是 \u0026amp;mut i32 // i=\u0026amp;mut 5; //一样会报错，因为 \u0026amp;mut 5 生命周期仅在函数内，函数返回后，i引用的值会被回收 } fn change_mut_i_mut_ref_change_ref(mut i:\u0026amp;mut i32){ *i=7; } #[tokio::test] async fn my_test1()-\u0026gt; crate::Result\u0026lt;()\u0026gt; { let mut i=1i32; println!","title":"Rust: 可变和引用"},{"content":"java 17是java目前最新的长期支持(LTS)版本，但是由于其强封装 JDK 的内部 API的新特性，导致dubbo项目无法直接使用jdk17编译运行。通过参考openjdk的说明，可以发现只需要添加相应参数即可绕开java 17的限制\n对于普通的dubbo项目，只需要在运行时添加\n--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED\n如上参数即可。如果项目的其它依赖也有类似问题则可能需要加入更多参数，参数的获得方式和详细示例将在下面给出\n本解决方案只能解决由于java 17强封装 JDK 的内部 API的特性造成的问题，其他的兼容性问题请寻找其它方案\n参数的获得方法和示例 我们以dubbo官方仓库中的demo为例 首先使用java 17作为我们的开发环境，通过\ngit clone git@github.com:apache/dubbo.git git checkout dubbo-2.7.14 cd dubbo-demo/dubbo-demo-annotation 获得dubbo官方仓库的代码中的demo，然后可以尝试直接使用java 17编译dubbo的demo\nmvn -U clean package --no-transfer-progress -D maven.test.skip=true 启动zookeeper docker run --name some-zookeepep -p 2181:2181 -it --rm zookeeper 作为注册中心 尝试运行provider\njava -jar dubbo-demo-annotation-provider/target/dubbo-demo-annotation-provider-2.7.14.jar 可以看到类似的报错:\nCaused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not \u0026quot;opens java.lang\u0026quot; to unnamed module @8807e25\n关键词 module java.basedoes not \u0026quot;opensjava.lang\u0026quot; to unnamed module @8807e25，根据openjdk的说明，我们只需要添加--add-opens java.base/java.lang=ALL-UNNAMED参数即可解决问题\n对应的报错应该都可以用类似得方法去解决，经过测试，demo中的dubbo项目需要\n--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED 如上参数即可运行\n在两个终端中分别运行provider和consumer:\njava --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED -jar dubbo-demo-annotation-provider/target/dubbo-demo-annotation-provider-2.7.14.jar java --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED -jar dubbo-demo-annotation-consumer/target/dubbo-demo-annotation-consumer-2.7.14.jar 可以发现报出了zookeeper的错误，不难想到，升级依赖的zookeeper的版本大概率可以解决问题\u0026hellip;\n在provider和consumer的 pom.xml 的依赖第一项添加最新版本的zookeeper依赖:\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.apache.zookeeper\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;zookeeper\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;3.7.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;io.netty\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;netty\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt; 再次运行provider和consumer\n可以看到consumer端成功输出了类似的结果\nresult :Hello world, response from provider: *.*.*.*/\u0026lt;unresolved\u0026gt;:20880 provider端也有对应的日志\nHello world, request from consumer: /*.*.*.*:43346 ","permalink":"https://rikaaa0928.github.io/posts/dubbo-2.7.14-with-java-17/","summary":"java 17是java目前最新的长期支持(LTS)版本，但是由于其强封装 JDK 的内部 API的新特性，导致dubbo项目无法直接使用jdk17编译运行。通过参考openjdk的说明，可以发现只需要添加相应参数即可绕开java 17的限制\n对于普通的dubbo项目，只需要在运行时添加\n--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED\n如上参数即可。如果项目的其它依赖也有类似问题则可能需要加入更多参数，参数的获得方式和详细示例将在下面给出\n本解决方案只能解决由于java 17强封装 JDK 的内部 API的特性造成的问题，其他的兼容性问题请寻找其它方案\n参数的获得方法和示例 我们以dubbo官方仓库中的demo为例 首先使用java 17作为我们的开发环境，通过\ngit clone git@github.com:apache/dubbo.git git checkout dubbo-2.7.14 cd dubbo-demo/dubbo-demo-annotation 获得dubbo官方仓库的代码中的demo，然后可以尝试直接使用java 17编译dubbo的demo\nmvn -U clean package --no-transfer-progress -D maven.test.skip=true 启动zookeeper docker run --name some-zookeepep -p 2181:2181 -it --rm zookeeper 作为注册中心 尝试运行provider\njava -jar dubbo-demo-annotation-provider/target/dubbo-demo-annotation-provider-2.7.14.jar 可以看到类似的报错:\nCaused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.","title":"Dubbo 2.7.14 With Java 17"}]