包含关键字 typecho 的文章

在没有域名只有IP地址的情况下,实现HTTPS访问是可能的,但需要通过一系列步骤来确保安全性和可访问性。以下是实现这一目标的详细步骤:

一、确认公网IP地址

首先,确保你拥有一个固定的公网IP地址。公网IP地址是互联网上的基本寻址方案,用于唯一标识互联网上的计算机或服务器,是实现外部直接访问的前提条件。动态IP地址可能不适合此场景,因为它们会频繁改变,导致SSL证书失效。

二、申请IP地址SSL证书

公网IP证书申请入口

选择证书颁发机构(CA)

打开JoySSL官网,写注册码230970,获取大额优惠跟技术支持。


准备申请材料:

准备好对IP地址的所有权或管理权限的证明,因为申请过程中通常需要验证你对IP的控制权。

完成验证流程:

按照CA的要求完成验证流程,这可能包括通过文件验证、邮箱验证或其他方式证明你对IP地址的控制权。

购买证书:

购买合适的证书类型,如DV(域名验证)或OV(组织验证)证书。需要注意的是,虽然传统上IP地址SSL证书可能更多是针对企业或组织机构的,但近年来个人用户也可能有条件申请,具体需咨询CA。

三、安装SSL证书

下载证书:
一旦申请被批准,从CA处下载你的SSL证书文件和中间证书。

上传证书:
将证书文件和私钥上传至你的Web服务器软件上,如Apache、Nginx或IIS。

配置服务器:
在服务器配置中,将IP SSL证书绑定到特定的公网IP地址上,而非传统域名。在Nginx等服务器软件的配置文件中,可以指定IP地址作为server_name。
确保服务器配置正确监听HTTPS端口,并正确处理HTTPS请求。
如果需要,配置端口转发,确保即使使用非标准端口,HTTPS连接也能正确建立。

在一些项目的对接中,团队经常会收到关于“一张显卡能跑多少路应用?”“需要准备多少服务器?”等实际部署问题。这些问题的答案,往往并非简单的数字计算,而是需要结合应用特性、硬件性能与系统架构进行综合评估。下面,我们针对几个高频问题,从实际经验出发,为大家提供一些选型参考与解答。

问题一:一个应用占8G显存,RTX Pro 6000 96G显卡是不是就能跑10个并发?

不完全是这样。
显存确实是决定并发数量的重要基础——从数字上看,96G显存似乎能轻松容纳10个8G应用。但在实际运行中,每个应用不仅占用显存,还会持续消耗GPU的图形处理资源(3D渲染能力)、视频编码资源,并依赖CPU调度与内存支持。
如果应用本身图形负载高,或多个实例同时运行产生资源争抢,就可能出现卡顿、排队等现象。因此,我们强烈建议以实际测试为准,在目标硬件上模拟真实并发场景,观察GPU利用率、帧率稳定性等指标,才能确定可靠的并发数量。

问题二:实时云渲染需要什么GPU和CPU?60个并发要配什么服务器?

使用点量云流实时云渲染对CPU和GPU的要求,一般要参考需要渲染的应用对GPU等资源情况。
GPU选型:参考需要渲染的应用对GPU等资源情况
如果您的3D应用较轻量(如简单模型、UI交互),消费级显卡如 RTX 4090 性价比很高;
如果是大型建筑漫游、复杂虚拟仿真、高精度模型等专业应用,则建议使用专业级显卡,如 RTX 6000,其在多实例并行与稳定性上表现更优。

CPU选型:尽量选择多核高频CPU
推荐 8核16线程以上的多核高频CPU,如Xeon Gold 6348。注意如果核心数/线程数过低,可能发生调度瓶颈。此外,需注意部分应用(如部分UE项目)对CPU的单核计算性能(主频)要求也较高,具体需要结合应用进行测试评估。若是是对并发要求不高或者3D应用本身比较简单,则没有特殊要求,可以选择工作站/消费级CPU 比如i9-13900k,以保证良好的进程调度与响应能力。

60并发如何配置服务器?
想要实现60路并发,所需的具体显卡数,完全取决于单张显卡能承载多少路流畅运行的应用实例。在预算有限或追求更高并发时,可考虑通过适当降低渲染帧率(如从60FPS调整至30FPS)或分辨率来有效降低单路应用的资源消耗。理论上,这有望显著提升单卡并发能力,例如原本支持30路的配置,经过优化可能支持60路。

假设经测试与优化后,一张显卡可稳定支持4个应用实例同时流畅运行,那么理论上需要15张显卡。我们通常建议将显卡分散到多台服务器中,例如配置8台2卡服务器,而非将所有显卡集中在一台。这样既能避免单机系统隐形瓶颈,也提升了整体方案的可靠性与可扩展性。

操作系统建议:优先安装 Windows Server 2019/2022,其对多GPU环境及长时间运行的支持更为稳定。

问题三:多并发下对网络和服务器有何要求?显卡选择要注意什么?

服务器与显卡注意事项
大并发下服务器的参数要求请参考问题二。GPU若选用数据中心级显卡(如 NVIDIA Tesla/A系列),必须配置 GRID 驱动,否则无法正常用于多用户图形渲染。
强烈建议进行多实例压力测试,确认显卡在目标应用下的实际并发能力,避免仅按显存大小估算。

网络带宽要求
网络需求主要取决于并发数与每路视频流的码率。一般1080P 清晰度下,单路建议预留 5–8Mbps码率。
而60路并发则需300–500Mbps左右宽带。若分辨率提升至2K/4K,或需要更高帧率,带宽需相应增加。

点量云流实时云渲染并发的规划,是一个从“应用特性”出发,结合“显卡算力、CPU调度、内存、网络与系统架构”的整体工程。点量云流平台自身的资源占用很低(仅需约5%的剩余算力),实际上,服务器能支持多少路并发,真正取决于客户所运行的应用本身对资源的消耗。因此,我们始终建议在选型前进行真实场景测试,用数据指导配置,避免资源浪费或性能不足。

如果您有具体的应用需要评估,欢迎联系我们安排测试,我们将为您提供更贴合业务场景的配置方案。

点赞 + 关注 + 收藏 = 学会了

整理了一个n8n小专栏,有兴趣的工友可以关注一下 👉 《n8n修炼手册》

对 n8n 初学者来说,不用花钱就能调用大模型API,是快速上手AI自动化工作流的关键。n8n作为可视化自动化工具,能通过API连接各类大模型,实现文本生成、情感分析、图文处理等功能,而免费API能帮我们零成本练手、验证创意,不用承担付费压力。

本文推荐几个适合 n8n 小白的免费大模型 API 服务商。

但需要看清本文的发布时间,也许半年后、一年后这些 API 就不再免费了。

部分服务商还需要你懂魔法。

如果你用过哪些比较好的大模型,也欢迎在评论区留言~

如果你还不清楚 n8n 如何对接大模型,我准备了2篇文章。

如果你是富哥,个人电脑配置很顶的话,可以用第1种方法。

本文整理的这些免费大模型 API 要用第2种方法对接。

如果第2种方法都无法对接的话,可以使用「HTTP 节点」来对接,具体操作请参考👉 『n8n』通过接入DeepSeek了解HTTP节点

推荐的服务商排名部分先后,能用就行😄

前摇结束,开始!

Hugging Face

⚡️Hugging Face: https://huggingface.co

Hugging Face 是全球知名的开源AI平台,拥有海量免费预训练模型,涵盖文本分类、句子嵌入、语音识别等各类任务,适合小白探索不同模型的能力,也能通过API快速集成到n8n中。

打开 Hugging Face 官网,登录后,点击右上角的头像,选择「Access Tokens」

来到「Access Tokens」页面,点击“+ Create new token”按钮。

输入一个 Token name,下面能选的都选上吧。

然后滑到页面底部,点击“Create token”按钮。

获取到令牌后找个地方保存好,这个令牌只展示一次。如果弄丢了就要按上面的步骤重新操作一次了。

打开 n8n,在界面面板搜索“hugging”,选择第一项。

如果你第一次使用的话,在“Credential to connect with”项里选择“+ Create new credential”创建一个 Hugging Face 的凭证。如果已经有凭证了就是下图这样了。

创建凭证的方法也很简单,将刚刚在 Hugging Face 申请的令牌复制到 API Key 这项里就行。

回到工作流就可以用它了。

Hugging Face 上还有其他模型可以申请,自己去研究一下吧~

Gemini

⚡️Google AI Studio:https://aistudio.google.com

Gemini 的开通方式有点麻烦,需要有 Visa 卡才行。

现在能用的免费模型只有 flash 系列的,pro 之前被白嫖太多了已经不开放了,以后会不会重新开放不好说。

打开 Google AI Studio,登录完,点击左下角的“Get API key”。

然后创建一个 API 密钥。

如果没项目的话,需要先创建一个项目。

创建完 API 密钥后,点击复制按钮。

来到 n8n 这边创建 Google Gemini 凭证就能用了。

LongCat(美团)

⚡️LongCat:https://longcat.chat/platform/api_keys

LongCat 是美团自主研发的大语言模型,每天刷新500万 token 给你用。而且响应速度很快。

登录后,在 API Keys 页面创建 API Key 就可以用了。

具体接入的 URL 可以看 LongCat 官方文档👉 https://longcat.chat/platform/docs/zh/

我用了 HTTP 节点接入,聊天对话的话 URL 可填入 https://api.longcat.chat/openai/v1/chat/completions

亲测能用。

百灵(阿里)

⚡️ 百灵:https://ling.tbox.cn/open

百灵大模型是蚂蚁集团推出的Ling-1T大模型对话体验平台,定位为全能型AI助手,兼顾基础文本处理与复杂推理,支持多模态能力,且适配OpenAI接口格式,能快速集成到n8n中。

百灵每天会刷新50万计算单位(token?)。

首次登录需要绑定致富宝。

绑定成功后,在后台就可以创建令牌了,并且每天能刷新免费额度。

在 n8n 这边给百灵创建一个 OpenAI 的凭证。

API Key 填你刚刚创建的。

Base URL 填这个 https://api.tbox.cn/api/llm/v1/

来到工作流这边你会发现没模型可以选。

你需要打开百灵的使用手册,选择一个模型,填入对应的“版本名称”。

https://alipaytbox.yuque.com/sxs0ba/ling/model_overview

Model 这项要选 By ID,值就填入模型的“版本名称”。

能嫖!

借助工具可快速实现自动化流程,落地时需关注多场景适配的工程效率问题。可试试RollCode 低代码平台的私有化部署、自定义组件、静态页面发布(SSG + SEO)能力。


以上就是本文的全部内容啦,想了解更多n8n玩法欢迎关注《n8n修炼手册》👏

如果你有 NAS,我非常建议你在 NAS 上部署一套 n8n,搞搞副业也好,帮你完成工作任务也好 《『NAS』不止娱乐,NAS也是生产力,在绿联部署AI工作流工具-n8n》

点赞 + 关注 + 收藏 = 学会了

在全球超过 220 个市场中,Airbnb 的预订支付长期以来主要依赖银行卡支付。为降低结账摩擦、提升可达性,并推动国际市场的用户采用率,Airbnb 推出了“Pay as a Local”(像本地人一样支付)计划,引入用户信任且在当地广泛使用的本地支付方式(Local Payment Methods,简称 LPM)。

这一举措使房客能够根据所在地区的支付习惯选择合适的付款方式,同时也让工程团队能够以更高效率扩展和支持新的支付方式。作为支付系统长期架构升级的一部分,Airbnb 将原本的单体系统迁移至面向领域的服务架构。核心领域涵盖收款(pay-ins)、付款(payouts)、交易履约、支付处理、钱包、激励、发卡与清结算等模块。其中,支付处理子域通过连接器与插件框架对接第三方支付服务商(PSP),支持 API 与文件两种集成方式,大幅降低集成成本,加快在不同市场的上线速度。

目前支持的本地支付方式包括:国家或地区特有的数字钱包(如 M-Pesa、MTN MoMo);在线银行转账(如 Online Banking Czech、Online Banking Slovakia);实时或即时银行支付(如 PixUPI);本地支付网络与卡组织(如 EFTPOS、Cartes Bancaires)。Airbnb 工程师表示,这种模块化架构显著减少了在不同市场接入新支付服务商所需的时间与工程投入。

