1. 背景介绍

基于websocket的及时通信中,客户端与服务端建立ws连接后,服务端将业务继续传递到下一级业务服务系统Business server后,下一级服务系统处理完毕后,要将结果反馈给客户端,而此时的客户端ws服务器存在多个实例时,处理方式上存在几种策略:

1) 比较常见的是基于ip哈希;

2)基于参数的hash;

这些,一般情况下可以满足业务需求,但是呢,在ws服务动态增减的时候,可能就会出现消息丢失。例如:当前有3台websocket的tomcat服务器,有一个ws连接建立在tomcat1上面,基于轮询的模式下,且websocket前端采用了心跳机制时,客户端的重连机制会让这个客户端连接转而连接到其他两个tomcat当中之一,例如tomcat2,而客户是无感知的, 请参照websocket连接的后台反向代理问题。若这个时候Business server回调及时通信系统IM后(HTTP),不做特别处理,在轮询的方式下(或者hash方式),会出现消息去往的websocket的tomcat服务器不是这个回复消息该去往的tomcat服务器。导致回复消息无法到达该客户的websocket连接通道上,从而出现消息丢失的问题。

如上图所示:客户请求如红色线条,起初,经过LB-》nginx1-》websocket server1-》other business server. 但是处理过程中,websocket server1宕机了,或者其他什么原因,不能对外服务了,客户端的心跳机制,使得websocket重连,连到了websocket server2上了。此时,other business server处理完了客户的请求,回调IM系统,投递客户请求的答案,有可能出现上图绿色的箭头所示,这个时候,该客户的消息是没有办法投递出去的,因为websocket的长连接是在客户端和websocket server2.

即使还有一种,就是基于redis的订阅发布,进行消息回传给ws服务器,这种做法,要增加redis服务器的压力,且可能存在多次发布操作,性能不好,放弃。

这里要介绍的是,基于http的接口调用,通过url后面带上query信息,指定ws所在的服务器的ip和port信息。然后在nginx上做特殊配置,实现消息回传时,指定调用ws服务器集群中的服务器,因为这个服务器上存在websocket的连接实例。

nginx的配置如下:

upstream ims_svr {
server 10.130.215.143:;
server 10.130.215.144:;
} #用于BI回调IMS系统定位消息具体去往那个tomcat服务,涉及websocket的连接要原路来还要原路回去
upstream
10.130.215.1438080 {
server 10.130.215.143:8080;
}
upstream 10.130.215.1448080 {
server 10.130.215.144:8080
;
}
server {
listen ;
server_name localhost; #charset koi8-r; #access_log logs/host.access.log main;
default_type text/html; location / {
root html;
index index.html index.htm;
} location /IMS {
proxy_pass http://ims_svr;
proxy_set_header Host $host:$server_port;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
} location ~ /IMS/(replyMessage|callback) {
proxy_pass http://$arg_ip$arg_port;
proxy_set_header Host $host:$server_port;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

上面的配置中,技巧就在于获取query中的ip字段和port字段的值,拼接在一起构成upstream的block名称。在proxy_pass的代理下,就可以去往特定服务器,即连接有websocket连接的那个tomcat上。

注意:这里你可能会说,websocket server同样会出现宕机啊,在other Business server回调的时候,对的。我们可以在other Business server和websocket server之间搭建redis服务器,作为信息中心节点,通过visistorId+sessionid作为redis的key,ip_port值作为这个key对应的键值,当other Business server回调IM前,获取一下这个visistorId在当前sessionid下的websocket建立在那个websocket server上,即获取IP和Port信息。在回调的HTTP接口的URL部分附着query信息,例如?ip=1.1.1.1&port=8080.

如上图所示的案例,客户请求经过LB-》nginx1-》websocket server1-》other business server-》LB-》nginx2-》websocket server1-》resp去往客户端(基于websocket长连接),因为HTTP请求,在LB上是基于轮询的,所以,HTTP回调的response信息,也可能是经过nginx1的,这个时候,基于我们的nginx配置规则,依然会使得resp反馈去往websocket server1上,保障resp响应一定是在websocket连接建立的通道上回复给客户端,确保resp消息不会丢失。

测试案例截图:

到此,整个方案介绍完毕,是不是觉得nginx很厉害?的确,nginx现在的web应用领域占有率非常高。

另外,这里要注意一个小细节:就是nginx获取query部分的变量时,$arg_<xxx>这里的xxx部分,不要出现下划线“_”了,否则会导致query部分的变量获取不到,我经历过这个血的教训

例如我开始的配置如下:

upstream ims_svr {
server 10.130.215.143:;
server 10.130.215.144:;
} #用于BI回调IMS系统定位消息具体去往那个tomcat服务,涉及websocket的连接要原路来还要原路回去
upstream 10.130.215.143_8080 {
server 10.130.215.143:;
}
upstream 10.130.215.144_8080 {
server 10.130.215.144:;
} server {
listen ;
server_name localhost; #charset koi8-r; #access_log logs/host.access.log main;
default_type text/html; location / {
root html;
index index.html index.htm;
} location /IMS {
proxy_pass http://ims_svr;
proxy_set_header Host $host:$server_port;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
} location ~ /IMS/(replyMessage|callback) {
proxy_pass http://$arg_ip_$arg_port;
proxy_set_header Host $host:$server_port;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

测试过程中,nginx后台报错如下:

access.log

10.130.207.217 - - [/Mar/::: +] "POST /IMS/replyMessage?ip=10.130.215.143&port=8080 HTTP/1.1"   "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"

error.log

// :: [error] #: * no resolver defined to resolve 8080, client: 10.130.207.217, server: localhost, request: "POST /IMS/replyMessage?ip=10.130.215.143&port=8080 HTTP/1.1", host: "10.130.207.217"

这里显然是$arg_ip_$arg_port值没有计算对。根本原因是$arg_ip_搞错了

切记,切记nginx的query的arg取值规则。

HTTP请求回调IM系统LB,确保服务定向调用的更多相关文章

  1. win10系统Mysql5.7服务启动报:"1053错误:服务没有及时响应启动或控制请求"

    win10安装Mysql5.7: MySQL压缩包解压后,在目录下增加my.ini配置文件 [mysqld] port = basedir=D:\Mysql datadir=D:\Mysql\data ...

  2. SpringMVC中使用Ajax POST请求以json格式传递参数服务端通过request.getParameter("name")无法获取参数值问题分析

    SpringMVC中使用Ajax POST请求以json格式传递参数服务端通过request.getParameter("name")无法获取参数值问题分析 一:问题demo展示 ...

  3. Linux系统自带服务罗列

    /ect/services 文件列出了系统详细的服务 红色字体为常用服务 acpid ACPI(全称 Advanced Configuration and Power Interface)服务是电源管 ...

  4. Java生鲜电商平台-生鲜系统中微服务架构设计与分析实战

    Java生鲜电商平台-生鲜系统中微服务架构设计与分析实战 说明: Java生鲜系统中微服务的拆分应该如何架构设计与分析呢?以下是我的实战中的设计与经验分析. 目录 1. 微服务简介2. 当前现状3. ...

  5. JWT对SpringCloud进行系统认证和服务鉴权

    JWT对SpringCloud进行系统认证和服务鉴权 一.为什么要使用jwt?在微服务架构下的服务基本都是无状态的,传统的使用session的方式不再适用,如果使用的话需要做同步session机制,所 ...

  6. 《即时消息技术剖析与实战》学习笔记11——IM系统如何保证服务高可用:流量控制和熔断机制

    IM 系统的不可用主要有以下两个原因: 一是无法预测突发流量,即使进行了服务拆分.自动扩容,但流量增长过快时,服务已经不可用了: 二是业务中依赖的这些接口.资源不可用或变慢时,比如发消息可能需要依赖& ...

  7. 『学了就忘』Linux服务管理 — 75、Linux系统中的服务

    目录 1.服务的介绍 2.Windows系统中的服务 3.Linux系统中服务的分类 4.独立的服务和基于xinetd服务的区别 5.如何查看一个服务是独立的服务还是基于xinetd的服务 (1)查看 ...

  8. 通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存

    很久没有更新dapr系列了.今天带来的是一个小的组件集成,通过多级缓存框架来实现对服务的缓存保护,依旧是一个简易的演示以及对其设计原理思路的讲解,欢迎大家转发留言和star 目录:一.通过Dapr实现 ...

  9. ubuntu 12.04 "系统的网络服务与此版本的网络管理器不兼容

    ubuntu 12.04 "系统的网络服务与此版本的网络管理器不兼容“ 2013-05-10 21:18 2271人阅读 评论(0) 收藏 举报 今天上午在实验室一顿乱整,不知道整坏了什么, ...

随机推荐

  1. busybox devmem 直接获取、修改内存信息

    /********************************************************************** * busybox devmem 直接获取.修改内存信息 ...

  2. Factor Graph因子图

    参考链接1: 参考链接2: 参考ppt3: Factor Graph 是概率图的一种,概率图有很多种,最常见的就是Bayesian Network (贝叶斯网络)和Markov Random Fiel ...

  3. 20155219&20155224 《信息安全系统设计基础》实验二 固件程序设计

    实验二 固件程序设计-1-MDK 0. 注意不经老师允许不准烧写自己修改的代码 1. 两人(个别三人)一组 2. 参考云班课资源中"信息安全系统实验箱指导书.pdf "第一章,1. ...

  4. 为什么会用let that=this

    问题一:不知道楼主有没有接触过jquery jquery里边有一个特别典型的例子能说明用_this的作用$("#btn").click(function(){ var _this ...

  5. day 016 面向对象---类与类的关系

    ---恢复内容开始--- 一  依赖关系(最轻的一种关系,在方法中引入另一个类的对象) class Elephant: def __init__(self,name): self.name=name ...

  6. PTA——简单计算器

    PTA 7-20 简单计算器 #include<stdio.h> int main() { int a,b; char c; scanf("%d",&a); w ...

  7. JavaSE笔记

    this关键字 哪个对象调用方法,方法定义中的this即为该对象的引用! static关键字 使用static声名的成员变量为静态成员变量,在第一次使用的时候被初始化,static成员变量只有一份 使 ...

  8. javascript json 判断项目 是否存在不存在插入foreach 组合 输出

    var a = []; var i; a.push({ key: "key1", value: 23 }); a.push({ key: "key2", val ...

  9. ACM C++

    杭电oj 2000 sort(a,a+sizof(a)); 对数组a中元素进行排序,参数1,2分别为要排序数组首末地址 故sort句换成下行也通过 sort(a,&a[3]); ac代码 // ...

  10. linux----CenterOS7中在线安装jdk

    summary: 一直以来,都在windows上玩java,今天是一个具有里程碑的一天,感觉正式踏入进入了linux大门. 原来一直以为在linux上安装jdk,需要去官网下载适合linux的jdk, ...