当前位置: 首页 > news >正文

多路复用实现单服百万级别RPS吞吐

多路复用其实并不是什么新技术,它的作用是在一个通讯连接的基础上可以同时进行多个请求响应处理。对于网络通讯来其实不存在这一说法,因为网络层面只负责数据传输;由于上层应用协议的制订问题,导致了很多传统服务并不能支持多路复用;如:http1.1,sqlserver和redis等等,虽然有些服务提供批量处理,但这些处理都基于一个RPS下。下面通过图解来了解释单路和多路复用的区别。

单路存在的问题

每个请求响应独占一个连接,并独占连接网络读写;这样导致连接在有大量时间被闲置无法更好地利用网络资源。由于是独占读写IO,这样导致RPS处理量由必须由IO承担,IO操作起来比较损耗性能,这样在高RPS处理就出现性能问题。由于不能有效的合并IO也会导致在通讯中的带宽存在浪费情况,特别对于比较小的请求数据包。通讯上的延时当要持大量的RPS那就必须要有更多连接支撑,连接数增加也对资源的开销有所增加。

多路复用的优点

多路复用可以在一个连接上同时处理多个请求响应,这样可以大大的减少连接的数量,并提高了网络的处理能力。由于是共享连接不同请求响应数据包可以合并到一个IO上处理,这样可以大大降低IO的处理量,让性能表现得更出色。

通过多路复用实现百万级RPS

多路复用是不是真的如此出色呢,以下在.net core上使用多路复用实现单服务百万RPS吞吐,并能达到比较低的延时性。以下是测试流程: 

由于基础通讯不具备消息包合并功能,所以在BeetleX的基础上做集成测试,主要BeetleX会自动合并消息到一个Buffer上,从而降低IO的读写。

测试消息结构

本测试使用了Protobuf作为基础交互消息,毕竟Protobuf已经是一个二进制序列化标准了。

请求消息

  [ProtoMember(1)]
        public int ID {  get; set; }
        [ProtoMember(2)]
        public Double RequestTime { get; set; }

响应消息

  [ProtoMember(1)]
        public int EmployeeID { get; set; }
        [ProtoMember(2)]
        public string LastName { get; set; }
        [ProtoMember(3)]
        public string FirstName { get; set; }
        [ProtoMember(4)]
        public string Address { get; set; }
        [ProtoMember(5)]
        public string City { get; set; }
        [ProtoMember(6)]
        public string Region { get; set; }
        [ProtoMember(7)]
        public string Country { get; set; }
        [ProtoMember(8)]
        public Double RequestTime { get; set; }

** 服务端处理代码**

   public static void Response(Tuple<IServer, ISession, SearchEmployee> value)
        {
            Employee emp = Employee.GetEmployee();
            emp.RequestTime = value.Item3.RequestTime;
            value.Item1.Send(emp, value.Item2);
            System.Threading.Interlocked.Increment(ref Count);
        }
        public override void SessionPacketDecodeCompleted(IServer server, PacketDecodeCompletedEventArgs e)
        {
            SearchEmployee emp = (SearchEmployee)e.Message;
            multiThreadDispatcher.Enqueue(new Tuple<IServer, ISession, SearchEmployee>(server, e.Session, emp));
        }

服务响应对象内容

   Employee result = new Employee();
            result.EmployeeID = 1;
            result.LastName = "Davolio";
            result.FirstName = "Nancy";
            result.Address = "ja";
            result.City = "Seattle";
            result.Region = "WA";
            result.Country = "USA";

接收消息后放入队列,然后由队列处理响应,设置请求相应请求时间并记录总处理消息计数。

客户端请求代码

  private static void Response(Tuple<AsyncTcpClient, Employee> data)
        {
            System.Threading.Interlocked.Increment(ref mCount);
            if (mCount > 100)
            {
                if (data.Item2.RequestTime > 0)
                {
                    double tick = mWatch.Elapsed.TotalMilliseconds - data.Item2.RequestTime;
                    AddToLevel(tick);
                }
            }
            var s = new SearchEmployee();
            s.RequestTime = mWatch.Elapsed.TotalMilliseconds;
            data.Item1.Send(s);
        }

客户端测试发起代码

    for (int i = 0; i < mConnections; i++)
            {
                var client = SocketFactory.CreateClient<BeetleX.Clients.AsyncTcpClient, TestMessages.ProtobufClientPacket>(mIPAddress, 9090);
                client.ReceivePacket = (o, e) =>
                {
                    Employee emp = (Employee)e;
                    multiThreadDispatcher.Enqueue(new Tuple<AsyncTcpClient, Employee>((AsyncTcpClient)o, emp));
                };
                client.ClientError = (o, e) =>
                {
                    Console.WriteLine(e.Message);
                };
                mClients.Add(client);
            }
            for (int i = 0; i < 200; i++)
            {
                foreach (var item in mClients)
                {
                    SearchEmployee search = new SearchEmployee();
                    Task.Run(() => { item.Send(search); });
                }
            }

整个测试开启了10个连接,在这10个连接的基础上进行请求响应复用。

测试配置

测试环境是两台服务器,配置是阿里云上的12核服务器(对应的物理机应该是6核12线程)

服务和客户端的系统都是:Ubuntu 16.04

Dotnet core版本是:2.14

测试结果

客户端统计结果

服务端统计信息

带宽统计

测试使用了10个连接进行多路复用,每秒接收响应量在100W,大部分响应延时在1-3毫秒之间

本文来自云栖社区合作伙伴“开源中国”

本文作者: 泥水佬

原文链接

相关文章:

  • 1024,码出未来!
  • 实验5
  • Sql Server内置函数实现MD5加密
  • [Google Guava] 1.1-使用和避免null
  • 资源下载汇总
  • DevExpress第三方控件使用实例之ASPxPopupControl弹出子窗体
  • 如何修复无法启动的docker容器
  • html----h1-6标签
  • [PHP] 算法-字符串的左循环的PHP实现
  • asp.net mvc中action接收客户端发送过来的html片段
  • flask高阶
  • 原型继承
  • 入门级的git使用指北
  • 【视频】ASP.NET Core MVC 2.* 入门
  • 一个简单高性能的Go router,和httprouter 差不多快,且支持正则
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • SegmentFault for Android 3.0 发布
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • JS题目及答案整理
  • Next.js之基础概念(二)
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • supervisor 永不挂掉的进程 安装以及使用
  • Swoft 源码剖析 - 代码自动更新机制
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • Vue2.x学习三:事件处理生命周期钩子
  • 从零搭建Koa2 Server
  • 基于 Babel 的 npm 包最小化设置
  • 开源SQL-on-Hadoop系统一览
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 网络应用优化——时延与带宽
  • 为什么要用IPython/Jupyter?
  • 字符串匹配基础上
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • puppet连载22:define用法
  • !!java web学习笔记(一到五)
  • (003)SlickEdit Unity的补全
  • (3)选择元素——(17)练习(Exercises)
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (一)appium-desktop定位元素原理
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • **CI中自动类加载的用法总结
  • .Net CF下精确的计时器
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET Micro Framework 4.2 beta 源码探析
  • .NET Reactor简单使用教程
  • .net 发送邮件
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • /usr/bin/perl:bad interpreter:No such file or directory 的解决办法
  • @Resource和@Autowired的区别
  • []串口通信 零星笔记