在分析了 20 多种全球本地支付方式的用户行为后,Airbnb 总结出三种基础支付流程范式:重定向型(Redirect)、异步型(Asynchronous) 与 直连型(Direct)。重定向流程会将用户跳转至第三方应用或网站完成支付,并在结束后返回一个确认凭证;异步流程(例如基于二维码的支付)则在外部交易完成后,通过 Webhook 通知 Airbnb;直连流程允许用户直接在 Airbnb 界面内输入支付凭证并即时完成处理。通过将这些流程标准化为可复用的范式,Airbnb 显著降低了工程复杂度,并简化了后续支付服务商的接入工作。

此处输入图片的描述

本地支付方式流程三种范式:Redirect、Asynchronous 与 Direct

为管理跨支付服务商的多步骤交互,Airbnb 构建了一个与 PSP 无关的多步骤交易(Multi-Step Transaction,MST)框架。MST 抽象并统一了授权、跳转、确认、扣款等步骤,提供一致的编排层,用于协调内部系统与外部支付流程。这一机制有效保障了涉及应用切换、会话交接及异步确认等复杂场景下的支付可靠性。

在集成与维护层面,Airbnb 进一步引入了集中式、基于 YAML 的支付方式配置系统。该系统作为唯一事实源(single source of truth),统一定义了支付可用性规则、输入校验、退款策略以及前端 UI 渲染指令。后端服务与结账组件均可动态读取该配置,使新支付方式的上线更多依赖声明式配置,而非代码修改,从而显著降低错误率。

Airbnb 支付配置在平台重构前后的对比

针对复杂支付流程,Airbnb 同步加强了测试与可观测性能力。内部自研的 PSP Emulator 能够模拟重定向与异步支付方式,在不依赖外部沙箱环境的情况下完成端到端测试。同时,集中式监控体系覆盖客户端、后端服务、PSP 及 Webhook 通道,并配备标准化告警机制,以便快速定位和响应问题。

所有新接入的支付方式都会自动启用关键指标采集,使工程团队能够实现全链路问题追踪。这一体系保障了不同支付流程下的一致稳定性,并支撑本地支付方式在全球范围内的规模化推广。

此处输入图片的描述

Airbnb 支付服务商(PSP)模拟器流程示意

“Pay as a Local”计划在业务与技术层面均取得了可量化的成果。在上线本地支付方式的市场中,Airbnb 观察到预订量和新用户参与度均有所提升。通过复用支付流程范式与配置驱动的架构,工程团队显著缩短了新支付服务商的集成周期。与此同时,可观测性能力的增强、标准化测试流程以及更清晰的升级与响应机制,也进一步提升了整个平台的稳定性。模块化服务、多步骤交易编排以及集中式配置三者的结合,使 Airbnb 能够以更低的维护成本、更快的接入速度,为全球用户提供一致且高度本地化的结账体验。

原文链接:

https://www.infoq.com/news/2026/02/airbnb-global-payaslocal/

一、核心功能设计

时间戳转换器包含三个主要模块:

  1. 实时时间戳显示: 自动刷新的当前时间戳(秒/毫秒)
  2. 时间戳转日期: 将Unix时间戳转换为可读日期格式
  3. 日期转时间戳: 将日期时间转换为Unix时间戳

在线工具网址:https://see-tool.com/timestamp-converter

工具截图:
工具截图.png

二、实时时间戳显示实现

2.1 核心状态管理

// 响应式数据
const autoRefresh = ref(true)           // 自动刷新开关
const currentSeconds = ref(0)           // 当前秒级时间戳
const currentMilliseconds = ref(0)      // 当前毫秒级时间戳

let refreshInterval = null              // 定时器引用

2.2 更新时间戳逻辑

// 更新当前时间戳
const updateCurrentTimestamp = () => {
  if (!process.client) return           // SSR 保护
  const now = Date.now()                // 获取当前毫秒时间戳
  currentSeconds.value = Math.floor(now / 1000)  // 转换为秒
  currentMilliseconds.value = now
}

关键点:

  1. SSR 保护: 使用 process.client 判断,避免服务端渲染错误
  2. Date.now(): 返回毫秒级时间戳,性能优于 new Date().getTime()
  3. 秒级转换: 使用 Math.floor() 向下取整

2.3 自动刷新机制

// 监听自动刷新开关
watch(autoRefresh, (val) => {
  if (!process.client) return

  if (val) {
    updateCurrentTimestamp()            // 立即更新一次
    refreshInterval = setInterval(updateCurrentTimestamp, 1000)  // 每秒更新
  } else {
    if (refreshInterval) {
      clearInterval(refreshInterval)    // 清除定时器
      refreshInterval = null
    }
  }
})

关键点:

  1. 立即更新: 开启时先执行一次,避免1秒延迟
  2. 定时器管理: 关闭时清除定时器,防止内存泄漏
  3. 1秒间隔: setInterval(fn, 1000) 实现秒级刷新

2.4 生命周期管理

onMounted(() => {
  if (!process.client) return
  updateCurrentTimestamp()
  if (autoRefresh.value) {
    refreshInterval = setInterval(updateCurrentTimestamp, 1000)
  }
})

onUnmounted(() => {
  if (refreshInterval) {
    clearInterval(refreshInterval)      // 组件销毁时清理定时器
  }
})

说明:

  • 组件挂载时初始化时间戳和定时器
  • 组件卸载时必须清理定时器,防止内存泄漏

三、时间戳转日期实现

3.1 格式自动检测

// 检测时间戳格式(秒 or 毫秒)
const detectTimestampFormat = (ts) => {
  const str = String(ts)
  return str.length >= 13 ? 'milliseconds' : 'seconds'
}

判断依据:

  • 秒级时间戳: 10位数字 (如: 1706425716)
  • 毫秒级时间戳: 13位数字 (如: 1706425716000)
  • 临界点: 13位作为分界线

3.2 核心转换逻辑

const convertTimestampToDate = () => {
  if (!process.client) return
  if (!timestampInput.value.trim()) {
    safeMessage.warning(t('timestampConverter.notifications.enterTimestamp'))
    return
  }

  try {
    let ts = parseInt(timestampInput.value)

    // 自动检测或手动指定格式
    const format = tsInputFormat.value === 'auto'
      ? detectTimestampFormat(ts)
      : tsInputFormat.value

    // 统一转换为毫秒
    if (format === 'seconds') {
      ts = ts * 1000
    }

    const date = new Date(ts)

    // 验证日期有效性
    if (isNaN(date.getTime())) {
      safeMessage.error(t('timestampConverter.notifications.invalidTimestamp'))
      return
    }

    // ... 后续处理
  } catch (err) {
    safeMessage.error(t('timestampConverter.notifications.convertFailed'))
  }
}

关键点:

  1. 输入验证: 检查空值和有效性
  2. 格式统一: 统一转换为毫秒级时间戳
  3. 有效性检查: isNaN(date.getTime()) 判断日期是否有效
  4. 异常捕获: try-catch 保护,防止程序崩溃

3.3 时区处理

// 获取本地时区偏移
const getTimezoneOffset = () => {
  const offset = -date.getTimezoneOffset()  // 注意负号
  const hours = Math.floor(Math.abs(offset) / 60)
  const minutes = Math.abs(offset) % 60
  const sign = offset >= 0 ? '+' : '-'
  return `UTC${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`
}

说明:

  • getTimezoneOffset() 返回的是 UTC 与本地时间的分钟差
  • 返回值为正表示本地时间落后于 UTC,需要取反
  • 格式化为 UTC+08:00 形式
// 获取指定时区的偏移
const getTimezoneOffsetForZone = (timezone) => {
  if (timezone === 'local') {
    return getTimezoneOffset()
  }

  try {
    const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }))
    const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }))
    const offset = (tzDate - utcDate) / (1000 * 60)
    const hours = Math.floor(Math.abs(offset) / 60)
    const minutes = Math.abs(offset) % 60
    const sign = offset >= 0 ? '+' : '-'
    return `GMT${sign}${hours}`
  } catch (e) {
    return ''
  }
}

关键技巧:

  • 使用 toLocaleString()timeZone 参数转换时区
  • 通过 UTC 和目标时区的时间差计算偏移量
  • 异常捕获处理无效时区名称

3.4 日期格式化输出

// 根据选择的时区格式化本地时间
let localTime = date.toLocaleString(
  locale.value === 'en' ? 'en-US' : 'zh-CN',
  { hour12: false }
)

if (tsOutputTimezone.value !== 'local') {
  try {
    localTime = date.toLocaleString(
      locale.value === 'en' ? 'en-US' : 'zh-CN',
      {
        timeZone: tsOutputTimezone.value === 'UTC' ? 'UTC' : tsOutputTimezone.value,
        hour12: false
      }
    )
  } catch (e) {
    // 时区无效时回退到本地时间
    localTime = date.toLocaleString(
      locale.value === 'en' ? 'en-US' : 'zh-CN',
      { hour12: false }
    )
  }
}

格式化选项:

  • hour12: false: 使用24小时制
  • timeZone: 指定时区(如 'Asia/Shanghai', 'UTC')
  • 根据语言环境自动调整日期格式

3.5 年中第几天/第几周计算

// 计算年中第几天
const getDayOfYear = (d) => {
  const start = new Date(d.getFullYear(), 0, 0)  // 去年12月31日
  const diff = d - start
  const oneDay = 1000 * 60 * 60 * 24
  return Math.floor(diff / oneDay)
}

// 计算年中第几周
const getWeekOfYear = (d) => {
  const start = new Date(d.getFullYear(), 0, 1)  // 今年1月1日
  const days = Math.floor((d - start) / (24 * 60 * 60 * 1000))
  return Math.ceil((days + start.getDay() + 1) / 7)
}

算法说明:

  1. 年中第几天: 当前日期 - 去年最后一天 = 天数差
  2. 年中第几周: (天数差 + 1月1日星期几 + 1) / 7 向上取整

3.6 相对时间计算

// 相对时间(如: 3天前, 2小时后)
const getRelativeTime = (timestamp) => {
  if (!process.client) return ''

  const now = Date.now()
  const diff = now - timestamp
  const seconds = Math.abs(Math.floor(diff / 1000))
  const minutes = Math.floor(seconds / 60)
  const hours = Math.floor(minutes / 60)
  const days = Math.floor(hours / 24)

  const isAgo = diff > 0  // 是否是过去时间
  const units = tm('timestampConverter.timeUnits')

  let value, unit
  if (seconds < 60) {
    value = seconds
    unit = units.second
  } else if (minutes < 60) {
    value = minutes
    unit = units.minute
  } else if (hours < 24) {
    value = hours
    unit = units.hour
  } else {
    value = days
    unit = units.day
  }

  return isAgo
    ? t('timestampConverter.timeAgo', { value, unit })
    : t('timestampConverter.timeAfter', { value, unit })
}

逻辑分析:

  1. 时间差计算: 当前时间 - 目标时间
  2. 单位选择: 自动选择最合适的单位(秒/分/时/天)
  3. 方向判断: 正数为"前",负数为"后"
  4. 国际化: 使用 i18n 支持多语言

3.7 完整结果对象

const weekdays = tm('timestampConverter.weekdays')
const timezoneLabel = tsOutputTimezone.value === 'local'
  ? `${t('timestampConverter.localTimezone')} (${getTimezoneOffset()})`
  : `${tsOutputTimezone.value} (${getTimezoneOffsetForZone(tsOutputTimezone.value)})`

tsToDateResult.value = {
  timezone: timezoneLabel,           // 时区信息
  local: localTime,                  // 本地时间
  utc: date.toUTCString(),          // UTC 时间
  iso: date.toISOString(),          // ISO 8601 格式
  relative: getRelativeTime(ts),    // 相对时间
  dayOfWeek: weekdays[date.getDay()],  // 星期几
  dayOfYear: getDayOfYear(date),    // 年中第几天
  weekOfYear: getWeekOfYear(date)   // 年中第几周
}

四、日期转时间戳实现

4.1 设置当前时间

// 设置为当前时间
const setToNow = () => {
  if (!process.client) return
  const now = new Date()
  const year = now.getFullYear()
  const month = String(now.getMonth() + 1).padStart(2, '0')
  const day = String(now.getDate()).padStart(2, '0')
  const hours = String(now.getHours()).padStart(2, '0')
  const minutes = String(now.getMinutes()).padStart(2, '0')
  const seconds = String(now.getSeconds()).padStart(2, '0')
  dateTimeInput.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}

