Xhair迁移 - 从Vercel到自部署

Xhair上线后收到一些访问速度的反馈,并且免费额度也有不少限制,因此决定自部署

Share

背景

在25年6月我花了一周时间开发上线了xhair,考虑到开发体验与效率,我将其直接部署在Vercel上,目前日UV在一百左右。 因为一些问题,我决定将应用迁移至香港的云服务器上

面临的问题

国内连接不畅通

Vercel部署后域名存在DNS污染,延迟高,部分用户无法访问。

日志显示访问被新加坡和美国的服务器处理,而我接近9成的用户来自国内,显然用户体验不佳。

feedback

用户反馈集中在页面打开慢和偶发访问失败,问题主要出现在国内网络环境。

免费额度限制

流量平稳后,每天用户约一百人,这不是一个很高的流量,但我仍收到很多条Vercel的告警,提示我即将超过免费的额度。

  1. Image Optimization

Next.JS应用中直接使用img标签渲染图片会被提示使用Next/Image替代

image-lint

然而Vercel提供的额度仅有5K,一周后就收到了警告,我不得不修改代码停止使用

vercel-image-usage
  1. Link Prefetch

Next.JS中跳转链接使用Link组件,会默认具备prefetch的能力。

在我的页面中可能会有几十个卡片,每个卡片都支持跳转,这意味着每次访问会有额外的几十次请求。

page-pros

卡片列表中的大量跳转链接会触发预取,请求数会随着列表规模快速增加。

很快Edge Request额度也不够了,我只得在每一条跳转链接上手动设置prefecth=false

<Link href={`/players/${player.name}`} prefetch={false}>
	{player.name}
</Link>
request-usage
  1. 其他

我还遇到了其他限制如fluid-compute等。使用Next.JS是为了更快的效率,我不希望为了各种限制去放弃框架特性并调整代码

fluid-usage

难以舍弃的开发体验

虽然有遇到问题,但难以否认使用Vercel部署很方便,我自21年至今部署了几十个项目,平台的使用体验很棒。

也可以轻松接入Analytics和Speed Insight等能力

替代方案

香港服务器

香港服务器可以满足低延迟和免备案这关键的两点,我购买了一台小厂的4C/4G/20M带宽的服务器,2年600元。 在北京直连延迟约40ms

Coolify

An open-source & self-hostable Heroku / Netlify / Vercel alternative.

首先需要部署coolify,也可以使用其提供的coolify cloud,5刀一个月,然后连接服务器就可以部署了,我就都用的一台服务器。

coolify-web

coolify以docker容器管理多个服务,我的Nextjs应用在构建时4G的内存不太够用,因此我设置了swap,也可以将镜像构建迁移至Github Action,但配置略麻烦。

可参考 https://github.com/coollabsio/coolify/discussions/2754

链接Github仓库后push代码后自动部署,包括一些基础功能如环境变量设置、域名配置等,大致体验可对齐Vercel。

coolify-dashboard

Umami

替代一下Vercel的Analytics,简洁够用。

umami

结果

虽然也会面临服务器厂商跑路和崩溃的风险,核心还是想要提升访问的速度以及摆脱vercel额度的限制

使用测速工具对比首页访问速度,国内访问时间9.7s -> 1.8s 降低了81.4%

vercel-speed

迁移前,国内访问首页加载时间接近 10 秒。

server-speed

迁移到香港服务器后,首页加载时间下降到 2 秒以内。

Read more

WASM初探

前言 最近开发上线了xhair.pro, 其核心的数据是通过爬虫下载.dem文件并使用一些开源的解析库如demoinfocs-golang,demoparser解析获取最终的数据 在本地下载解析上传可以随意配置环境,但浏览器无法直接运行Golang或Rust的代码。想要将解析的能力制作成web应用,需要使用WebAssembly技术。 WASM WebAssembly 是一种新的编码方式,可以在现代的 Web 浏览器中运行——它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C/C++、C# 和 Rust 等语言提供编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。 我们可以把Rust、Go等语言编译成.wasm后缀的二进制文件,浏览器可以通过WebAssembly的API加载该模块,并调用相关能力 实践 不同编程语言需要通过不同的方式编译至wasm,我尝试了Go与Rust。 Golang实践 1. 首先需要配置Go的环境 go 安装 2. 初始化项目 mkdir w

By pureink

家用服务器公网访问方案

前言 因为ipv4地址数量受限,家庭宽带几乎都没有公网IP。在局域网内可以使用类似192.168.0.100这样的内网IP进行访问,但是想要在外访问或是提供给朋友使用是不行的。 方案一:内网穿透 可以使用具备公网IP的服务器进行中转,用户访问具备公网IP的服务器,该公网服务器再与家庭服务器通信,最终用户访问到家庭内网。 方案对比 方案 价格 优缺点 使用免费服务如ngrok、cloudflare tunnel 无 速度受限,部分功能需要收费如绑定域名 购买云服务器搭建 中 价格高,需要部署维护 购买FRP服务如sakura frp 低 有流量和带宽限制 个人建议 临时需求可以使用ngrok,例如给朋友看正在开发的网站 长期需求建议购买FRP服务,服务器安装frp软件后,可以通过网页配置隧道如下图,可以使用香港的服务器省去域名的备案,但不太适合云盘等大流量应用。 方案二:DDNS 内网穿透需要付费,并且有带宽和流量限制,额外的一个中转服务器也会影响访问速度。 好在ipv6已经逐渐普及,若服务端和客户端同时支持ipv6网络,

By pureink

前端表单开发经验

前言 表单是前端开发中常见的场景,注册登录、调查问卷,乃至租赁一个云服务器都是在填写表单。 本文根据我的表单开发经验分享表单开发中,为什么需要表单库进行辅助。 原生表单 前端技术上,表单对应form标签,MDN有详细的介绍。 以一个注册表单为例,我们需要如下标签 这离用户体验还很远,想要完善它的功能,都要依靠JS,原生表单有很多能力缺口,例如: 1. 无法展示错误信息 为了告知用户错误的原因,常见在输入框底部进行红色文案的提示,如下图。HTML会给校验失败的元素添加CSS 伪类 :invalid,但这还远远不够。 2. 不支持自定义校验 内置支持require、pattern正则这些能力,但确认密码的校验需要和第一次填写的密码进行对比,没有相关的属性可以用。 React中的表单 尝试在React下实现一个完整的注册表单。 我们维护了两个状态对象,一个formValue代表表单值,一个error用于渲染错误信息。每一个input输入框传入value与onChange使其受控,用户输入后会修改formValue并进行校验。同时提交时也会重新进行校验,满足条

By pureink