`

实现一个简单的服务端推方案

 
阅读更多

实现一个简单的服务端推方案

客户端和服务端的交互有和拉两种方式:如果是客户端拉的话,通常就是Polling;如果是服务端推的话,一般就是Comet,目前比较流行的Comet实现方式是Long Polling。

注:如果不清楚相关名词含义,可以参考:Browser 與 Server 持續同步的作法介紹

先来看看Polling,它其实就是我们平常所说的轮询,大致如下所示:

Polling

Polling

因为服务端不会主动告诉客户端它是否有新数据,所以Polling的实时性较差。虽然可以通过加快轮询频率的方式来缓解这个问题,但相应付出的代价也不小:一来会使负载居高不下,二来也会让带宽捉襟见肘。

再来说说Long Polling,如果使用传统的LAMP技术去实现的话,大致如下所示:

Long Polling

Long Polling

客户端不会频繁的轮询服务端,而是对服务端发起一个长连接,服务端通过轮询数据库来确定是否有新数据,一旦发现新数据便给客户端发出响应,这次交互便结束了。客户端处理好新数据后再重新发起一个长连接,如此周而复始。

在上面这个Long Polling方案里,我们解决了Polling中客户端轮询造成的负载和带宽的问题,但是依然存在服务端轮询,数据库的压力可想而知,此时我们虽然可以通过针对数据库使用主从复制,分片等技术来缓解问题,但那毕竟只是治标不治本。

我们的目标是实现一个简单的服务端推方案,但简单绝对不意味着简陋,轮询数据库是不可以接受的,下面我们来看看如何解决这个问题。在这里我们放弃了传统的LAMP技术,转而使用Nginx与Lua来实现。

Modified Long Polling

Modified Long Polling

此方案的主要思路是这样的:使用Nginx作为服务端,通过Lua协程来创建长连接,一旦数据库里有新数据,它便主动通知Nginx,并把相应的标 识(比如一个自增的整数ID)保存在Nginx共享内存中,接下来,Nginx不会再去轮询数据库,而是改为轮询本地的共享内存,通过比对标识来判断是否 有新消息,如果有便给客户端发出响应。

注:服务端维持大量长连接时内核参数的调整请参考:http长连接200万尝试及调优

首先,我们简单写一点代码实现轮询(篇幅所限省略了查询数据库的操作):

lua_shared_dict config 1m;

server {
    location /push {
        content_by_lua '
            local id = 0;

            local ttl = 100;

            local now = ngx.time();

            local config = ngx.shared.config;

            if not config:get("id") then
                config:set("id", "0");
            end

            while id >= tonumber(config:get("id")) do
                local random = math.random(ttl - 10, ttl + 10);

                if ngx.time() - now > random then
                    ngx.say("NO");
                    ngx.exit(ngx.HTTP_OK);
                end

                ngx.sleep(1);
            end

            ngx.say("YES");
            ngx.exit(ngx.HTTP_OK);
        ';
    }

    ...
}

注:为了处理服务端不知道客户端何时断开连接的情况,代码中引入超时机制。

其次,我们需要做一些基础工作,以便操作Nginx的共享内存:

lua_shared_dict config 1m;

server {
    location /config {
        content_by_lua '
            local config = ngx.shared.config;

            if ngx.var.request_method == "GET" then
                local field = ngx.var.arg_field;

                if not field then
                    ngx.exit(ngx.HTTP_BAD_REQUEST);
                end

                local content = config:get(field);

                if not content then
                    ngx.exit(ngx.HTTP_BAD_REQUEST);
                end

                ngx.say(content);
                ngx.exit(ngx.HTTP_OK);
            end

            if ngx.var.request_method == "POST" then
                ngx.req.read_body();

                local args = ngx.req.get_post_args();

                for field, value in pairs(args) do
                    if type(value) ~= "table" then
                        config:set(field, value);
                    end
                end

                ngx.say("OK");
                ngx.exit(ngx.HTTP_OK);
            end
        ';
    }

    ...
}

如果要写Nginx共享内存的话,可以这样操作:

shell> curl -d "id=123" http://<HOST>/config

如果要读Nginx共享内存的话,可以这样操作:

shell> curl http://<HOST>/config?field=id

注:实际应用时,应该加上权限判断逻辑,比如只有限定的IP地址才能使用此功能。

当数据库有新数据的时候,可以通过触发器来写Nginx共享内存,当然,在应用层通过观察者模式来写Nginx共享内存通常会是一个更优雅的选择。

如此一来,数据库就彻底翻身做主人了,虽然系统仍然存在轮询,但已经从轮询别人变成了轮询自己,效率不可相提并论,相应的,我们可以加快轮询的频率而不会造成太大的压力,从而在根本上提升用户体验。

突然想起另一个有趣的服务端推的做法,不妨在一起唠唠:如果DB使用Redis的话,那么可以利用其提供的BLPOP方 法来实现服务端推,这样的话,连sleep都不用了,不过有一点需要注意的是,一旦使用了BLPOP方法,那么Nginx和Redis之间的连接便会一直 保持下去,从Redis的角度看,Nginx是客户端,而客户端的可用端口数量是有限的,这就意味着一台Nginx至多只能建立六万多个连接 (net.ipv4.ip_local_port_range),有点儿少。

当然,本文的描述只是沧海一粟,还有很多技术可供选择,比如Pub/SubWebSocket等等,篇幅所限,这里就不多说了,有兴趣的读者请自己查阅。

This entry was posted in Technical and tagged Lua, Nginx by 老王. Bookmark the permalink.

23 thoughts on “实现一个简单的服务端推方案

分享到:
评论

相关推荐

    Spring Boot 集成 WebSocket 实现服务端推送消息到客户端.docx

    总结起来,Spring Boot 结合 WebSocket 可以有效地实现服务端到客户端的消息推送,降低了网络资源的消耗,提升了实时性。通过配置和编写相应的端点服务类,我们可以方便地构建起一个稳定的 WebSocket 通信系统。

    若依集成CIM(即时推送系统)实现将服务端修改为SpringBoot+Vue前后端分离版代码.rar

    CIM是一套基于mina或netty框架下的推送系统,或许有一些企业有着自己一套即时通讯系统的需求,那么CIM为您提供了一个解决方案,目前CIM支持websocket,android,ios,桌面应用,系统应用等多端接入支持,可应用于移动...

    服务端推送到客户端-goeasy

    【服务端推送到客户端-goeasy】是一种技术实现方式,主要用于实现实时通信,使得服务器可以主动将数据发送到客户端,而不仅仅局限于客户端发起请求后服务器响应的传统HTTP模式。GoEasy是一个基于WebSocket协议的实时...

    最全极光推送服务端

    极光推送(JPush)是一款广泛使用的第三方推送平台,它提供了一整套完整的SDK和服务,帮助开发者轻松实现对Android、iOS以及Web等多平台的消息推送功能。在本示例中,`PushServerDemo`作为核心代码库,将详细介绍...

    个推SDK Demo

    "个推SDK Demo"是一个展示如何在应用中集成和使用个推服务的示例项目。个推(Getui)是中国领先的移动互联网推送服务提供商,它为开发者提供了高效、稳定的推送解决方案,帮助他们实现向用户精准推送消息的功能。...

    Android推送实现方案探讨

    AndroidPN作为基于XMPP的一个开源项目,提供了一套较为完整的推送解决方案,包括服务端和客户端的代码包。 - **方案三:MQTT协议** MQTT协议作为一种轻量级的发布/订阅模式的物联网传输协议,近年来也被应用于...

    MQTT方案消息推送安卓手机端+php服务端案例

    本案例是关于如何利用MQTT协议实现安卓手机端与PHP服务端的消息推送。 一、MQTT协议基础 1. MQTT协议的特点: - 低开销:MQTT协议设计简洁,减少了数据传输的负担。 - 可靠性:通过持久连接和QoS(Quality of ...

    Android 基于MQTT的安卓消息推送php服务端源码+安卓端源码-IT计算机-毕业设计.zip

    这份资源包含了一个基于MQTT协议的Android消息推送系统,涵盖了客户端和服务端的源代码,适合进行Android毕业设计或论文研究。下面将详细讲解这个系统的关键技术和实现步骤。 首先,MQTT(Message Queuing ...

    Android平台校园消息推送服务的设计与实现,基于AndroidPN实现的即时通信系统,Android服务端部分。.zip

    在Android平台上设计并实现一个校园消息推送服务是一项挑战性的工作,因为这涉及到多个技术领域,包括移动应用开发、网络通信以及后台服务管理。基于AndroidPN(Android Push Notification)的即时通信系统是解决这...

    Android 内置RTSP/RTMP服务器,实现局域网内视频推流与播放 Demo

    本方案实现了如下功能 : 平板端内置RTSP/RTMP服务器 平板端获取摄像头的画面并同步进行RTMP推流 (目前仅支持了RTMP推流,未实现RTSP推流) 手机端支持对RTSP/RTMP视频流的播放 具体可以看我的博客 : ...

    多种方式模拟服务器推送客户端

    使用Iframe刷新的方式是创建一个隐藏的Iframe,设置其src指向一个可以返回新数据的服务器端页面。当服务器端有新数据时,更新页面内容,浏览器会自动刷新Iframe,从而达到推送的效果。这种方式简单,但可能导致页面...

    comet4j 服务端向浏览器实时推送消息(支持指定用户推送)

    Comet4j 是一个实现这种技术的开源框架,它基于 Comet 技术,允许服务器端主动向客户端推送数据,而不是传统的请求-响应模式。 首先,Comet 技术是Web服务器向浏览器推送数据的一种策略,解决了HTTP协议本身无状态...

    激光推送服务端远程API

    这个过程会生成一个唯一的AppKey,它是连接应用和极光推送服务器的关键。 2. **设备注册**:在用户安装并运行应用程序时,应用会通过API向极光推送服务器发送设备注册请求,获取一个DeviceToken。DeviceToken是设备...

    极光推送客户端及服务端dome

    极光推送(JPush)是深圳市极光信息技术有限公司开发的一款云服务产品,提供了一整套跨平台的消息推送解决方案。通过集成极光SDK,开发者可以轻松地实现在后台向目标用户群体发送通知或者数据消息,无论用户是否在...

    极光推送服务端jar包源码

    总之,极光推送服务端jar包源码是一个包含多个组件和功能的复杂系统,涉及到网络通信、数据库操作、消息处理、安全控制等多个方面,为开发者提供了高度定制化的推送解决方案。如果你需要深入了解或修改这个系统,对...

    人工智能-项目实践-C#-C#基于socket的消息推送服务端.zip

    本项目实践聚焦于使用C#编程语言构建一个基于Socket的消息推送服务端,这是一项核心的技术,对于构建分布式系统、实时应用以及物联网(IoT)解决方案至关重要。 首先,我们需要了解Socket。Socket是网络编程的基础,...

    利用服务器推送技术实现站内短消息(java)

    让client与service建立一个长连接,不用client手动request,service会自动response,当有好友在线的时候,会自动把好友的信息加载到select里,点击好友发送短消息时,会在浏览器(IE)右下角弹出消息框,但是现在有一...

    推送实例push

    在提供的压缩包中,JPushClientExample.java是一个Java源文件,用于演示如何在Android应用中集成极光推送。以下是对这个文件主要部分的解释: 1. 引入依赖库 首先,你需要在项目中引入极光推送的SDK依赖。这通常是...

    基于MQTT的安卓消息推送php服务端源码+安卓端源码.zip

    总的来说,这个资源提供了从服务端到客户端的完整解决方案,使开发者能够理解并实现基于MQTT的消息推送系统。这涉及到网络通信、消息处理、安全性和用户交互等多个方面,对深入学习PHP和Android开发,以及物联网通信...

    Android XMPP PUSH 推送手机端、服务端源代码

    - **多客户端支持**:处理一个用户可能在多个设备上同时在线的情况,确保消息只推送给活跃设备。 - **安全性与稳定性**:确保推送服务的安全性,防止未经授权的访问,同时提供高可用性和负载均衡。 3. **源代码...

Global site tag (gtag.js) - Google Analytics