格式化技巧:

  • padStart(2, '0'): 补齐两位数(如: 9 → 09)
  • 月份需要 +1 (getMonth() 返回 0-11)
  • 格式: YYYY-MM-DD HH:mm:ss

4.2 核心转换逻辑

const convertDateToTimestamp = () => {
  if (!process.client) return

  if (!dateTimeInput.value) {
    safeMessage.warning(t('timestampConverter.notifications.selectDateTime'))
    return
  }

  try {
    const date = new Date(dateTimeInput.value)

    // 验证日期有效性
    if (isNaN(date.getTime())) {
      safeMessage.error(t('timestampConverter.notifications.invalidDateTime'))
      return
    }

    // 根据时区调整
    let finalDate = date

    if (dateInputTimezone.value === 'UTC') {
      // UTC 时区: 需要加上本地时区偏移
      finalDate = new Date(date.getTime() + date.getTimezoneOffset() * 60000)
    } else if (dateInputTimezone.value !== 'local') {
      // 其他时区: 计算时区差异
      const localDate = date
      const tzString = localDate.toLocaleString('en-US', {
        timeZone: dateInputTimezone.value
      })
      const tzDate = new Date(tzString)
      const offset = localDate.getTime() - tzDate.getTime()
      finalDate = new Date(localDate.getTime() - offset)
    }

    const ms = finalDate.getTime()
    const seconds = Math.floor(ms / 1000)

    dateToTsResult.value = {
      seconds,                    // 秒级时间戳
      milliseconds: ms,           // 毫秒级时间戳
      iso: finalDate.toISOString()  // ISO 8601 格式
    }

    safeMessage.success(t('timestampConverter.notifications.convertSuccess'))
  } catch (err) {
    safeMessage.error(t('timestampConverter.notifications.convertFailed'))
  }
}

时区处理详解:

  1. 本地时区 (local):

    • 直接使用用户输入的日期时间
    • 不做任何调整
  2. UTC 时区:

    • 用户输入的是 UTC 时间
    • 需要加上 getTimezoneOffset() 转换为本地时间戳
    • 例: 输入 "2024-01-01 00:00:00 UTC" → 北京时间 "2024-01-01 08:00:00"
  3. 其他时区 (如 Asia/Tokyo):

    • 计算目标时区与本地时区的偏移量
    • 通过 toLocaleString() 转换时区
    • 调整时间戳以反映正确的时间

4.3 时区转换原理

// 示例: 将 "2024-01-01 12:00:00" 从东京时区转换为时间戳

// 步骤1: 创建本地时间对象
const localDate = new Date('2024-01-01 12:00:00')  // 假设本地是北京时间

// 步骤2: 转换为东京时区的字符串
const tzString = localDate.toLocaleString('en-US', { timeZone: 'Asia/Tokyo' })
// 结果: "1/1/2024, 1:00:00 PM" (东京比北京快1小时)

// 步骤3: 将字符串解析为日期对象
const tzDate = new Date(tzString)

// 步骤4: 计算偏移量
const offset = localDate.getTime() - tzDate.getTime()
// offset = -3600000 (负1小时的毫秒数)

// 步骤5: 应用偏移量
const finalDate = new Date(localDate.getTime() - offset)

核心思想:

  • 通过两次转换计算时区差异
  • 利用偏移量调整时间戳
  • 确保时间戳代表的是正确的绝对时间

五、Date 对象核心 API 总结

6.1 创建日期对象

// 当前时间
new Date()                          // 当前日期时间
Date.now()                          // 当前时间戳(毫秒)

// 从时间戳创建
new Date(1706425716000)             // 毫秒时间戳
new Date(1706425716 * 1000)         // 秒时间戳需要 * 1000

// 从字符串创建
new Date('2024-01-28')              // ISO 格式
new Date('2024-01-28 12:00:00')     // 日期时间
new Date('Jan 28, 2024')            // 英文格式

// 从参数创建
new Date(2024, 0, 28)               // 年, 月(0-11), 日
new Date(2024, 0, 28, 12, 0, 0)     // 年, 月, 日, 时, 分, 秒

6.2 获取日期信息

const date = new Date()

// 获取年月日
date.getFullYear()      // 年份 (2024)
date.getMonth()         // 月份 (0-11, 0=1月)
date.getDate()          // 日期 (1-31)
date.getDay()           // 星期 (0-6, 0=周日)

// 获取时分秒
date.getHours()         // 小时 (0-23)
date.getMinutes()       // 分钟 (0-59)
date.getSeconds()       // 秒 (0-59)
date.getMilliseconds()  // 毫秒 (0-999)

// 获取时间戳
date.getTime()          // 毫秒时间戳
date.valueOf()          // 同 getTime()

// 时区相关
date.getTimezoneOffset()  // 本地时区与 UTC 的分钟差

6.3 设置日期信息

const date = new Date()

// 设置年月日
date.setFullYear(2024)
date.setMonth(0)        // 0-11
date.setDate(28)

// 设置时分秒
date.setHours(12)
date.setMinutes(30)
date.setSeconds(45)
date.setMilliseconds(500)

// 设置时间戳
date.setTime(1706425716000)

6.4 格式化输出

const date = new Date()

// 标准格式
date.toString()         // "Sun Jan 28 2024 12:00:00 GMT+0800 (中国标准时间)"
date.toDateString()     // "Sun Jan 28 2024"
date.toTimeString()     // "12:00:00 GMT+0800 (中国标准时间)"

// ISO 格式
date.toISOString()      // "2024-01-28T04:00:00.000Z"
date.toJSON()           // 同 toISOString()

// UTC 格式
date.toUTCString()      // "Sun, 28 Jan 2024 04:00:00 GMT"

// 本地化格式
date.toLocaleString()           // "2024/1/28 12:00:00"
date.toLocaleDateString()       // "2024/1/28"
date.toLocaleTimeString()       // "12:00:00"

// 自定义本地化
date.toLocaleString('zh-CN', {
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  hour12: false,
  timeZone: 'Asia/Shanghai'
})

一、单文件组件(.vue)核心定义与结构

每个.vue文件对应一个Vue单文件组件,是Vue组件的专属文件格式,由模板、样式、逻辑三部分构成,各部分各司其职且结构固定。

1. 三大组成部分说明

组成部分对应标签核心功能关键注意点
模板<template>搭建当前组件的DOM结构,仅作为包裹容器,不会被渲染为真实DOM元素每个组件最多1个顶层<template>;Vue3支持多根节点,Vue2仅支持单根节点(必须有唯一外层根标签包裹)
样式<style>通过CSS代码为当前组件设置样式可添加scoped属性实现组件样式隔离,避免样式污染
逻辑<script>通过JavaScript代码处理组件的数据定义、业务逻辑Vue3提供setup语法糖,简化数据和方法的定义与暴露

二、数据绑定核心内容

Vue通过数据绑定实现数据与页面分离,最终达成数据驱动视图的效果,核心解决重复编写页面模板的问题(如图书商城复用图书详情页模板,仅修改数据展示不同内容)。
数据绑定分为定义数据输出数据两个核心步骤,且普通数据无响应式,需通过专属函数处理为响应式数据,才能实现数据变化视图同步更新。

1. 初识数据绑定

1.1 定义数据

Vue3提供基础写法setup语法糖写法(推荐),语法糖可大幅简化代码,提高开发效率。

写法1:基础写法(setup函数)
<script>
export default {
    setup() {
        return {
            数据名: 数据值,
            // 可定义多个数据,以键值对形式存在
            ...
        }
    }
}
</script>
  • 核心要点:export default是模块导出语法;setup()是Vue3组合式API的起点,需通过return暴露数据给模板;组件实例创建时执行该代码。
写法2:setup语法糖写法(推荐)
<script setup>
// 直接定义变量即可,无需export和return,自动暴露给模板
const 数据名 = 数据值;
</script>
  • 核心要点:在<script>标签添加setup属性即可使用,代码更简洁,是Vue3开发首选方式。

1.2 输出数据

使用Vue提供的Mustache语法(双大括号语法),在<template>中作为占位符,页面渲染时会被替换为实际数据。

基本语法
<template>
  {{ 数据名 }}
</template>
支持的表达式类型

Mustache语法可直接解析表达式,返回结果作为输出内容,示例如下:

<template>
  {{ 'Hello Vue.js' }}       <!-- 字符串表达式 -->
  {{ number + 1 }}            <!-- 算术运算表达式 -->
  {{ obj.name }}              <!-- 对象属性取值表达式 -->
  {{ ok ? 'YES' : 'NO' }}     <!-- 三元运算符表达式 -->
  {{ '<div>HTML标签</div>' }} <!-- HTML字符串(会被当作纯文本输出,不解析标签) -->
</template>

1.3 基础数据绑定实操示例

步骤1:创建src\components\Message.vue文件,编写代码

<template>{{ message }}</template>
<script setup>
const message = '不积跬步,无以至千里'
</script>

步骤2:修改src\main.js文件,切换展示组件

import { createApp } from 'vue'
import './style.css'
// 替换为自定义的Message组件
import App from './components/Message.vue'

createApp(App).mount('#app')

页面效果
基础数据绑定页面效果

2. 响应式数据绑定

2.1 普通数据的问题

直接定义的普通数据,修改后数据本身会变化,但页面视图不会同步更新,示例验证如下:
修改src\components\Message.vue

<template>{{ message }}</template>
<script setup>
let message = '不积跬步,无以至千里'
// 2秒后修改数据
setTimeout(() => {
    console.log("更新前的message:" + message)
    message = '长风破浪会有时, 直挂云帆济沧海'
    console.log('更新后的message:' + message)
}, 2000)
</script>

效果验证
普通数据修改效果

  • 控制台:能打印出更新前、后的数据值,说明数据本身已修改;
  • 页面:始终显示原始数据,说明视图未同步更新。

2.2 响应式数据定义函数

Vue3提供ref()reactive()toRef()toRefs()四个函数,用于将普通数据处理为响应式数据,实现数据变化 → 视图自动同步更新,四个函数适用场景不同,需按需选择。

函数1:ref()
  • 作用:将基本类型数据/引用类型数据转换为响应式数据,是Vue3中最常用的响应式函数;
  • 语法

    // 导入ref函数
    import { ref } from 'vue'
    // 定义响应式数据
    const 响应式数据 = ref(初始数据值)
    // 修改响应式数据(必须通过.value属性)
    响应式数据.value = 新值
  • 实操示例
    ① 创建src\components\Ref.vue

    <template>{{ message }}</template>
    <script setup>
    // 导入ref函数
    import { ref } from 'vue'
    // 定义ref响应式数据
    const message = ref('会当凌绝顶,一览众山小')
    // 2秒后修改数据
    setTimeout(() => {
        message.value = '锲而不舍,金石可镂'
    }, 2000)
    </script>

    ② 修改src\main.js切换组件

    import App from './components/Ref.vue'

    页面效果
    初始效果:ref初始效果
    2秒后效果:ref更新效果

函数2:reactive()
  • 作用:专门创建响应式对象/响应式数组,仅支持引用类型(对象、数组),不支持基本类型;
  • 语法

    // 导入reactive函数
    import { reactive } from 'vue'
    // 定义响应式对象/数组
    const 响应式对象 = reactive(普通对象/普通数组)
    // 修改响应式数据(直接修改属性/元素,无需.value)
    响应式对象.属性名 = 新值
  • 实操示例
    ① 创建src\components\Reactive.vue

    <template>{{ obj.message }}</template>
    <script setup>
    // 导入reactive函数
    import { reactive } from 'vue'
    // 定义reactive响应式对象
    const obj = reactive({ message: '不畏浮云遮望眼,自缘身在最高层' })
    // 2秒后修改数据
    setTimeout(() => {
        obj.message = '欲穷千里目,更上一层楼'
    }, 2000)
    </script>

    ② 修改src\main.js切换组件

    import App from './components/Reactive.vue'

    页面效果
    初始效果:reactive初始效果
    2秒后效果:reactive更新效果

