You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js App Router中如何实现单例Unleash Client实例

Unleash服务器超时与Next.js实例过多问题

我的Unleash服务器出现超时问题,Next.js应用每秒请求数(RPS)最高仅约150,数值过低。我怀疑这是应用存在多个Unleash Client实例导致的。

我按照@unleash/nextjs文档在Next.js App Router中做了如下实现:

Dependencies
- "@unleash/nextjs": "^1.4.1"
- "next": "14.2.3" (使用App Router)


export default async function getFlag(): Promise<{
  isEnabled: (name: string) => boolean
  getVariant: (name: string) => IVariant
}> {
  const sessionId = `${Math.floor(Math.random() * 1_000_000_000)}`

  try {
    const definitions = await getDefinitions({
      fetchOptions: {
        next: { revalidate: 0 },
      },
    })

    const { toggles } = evaluateFlags(definitions, {
      sessionId,
    })
    const flags = flagsClient(toggles)

    return flags
  } catch (e) {
    
  }
}


export default async function Layout(props: PropsWithChildren) {
  const flag = await getFlag();

// 在这里使用flag
  return <SomeComponent />;
}

我的问题:

  1. 当前实现是否确实创建了多个客户端实例,进而导致Unleash服务器崩溃?
  2. 如果是这样,如何在Next.js SSR应用的所有路由处理器中仅使用单个客户端实例?

问题解答

1. 当前实现是否会创建多实例并导致服务器超时?

是的,你的实现确实会引发大量不必要的Unleash请求,进而拖垮服务器:

  • getDefinitions设置了revalidate: 0,意味着每次请求都会重新拉取全量的feature flag定义,完全没有缓存复用。
  • 每个请求进入Layout组件时,都会调用getFlag(),每次调用都会创建新的flagsClient实例,同时触发一次对Unleash服务器的请求。
  • 当RPS达到150时,Unleash服务器每秒要处理150次全量拉取请求,很容易被打满,最终出现超时。

2. 如何实现单实例与请求复用?

你需要结合Next.js的缓存机制和单例模式,复用flag定义和客户端逻辑,具体操作如下:

步骤1:创建单例Unleash工具类

在项目中新建utils/unleash.ts文件,实现缓存与单例逻辑:

import { getDefinitions, evaluateFlags, flagsClient, IVariant } from '@unleash/nextjs';

// 缓存flag定义,避免重复请求
let cachedDefinitions: Awaited<ReturnType<typeof getDefinitions>> | null = null;
// 缓存过期时间(可根据业务调整,示例为5分钟)
const CACHE_TTL = 5 * 60 * 1000;
let lastFetchTime = 0;

export async function getUnleashFlags(sessionId: string) {
  const now = Date.now();
  // 缓存失效时重新拉取定义
  if (!cachedDefinitions || now - lastFetchTime > CACHE_TTL) {
    cachedDefinitions = await getDefinitions({
      fetchOptions: {
        next: { revalidate: CACHE_TTL / 1000 }, // 转换为秒数设置revalidate
      },
    });
    lastFetchTime = now;
  }

  const { toggles } = evaluateFlags(cachedDefinitions, { sessionId });
  return flagsClient(toggles);
}

步骤2:在Layout中复用工具类

修改你的getFlag函数和Layout组件:

import { getUnleashFlags } from '@/utils/unleash';

export default async function getFlag(): Promise<{
  isEnabled: (name: string) => boolean
  getVariant: (name: string) => IVariant
}> {
  // 建议用用户ID/稳定匿名ID替代随机sessionId,保证同用户评估结果一致
  const sessionId = `${Math.floor(Math.random() * 1_000_000_000)}`;
  try {
    return await getUnleashFlags(sessionId);
  } catch (e) {
    // 异常降级,返回默认值避免影响业务
    return {
      isEnabled: () => false,
      getVariant: () => ({ name: 'disabled', enabled: false }) as IVariant
    };
  }
}

export default async function Layout(props: PropsWithChildren) {
  const flag = await getFlag();
  // 使用flag
  return <SomeComponent />;
}

额外优化建议

  • 替换随机sessionId:如果用户有登录态,用用户ID;无登录态时用Next.js的cookies生成稳定匿名ID,确保同用户的flag评估结果一致,减少不必要的变体计算。
  • 调整CACHE_TTL:如果你的feature flag不会频繁变更,可以把缓存时间设得更长(比如10-15分钟),进一步降低Unleash服务器的请求压力。

内容的提问来源于stack exchange,提问作者yosiawan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:35:08