microelec

microelec 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

通信老兵,从LTE无线通信到webRTC音视频,从基础设施到音视频应用。

个人动态

microelec 关注了标签 · 10月13日

webrtc

WebRTC的全称是网页实时通讯(Web Real Time Communication),它无需插件,就可以实现浏览器之间的交流功能,这意味着终端用户(比如,你)无需安装任何东西,就能在浏览器里面进行实时地声音和视频通话。

关注 84

microelec 发布了文章 · 10月13日

WebRTC 框架下的实时视频关键路径

在当前数字化浪潮下,基于互联网的实时音视频通信已经成为越来越多互联网应用的标配。得益于互联网基础设施的日趋完善和优秀开源解决方案WebRTC的出现,音视频实时通信的开发门槛也降低很多。但音视频应用的技术和工程实现复杂度还是比较高的,有必要深入其中搞清楚实现的来龙去脉。本文基于webRTC框架对实时音视频通信的一些关键实现做一些讨论,但其原理是相通的并不局限于webRTC。

WebRTC实时音视频通信框架

从它的名字上也可以看出webRTC是作为web端的实时通信方案提出的,目前在google大力推动下已经成为主流浏览器的标配功能。基于图1的架构实现信令服务器,web APP就可以轻松获得音视频通信的能力。

上面的场景只是简单的P2P(点对点)两方的通信,如果要支持多方通信和克服实际网络环境中各类防火墙的挑战,通常会引入更多的服务器功能单元,比如MCU(Multipoint Control Unit),SFU(Selective Forwarding Unit)。如图2,增加了Media Server承担MCU/SFU等功能,增加Data Server承担媒体类的信息交互,这种架构下原来的点对点的去中心结构变成了星状的结构。

通信的双方有三类交互信息,根据信息各自的特点可以有不同的传输方式。其中,signaling plane信令通道webRTC并没有实现,预留好了控制接口。Data plane和media plane,webRTC提供了一套完备的方案。

信令(signaling plane):交互协商双方的音视频编解码能力,完成通信链路(data/media plane)建立所必需的信息和参数交互。

数据(data plane):交互双方非音视频类的信息,一般实时性要求不高。

媒体(media plane):交互音视频数据,实时性要求高。

三条通信通道各有各的角色,各自完成自己的任务。media plane的整个链路处理最为复杂,实时要求也最高。本文仅就media plane的视频流(video)在SFU架构下端到端处理过程做一个完整解剖,参考webRTC m79分支做了逻辑抽象,对其他非webRTC的视频处理系统也有参考意义。

WebRTC的视频流关键路径

视频从发送端摄像头采集到接收端显示器渲染,我们定义为一个端到端的处理过程。接下来描述的过程是典型的精简系统的处理过程,实际生产线上的处理过程有可能还会多一些额外步骤。一件事情如果能抓住它的主线,那这件事情就不会跑偏。

media plane的传输通道的协议栈如图3所示,ICE(Interactive Connectivity Establishment )用来防火墙穿透和链路连通性监测。DTLS(Datagram Transport Level Security)过程完成密钥的交换,是后续media加密的辅助过程,如果不加密可以跳过,浏览器是默认打开加密功能的。在经过ICE和DTLS两个辅助过程后,media最终在(S)RTP/(S)RTCP之上传输。我们重点关注在这个媒体信息的传输路径。接下来我们看一下视频画面是怎样流过端到端的,关键路径如图4所示。

按照视频画面的处理顺序我们逐步看一下每个步骤发生了什么事情

发送方向(采集端-媒体服务器)

video capture:webRTC提供了丰富的设备管理功能,可以根据摄像头的能力和业务需要订阅不同等级的视频输出,可以控制摄像头输出的分辨率和帧率。

video adapter:视频帧适配器,可以裁剪视频画面,降低/升高视频辨率或丢帧以改变输入到encoder的数据量。video adapter与encoder之间会有交互,两者配合可以控制编码器的码率输出。

encoder:编码器,webrtc使用了openh264开源实现。

RTP/RTCP packetizer:组包器,按照RTP/RTCP协议规范组包。