函数3:toRef()
  • 作用:将响应式对象中的单个属性转换为独立的响应式数据,修改该数据会同步更新原响应式对象;
  • 语法

    // 导入reactive、toRef函数
    import { reactive, toRef } from 'vue'
    // 先定义基础响应式对象
    const 响应式对象 = reactive({ 属性1: 值1, 属性2: 值2 })
    // 将单个属性转为响应式数据
    const 响应式属性 = toRef(响应式对象, '属性名')
    // 修改数据(需通过.value)
    响应式属性.value = 新值
  • 实操示例
    ① 创建src\components\ToRef.vue

    <template>
        <div>message的值:{{ message }}</div>
        <div>obj.message的值:{{ obj.message }}</div>
    </template>
    <script setup>
    // 导入所需函数
    import { reactive, toRef } from 'vue'
    // 定义基础响应式对象
    const obj = reactive({ message: '黑发不知勤学早,白首方悔读书迟' })
    // 将obj的message属性转为独立响应式数据
    const message = toRef(obj, 'message')
    // 2秒后修改数据
    setTimeout(() => {
        message.value = '少壮不努力,老大徒伤悲'
    }, 2000)
    </script>

    ② 修改src\main.js切换组件

    import App from './components/ToRef.vue'

    页面效果
    初始效果:toRef初始效果
    2秒后效果:toRef更新效果

函数4:toRefs()
  • 作用:将响应式对象中的所有属性一次性转换为独立的响应式数据,返回一个包含所有响应式属性的对象,可通过解构赋值快速使用,修改属性会同步更新原响应式对象;
  • 语法

    // 导入reactive、toRefs函数
    import { reactive, toRefs } from 'vue'
    // 先定义基础响应式对象
    const 响应式对象 = reactive({ 属性1: 值1, 属性2: 值2 })
    // 将所有属性转为响应式数据,解构赋值获取
    const { 属性1, 属性2 } = toRefs(响应式对象)
    // 修改数据(需通过.value)
    属性1.value = 新值
  • 实操示例
    ① 创建src\components\ToRefs.vue

    <template>
        <div>message的值:{{ message }}</div>
        <div>obj.message的值:{{ obj.message }}</div>
    </template>
    <script setup>
    // 导入所需函数
    import { reactive, toRefs } from 'vue'
    // 定义基础响应式对象
    const obj = reactive({ message: '盛年不重来,一日难再晨' })
    // 将obj的所有属性转为响应式数据,解构获取message
    let { message } = toRefs(obj)
    // 2秒后修改数据
    setTimeout(() => {
        message.value = '及时当勉励,岁月不待人'
    }, 2000)
    </script>

    ② 修改src\main.js切换组件

    import App from './components/ToRefs.vue'

    页面效果
    初始效果:toRefs初始效果
    2秒后效果:toRefs更新效果

三、核心知识点总结

1. 单文件组件关键

  1. Vue3 对<template>的根节点限制放宽,支持多根节点,解决Vue2外层根标签的冗余问题;
  2. <script setup>是Vue3推荐写法,无需export defaultreturn,直接定义数据/方法即可暴露给模板;
  3. <style scoped>是组件样式隔离的核心方式,开发中建议默认添加。

2. 数据绑定关键

  1. 基础数据绑定通过定义数据(setup)+ 输出数据(双大括号)实现,仅能完成数据的初始展示;
  2. Mustache语法支持各类简单表达式,但会将HTML字符串解析为纯文本,无法渲染DOM。

3. 响应式数据核心

  1. 响应式是Vue数据驱动视图的核心底层,普通数据需通过Vue3专属函数处理后才具备响应式;
  2. ref()是通用响应式函数,支持所有数据类型,修改时必须加.value(模板中使用无需加);
  3. reactive()仅支持对象/数组,修改时直接操作属性/元素,无需.value
  4. toRef()toRefs()基于已有响应式对象创建,用于拆分对象属性,实现属性的独立响应式,修改后会同步更新原对象;
  5. 所有响应式函数使用前必须先从vue中导入,否则会报错。

4. 开发实操注意

  1. 切换组件的核心方式是修改src\main.jsimport App from 'xxx'的导入路径;
  2. 定时器是验证响应式的常用方式,可直观看到数据和视图的更新效果;
  3. 开发中优先使用setup语法糖,简化代码编写;优先使用ref()定义响应式数据,通用性更强。

文本编码转换器在线工具分享

大家好,今天给大家推荐一款我基于 Vue.js 精心开发的实用在线工具——文本编码转换器

在日常上网或编程开发中,我们经常会遇到各种看不懂的“乱码”或者需要特定格式的字符。比如网页源代码里的 &#x4E2D;,或者是 Base64 编码的加密字符串。为了方便大家快速进行格式转换,我开发了这个全能的文本编码转换工具。

在线工具网址:https://see-tool.com/encoding-converter

工具截图:
在这里插入图片描述

为什么开发这个工具?

虽然网上有很多类似的工具,但往往功能单一,界面简陋,或者广告满天飞。作为一个对用户体验有追求的开发者,我利用 Vue 的响应式特性,打造了这款无广告、反应快、支持格式全的在线转换器。

核心功能介绍

这款工具目前支持 12种 常见的编码格式相互转换,堪称“编码界的瑞士军刀”:

  • 基础格式:普通文本、二进制 (Binary)、八进制、十进制、十六进制 (Hex)
  • Web开发:Base64、HTML实体 (十进制/十六进制)、Punycode (域名编码)
  • 字符编码:Unicode 转义 (\uXXXX)、Unicode 码点 (U+XXXX)、UTF-8 Hex

无论你是想把一串文字转换成 0101 的二进制代码装酷,还是解析一段不明所以的 Base64 字符串,它都能轻松搞定。

使用场景与特色

  1. 所见即所得:得益于 Vue 的高效性能,工具采用实时计算模式。你在左边输入,右边立刻显示结果,无需频繁点击“转换”按钮,体验丝般顺滑。
  2. 高度自定义:为了满足程序员的需求,支持自定义输出的分隔符(空格、逗号、冒号等)和前缀(如 0x, \x),甚至可以选择输出结果是否大写。
  3. 双向互转:点击中间的交换按钮,即可一键互换输入和输出格式,加密解密一步到位。
  4. 字符深度分析:除了整段转换,工具还贴心地提供了“字符详情”功能。当输入少量文字时,会自动分析每个字符的 Unicode 码点、UTF-8 字节序列等深层信息,是学习字符编码原理的好帮手。

安全隐私

请放心使用,本工具是纯前端应用。所有的转换计算都在你的浏览器本地完成,不会上传任何数据到服务器。你的文本内容绝对安全隐私,即便是敏感数据也能放心处理。

希望这个小工具能成为你数字生活中的得力助手。欢迎收藏使用,如果有任何建议或发现 Bug,也欢迎随时反馈给我!

工具网址和截图

在线工具网址:https://see-tool.com/encoding-converter

工具截图:
在这里插入图片描述

文本编码转换器功能核心实现解析

本文将深入探讨文本编码转换器(Text Encoding Converter)的核心 JavaScript 实现逻辑。该工具旨在实现普通文本与多种编码格式(如十六进制、二进制、Base64、Unicode 等)之间的相互转换。

1. 核心转换机制

整个工具的转换逻辑基于一个统一的入口函数 convert,它根据输入和输出格式,通过查找表(Lookup Table)调用相应的转换函数。

核心的字节处理依赖于浏览器原生的 TextEncoderTextDecoder API,这确保了对 UTF-8 的正确处理。

// 字符串转字节数组
const encoder = new TextEncoder();
const bytes = encoder.encode(text);

// 字节数组转字符串
const decoder = new TextDecoder('utf-8');
const text = decoder.decode(new Uint8Array(bytes));

2. 格式转换实现细节

2.1 进制转换 (Hex, Binary, Octal, Decimal)

对于二进制、八进制、十六进制等数字格式,核心思路是将文本转换为字节数组,然后利用 Number.prototype.toString(radix) 将每个字节转换为对应的进制字符串。

Hex(十六进制)为例:

textToHex: function(text, delimiter, prefix, uppercase) {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(text);
    let hex = Array.from(bytes).map(b => {
        // 每个字节转16进制,并补齐2位
        let h = b.toString(16).padStart(2, '0');
        if (uppercase) h = h.toUpperCase();
        return prefix + h;
    });
    return hex.join(delimiter);
}

反向转换则是移除前缀和分隔符后,使用 parseInt(chunk, 16) 还原字节。

2.2 Base64 编码

JavaScript 原生的 btoaatob 函数只能处理 ASCII 字符。为了支持中文等 Unicode 字符,我们需要先对字符串进行编码处理。

文本转 Base64 的健壮实现:

textToBase64: function(text) {
    try {
        // 方法1: 使用 TextEncoder 获取字节,构造二进制字符串
        const encoder = new TextEncoder();
        const bytes = encoder.encode(text);
        let binary = '';
        bytes.forEach(byte => binary += String.fromCharCode(byte));
        return btoa(binary);
    } catch (e) {
        // 方法2: 降级方案,使用 encodeURIComponent 处理
        return btoa(unescape(encodeURIComponent(text)));
    }
}

Base64 转文本

base64ToText: function(base64) {
    const binary = atob(base64.trim());
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
    }
    const decoder = new TextDecoder('utf-8');
    return decoder.decode(bytes);
}

2.3 Unicode 转义与码点

处理 Unicode 转义(如 \u4E2D)时,关键在于正确处理代理对(Surrogate Pairs)。对于超出基本多文种平面(BMP, U+0000 到 U+FFFF)的字符(例如 Emoji),JavaScript 的字符串长度为 2。

我们使用 codePointAt(0) 来获取完整的码点值:

textToUnicodeEscape: function(text, delimiter, uppercase) {
    let result = [];
    for (let char of text) {
        let code = char.codePointAt(0);
        // 如果码点超过 0xFFFF,说明是代理对,JS 会将其视为两个字符
        if (code > 0xFFFF) {
            // 手动计算代理对(虽然 ES6 for-of 循环会自动正确迭代字符)
            const high = Math.floor((code - 0x10000) / 0x400) + 0xD800;
            const low = (code - 0x10000) % 0x400 + 0xDC00;
            // ... 转换为 \uXXXX\uXXXX 格式
            let h1 = high.toString(16).padStart(4, '0');
            let h2 = low.toString(16).padStart(4, '0');
            result.push('\\u' + h1);
            result.push('\\u' + h2);
        } else {
            // ... 普通字符转换为 \uXXXX
            let h = code.toString(16).padStart(4, '0');
            result.push('\\u' + h);
        }
    }
    return result.join(delimiter);
}

注意:使用 for...of 循环可以正确遍历字符串中的 Emoji 等宽字符,而普通的 for(let i=0;...) 则会把它们拆分成两个。

2.4 Punycode 转换

Punycode 是国际化域名(IDN)使用的编码。本项目采用了一个巧妙的利用浏览器原生 API 的方法,避免引入庞大的第三方库:

punycode: {
    encode: function(input) {
        try {
            // 利用 URL API 自动进行 Punycode 编码
            const url = new URL('http://' + input);
            return url.hostname.replace(/^xn--/, '');
        } catch (e) {
            // 降级处理...
        }
    },
    decode: function(input) {
        // 利用 URL API 自动解析
        const testUrl = 'http://' + input;
        const url = new URL(testUrl);
        return url.hostname;
    }
}

这是一个非常轻量且高效的实现方式。

2.5 HTML 实体

HTML 实体的转换相对直接,主要将字符转换为其对应的十进制或十六进制引用:

textToHtmlDecimal: function(text, delimiter) {
    let result = [];
    for (let char of text) {
        let code = char.codePointAt(0);
        result.push('&#' + code + ';');
    }
    return result.join(delimiter);
}

3. 字符详情分析

工具还提供了一个 getCharacterInfo 函数,用于分析单个字符的详细信息。它不仅返回字符本身,还计算其 Unicode 码点、UTF-8 字节序列等。

function getCharacterInfo(char) {
    const codePoint = char.codePointAt(0);
    const encoder = new TextEncoder();
    const utf8Bytes = encoder.encode(char);
    
    return {
        char: char,
        codePoint: codePoint, // 数字形式
        hex: codePoint.toString(16).toUpperCase(), // Hex 形式
        utf8: Array.from(utf8Bytes) // UTF-8 字节序列
              .map(b => b.toString(16).toUpperCase().padStart(2, '0'))
              .join(' ')
    };
}

