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. scrapy中自动补全url

    url = "https:" + url 或者url = response.urljoin(url) #这里代表的是自动补全url

  2. PCMU G.711U/PCMA G.711A简介

    PCMA(G.711A) 类型:Audio 制定者:ITU-T 所需频宽:64Kbps(90.4) 特性:PCMU和PCMA都能提供较好的语音质量,但是它们占用的带宽较高,需要64kbps. 优点:语 ...

  3. SQL注入之Sqli-labs系列第九关和第十关(基于时间盲注的注入)

    开始挑战第九关(Blind- Time based- Single Quotes- String)和第十关( Blind- Time based- Double Quotes- String) gog ...

  4. Django之静态文件配置

    在项目目录中打开settings.py,在最下面配置静态文件(css文件,js文件以及其他静态配置文件),比如说html使用到了jQuery框架,我们要在项目根目录下创建statics(可自定义),将 ...

  5. hdu4338 Simple Path

    Everybody knows that totalfrank has absolutely no sense of direction. Getting lost in the university ...

  6. Go Example--协程

    package main import "fmt" func main() { //main gorouting中调用f函数 f("direct") //重新建 ...

  7. nginx配置基于域名的虚拟主机

    其实基于域名和基于ip的虚拟主机配置是差不多的,在配置基于ip的虚拟主机上我们只需要修改几个地方就能变成基于域名的虚拟主机,一个是要修改域名,一个是host文件直接看代码 [root@localhos ...

  8. IP、端口号、MAC

    1.端口 端口是TCP/IP协议簇中,应用层进程与传输层协议实体间的通信接口.端口是操作系统可分配的一种资源:应用程序通过系统调用与某端口绑定后,传输层传给改端口的数据都被相应进程接收,相应进程发给传 ...

  9. Ubuntu遇到问题“Could not install packages due to an EnvironmentError: [Errno 13] 权限不够: ”

    Ubuntu在使用一些pip的时候会遇到:“Could not install packages due to an EnvironmentError: [Errno 13] 权限不够:”的问题. 在 ...

  10. 01python简介

    目录 1.  Python起源 2.  解释器 3.  Python 的设计目标 4.  Python 的设计哲学 5.  为什么选择  Python ? 6.  Python 特点 7.  Pyth ...