Pacer queue:发送队列,组包器输出的RTP包放入该队列中等待发送。

Pacer&prober:发包器&带宽探测器,发包器根据当前预估的带宽限制,往Transport平稳发包。带宽探测器用于探测当前网络带宽上限,探测包的发送跟当前发送的实际有效包速率有关。

Transport:网络传输模块,加密,端口复用等网络传输功能。

接收方向(媒体服务器-播放端)

Transport:网络传输模块,解密,de端口解复用等网络传输功能。

RTP/RTCP depacketizer:解包器,按照RTP/RTCP协议规范解包,。

Packet buffer:视频包缓存,和解包器一起负责RTP包的乱序和丢包处理。去掉RTP包头后,视频包会存入该buffer,每次来了新包都会检查当前的缓存里面是否已经可以构成完整的视频帧,如果完整则组成完整帧继续下一步的处理,否则则结束当前包的处理。

Frame queue:帧队列,负责缓存当前待解码的视频帧。

Decoder:解码器,负责视频帧解码。解码的开始时机考虑了网络抖动、编码器延迟、渲染延迟。

Video render:渲染器,视频渲染接口模块。

媒体服务器

媒体服务器的实现不在webRTC的范围内,但也有不少优秀的开源实现,比如Kurento/Jitsi/Janus/Srs/OWT等。为了更好的服务业务需求,更多的音视频解决方案公司打造自己的媒体服务器,将webRTC媒体流引入到自己的媒体解决方案中。以Kurento为例,介绍一下Media server上的视频流处理。图片来源https://www.kurento.org/kurento-architecture

Kurento Media Server的媒体处理流水线是基于GStreamer实现的,其模块化和可扩展性做的非常好。一路视频流根据不同的业务需求,可能经过不同复杂程度的处理。列举几类典型的处理流水。

Stream s1:媒体流做了个传输层的路由后直接出去,这时Media Server只是做了简单的路由。这也是很有意义的,很多公司的音视频专网或者SDN依赖于这样的功能,使得用户可以就近接入到音视频云端服务器。注:云端的媒体流路由方案很多,media server集成route功能是其中一种方式。

Stream s2:媒体流做了完整的编解码-转码的过程,这个过程对Media Server的考验就比较重了,需要消耗很多计算资源。转码的部分可能是不同编码的转换如H.265->H.264,也可以是同一codec的码率转换。

Stream s3:媒体流相对s2还做了更多的操作,比如图像增强、混流等,甚至更多。

三条流s1-s2-s3,复杂度逐级在上升,需要哪些处理完全取决于业务需求。参与处理的服务器节点可以是媒体网关,可以是媒体路由,可以是转码服务器,可以是录制服务器,或者多个功能的叠加。设计合理的架构、优秀的功能模块抽象与划分,做到功能易扩展、集群易扩容,这是优秀架构师的修炼目标。

总结

本文对webRTC框架下典型的视频流端到端处理做了一次梳理,对理解类似音视频处理方案多有裨益。音视频的系统好比我们的自来水系统,该文讲解了整个处理过程是怎样的,但是输水管道不同段不同时间有大有小,有的管道有时还漏水,怎样在这样的传输通道上高效的输水?怎样在复杂网络条件下的互联网上传输实时要求很高的音视频?webRTC还有很多法宝,比如拥塞控制、码率自适应、容错机制等等,这些控制模块计算感知当前的传输和计算资源情况(网络、CPU、时延等),实时控制关键路径上的模块做出调整,力求公平高效的传输音视频。

本人原创文章,首发在infoq
https://xie.infoq.cn/article/...
查看原文

赞 0 收藏 0 评论 0

microelec 发布了文章 · 2018-09-29

基于MARS的移动APP网络通信开发实践

Mars简介

MARS作为优秀的跨平台网络层通信方案开源1年多了,github上收获过万的star,期间较为稳定更新并不频繁。基于内核socket MARS针对弱网络环境下的移动应用做了很多比较实用的优化,详细的优化点和原理在其开源项目的wiki里有很多文档说的比较清楚了Mars wiki。本人刚好参与了多款具有IM功能的应用开发,底层网络通信集成了MARS,该底层通讯模块已经稳定服务于Android/Ios/windows平台上多款产品。网上有关MARS使用的实践经验还比较少见,这里总结一下供大家参考。