总结

本项目的文本编码转换器通过充分利用 TextEncoder/TextDecoderURL API 以及 ES6+ 的字符串处理特性(如 codePointAtfor...of),以原生 JavaScript 实现了高效、轻量的多格式转换,无需依赖任何重型第三方库。

V2EX 的图片库新增了基于 GPT 模型的生成功能。

如果你已经有图片库功能的访问权限,那么在图片库首页就可以找到这个功能的入口:

https://www.v2ex.com/i

每次生成会消耗 100 铜币。

如果你想开通图片库功能,那么可以下面两种方式任选其一:

  • 充值至少一次
  • 持有至少 10000 $V2EX token

PRO 会员和 10K 以上持有者当然已经可以使用。

这个功能今天刚刚完成部署,大家在尝试的过程中如果发现什么问题,欢迎反馈。

GPT 图片生成模型在 2024-2025 年期间的数次更新,使其在某些实用领域,比如图标和界面元素生成,变得非常有用。

V2EX 计划之后加入基于提示词模版和分享的一些高级功能,解锁 GPT 图片模型的更多可能性。


这个新功能的 API 服务提供商是 HodlAI ,欢迎来 HodlAI 节点了解更多:

https://www.v2ex.com/go/hodlai

HP AMP 125 打印机驱动安装包下载分享与安装使用教程(Windows)

适用系统:Windows 10 / Windows 11(64位)
关键词:HP AMP 125 驱动下载、HP AMP 125 无法打印、HP 驱动安装失败、USB 打印机识别异常

在家庭办公和小型企业环境中,打印机已经不仅仅是一个简单的输出设备,更是日常工作流的重要环节。HP AMP 125 作为一款入门级黑白激光一体机,以小巧的体积和高性价比受到不少用户青睐。然而,由于它属于区域定制型号,HP 官方并未提供完整的专属驱动,这使得许多用户在系统升级、重装或更换电脑后,常常遇到驱动缺失、打印异常或扫描功能无法使用的问题。本文旨在通过提供可用的替代驱动、详细的安装步骤以及常见故障解决方法,让用户无需等待官方更新,也能轻松恢复 AMP 125 的打印与扫描功能,实现设备的稳定使用和高效办公。

一、前言

HP AMP 125 是一款定位于家庭与小型办公场景的入门级黑白激光一体机,支持打印、复印和扫描,价格亲民、体积小巧。但很多用户在重装系统或更换电脑后,都会遇到一个问题:

官网找不到 AMP 125 的驱动,系统自动识别失败,打印机显示“未指定设备”或“驱动程序不可用”。

本文将提供:

  • 可用的 HP AMP 125 驱动解决方案
  • 完整安装步骤
  • 常见错误的排查方法
    让你 5 分钟内恢复正常打印。

驱动安装包下载分享

直接放到之前写的文章里了,免费开源,下载学习即可。
https://blog.csdn.net/weixin_52908342/article/details/157721892
image.png

二、HP AMP 125 驱动获取方式

由于 AMP 125 是区域型号(部分市场为定制型号),HP 官网并没有单独列出完整驱动页面。但它的硬件核心与 HP Laser 107 / MFP 135 / 136 系列一致,因此可以直接使用其通用驱动。

推荐驱动方案(稳定可用)

型号是否可用说明
HP Laser 107a / 107w✅ 可用单功能版本
HP Laser MFP 135a / 135w✅ 可用多功能一体机
HP Laser MFP 136nw✅ 可用网络版

只要是 同平台引擎的 PCL6 驱动,都可以正常驱动 AMP 125。


三、驱动安装步骤(Windows 10 / 11)

1. 连接打印机

  • 使用 USB 数据线连接电脑
  • 开机后,不要让 Windows 自动安装驱动(若已安装,先删除)

2. 卸载旧驱动(如安装失败)

  1. 控制面板 → 设备和打印机
  2. 删除所有 HP Laser / AMP 相关设备
  3. 打开:

    打印服务器属性 → 驱动程序 → 删除对应驱动
  4. 重启电脑

3. 安装通用驱动

  1. 下载 HP Laser 135/136 PCL6 驱动
  2. 右键 → 以管理员身份运行
  3. 选择 USB 连接
  4. 安装完成后重启

4. 绑定正确端口

  1. 打开:设备和打印机
  2. 右键 AMP 125 → 打印机属性
  3. 端口 → 选择 USB001 (Virtual printer port for USB)
  4. 应用 → 确定

四、扫描功能无法使用的解决方法

AMP 125 的扫描模块依赖 HP Scan 软件,建议安装:

  • HP Scan Extended
  • 或 Windows 自带:扫描与传真

路径:

开始 → 扫描 → 选择设备 → 开始扫描

五、常见问题解决

1. 显示“驱动程序不可用”

  • 说明驱动架构不匹配
  • 请确认安装的是 x64 版本

2. 打印任务卡住 / 队列不动

net stop spooler
del /Q /F %systemroot%\System32\spool\PRINTERS\*.*
net start spooler

3. 打印乱码

  • 打印机属性 → 高级
  • 驱动程序 → 切换为 PCL6

六、使用建议与维护

  • 定期清理粉盒残粉
  • 长时间不用请断电
  • 建议关闭“节电深度睡眠”(避免无法唤醒)

七、总结

HP AMP 125 虽然在官网缺少直接驱动支持,但通过 HP Laser 135/136 通用驱动方案,完全可以稳定运行在 Windows 10/11 上。

如果你遇到:

  • 驱动装不上
  • 打印机显示异常
  • 扫描功能失效

可以直接按本文步骤排查,基本都能解决。

在这里插入图片描述

HP AMP 125 作为一款定位入门级的激光一体机,硬件本身稳定可靠,但由于其属于区域定制型号,在 HP 官方驱动体系中并没有被单独完整列出,导致很多用户在重装系统、更换电脑或升级 Windows 版本后,都会遇到“找不到驱动”“驱动不可用”“打印机未指定”等问题,从而误以为设备已经过时或损坏。实际上,AMP 125 的核心引擎与 HP Laser 107 / 135 / 136 系列完全兼容,只要使用同平台的 PCL6 通用驱动,并正确绑定 USB 端口,就可以实现与原厂驱动几乎一致的打印与扫描体验。本文从驱动来源替代方案、安装前环境清理、手动端口绑定、扫描功能补全到常见故障修复,完整覆盖了 AMP 125 在 Windows 10 / 11 环境下的真实使用场景,既解决了“装得上”,也解决了“用得稳”的问题。只要按流程操作,即使是从未接触过打印机驱动的用户,也能在短时间内恢复设备正常工作,避免因官方支持缺失而造成的资源浪费,让这台性价比极高的打印机继续发挥应有的价值。

工具介绍

今天分享一个我用 Vue3 开发的实用工具——时间戳转换器。它能快速完成时间戳与日期之间的转换,支持多时区、智能检测格式,完全免费且保护隐私。

在线工具网址:https://see-tool.com/timestamp-converter

工具截图:
在这里插入图片描述

什么是时间戳?

时间戳是从 1970年1月1日 00:00:00 UTC 开始计算的秒数或毫秒数,是计算机表示时间的标准方式。

  • 秒级: 1706425716 (10位数字)
  • 毫秒级: 1706425716000 (13位数字)

时间戳全球统一、便于计算,但人类难以直接理解,因此需要转换工具。

核心功能

1. 实时时间戳显示 ⏰

页面顶部实时显示当前的秒级和毫秒级时间戳,每秒自动更新,支持一键复制。适合快速获取当前时间戳用于测试或记录。

2. 时间戳转日期 📅

输入时间戳,自动转换为可读的日期时间,提供:

  • 本地时间、UTC 时间、ISO 8601 格式
  • 相对时间(如"3天前")
  • 星期几、年中第几天、第几周

支持自动检测秒级/毫秒级格式,可选择不同时区显示。

3. 日期转时间戳 🔄

选择日期时间,快速获取对应的秒级和毫秒级时间戳。支持选择输入时区,确保转换准确。

特色亮点

  • 🌍 多时区支持: 覆盖全球主要时区(中国、日本、美国、欧洲等)
  • 🔍 智能检测: 自动识别时间戳格式
  • 🌐 双语界面: 中英文切换
  • 📱 响应式设计: 支持电脑、平板、手机
  • 🔒 隐私安全: 本地计算,不上传数据
  • 快速响应: Vue3 技术栈,性能优秀

使用场景

  1. 查看日志: 日志中的时间戳转换为可读时间
  2. 数据分析: 数据库导出的时间戳批量理解
  3. API 测试: 快速获取测试用的时间戳参数
  4. 跨时区协作: 转换不同时区的时间,避免混乱

技术实现

工具采用现代化前端技术栈:

  • 框架: Vue 3 + Nuxt 3
  • UI 组件: TDesign Vue Next
  • 样式: Tailwind CSS
  • 国际化: Vue I18n

所有计算在浏览器本地完成,不会上传任何数据到服务器,保证隐私安全。

使用小技巧

  1. 快速复制: 每个结果旁都有复制按钮
  2. 自动刷新: 可关闭实时更新,手动刷新
  3. 当前时间: 点击"当前时间"按钮快速填入
  4. 格式检测: 不确定格式时选择"自动检测"

常见问题

Q: 时间戳会受时区影响吗?
A: 不会!时间戳基于 UTC,全球统一。同一时刻在不同时区显示不同,但时间戳相同。

Q: 为什么转换结果不对?
A: 检查是否混淆了秒级和毫秒级(相差1000倍),或时区设置不正确。

Q: 工具会保存我的数据吗?
A: 完全不会!所有计算在本地完成,不上传任何数据。

结语

时间戳转换器是开发者和数据工作者的必备工具。我用 Vue3 开发这个工具,希望能帮助更多人高效处理时间数据。工具完全免费、无广告、保护隐私,欢迎使用和分享!


技术栈: Vue 3 + Nuxt 3 + TDesign + Tailwind CSS
特点: 多时区 | 智能检测 | 双语支持 | 隐私安全
开发: 个人开发,持续维护中

感谢使用!🎉

PyTorch 的即时执行模式在原型开发阶段很方便,但在推理性能上存在明显短板。每个张量操作独立启动 kernel、独立访问显存,导致内存带宽成为瓶颈GPU 算力无法充分利用。

torch.compile 通过提前构建计算图来解决这个问题。它的核心策略是操作融合和缓冲区复用:第一次调用需要编译而之后的推理会快很多。在 PyTorch 官方的基准测试中,各种模型平均获得了 20%-36% 的加速。

即时执行意味着每个操作独立运行。一个 32 层、每层 100 个操作的模型,前向传播一次就要触发 3200 次 kernel 启动,这些开销全部叠加到推理延迟里。

延迟飙升的根本原因是什么?内存才是即时执行成为瓶颈。Nvidia H100 能跑到 300+ TFLOPs但内存带宽只有约 3 TB/s。所以内存搬运的代价太高了,即时执行模式在规模化场景下根本撑不住。每个操作至少要做三次内存访问:从 VRAM 读输入张量、把中间结果写回 VRAM、再从 VRAM 读权重。

比如说这个简单的表达式

x = torch.relu(torch.matmul(a, b) + c)

,即时执行模式下至少要六次内存传输:分别读 a、b、c,写矩阵乘法结果,读这个结果,写最终输出。内存带宽很快就被打满了,GPU 核心反而闲着。

所以问题的本质在于:独立的操作没法融合内存传输,造成大量冗余的 VRAM 访问。

生产环境下情况更糟。CPU 要处理成千上万的并发请求,花在 PyTorch 调度器上的时间可能比真正计算还多,吞吐量被严重拖累。

计算图

torch.compile 要解决的就是这种逐操作的开销。它会提前捕获整个计算图,核心靠两个组件:TorchDynamo 是一个 Python JIT 编译器,负责拦截字节码执行;TorchInductor 是后端,为 GPU 生成优化过的 Triton kernel,为 CPU 生成 C++ 代码。

PyTorch 里这个计算图叫 FX Graph,把操作表示成有向无环图(DAG)的节点。调用 torch.compile 时,TorchDynamo 分析 Python 字节码,生成 FX 图:节点是张量操作,边是数据依赖。

TorchInductor 拿到 FX 图后会做三件事:操作融合、内存规划、Triton 自动调优。

