惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
博客园 - 司徒正美
D
DataBreaches.Net
J
Java Code Geeks
月光博客
月光博客
MyScale Blog
MyScale Blog
博客园 - Franky
Jina AI
Jina AI
量子位
博客园 - 聂微东
博客园 - 叶小钗
M
MIT News - Artificial intelligence
N
Netflix TechBlog - Medium
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
SegmentFault 最新的问题
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
博客园 - 【当耐特】
Blog — PlanetScale
Blog — PlanetScale
I
InfoQ
小众软件
小众软件
B
Blog RSS Feed
Hugging Face - Blog
Hugging Face - Blog
博客园_首页

博客园 - Hey,Coder!

微信小程序 web方案实现调音器功能 systemctl slice配置docker最大占用cpu 内存 pipenv环境变量配置 .net8验证码 数据库差异对比工具 docker部署prometheus grafana pgexporter Alertmanager ubuntu24 换源 ssh配置 ubuntu24 Harbor部署 ubuntu24 kvm部署 cockpit web管理界面 .net api代理 Ubuntu 24.04 DMAR IOMMU 报错修复 uniapp iOS App 上架 vs文件软链接 Docker 网络别名 .net8通用中间件处理公共返回值结构 .net环境下OpenTelemetry Body 读取注意事项 jaeger界面显示异常 .net opentelemetry collector jaeger c# opentelemetry 自定义export c# opentelemetry 自定义export c# 生成对象的初始化代码 Elasticsearch ILM 与 Data Stream 概念及实例说明文档 portainer httphelper封装 正交软件架构 docker postgresql17 主从复制 rabbitmq总结与c#示例 docker rabbitmq quartz dashboard docker consul registrator 服务自动注册consul 微服务动态扩容
Ocelot + Consul + SignalR 粘性会话
Hey,Coder! · 2026-06-22 · via 博客园 - Hey,Coder!

Ocelot + Consul + SignalR 粘性会话

一、核心结论(速览)

问题 答案
是否需要 Hub 返回 Cookie? ❌ 不需要
Cookie 由谁生成? Ocelot 网关
Cookie 值是什么? ✅ Ocelot 生成的随机 GUID
实例标识来源? Consul 返回的 Host:Port
下游服务是否感知? ❌ 完全无感知
SignalR 是否还需要 Redis? ✅ 多实例广播仍需

二、底层工作原理

Ocelot 的 CookieStickySessions 负载均衡器在网关层完全接管了会话亲和性,其执行流程如下:

  1. 首次请求:客户端不带粘性 Cookie,Ocelot 向 Consul 查询 ServiceName 对应的健康实例列表(如 192.168.1.10:5000, 192.168.1.11:5000)。
  2. 实例选择:Ocelot 按内置策略(通常是 RoundRobin)选中一个实例(如 192.168.1.10:5000)。
  3. 映射存储:Ocelot 生成一个随机 GUID(如 abc123),并在内存字典中建立映射:{ "abc123" -> "192.168.1.10:5000" }
  4. 下发 Cookie:网关在 HTTP 响应头中写入 Set-Cookie: Ocelot_SignalR_Affinity=abc123
  5. 后续请求:浏览器自动携带该 Cookie,Ocelot 查字典找到对应实例并固定转发,确保 SignalR 的 /negotiate 和 WebSocket 升级落在同一台机器。

关键点:Cookie 的值是 Ocelot 的随机 Key,绑定的目标是 Consul 的具体节点地址,而非服务名。

三、完整配置文件

1. ocelot.json(网关配置)

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/hubs/chat/{*any}",
      "UpstreamHttpMethod": ["Get", "Post", "Put", "Delete"],
      "DownstreamPathTemplate": "/hubs/chat/{*any}",
      "DownstreamScheme": "http",
      "ServiceName": "signalr-service",
      "LoadBalancerOptions": {
        "Type": "CookieStickySessions",
        "Key": "Ocelot_SignalR_Affinity",
        "Expiry": 3600000
      }
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "Scheme": "http",
      "Host": "consul",
      "Port": 8500,
      "Type": "Consul"
    },
    "BaseUrl": "http://localhost:5000"
  }
}

2. Program.cs(Ocelot 网关启动项)

using Ocelot.DependencyInjection;
using Ocelot.Middleware;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOcelot();

var app = builder.Build();
app.UseWebSockets();          // 必须启用 WebSocket
await app.UseOcelot();
app.Run();

3. SignalR 客户端(JavaScript)

⚠️ 必须配置 withCredentials: true,否则浏览器不会保存/发送网关下发的 Cookie。

const connection = new signalR.HubConnectionBuilder()
    .withUrl("http://localhost:5000/hubs/chat", {
        withCredentials: true
    })
    .withAutomaticReconnect()
    .build();

async function start() {
    try {
        await connection.start();
        console.log("SignalR Connected.");
    } catch (err) {
        console.error(err);
        setTimeout(start, 5000);
    }
};
connection.onclose(async () => { await start(); });
start();

4. SignalR 服务端(Redis Backplane)

即使有了 Sticky Session,为了跨实例广播消息,仍需配置 Redis。

// Program.cs of SignalR Service
builder.Services.AddSignalR()
    .AddStackExchangeRedis("redis:6379", options =>
    {
        options.Configuration.ChannelPrefix = "MyApp:SignalR";
    });

四、Consul 服务注册示例

确保 Consul 中注册的地址是 Ocelot 能直接访问的 IP 或容器名。

{
  "ID": "signalr-node-1",
  "Name": "signalr-service",
  "Address": "signalr-svc-1",
  "Port": 5000,
  "Check": {
    "HTTP": "http://signalr-svc-1:5000/health",
    "Interval": "10s"
  }
}

五、注意事项

场景 说明
Ocelot 多实例部署 ⚠️ Ocelot 的粘性字典存在本机内存,若前面有 LB,需在 Nginx/K8s 再做一层 Sticky。
跨域 (CORS) 网关和 SignalR 服务必须配置 AllowCredentials(),且不能设置 AllowAnyOrigin()
实例重启/下线 若 Consul 摘除实例,Ocelot 内存映射失效,客户端需重连并获取新 Cookie。
LongPolling 同样依赖 Cookie 粘性,确保每次轮询请求都带上 Cookie。
Docker/K8s 确保 DownstreamHostAndPorts 或 Consul 注册的是容器网络可达地址。

六、一句话总结

SignalR 的会话粘性完全由 Ocelot 网关通过 Set-Cookie 实现,基于 Consul 返回的具体实例地址进行绑定,Hub 无需编写任何 Cookie 逻辑,只需配合 Redis Backplane 即可在微服务下稳定运行。