Mars使用实践

MARS支持长连接的同时也支持短链接,短链接主要映射成有限制的http连接。短连接不是MARS的长处,不在本文涉猎,后面提到的所有连接如无特指均为长连接。

长连接数据流及API一览

读完文档就能把MARS用起来还是得靠运气的,索性把代码走读了一下,刚好可以梳理梳理长连接的数据流。
长连接数据流

上面数据流展示了client端要发送数据的整个过程和涉及到主要API,以Android API为例,MARS提供了涉及数据输出的以下重要API

//初始化
public static void init(Context _context, Handler _handler)
//设置长连接server
public static        void setLonglinkSvrAddr(final String host, final int[] ports)
//client发送任务接口
public static native void startTask(final Task task);

//server主动推送回调
void onPush(final int cmdid, final byte[] data);
//client发送数据的回调
boolean req2Buf(final int taskID, Object userContext, ByteArrayOutputStream reqBuffer, int[] errCode, int channelSelect);
//client收到回应数据的回调
int buf2Resp(final int taskID, Object userContext, final byte[] respBuffer, int[] errCode, int channelSelect);
//client发送任务结束的回调
int onTaskEnd(final int taskID, Object userContext, final int errType, final int errCode);

实际过程中MARS提供的接口就比较复杂了,这边也放一张总结图感受一下。
MARS接口

task概念及消息流程

Mars对外提供的消息收发接口是基于task的,要先理解task的概念。Mars通过任务来描述一次数据的发送、应答和最终结束。

  • APP启动发送数据 startTask
  • MARS回调 req2Buf 从APP获得该任务要传输的数据
  • MARS回调 buf2Resp 向APP投递该任务的应答数据
  • MARS回调 onTaskEnd 通知APP该任务执行状态,成功或者失败

数据传输过程有许多控制参数,任务的定义就是这些控制参数的集合。

public int taskID;  // 任务唯一标识,会自动生成。
public int channelSelect;   // 任务走长连还是短连,或者两个都可以,可选值见 EShort。ELong EBoth
public int cmdID; // 长连的 cgi 命令号,用于标识长连请求的 cgi。长连必填项,相当于短连的 cgi。
public String cgi;  // 短连的 URI,短连必填项。
public ArrayList<String> shortLinkHostList;    //短连所用 host 或者 ip,如果是走短连的任务,必填项。

//optional
public boolean sendOnly; // true 为不需要等待回包,false 为需要等待回包。默认值为 false
public boolean needAuthed;  // true 为需要登陆态才能发送的任务,false 为任何状态下都可以发送的任务,默认值为 true。
public boolean limitFlow; // true 在手机网络情况下会走流量限制,false 不会。默认值为 true。大数据包请置为 false。
public boolean limitFrequency; // true 会走频率限制,false 不会。默认值为 true。 频繁发送相同包内容的 Task 请置为 false。

public int channelStrategy;     // channelSelect 为 EBoth 情况下,该值为 ENORMAL 长连存在则走长连,该值为 EFAST,即使长连存在,但是长连接队列里有别的任务的时候,会优先走短连接。默认值为 ENORMAL
public boolean networkStatusSensitive;  // true 没网络的情况下任务会直接返回失败,不会尝试去走网络,false 即使没网络,也会尝试建立连接。默认为 false。
public int priority;    // 任务的优先级,可选值见 ETASK_PRIORITY_XX。
public int retryCount = -1; // 任务重试次数,设为-1,如果任务失败,会走 Mars 的重试逻。辑,设置大于等于0的数,会以此为准,默认值-1。
public int serverProcessCost;   //该 Task 等待SVR处理的最长时间,也即预计的SVR处理耗时。
public int totalTimeout;        // 该 Task 总的超时时间,设置小于等于零的值,会走 Mars 的超时逻辑,否则以此值为准,默认值为0。
public Object userContext;      // 用户变量,可填任何值,Mars 不会更改该变量。
public String reportArg;    // 统计上报所用,可忽略。