操作融合

还是前面那个例子

x = torch.relu(torch.matmul(a, b) + c)

。即时执行要六次 VRAM 传输,TorchInductor 把它们融合成一个 Triton kernel:先把 a、b、c 的分块加载到片上 SRAM(共享内存),在寄存器里算矩阵乘法,加法和 ReLU 也在寄存器里做完,最后只把结果写回 VRAM。

内存传输从 6 次降到 2 次,减少了 3 倍。

内存规划

TorchInductor 不会给每个中间结果都分配新内存,而是让生命周期不重叠的缓冲区共用同一块空间——和编译器复用寄存器是一个思路。这相当于在整个计算图上做全局缓冲区复用,对激活模式不规则的 Transformer 模型特别有效。另一个好处是压低峰值内存占用,能跑更大的 batch。

Triton 自动调优

Triton 自动调优会针对具体硬件和输入 shape,自动搜索最优的 kernel 配置:tile 大小、线程块维度、流水线深度这些参数都不用手动调。

结果

第一次调用时,大模型的编译可能要几分钟。但后续调用只需要几毫秒加载预编译好的 kernel。初始开销会在后续推理中摊销掉,特别适合生产场景下模型持续运行的情况。冷启动慢一点,后面每个请求都快很多。

PyTorch 官方在 165 种模型(Transformer、CNN、扩散模型都有)上做了基准测试,torch.compile 在 float32 精度下平均加速 20%,开启自动混合精度(AMP)后加速 36%。

用起来也很简单:

 import torch  

# For a model  
model = YourModel()  
compiled_model = torch.compile(model)  

# Or for a function, also enables Triton autotuning  
@torch.compile(backend="inductor")    
def forward_pass(x, weights):  
    return torch.relu(torch.matmul(x, weights))  

 output = compiled_model(input_tensor)

这就是 torch.compile 的大致原理:不再为每个操作单独启动 kernel、单独搬运数据,而是用一个 kernel 处理多个操作,共享内存缓冲区。内存瓶颈的影响被大幅削减,GPU 算力利用率上去了。

总结

这种加速具有普适性,不只对大语言模型有效,CNN、扩散模型等架构同样适用。torch.compile 的价值在于:它把原本需要手写 CUDA 或 Triton 才能实现的优化,封装成了一行代码的事情。对于生产环境下的推理服务,这是目前性价比最高的优化手段之一。

https://avoid.overfit.cn/post/271bbf42f4a946c3a92b8a9745e223db

作者:Aryan Keluskar

IEEE 近日发布了一项新的隐私标准:7012-2025 IEEE 标准,其昵称为 MyTerms。该标准定义了一套机制,用于在个人与在线服务提供方之间交换个人信息,并明确规定个人如何在交易过程中执行自身的隐私要求。

这一新标准在伦敦举行的一场活动上正式发布。Linux Journal 主编、《The Intention Economy: When Customers Take Charge》一书作者,同时也是 MyTerms 最早倡导者之一、该标准委员会联合主席的 Doc Searls 在现场介绍了这一标准。他将 MyTerms 描述为一种尝试:把我们在现实世界中对“隐私”的理解,与在线世界重新对齐。

Searls 表示,MyTerms 是一次“彻底翻转既有规则”的尝试。在这一模式下,用户成为“第一方”,不再被动接受网站条款,而是通过选择一套预先定义的条款,或使用一份可在不同网站间通用的默认协议,来主动决定自己如何与作为“第二方”的服务和产品提供者互动。

这些条款既可以用于持续性的关系,比如“仅用于服务交付”或“在支持数据可携带性的前提下用于服务交付”,也可以用于一次性的数据贡献场景,例如“用于 AI 训练和运行”“共享意图数据”等。一旦第二方接受相关条款,该条款即构成一份合同,在法律上约束其必须按照第一方的约定方式使用数据。

MyTerms 协议以机器可读格式定义,可以通过 HTTP 头信息或其他机制进行传输。协议双方都会各自保留一份完全一致的记录。

此处输入图片的描述

Searls 指出

这一机制将使 Cookie 提示彻底过时,并为个人与组织、客户与企业、需求与供给之间的关系奠定更加稳固的基础。

专注于数字身份与用户主导个人数据的公司 Customer Futures Ltd. 创始人 Jamie Smith 也表示:“我们已经接近当前在线模式的可接受极限。人们不断点击自己根本不会阅读的条款和条件,……而这些条款并没有真正完成它们应该承担的功能。”

Smith 认为,在当前这个由 AI Agent 逐渐替代人类进行上网操作的时代,MyTerms 显得尤为重要。这些 AI Agent 正在代表用户完成购物、预订、投诉处理、与各类机构互动等任务,而 Cookie 并不适用于“代理型商业”(agentic commerce)。在未来,每个 Agent 都将拥有自己的身份,并发布自己的条款,也就是合同。

他表示:

随着 Agent 成为新的客户渠道,MyTerms 将成为企业与 Agent 之间建立信任、并展开交互的重要基石。

Consumer Reports 实验工程负责人 Dan Leninger 描绘了一个未来场景:用户通过在线推荐系统购买一件家电。用户不仅会提交自己的 MyTerms 条款,还会附加额外条件,例如最高价格、最晚交付时间等。推荐系统随后只会展示那些已接受用户条款的商家,用户即可完成购买。

年龄验证平台 Yoti 的首席商务官 John Abbott 则讨论了 MyTerms 对儿童的潜在影响,尤其是在年龄验证方面。他认为,MyTerms 为个人根据自身年龄明确数据使用方式提供了一种更合适的机制,也有助于企业应对全球范围内日益严格的监管要求。

MyTerms 标准的制定历时约八年,其项目授权请求(Project Authorization Request,PAR)最早于 2017 年 12 月提出并获得批准。若想进一步深入了解 MyTerms,可以阅读 Doc Searls 围绕该主题撰写的大量文章。

原文链接:

https://www.infoq.com/news/2026/02/myterms-privacy-cookies/

视频点播业务,波形图如下,支持按流量计费/日 95 计费,用量预估 3000PB/年左右

日 95 计费要求:4400/Gbps 以下
流量计费要求 21/TB 1 系数 1024 进制
人民币支付
能做到这个价以下请留下联系方式
1770108812491.png

本文在鲲鹏920openEuler,从0开始使用Containerd部署k8s1.30.13+Ks。

1.说明

关于kt

kt是基于kk二次开发的产物,具备kk的所有功能。二开主要为适配信创国产化环境、简化arm部署过程和国产化环境离线部署。支持arm64amd64架构国产操作系统,已适配芯片+操作系统 如下。

kt新增功能点

  • 适配arm架构harbor和支持,部署体验与X86一样简单。
  • 离线环境部署增强。常用国际和国产操作系统依赖,内置到安装包中。已适配芯片和操作系统如下

    • ./kt init-os 一条命令完成操作系统依赖安装和初始化操作。
    • CPU:鲲鹏、飞腾、海光、兆芯、intel、amd等。
    • OS:Centos、Rocky Linux、Ubuntu、Debian、银河麒麟V10、麒麟V11、麒麟国防版、麒麟信安、中标麒麟V7、统信UOS、华为欧拉、移动大云、阿里龙蜥、TencenOS等。
  • 支持开启防火墙,只暴露30000-32767端口,其他k8s端口添加到节点白名单。

    • ./kt firewall 一条命令自动获取节点信息开白名单和防火墙。

kt版本更新和下载地址

  • kt: kt
  • 关注我不迷路

2.环境准备

服务器基本信息

<!-- 这是一张图片,ocr 内容为: -->

主机名架构OS配置IP
masterarm64openEuler2核4G192.168.0.101
nodearm64openEuler2核4G192.168.0.133
harborarm64openEuler2核4G192.168.0.232

2.1 上传离线制品

操作系统不需要安装docker,不需要设置selinux,swap等操作,全新的操作系统即可。

将离线制品、配置文件、kt和sh脚本上传至服务器其中一个节点(本文以master为例),后续在该节点操作创建集群。本文使用kt:3.1.13.1版本

<!-- 这是一张图片,ocr 内容为: -->

2.2 修改配置文件

根据实际服务器信息,配置到生成的config-sample.yaml

kind: Cluster
metadata:
  name: sample
spec:
  hosts:
  - {name: master, address: 192.168.0.101, internalAddress: 192.168.0.101, user: root, password: "123213", arch: "arm64"}
  - {name: node1, address: 192.168.0.133, internalAddress: 192.168.0.133, user: root, password: "123213", arch: "arm64"}
  - {name: harbor, address: 192.168.0.232, internalAddress: 192.168.0.232, user: root, password: "123213", arch: "arm64"}
  roleGroups:
    etcd:
    - master
    control-plane:
    - master
    worker:
    - node1
    # 如需使用 kt 自动部署镜像仓库,请设置该主机组 (建议仓库与集群分离部署,减少相互影响)
    # 如果需要部署 harbor 并且 containerManager 为 containerd 时,由于部署 harbor 依赖 docker,建议单独节点部署 harbor
    registry:
    - harbor
  controlPlaneEndpoint:
    ## Internal loadbalancer for apiservers 
    internalLoadbalancer: haproxy

    domain: lb.kubesphere.local
    address: ""
    port: 6443
  kubernetes:
    version: v1.30.14
    clusterName: cluster.local
    autoRenewCerts: true
    containerManager: containerd
  etcd:
    type: kubekey
  network:
    plugin: calico
    kubePodsCIDR: 10.233.64.0/18
    kubeServiceCIDR: 10.233.0.0/18
    ## multus support. https://github.com/k8snetworkplumbingwg/multus-cni
    multusCNI:
      enabled: false
  registry:
    type: harbor
    registryMirrors: []
    insecureRegistries: []
    privateRegistry: "dockerhub.kubekey.local"
    namespaceOverride: "kubesphereio"
    auths: # if docker add by `docker login`, if containerd append to `/etc/containerd/config.toml`
      "dockerhub.kubekey.local":
        username: "admin"
        password: Harbor@123 # 此处可自定义,kk3.1.8新特性
        skipTLSVerify: true # Allow contacting registries over HTTPS with failed TLS verification.
        plainHTTP: false # Allow contacting registries over HTTP.
        certsPath: "/etc/docker/certs.d/dockerhub.kubekey.local"
  addons: []

2.3 系统初始化

解压kt-centos.tar.gz文件后执行./kt init-os -f config-sample.yaml 已适配操作系统和架构见1.说明

该命令kt会根据配置文件自动判断操作系统和架构以完成所有节点的初始化配置和依赖安装。

<!-- 这是一张图片,ocr 内容为: -->

3 创建 Harbor私有仓库

3.1 创建镜像仓库

./kt init registry -f config-sample.yaml -a artifact-arm-k8s13014-ks3.4.1.tar.gz

此命令会在harbor节点自动安装dockerdocker-compose

<!-- 这是一张图片,ocr 内容为: -->

<!-- 这是一张图片,ocr 内容为: -->

3.2 创建harbor项目

<font style="background-color:rgb(255,245,235);">说明:</font>

<font style="background-color:rgb(255,245,235);">Harbor 管理员账号:</font><font style="background-color:rgb(255,245,235);">admin</font><font style="background-color:rgb(255,245,235);">,密码:</font><font style="background-color:rgb(255,245,235);">Harbor@123</font><font style="background-color:rgb(255,245,235);">。密码同步使用配置文件中的对应password</font>

<font style="background-color:rgb(255,245,235);">harbor 安装文件在 /opt/harbor<font style="background-color:rgb(255,245,235);"> 目录下,可在该目录下对 harbor 进行运维。</font>

创建 Harbor 项目

chmod +x create_project_harbor.sh && ./create_project_harbor.sh

<!-- 这是一张图片,ocr 内容为: -->

4 创建k8s和KubeSphere

./kt create cluster -f config-sample.yaml -a artifact-arm-k8s13014-ks3.4.1.tar.gz

此命令kt会自动将离线制品中的镜像推送到harbor 私有仓库

执行后会有如下提示,输入yes/y继续执行

<!-- 这是一张图片,ocr 内容为: -->

等待一段时间,直至出现熟悉的等待安装完成的小箭头>>--->

<!-- 这是一张图片,ocr 内容为: -->

期间可以另开一个窗口用以下命令查看部署日志

kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l 'app in (ks-install, ks-installer)' -o jsonpath='{.items[0].metadata.name}') -f

继续等待一段时间,可以看到在内核3.10.0上面使用containerd成功部署了1.30.14版本+ks

<!-- 这是一张图片,ocr 内容为: -->

5 验证

<!-- 这是一张图片,ocr 内容为: -->

ps:default-http-backend那个pod显示:ImagePullBackOff,没啥用,不需要理会。

登录页面

<!-- 这是一张图片,ocr 内容为: -->

集群管理

<!-- 这是一张图片,ocr 内容为: -->

集群节点

<!-- 这是一张图片,ocr 内容为: -->

监控告警

<!-- 这是一张图片,ocr 内容为: -->

集群信息

<!-- 这是一张图片,ocr 内容为: -->

节点情况

<!-- 这是一张图片,ocr 内容为: -->

配置文件默认只安装了监控,如果需要安装其他组件,可以自行在自定义资源中开启

<!-- 这是一张图片,ocr 内容为: -->

一、核心配置结构说明

containerd 2.x 对镜像仓库配置进行了结构化优化,所有相关配置均集中在 /etc/containerd/certs.d/ 目录下,遵循 "一仓库一目录" 的配置原则:

  • 每个镜像仓库(registry)对应一个独立目录,目录名需与仓库域名(或 IP 地址)完全一致
  • 每个目录下必须包含一个 hosts.toml 文件,用于定义仓库连接参数
  • hosts.toml 核心配置项:仓库服务地址(server)、操作权限(capabilities)、认证信息(auth)、证书验证开关(skip\_verify)

二、分步配置实战

1. 版本确认

首先通过以下命令验证 containerd 版本是否为 2.x 系列:

\\\[root@k8s-master ~]# containerd --version
containerd containerd.io v2.2.0 1c4457e00facac03ce1d75f7b6777a7a851e5c41

2. 创建配置目录

根据 Harbor 仓库地址创建对应的配置目录,目录名需与仓库域名(或 IP)严格匹配(示例中 Harbor 仓库地址为 harbor.liyb.com):

