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

推荐订阅源

cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
L
LangChain Blog
雷峰网
雷峰网
WordPress大学
WordPress大学
S
Security Affairs
腾讯CDC
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Hacker News: Ask HN
Hacker News: Ask HN
T
Tailwind CSS Blog
SecWiki News
SecWiki News
罗磊的独立博客
The Last Watchdog
The Last Watchdog
博客园 - 三生石上(FineUI控件)
N
Netflix TechBlog - Medium
Hugging Face - Blog
Hugging Face - Blog
T
Tor Project blog
V
Vulnerabilities – Threatpost
Microsoft Azure Blog
Microsoft Azure Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
GbyAI
GbyAI
M
MIT News - Artificial intelligence
Help Net Security
Help Net Security
MongoDB | Blog
MongoDB | Blog
AWS News Blog
AWS News Blog
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - Franky
Security Latest
Security Latest
G
GRAHAM CLULEY
C
CERT Recently Published Vulnerability Notes
H
Heimdal Security Blog
Recent Announcements
Recent Announcements
Apple Machine Learning Research
Apple Machine Learning Research
W
WeLiveSecurity
The Cloudflare Blog
B
Blog RSS Feed
B
Blog
Vercel News
Vercel News
T
Threatpost
小众软件
小众软件
H
Help Net Security
Jina AI
Jina AI
T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News

博客园 - Hey,Coder!

httphelper封装 正交软件架构 docker postgresql17 主从复制 rabbitmq总结与c#示例 docker rabbitmq quartz dashboard docker consul registrator 服务自动注册consul 微服务动态扩容 ubuntu24.04 安装docker RAG 检索增强生成 软考 - 架构设计师 知识点总结 c# MailKit3.4.3 发送邮件、附件 nginx 反向代理postgresql c# 信号量 elsa 3.5 中间件记录最后执行的节点数据 xp密钥 c# System.Text.Json 反序列化Dictionary<string,object>时未转换基础类型的处理方法 docker配置代理 openclaw 接入 LMStudio的模型服务 wpf canvas 移动 缩放 windows平台openclaw搭建 wpf scrollerview触摸滚动 c# 动态切换sqlsugar连接字符串 c# Elastic.Clients.Elasticsearch 动态查询 c# es 封装Elastic.Clients.Elasticsearch c# scrollerview滚动到指定元素位置 WPF 重写Expander 基于elsa工作流封装一套变量、组件的体系 常用活动重写 DynamicExpresso 轻量级动态表达式库 c# elsa 3.5.2 程序化工作流常用功能及自定义中间件 leaflet docker 连接老版本的sqlserver ssl错误 c# quartz 动态创建任务 控制任务运行和停止 依赖注入 cefsharp 模拟点击 批量重置数据库的数据 docker 复制远程镜像本地并创建容器 c# newtonsoft dynamic
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 即可在微服务下稳定运行。