多ip

server端配置多个IP,MARS同时发起多个连接并取其中最快建立的连接使用,其他释放掉。该策略确实能提高client建立连接的成功率和速度,同时也给server端带来了并发的压力,需要根据自身的用户规模和server资源情况谨慎使用。我们开启了多IP的功能,有几点值得注意。
MARS提供的接口上定义了几种不同的ip,一定要小心应用。

IP使用
Debug IP调试IP,线上勿用。
NewDns IP自开发DNS解析IP。
DNS IPMARS解析出的DNS IP。
Backup IP保底IP。
  • 通过 setLonglinkSvrAddr 配置了server的域名地址,虽然该域名对应多个IP,但不一定多IP的功能就启用了。很多情况下MARS DNS解析时,DNS服务器返回的IP会根据运营商情况只返回一个IP地址。
  • 可以通过 onNewDns 的回调,自己把多个IP传给MARS使用,解决1的问题。
  • BackupIp推荐配置一个稳定的IP,不要空着。因为前面的各类IP在多次失败的情况下会短期禁用掉,但backupIp会一直生效。

认证

安全是永恒的话题,长连接建立后的第一件事情就是用户鉴权认证。过程就是client发送一些server端认识的信息来证明自己是合法用户,可以继续通信。MARS提供了 makesureAuthed/getLongLinkIdentifyCheckBuffer/onLongLinkIdentifyResp 等接口给APP,但该接口是通过回调的方式被动触发发送鉴权信息的。APP主动发起鉴权信息,也同样可以走通用 startTask 接口。

  • 比较需要注意的是当APP的鉴权信息发送改变(token失效/登出重新登录)时,就需要这种主动断开当前连接重新鉴权。

重连

MARS一直致力于维持连接常在,连接断开会自动重连。可惜没有提供给APP主动断开连接和重连的API,APP会有场景需要主动断开当前连接,比如上面提到的认证信息更新时或者用户业务登出时。MARS的 redoTasks会有断开连接的效果,我们开发APP时就比较讨巧的用了这个API来做主动重连的操作。

心跳改造

心跳是保持长连接的必需手段,MARS也提供了智能心跳的方案。很遗憾我们的产品是server端主动发心跳包的方案,刚好跟MARS相反的方向。稍稍改造禁用掉MARS的客户端心跳,走 onPushstartTask接口同样可以实现心跳。

APP协议实现

MARS要求实现longlink_packer.cc.rewriteme中定义的函数来达到自定义APP协议的目的。实际产品中server端和client的通信协议肯定需要开发定制的,这部分的实现几乎是必需的。
可以根据产品自己的特性定制私有的通讯协议,这里本人给出一个通讯协议的例子

    struct MessageFormat
    {
        uint32_t magicNum; // magically defined num for error message checking
        uint32_t messageId; // unqiue message identification
        uint32_t len;       // body length
        char data[];     // body start byte
    };

这几乎是最精简的一个通讯协议了,尤其比较重要的是messageId。messageId对应于MARS的taskId,用于串联起来IM消息的发送和应答消息对。比如A发送了messageId=1(taskId=1)的“How are you?”到B,B收到后同样以messageId=1(taskId=1)回应“I'm fine"。这样在对A端MARS taskId=1的任务管理全靠这个messageId来标记了。同时有几点注意事项如下:

  • req2Buf/buf2Resp/onPush/onTaskEnd/__unpack_test 等数据传输相关的回调都是发生在长连接线程里,切记不要在这些回调里面做阻塞性或者耗时的操作,会影响数据传输的效率和连接的维持。
  • __unpack_test 回调主要是解决业务包投递时机的问题。tcp是流式协议,业务包有可能分成多个tcp包投递,通过该回调来告诉MARS是否已经收到完整的业务包,是否可以往业务层投递了。
  • onTaskEnd 用来回调给业务层发送任务的最终状态。通常业务层的发送包都会期望一个业务层的应答包,这样顺序就是startTask-->req2Buf(业务组包)-->server-->buf2Resp(业务解包)-->onTaskEnd。如果client只是发送业务包不要求业务应答(task属性设置为send_only=true),顺序是这样的startTask-->req2Buf(业务组包)-->onTaskEnd-->server,onTaskEnd直接返回成功不代表server端肯定收到了该业务包。