mkdir -p /etc/containerd/certs.d/harbor.liyb.com
注意:若仓库未使用域名(直接通过 IP 访问),可直接以 IP 地址作为目录名(如 /etc/containerd/certs.d/192.168.1.100

3. 编写 hosts.toml 配置文件

创建并编辑 hosts.toml 文件,配置仓库连接参数:

vi /etc/containerd/certs.d/harbor.liyb.com/hosts.toml

添加如下配置内容:

server = "https://harbor.liyb.com"

\\\[host."https://harbor.liyb.com"]
capabilities = \\\["pull", "resolve", "push"]  # 支持的操作:拉取、解析、推送
skip\\\_verify = true  # 自签名证书时启用(跳过证书验证)
\\\[host."https://harbor.liyb.com".auth]
username = "admin"  # Harbor 登录用户名
password = "Harbor12345"  # Harbor 登录密码

配置说明:

  • 若使用企业级可信证书,无需设置 skip\\\_verify = true,只需将 CA 证书文件放置到对应配置目录(如 /etc/containerd/certs.d/harbor.liyb.com/)即可
  • 权限配置可根据实际需求调整,例如仅需拉取镜像时可改为 capabilities = \\\["pull", "resolve"]

4. 确认主配置文件路径

检查 containerd 主配置文件 /etc/containerd/config.toml 中是否正确指定了仓库配置路径,确保以下配置项存在且无误:

toml

# /etc/containerd/config.toml
\\\[plugins."io.containerd.grpc.v1.cri".registry]
config\\\_path = "/etc/containerd/certs.d"  # 仓库配置目录路径(默认已启用)
注意:containerd 2.x 默认启用该配置,但生产环境部署时务必手动校验,避免路径配置错误导致配置失效

三、配置验证方法

1. 使用 nerdctl 验证(推荐)

通过 nerdctl 工具拉取 Harbor 仓库镜像,验证配置是否生效:

nerdctl pull harbor.liyb.com/prod/nginx:1.27

成功输出示例:

plaintext

harbor.liyb.com/prod/nginx:1.27: manifest-sha256:114dff0fc8ee3d0200c3a12c60e3e2b79d0920dd953175ecb78a0b157425b25e: done
config-sha256:1e5f3c5b981a9f91ca91cf13ce87c2eedfc7a083f4f279552084dd08fc477512: done
elapsed: 0.1 s
total: 0.0 B (0.0 B/s)

2. Kubernetes 节点验证

在 Kubernetes 节点上通过 crictl 工具拉取镜像(适用于 K8s 集群环境):

crictl pull harbor.liyb.com/prod/nginx:1.27
关键注意点:镜像名称必须显式包含 Harbor 仓库地址(完整格式:仓库地址 / 项目名 / 镜像名:标签),否则会导致 K8s Pod 拉取镜像失败

四、高频踩坑与解决方案

image.png

[大模型实战 03预备] 云端炼丹房 1:Google Colab 上手指南

核心摘要 (TL;DR)

  • 痛点:本地电脑显存不足,跑不动 7B 以上的大模型,或者运行速度如蜗牛。
  • 方案:利用 Google Colab 提供的免费 Tesla T4 GPU 算力。
  • 技巧:通过挂载 Google Drive,解决 Colab 运行时重置导致模型文件丢失的问题。
  • 目标:配置好云端环境,为下一篇“云端运行 RAG”打好地基。

前言

Ollama因为有llama.cpp库和量化技术的加成,是可以在cpu和更日常的电脑上运行的,但是性能是远比不上在专业的显存设备上的。
有高端显卡(NVIDIA 4090/5090/A100/H100),可以在自己的服务器上脱缰运行小规模的大模型。但是对于没有高端显卡设备的友人们也不用担心, 我们可以使用谷歌大善人带给我们的免费GPU算力:爱来自Google Colab。 本篇博文的主要目的就是提前带各位友人们从零上手Colab的核心操作,确保在我们后续的实战过程中的流畅操作。

1. Google Colab

一言概之,Google Colab = Jupyter Notebook + 云端服务器

  • Jupyter Notebook:我们知道python是一门动态脚本语言,意味着我们可以一边编写,一边以交互式的方式看到当前结果,然后还能继续往下写。Jupyter Notebook就是一种可以一边写代码,一边写文档,还能实时看到代码运行结果的交互式笔记。
  • 云端服务器:区别于在我们本地环境写代码时,代码在我们的本地电脑,换一台电脑就需要重新拉取代码运行,在云端服务器编码是在远程的服务器编码,我们通过自己的电脑,甚至手机或者任何能联网打开浏览器的设备,连接上远程的那台服务器进行代码编写和模型训练。会更为灵活,不受设备限制。

2. 快速介绍

2.1 访问与创建

  1. 咱们确保有一个Google账户,并且登录
  2. 访问Google Colab 官网,就会进入到一个欢迎界面
    进入Colab的欢迎页面截图
  3. 点击菜单栏上File->new notebook in drive创建新的笔记本
    Colab菜单栏打开File鼠标指向其下拉菜单new notebook in drive的截图
    创建新的notebook后的新notebook界面截图

2.2 界面介绍

在新的notebook界面,我们可以看到

  • 文件名:左上角“Untitled0.ipynb”的文件名,可以单击重命名,ipynb就是jupyter notebook的后缀名
    notebook界面重新重命名后的文件名截图
  • 单元格:页面中心一长条带一个▶按钮的就说单元格,也叫Cell,是我们的核心编码区域, Jupyter notebook的逻辑是“一段一段”执行代码,而非我们平常写代码时候写完一整个文件再执行。
    notebook界面中心单元格的截图
  • 快捷操作栏:在单元格上方的位置有一条快捷菜单栏,支持我们添加新的代码块(Code Cell)和文本块(Text Cell),运行全部单元格(Run All)。
    在单元格上方的快捷操作栏的截图
  • 左侧工具栏: 包含目录速览,查找替换,密钥管理,数据查看等等工具。
    左侧工具栏的截图
  • 变量和终端:这里的变量按钮可以查看执行到当前的变量信息,就不用去print变量了,很方便。终端按钮就和Linux终端一样,可以用来执行一些命令。
    最下方的变量按钮和终端按钮

3. 核心操作

在界面介绍时,咱们快速介绍了一下两种单元格:代码块文本块,接下来可以稍微多了解一点点这两种单元格

3.1 代码块

就是我们的主力战场,编写Python代码的地方,可以快速体验一下使用流程

  • 直接输入python代码,然后点击运行(那个▶按钮或者使用快捷键 Shift + Enter
    在代码块中写入代码后运行之后的界面截图
  • 可以看到代码块左侧有一个[1],一个绿色的√,代码块下方有输出的打印结果
    前面的序号标明代码块的执行顺序,因为我们可以乱序执行,执行完下方代码块再回来执行前面的代码块

3.2 文本块

jupyter notebook是支持直接渲染markdown格式的文档的,所以也有人直接用它当文档。相比于我们用注释去记录,markdown格式的文本块会更直观。

  • 点击上面的➕Text按钮(或者在当前单元格上方/下方中间浮现显示的快捷按钮)去新增一个文本块
  • Shift+Enter快捷键“运行/渲染”它,
    输入了# This is Title!!的文本块截图
    渲染之后的文本块截图

4. 开启免费GPU算力

默认状态下Colab是使用的CPU,我们接下来去开启GPU

  • 点击顶部菜单栏的Runtime(运行时)下拉菜单中的Change runtime type(更改运行时类型)
    点击Runtime下来菜单,鼠标指向Change runtime type的截图
  • 选择Hardware accelerator(硬件加速器)T4 GPU.
    进入change runtime type后鼠标选择T4GPU的截图
  • 弹出的窗口警告我们会断联当前运行时,切换到T4GPU的硬件,选择OK
    点击T4GPU后,弹出结束运行时的截图
  • 保存,然后会发现之前运行过的代码块失活了(前面框框里的数字消失了,所有运行过的代码块需要重新运行)
  • 我们来输入以下代码验证
!nvidia-smi

nvidia-smi的运行结果截图
从返回的表格结果中,能看到咱们的设备是TeslaT4。

在notebook代码块中以!开头即可运行命令,这里等效为在terminal中运行nvidia-smi
PS:除了切换文件夹得用%cd而不是!cd

5. 下载大模型

我们使用Colab主要是为了使用大模型以及训练大模型,对于Colab而言,模型的下载有个痛点:Colab是临时的,哪怕我们通过命令下载了好几个G的模型,甚至好几十G的模型,但是每次重置运行时的时候,这一切都会灰飞烟灭,消散如烟。为了避免每次都重新下载,浪费时间,我们可以通过挂在Google Drive来保存模型。

5.1 挂载Google Drive

  1. 我们运行以下代码
from google.colab import drive
drive.mount('/content/drive')
  1. 然后在弹出授权窗口中授权
    运行完挂载代码后,弹出的授权提示截图
  2. 就能在代码块下方看见已经成功挂载的打印信息
    成功挂载google drive后的打印信息截图

5.2 配置HuggingFace环境变量和Token

在下载受限模型(如 Llama 3)时,你需要 Hugging Face Token。

  1. Hugging Face Settings 获取 Token。
  2. 在 Colab 左侧钥匙图标(Secrets)里添加 HF_TOKEN

Colab 左侧 Secrets 面板配置 HF_TOKEN 的截图

5.3 指定缓存路径下载

因为咱们在Colab环境,是国外的魔法环境,我们可以直接使用hugging face来下载模型,我们接下来指定一下模型下载的缓存路径到挂载的Google Drive。

  1. 咱们先切回CPU环境,因为下载模型并不需要GPU,切回去可以节约一点咱们的额度。
  2. 输入以下代码然后运行
from google.colab import drive
import os

# 1. 挂载云盘
if not os.path.exists('/content/drive'):
    drive.mount('/content/drive')

# 2. 准备目录
cache_dir = "/content/drive/MyDrive/huggingface_cache"
os.makedirs(cache_dir, exist_ok=True)

# 3. 设置 Token (如果你在左侧 Secrets 设置了 HF_TOKEN,这里自动读取)
# 如果没设置,请手动把下行代码引号里换成你的 token,或者留空试下(Qwen 有时不需要)
my_token = os.getenv('HF_TOKEN') or ""

print("屏幕可能会静止 5-10 分钟,请盯着左边的小圆圈转动即可。")

cmd = f"huggingface-cli download Qwen/Qwen2.5-7B-Instruct --cache-dir {cache_dir} --quiet"
if my_token:
    cmd += f" --token {my_token}"

# 执行命令
result = os.system(cmd)

if result == 0:
    print("\n 下载成功!")
else:
    print("\n 下载失败,请检查网络或 Token。")
  1. 然后运行下面的命令检验模型是否下载完毕
# check disk usage (查看磁盘占用)
# -s: 汇总大小, -h: 人类可读格式 (GB/MB)
!du -sh /content/drive/MyDrive/huggingface_cache/models--Qwen--Qwen2.5-7B-Instruct

看到的结果应该是15G大小的文件
运行du -sh /content/drive/MyDrive/huggingface_cache/models--Qwen--Qwen2.5-7B-Instruct后的结果截图

一般情况下,建议模型下载和数据处理都在CPU模式下进行,然后处理完毕存入云盘. 4. 然后新建代码块,运行如下代码,来确认模型是否能够被识别

import os
import glob
from transformers import AutoConfig, AutoTokenizer

# 1. 设置你的缓存根目录
base_cache_path = '/content/drive/MyDrive/huggingface_cache'

# 2. 构造快照目录的通配符路径
# 结构通常是: base / models--ID / snapshots / <哈希值>
snapshot_pattern = os.path.join(
    base_cache_path,
    "models--Qwen--Qwen2.5-7B-Instruct",
    "snapshots",
    "*"  # 这里用 * 匹配那个随机生成的哈希文件夹
)

# 3. 寻找真实的文件夹路径
found_folders = glob.glob(snapshot_pattern)

if not found_folders:
    print(" 错误:找不到 snapshots 文件夹,请检查下载是否成功或路径是否正确。")
else:
    local_model_path = found_folders[0]

    print(f"锁定本地模型路径: {local_model_path}")
    print("正在尝试直接加载...")

    try:
        config = AutoConfig.from_pretrained(local_model_path)
        tokenizer = AutoTokenizer.from_pretrained(local_model_path)

        print("\n成功!模型可以被正确加载。")
        print(f"模型隐藏层维度: {config.hidden_size}")
        print(f"词表大小: {tokenizer.vocab_size}")

    except Exception as e:
        print(f"\n加载依然失败。可能是 Google Drive 的软链接失效了。")
        print(f"错误信息: {e}")

通过运行模型加载命令,显示模型成功加载的截图

05. 常见问题 (Q&A)

Q: CPU 和 GPU 跑大模型,性能差异到底有多大?
A: 差异巨大,就像法拉利拖拉机的区别。

  • CPU (中央处理器):像一个知识渊博的教授,计算能力强但只能一个一个任务串行处理。推理大模型时,它需要逐个计算矩阵乘法,生成一个字可能需要好几秒。
  • GPU (图形处理器):像一个由几千名小学生组成的方阵,虽然单人能力不如教授,但能同时进行大规模并行计算。大模型的本质是海量的矩阵运算,GPU 可以瞬间完成,生成速度通常是 CPU 的几十倍甚至上百倍。

Q: 那一台 RTX 4090 能运行多大的模型?能微调多大?
A: RTX 4090 拥有 24GB 显存,这是核心瓶颈。

  • 推理 (运行)

    • 4-bit 量化:显存占用 ≈ 参数量 × 0.7。4090 极限可以跑 30B - 34B 参数的模型(如 Yi-34B-Chat-Int4)。
    • 全精度 (FP16):显存占用 ≈ 参数量 × 2。4090 最多跑 10B - 12B 参数的模型。
  • 微调 (训练)

    • 全量微调:想都不要想,需要几百 GB 显存。
    • LoRA / QLoRA (轻量微调):这是咱们个人玩家的主流。4090 可以轻松微调 7B - 10B 的模型。

Q: 动态脚本语言 (Python) 和常规预编译语言 (C++/Java) 有什么区别?
A:

  • 预编译语言 (C++/Java):像写书。写完一整本书(代码),送去印刷厂(编译),最后出来成品书(可执行文件)。执行速度快,但修改麻烦,改一个字要重新印刷。
  • 动态脚本语言 (Python):像聊天。你说一句(写一行代码),解释器就执行一句。虽然执行速度稍慢,但胜在交互性极强。在数据科学和 AI 领域,我们需要频繁查看数据的中间结果(比如查看模型输出的张量形状),Python 的这种特性让它成为了 AI 领域的霸主。

Q: Colab 里的 T4, A100, TPU 都有什么差别?
A:

  • T4 (免费版标配):入门级推理卡,16GB 显存。跑 7B 模型推理没问题,微调 QLoRA 勉强够用。咱们薅羊毛主要就薅它。
  • A100 (付费版):顶级计算卡,40GB/80GB 显存。速度极快,显存极大,适合跑大参数模型或进行严肃的训练任务。Colab Pro/Pro+ 才能刷到。
  • TPU (Tensor Processing Unit):Google 专门为机器学习定制的芯片,处理矩阵运算比 GPU 更快,但生态和兼容性(PyTorch 支持)不如 Nvidia GPU 通用,上手门槛稍高。

本文作者: Algieba
本文链接: https://blog.algieba12.cn/llm02-1-online-environment-colab/
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

在跨境电商、海外营销或日常学习中,Google 账号(Gmail)一直是通往海外互联网世界的“基础设施”。

尤其是最近一年,随着 ChatGPT、Gemini、Claude、Midjourney 等顶尖 AI 模型的爆发式增长,国内用户对 Google 账号的需求呈现井喷之势。 想要体验最前沿的 AI 生产力工具,或是申请 API、使用第三方 AI 服务,一个稳定的 Google 账号往往是必不可少的“硬通货”和快捷登录(SSO)的“金钥匙”。

然而,许多朋友发现,现在的 Google 账号变得前所未有的“娇气”——

  • 刚为了用 AI 注册的新号,还没登录进去就显示“已停用”;
  • 登录一下就要手机验证,验证了还通过不了;
  • 甚至用了几年的老号,也在这波风控潮中莫名“阵亡”。

这背后的核心原因,正是因 AI 需求暴增导致的 Google 风控系统(Risk Control)全面应激升级。为了抵御大规模的脚本注册和滥用,Google 祭出了更智能的算法,全自动无差别地检测账号的“异常行为”。

今天就来复盘:账号为什么容易被封?如何科学保号?万一被封了该如何申诉?


第一部分:排雷篇——你的账号为什么会被封?

根据 Google 最新的风控逻辑,封号原因主要归结为以下五大类。其中,网络环境的不纯净是绝大多数人“踩雷”的根本原因。

1. 登录环境与 IP 的“致命伤” (最核心原因)

Google 的安全系统对 IP 地址及其背后的行为轨迹极度敏感。

  • IP 频繁“瞬移”: 如果你的账号在短时间内跨越国界(例如:上午在美国 IP,下午变成了日本 IP),系统会直接判定为账号被盗或异常。
  • 使用了“被污染”的共享 IP: 很多廉价或免费的代理工具(机场)使用的是万人共享的 IP 池。如果这个 IP 之前被人用来大规模注册账号薅羊毛,你的账号会因为“连坐机制”被牵连封禁。
  • 设备环境混乱: 频繁在手机、电脑、平板间切换,且混合使用家庭 WiFi、热点和公共网络,极易触发安全警报。

2. 成品号的“源头原罪”

为了快速使用 AI 工具,很多人选择购买现成的“成品号”,但这风险极高:

  • 批量注册的风险: 号商通常利用脚本和虚拟信用卡批量生成账号。一旦关联的虚拟卡被风控,成千上万个关联账号会瞬间失效。
  • 历史污点: 你买到的号可能已经被用过(如刷 YouTube 播放量),早已在系统的“黑名单”边缘。

3. 像“机器人”一样的机械操作

Google 的算法非常擅长识别非人类行为:

  • 高频操作: 短时间内大量发邮件、加好友、建频道。
  • 缺乏真实互动: 新号没有经过“养号”,上来就直接授权登录各类 AI 平台,没有正常的搜索浏览记录。
  • 设备群控: 同一台电脑/手机上频繁切换登录多个账号。

4. 内容与政策违规

这是硬伤,包括在 YouTube 或 Blogger 发布违规内容(暴力、色情、侵权),或发送垃圾邮件骚扰他人。

5. 账号长期处于“亚健康”状态

  • 僵尸号: 注册超过 3 个月未登录。
  • 安全裸奔: 未开启两步验证(2FA),导致账号权重极低。

第二部分:实操篇——如何科学“保号”?

保号的核心逻辑只有一个:让 Google 相信你是一个真实、稳定、高价值的用户,而不是一个等待注册 AI 接口的机器人。

1. 打造“铜墙铁壁”的网络环境

  • 固定 IP 是王道: 尽量使用静态住宅 IP,拒绝频繁变动的动态 IP。
  • 拒绝“瞬移”: 保持登录地区的一致性。
  • 物理隔离: 坚持 “一机一号” 原则。如果有多个账号,强烈建议使用指纹浏览器(如 AdsPower)来隔离设备指纹,防止关联。

2. 完善安全设置(提升权重)

  • 开启两步验证 (2FA): 这是保号的护身符。建议使用 Google Authenticator App,比短信验证更安全稳定。
  • 绑定辅助信息: 务必填写真实的辅助邮箱和手机号,这是找回账号的唯一救命稻草。
  • 清理授权: 定期检查并撤销不明来源的第三方应用授权。

3. 模拟真人的“碎片化”操作

不要像机器一样批量工作。刷 YouTube 时记得点赞、收藏;搜索时模拟正常浏览。敏感操作(如改密、绑卡)之间至少间隔 24 小时。

4. 科学养号“三部曲”

对于新号或刚买的成品号,请严格执行以下养号流程,再通过 OAuth 登录 AI 工具:

  • 初期(1-7天):建立信任。 每天登录 30-60 分钟,仅做基础搜索、浏览新闻、看 YouTube 视频(>5分钟)。严禁修改密码和资料。
  • 中期(8-30天):丰富行为。 开始点赞评论、使用 Google Drive 上传文件、用 Maps 查路线。此时可完善两步验证。
  • 长期(30天+):维持权重。 每周保持 3-5 次活跃登录,尝试发布原创内容(博客或视频)以提升账号贡献值。

第三部分:急救篇——账号被封了如何申诉?

如果不幸收到“账号已停用”的通知,不要慌张,按照以下步骤进行“心肺复苏”。

1. 寻找申诉入口

  • 情况 A:未收到邮件,登录受阻。
    手动打开 Google 账号恢复页面。这通常需要经历繁琐的人机验证(可能长达半小时),需要极大的耐心。
  • 情况 B:收到停用通知邮件(推荐)。
    直接点击邮件中的申诉链接。这种方式可以跳过人机验证,直接进入正题,成功率通常更高。

2. 撰写“高成功率”的申诉理由

在填写申诉表单时,请遵循以下原则:

  • 使用英文: 虽然中文也可以,但英文模板能让全球审核团队更快处理。
  • 态度诚恳: 简短、真实、礼貌。
  • 话术建议: 强调账号对你工作/学习的重要性。如果你使用了代理,可以委婉表达为“因出差或网络加速需求导致环境波动”,并保证自己一直遵守 Google 服务条款。
  • 联系邮箱: 留一个干净、常用的非 Gmail 邮箱接收结果。

3. 等待与后续

  • 审核周期: 通常为 1-2 天。在此期间,切勿频繁尝试登录,否则会增加恢复难度。
  • 坚持尝试: 如果第一次被拒,不要气馁。稍微修改措辞,补充更多细节(如设备变化说明),尝试申诉 2-3 次。不同的审核人员尺度可能不同。

4. 解封后的加固

账号一旦找回,立即在干净、稳定的网络环境下登录,完成手机/邮箱验证,并立刻开启两步验证,防止“二进宫”。


最后总结:
在 AI 时代,Google 账号不仅仅是一个邮箱,更是我们接触世界前沿科技的身份 ID。“少折腾、多稳定、真实化”是保号的九字真言。与其被封后焦头烂额地申诉,不如现在就动手检查一下你的账号环境和安全设置吧!

本文由mdnice多平台发布