我这边有一个MARS的二次封装,提供了上面简单的通讯协议同时封装了Mars task的管理,有兴趣的同学可以参考一下,文末有链接地址。

日志

MARS xlog通过磁盘文件内存映射的方式获得高效可靠的日志方案,详细原理见高性能日志模块xlog。实际线上产品使用推荐

  • 每个进程一个日志文件,每个进程需要单独配置日志
  • 使用异步日志打印
  • 定义XLOGGER_TAG来嵌入日志tag,方便日志过滤
  • 每条日志设置合理等级,控制日志文件大小
  • 日志内不包含敏感信息可以不加密

监控

MARS有单独的网络监控模块SDT,目前还不能独立使用。网络通信模块STN里面也有很多网络情况和任务统计的实现,可以稍微改造一下把这些统计项暴漏给APP层。APP就可以搜集统计这些信息汇总到server端,然后运营人员可以比较轻松的了解当前所有客户端的网络表现啦。
顺带提一下MARS的上报长连接状态的接口 reportConnectInfo 一个小小的提示。该回调函数上报的状态存在一定的迷惑性。底层网络长连接状态发生变化时会触发该状态上报接口调用,但真正调用到该接口时上报的网络状态反应的是当时的连接状态。举个例子,连接断开触发上报,上报接口 reportConnectInfo 是在另外一个线程里被调用的,真正调用时状态可能已经变为已连接了,这样APP就缺失一个感知连接断开的机会。所以APP不能直接依赖该接口做严格的逻辑处理或状态维护。

使用总结

  • IM长连接维持“费尽心机”。多ip并发连接,超时重传策略,智能心跳,网络RTT时间监测,玩的花样百出,甚至连电信运营商网络这层的保活都做了,结果就是MARS提供了更灵敏、反应更迅速、更适合移动通信的网络通道。
  • 日志方案稳定高效,性能很好,使用期间基本没遇到丢日志的问题。
  • 跨平台,android/IOS/windows一致性的通讯能力体验,同时节省开发资源。
  • 接口繁冗,深度使用需要使用者仔细读源代码。
  • 文档不够友好,社区不活跃。
  • MARS层次可以更清晰些,突出网络层通道的重点。剥离业务层的功能,比如认证功能。去除task概念代之以跟业务层约定简洁的协议头(比如所有包开头的32bit为包sequence),这样接口可能会简洁很多。

总的来说,MARS是一款出色的移动通信产品网络层解决方案,如果你需要移动端实时通信可以尝试在产品中集成MARS。如果你觉得接口使用有些复杂,我这边有一个MARS的二次封装,你可以做一个参考或者直接用一下,至少看起来简单了很多。比如这个C++的例子:

//推送监听类
class PushHandler :PushListener {
    virtual void onPush(const std::string &message) {

    }
};
//应答监听类
class ResponseHandler :ResponseListener {
    virtual void onResponse(const std::string &message) {
        printf("response received:%s \n",message.c_str());
    }
    virtual void onError(const int err, const std::string &errMsg) {
        printf("message send failed:%d \n",err);
    }
    virtual void onSuccess() {
        printf("message send ok \n");
    }
};

int main(int argc, char* argv[]) {
    MarsConfig config("39.106.56.27",9001);
    init(config);
    PushHandler pushHandler;
    registerPushListener((PushListener*)&pushHandler);
    _sleep(2000);
    ResponseHandler responseHandler;
    std::string message = "hello";
    sendMessage(message.c_str(), message.size(), (ResponseListener*)&responseHandler);
    _sleep(200000);
    return 0;
}

这个MARS的二次封装我放在了github上,大家可以作为一个了解怎样使用MARS的入口
MarsWrapper

查看原文

赞 1 收藏 1 评论 0

microelec 回答了问题 · 2017-02-28

如何存储用户评论信息?

1.用redis不太科学,用内存来存大量的评论和回帖不经济,去哪搞那么多内存,存不下啊。

2.回帖和评论是比较结构化的数据,可以使用关系db存储,分表策略有很多,表特别大以后可以垂直或水平分表。可以按时间,或按访问习惯等等规则分,自行google。

关注 8 回答 4

microelec 回答了问题 · 2017-02-28

全站异步加载数据的好处是什么?

1.快。前端没有动态内容,第一遍显示静态内容可以以最快的速度显示,因为是静态可以直接放在CDN上就更快了。减少动态的内容,精益策略,只有在必须显示的时候才异步拉取,这样整个体验会流畅很多。
2.server端压力小,可以支持更多的并发。
3.前后端比较好的分离,松散耦合。前端HTML+JS自称一个小系统,跟后端通过定义好的webapi交互数据。比如现在比较流行的react技术。

关注 3 回答 2

microelec 回答了问题 · 2017-02-28

一个简单的Python脚本执行需要多少进程?

主进程的父进程是当前与你交互的shell进程。你的程序里面只有main的主进程和一个子进程,无他。

关注 3 回答 2

microelec 回答了问题 · 2017-02-28

多个进程去读取redis消息队列是否会发生冲突?

redis自己保证队列读写的互斥。redis是单线程,工作方式大概是这样的。你的多进程读取其实是可以并行发多个读取请求包,这些请求包到达redis的socket buffer中,redis的处理是串行响应你的请求,无锁。如果哪天redis改成多线程的方式,同样可以通过加锁互斥保证队列的读取不会出事情。

关注 4 回答 3

microelec 回答了问题 · 2017-02-28

解决有关redis等内存数据库 性能 和 必要性 的疑惑

1.应用场景不同导致redis可以有多种部署方式,本机还是跨机缓存是根据要解决的问题来看。比如业务程序是高cpu消耗的,部署的机器只需要一般的内存配置即可。缓存的数据又比较多,需要单独部署,甚至需要多台组成集群。又比如系统比较大以后,需要各种模块化,微服务化,每个服务都可以独立演进,计算和数据分离也是比较常见的方式。另外不用担心网络io带来的开销,在没有达到网络带宽瓶颈之前,网络io都不是问题,延迟在ms级别,很划算的。

2.redis作为缓存和kv数据库还有持久化功能,断电重启后还可以恢复。当然如果你只是需要缓存机制你可以选择自己编程维护一套缓存,看你自己的需要,代价就是你要自己实现缓存的机制,自动失效,缓存写满等等功能。

关注 5 回答 3

microelec 发布了文章 · 2016-05-05

Nginx cache初体验

前言

大家都知道cache对于网站性能的重要性,提高用户响应速度,减轻后端压力,节省资源等等。一谈到cache其实是个很大话题,从前端浏览器到最终响应请求的服务器可能要经过很多次跳转,每次跳转经过的服务器都有机会提供cache。单从成本上而言,越靠近用户的cache越经济,实际情况中都需要根据当前线上的业务部署情况,开发成本收益比,可维护性等因素考虑采取在哪个地方cache,如何cache。粗线条一点根据cache的位置,一般会有浏览器,web服务器,CDN和应用服务器cache等。这里我结合最近自己完成的nginx服务器上的cache工作,谈一下nginx提供了哪些实用的cache服务。


Nginx承担反向代理服务器的工作,一般它处理的业务都是比较通用的处理,或者说一般不会处理很个性化的请求。比如安全,解压缩功能对所有请求都是通用的,这是nginx可以搞定的,它一般不会终结业务类的处理,而是将他们交给后面的应用服务器。正是因为Nginx的这种特性,如果在nginx节点上做cache,一般cache的是整个http请求的response。自然cache的key也一般从http请求的url和参数得来。目前我接触到两种nginx cache方式:

  • 本地文件系统cache (proxy_cache )

  • 集中的内存cache (srcache-nginx-module)

Proxy_cache

这是Nginx原生的ngx_http_proxy_module自带的cache解决方案。http response的内容以文件的形式存在了本地的文件系统,同时开了一个share memory冗余存key值可以快速判断是否cache命中。一般会这么部署。
clipboard.png
如何配置nginx使能proxy cache,可以参考NGINX CONTENT CACHINGA Guide to Caching with NGINX
因为这个方案是本地文件系统存cache,可以比较容易理解具有以下缺点:

  1. cache冗余,不同的nginx node间分别存cache。

  2. cache一致性,因为是冗余的,各个节点间cache的失效时间是不同步的。

  3. cache访问速度慢,读磁盘自然会慢一些。

  4. cache效率低,因为各个node各有自己的cache不共享,即使某个node cache里存了某个请求,如果另外的nginx node来处理请求还是cache miss。

基于上面的问题,有更好的方案,比如agentzhang的srcache-nginx-module模块。

srcache-nginx-module

这个模块旨在构建一个cache中间层,cache的存储介质是集中的,一般使用memcached或者redis。这样解决了冗余,也就解决了上面的各种问题,同时速度也快很多。对比着,它是这样部署的。另外集中的cache存储可以选择redis或者memcached(memc-nginx-module提供API),memcached集群方案需要nginx配置配合解决。

clipboard.png

顺便感谢agentzhang将Lua引入到nginx。nginx出色的扩展性已经很灵活了,lua-nginx-module将脚本语言处理文本的便捷性直接引入nginx,使得开发人员可以方便的在http request/response处理的各个阶段都能嵌入定制的功能。比如我可以很方便的使用lua模块在rewrite阶段根据url和args生成定制的cache key--刨除一些参数(比如不影响response结果的那些参数)。附上两篇不错的lua-nginx-module的资料:
lua-nginx-module
OpenResty最佳实践
使用memc-nginx和srcache-nginx模块构建高效透明的缓存机制

另外附上自己的nginx.conf以备后查。

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
error_log  logs/error.log  info;

pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
    #
    # cache server 1
    upstream memcache1 {
        server 127.0.0.1:11211;
        #keepalive 512 single;
    }
    # cache server 2
    #upstream memcache2 {
    #    server 127.0.0.1:11211;
    #}
    upstream_list memcache_servers memcache1;

    server {
        listen       8080;
        server_name  localhost;
        #memc-nginx-module
        location /memc {
            internal;
            memc_connect_timeout 100ms;
            memc_send_timeout 100ms;
            memc_read_timeout 100ms;
            set $memc_key $query_string;
            set $memc_exptime 30;
            set_hashed_upstream $backend memcache_servers  $memc_key;
            memc_pass $backend;
        }
        location /s {
            # key gerneration for cache
        set $args_for_key '';
            rewrite_by_lua '
               ngx.var.args_for_key = string.gsub(ngx.var.args, "queryid=%d+","queryid=0")
            ';
            #echo $args_for_key;
            srcache_fetch GET /memc $uri$args_for_key;
            srcache_store PUT /memc $uri$args_for_key;
            proxy_pass http://192.168.1.100:8000;
        }
        location / {
            root   /var/www;
            index  index.html index.htm index.php;
        }
        
        
    }
    server {
        listen 8081;
        location / {
            default_type text/html;
            content_by_lua '
                ngx.say("<p>hello, world</p>")
            ';
        }
    }


}
查看原文

赞 0 收藏 5 评论 0

microelec 回答了问题 · 2016-04-06

在网页中实时聊天的话,并发大了会造成页面视频特别卡,这个有什么方法可以优化呀

猜测你是在使用HTML5的WebRtc,首先音视频的编解码就比较耗资源,你能做的可能比较有限,提供一些思路供参考。

  1. 仔细阅读理解HTML的API,看有没有你能控制的。

  2. 有没有设置码率或者压缩率的地方,

  3. 有没有设置codec的地方,

关注 5 回答 4

认证与成就

  • 获得 19 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-03-02
个人主页被 553 人浏览