联想Windows平板Miix2-8安装Linux的方法和总结(2017年3月22日更新状态:完美用做家用服务器)

背景

2年前买了Miix2-8 32G版,搭配Intel Z3740 CPU,2G内存,出厂为32位的Windows 8系统,去年Windows 10出来之后,毫无压力升级到Windows 10,系统稳定性提高不少,后来闲置在家里,清空了机器,装了干净的原版Windows 10,自带OEM激活,使用VMware 10(只有10可以在32位Windows上使用CPU虚拟化运行64位的系统)安装了个Ubuntu14.04服务器版,给1G内存和10G硬盘,用极路由2的USB口供电,搭配花生壳内网穿透,当服务器使用。

服务器上跑了我的微信公众号,我的Blog(也就是读者现在看到的这个),还有一些常用脚本定期运行,使用花生壳做内网映射,2个端口,一个用于80端口的HTTP服务,另一个用于SSH服务,稳定运行了大半年,没有出过故障。

但是总有一颗折腾的心在跳跃,因为平板的内存只有2G,虚拟机只能分配1G内存,并且磁盘读写并没有系统直接操作来的快速,而且磁盘容量有限,因此一直想直接安装Linux到平板上。这2年也略微尝试过几次,均以失败告终,主要是想安装64位系统,而Miix2自带的是32位的UEFI(无64位UEFI),无法引导64位系统,安装盘都进不去,更别说安装系统了,网上针对Miix2的Linux安装文章也是少得可怜,基本上找不到直接的资料。

经过2年的发展,UEFI启动已经成熟了不少,Debian 8.0已经支持了64位系统使用32位引导,此时再启动这个工作,应该比之前要容易不少,于是花了一些时间来尝试安装各主流发行版。下面是我走过不少弯路之后的一个关于Miix2-8平板安装Linux的总结。

准备的额外硬件
  • USB HUB,最好可同时供电,用于连接鼠标键盘以及U盘。
  • 一个U盘,2G以上容量即可。
  • 【选用】外置USB无线网卡,或者外置USB有线网卡。因为安装过程中内置网卡无法识别,我用的是水星USB无线网卡,没有USB有线网卡,读者可自行测试一下是否可用,在Ubuntu安装后可加载无线网卡驱动,因此可不用,其他系统比如Debian需要。
安装操作步骤

所有的安装方法都是采用下载原版ISO镜像,用U盘引导工具(比如UltraISO)制作U盘启动。一些对镜像的修改可在刻录完后直接在U盘中修改。

Miix2-8 UEFI启动方法:开机时按住【音量键上】+【开关键】。

Miix2-8 强制关机方法:长按【音量键下】+【开关键】10秒左右。

问题列表

以下列出我在折腾过程中遇到的一些问题,测试的系统版本太多(主要是Ubuntu的版本太多了),就不列出具体的系统版本号了,特殊情况我会单独列出来,尽量使用14年以后的版本,14年以前的版本我试了一下,也有各种问题,就不说了,新版本对于硬件的兼容性会好很多。

另外,所有系统基本上都是64位的,32位的部分安装尝试过,但是没有去尝试解决问题,可能会有部分遗漏,毕竟版本太多,问题也是逐步发现并修复。关键问题解决途径会重点标记。

  1. 64位系统安装镜像无法在32位UEFI中启动,表现于引导菜单中无法识别启动盘。
    下载bootia32.efi,刻录U盘镜像后放入/efi/boot/目录下即可。
  2. Ubuntu 15.04以及之前的Server版本
    无法识别内置磁盘,暂时不知道如何处理。
  3. Ubuntu 15.10 Server版
    能识别磁盘,但是写入一定时间后随机报错,无法完成安装,/var/log/syslog中有错误日志:”mmc0: Timeout waiting for hardware interrupt”,参考此处说是老内核对eMMC磁盘的兼容性有问题,具体原因没找到(2016年9月20日更新:后面有解决办法,2017年3月22日更新:通过自己编译新内核完美解决)。
  4. Ubuntu 14.04.4 桌面版64位
    进入系统后屏幕一闪就黑了,实际上是内置显卡配置问题,在启动选择时按E,进入grub编辑模式,找到linux后的部分,加上nomodeset,按F10启动即可,此时是没有显卡加速的,因此图形会有点问题。此方法可以进入Live CD。
    安装过程中磁盘写入出问题,无法完成安装,放弃。
  5. Ubuntu 15.10 桌面版64位和Ubuntu 16.04.1桌面版64位
    这是一个相对来说比较好的版本,实现的功能也最多。

    • 安装系统
      使用nomodeset模式可以完成安装,只是最后一步安装grub会出错,需要使用安装盘进入grub手工引导,进入安装盘选择菜单时,按C进入grub,输入以下命令,其中*表示当前系统版本的内核,不同的系统不一样,比如vmlinuz-3.16.0-23-generic,可以使用Tab键补全,也有的系统有2个内核,比如Ubuntu 15.10,可随便选择一个,保证linux和initrd选择的内核相同即可。
      root=/dev/mmcblk0p2表示根目录的设备,这个可以进入Live CD模式通过命令”ls -l /dev”看看。

      正常情况下,可以进入系统了,如果不能进入系统,进入Live CD模式看看/var/log/syslog或者/var/log/dmesg。
    • 安装32位grub,参考来源这里,我这边做个简单的翻译
      • 打开终端安装依赖包:
      • 从下载grub代码:ftp://ftp.gnu.org/gnu/grub/
      • 编译32位grub:
      • 安装grub:
      • 替换启动项,也许/boot/efi/EFI/中没有ubuntu这个目录,使用它里面自带的目录即可:
      • 更新grub:
      • 参考文章说此时可以使用efibootmgr命令查看当前启动项,我测试后发现并不管用,必须要重启后才有用。
        另外参考文章说Ubuntu 15.04可以使用apt-get直接安装32位grub,我在15.10中测试失败:
    • 解决显卡加速问题
      前文说到可以使用nomodeset禁止Linux加载显卡驱动来避免加载驱动时导致显示错误,但是这样同时也失去了显卡加速,整个界面会很卡。经过很长时间的搜索,在这里找到了可用的解决方法,但是这种方式每次更新内核或者执行sudo update-grub时都需要重新修改一遍:

      2016年9月20日更新:另一种更好的方式来源于riverzhou,执行sudo vim /etc/default/grub,修改或者添加以下行:
    • 加载内置WiFi驱动
      进入系统后,执行下面的命令安装驱动,其中*使用自动补全即可。
      如果没有文件/sys/firmware/efi/efivars/nvram-*,可以到这里下载一个,测试过是OK的。
    • 屏幕背光关闭
      正常情况下,miix2安装了Linux后,发现无论如何屏幕背光都无法关闭,使用了riverzhou内核后启动时无法显示屏幕内容,但是等待屏幕休眠超时后,可以正常显示,并且屏幕背光也可以关闭,具体编译过程可以参考此贴
    • 问题
      • 【已解决】安装到这里,我本以为这个系统作为家用小服务器使用已经很完美了,各种使用都没太严重的问题。但当我往里面恢复数据时,还是会死机,会出现磁盘IO错误,在大量磁盘写入的情况下,磁盘出错。搜索了一下,没有发现解决方案,放弃。
        • 2016年9月11日更新:
          这个问题在Ubuntu论坛提出半年后,一位网友riverzhou终于发现了问题所在并且给出了一个解决方法。问题是由于CPU睡眠模式导致,在内核启动参数中设置CPU睡眠模式最高设置为C1即可。具体方法是,使用U盘安装时,修改引导参数(在前面加nomodeset的地方),添加intel_idle.max_cstate=1
          系统安装成功后,也在新的/boot/grub/grub.cfg中所有linux内核引导参数后加上这一参数即可。
          需要注意的是,每次更新内核后,需要重新修改这个文件,包含上面3D加速的修改。
        • 2016年9月20日更新:执行sudo vim /etc/default/grub,修改或者添加以下行:

          其中i915.force_backlight_pmic=1是riverzhou给的内核关闭屏幕的参数。
        • 2017年3月22日更新:使用riverzhou大大提供的内核,下载后编译安装,不需要加任何编译参数,完美解决。
  6. Debian 8.3 64位(debian-8.3.0-amd64-netinst.iso)
    此镜像为比较老的3.16内核,由于是网络安装,并且内置网卡在安装阶段无法使用(实际上在安装好之后也无法使用),需要使用外置USB网卡联网,我用的水星外置网卡需要从Debian官网下载驱动,放入启动U盘根目录或者firmware目录,图形界面安装完全无问题,磁盘读写也没问题,32位引导也没问题。可惜的是,无法开启内置显卡加速以及内置Wifi,使用上面Ubuntu的方法,均以失败告终。
  7. Fedora 23 64位(Fedora-Live-Workstation-x86_64-23-10.iso)
    4.2.3内核,比较新,但是镜像的格式需要比较大的改动才能够进入安装页面,下面是具体操作步骤:

    • U盘镜像制作好后,将bootia32.efi放入/EFI/BOOT目录下。
    • 在根目录创建/boot/grub目录,把U盘下的/EFI/BOOT下的fonts文件夹和grub.cfg文件复制到/boot/grub目录下,可能是bootia32.efi只认这个目录的引导文件,不这样做的话,开机引导就直接进入grub命令行界面了,无法使用grub配置文件。
    • 修改刚刚复制过来的/boot/grub/grub.cfg文件:
      • grub提示找不到linuxefi命令:将所有的linuxefi改成linux,一共3处。
      • grub提示找不到initrdefi命令:将所有的initrdefi改成initrd,一共3处。
      • 引导后会提示找不到label为”Fedora-Live-WS-x86_64-23-10″的磁盘:将”Fedora-Live-WS-x86_64-23-10″改成”Fedora-Live”,看了一下磁盘label,只有”Fedora-Live”,于是就这样改了,一共4处。
    • 进行过上面的操作后,可以进入引导选单界面了,选择”Troubleshooting”,然后选择”Start Fedora Live in basic graphics mode”启动,其实就是引导参数加上了nomodeset。直接启动会黑屏。
    • 启动后,选择安装,本以为一切顺利,结果还是在安装过程中死机了,推测还是内核太高,磁盘读写有问题,和Ubuntu 15.10以及之后的系统一样。
最终状态

经过一周左右的折腾,从网上找到一些问题的解决方法,但是还是没办法达到完美的使用的状态,最后还是找了一个精简版的Windows 10,搭配VMware 10使用。如果对于上面的问题,有解决的方法,希望大牛们可以不吝赐教。

2017年3月22日更新:目前安装Ubuntu 16.04的方法:
先使用启动参数nomodeset和intel_idle.max_cstate=1安装,完成后安装必须的编译工具,gcc,make之类,将riverzhou打过补丁的内核(目前我用的是4.10.y分支)Clone下来编译安装,去掉加上的内核参数即可。
来个截图:


下面总结一下目前折腾的结果,作为家用服务器的话,跑个crontab定期任务,一个Blog,一个小型网站,一个微信公众号,基本上完美了:

  • 没有尝试解决(优先级较低)
    • 重力感应(无法使用)
    • 屏幕旋转
      • Ubuntu 16.04.1下,可以调整屏幕方向,但是触屏方向没有跟着改,改了方向后触屏无法使用。
    • 前后摄像头(无法使用)
    • 麦克风(无法使用)
    • 声音(无法使用)
  • 已经解决
    • 屏幕背光关闭:使用riverzhou打过补丁的内核
    • 屏幕显示:首次安装时使用nomodeset,然后使用riverzhou打过补丁的内核,可开启3D加速。
    • 读卡器:Ubuntu 16.04.1安装好后即可使用,不支持写入
    • 显卡加速:后面使用riverzhou打过补丁的内核
    • 触屏:OK,不支持多点触摸
    • WiFi:Ubuntu中加载驱动,15.10和16.04.01经过测试可用。
    • USB:OK
    • 硬盘:Debian 8.3中完美,Ubuntu使用riverzhou打过补丁的内核
    • 内存:OK
2017年10月27日更新

目前4.13.9官方内核已经修复触屏和屏幕关闭的问题了,只需要使用64位uefi安装好Ubuntu 16.04,然后到http://kernel.ubuntu.com/~kernel-ppa/mainline下载最新版本的内核(目前是4.13.9)的deb包并且安装,然后重启就好了,下载地址:(http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.13.9/)

重启完毕之后,如果发现还存在死机的情况,那么在/etc/default/grub的GRUB_CMDLINE_LINUX中加上”intel_idle.max_cstate=1″应该会解决这个问题。

参考资料
  1. Installing Ubuntu on BayTrail tablets (version 2)
  2. UBUNTU (OR OTHER LINUX) ON THE ASUS TRANSFORMER BOOK T100
  3. 4.4r3, Miix 2 8inch doesn’t boot
  4. LATEST STEPS TO INSTALL UBUNTU ON THE ASUS T100TA
  5. 【求助】联想miix2 8安装Linux的问题(显示和eMMC磁盘识别问题)探讨和解决
  6. Installing Debian On Asus T100TA

Linux下为Zabbix新增监控:以磁盘IO监控为例

背景介绍

Zabbix是一款优秀的开源监控系统,支持主流操作系统,可以监控磁盘容量、CPU、内存、网络等指标,也可以自定义监控数据。目前我用Zabbix来监控我的博客所在服务器的运行情况。具体安装和使用方法可以Google搜索一下,网上很多教程,也比较容易。本文主要讲一下如何使用Zabbix做自定义监控。

问题描述

Zabbix默认带了很多监控项,这些监控项分布在一些默认的模板中,每个服务器可以匹配多个模板,从而使用模板中所有的监控项目(部分不支持的项目不会监控)。比如Linux模板中包含了针对Linux的常用监控,如下图所示:

只是内置的模板可能并不包含一些个性化的监控项,比如特定进程是否存活,或者磁盘IO读写情况。下面将以Linux为例讲解如何在Zabbix中添加自定义的监控项。

Linux磁盘监控方法

Linux下从启动以来总磁盘读写量可以通过/proc/vmstat文件读取到,磁盘读写量分别为pgpgin和pgpgout字段,单位为kB,这也是vmstat或者dstat读取数据的来源:

Linux下有很多命令可以获得磁盘读写量,比如vmstat,dstat等,但是这些命令启动时只能获得系统从启动以来平均磁盘读写速度,如果要获得当前磁盘读写速度,必须要等1秒,而且并不能反映每个采样点之间的磁盘读写情况,比如每分钟采样一个点,但是这个数据获得的是当前那一秒的磁盘读写量,不能反映这一分钟的平均磁盘读写量,这个数据会有波动而且不准确。

因此每隔一段时间,读取一次/proc/vmstat,将2次的差值与时间差值相除,即可得到2次读取间平均每秒的磁盘读写量。

我们必须提供一个方法,让Zabbix在获得采样点时,能够获得从上次采样点到本次采样点之间的平均数据,每次读取/proc/vmstat后,将时间和本次数据备份下来,以便下次使用。第一次运行由于没有上次数据,直接返回0或者返回-1表示没数据即可,下面给出python脚本实现:

使用方法:

添加监控到Zabbix
  1. 在被监控的服务器上(zabbix-agent)添加新的监控项:
    sudo vim /etc/zabbix/zabbix_agentd.conf.d/zabbix_vmstat.conf

    其中vmstat.[*]表示监控项可接收自定义参数,数据获得通过执行逗号侯庙的脚本获得,脚本参数就是监控项。这个参数即是/proc/vmstat文件中各项数值的第一列Key。
    具体的监控项会在最后代入*号表示的数据,比如pgpgout表示为vmstat.[pgpgout]。
  2. 重启zabbix-agent:
    sudo /etc/init.d/zabbix-agent restart
  3. 检查是否能够获得数据:
    zabbix_get -s 127.0.0.1 -k vmstat.[pgpgout]
    上述命令需要执行至少2次,如果成功,能会打印出当前磁盘IO写入数据(kB/s)。
  4. 在zabbix服务端管理页面进入:【组态】——【主机】——指定监控主机的【项目】——右侧【创建监控项】,按照下图所示填写后保存,读取的数据只需要将部分项目改掉即可,比如pgpgout改成pgpgin:

    名称:监控名,可以通过$1、$2取到参数名称,比如上例中$1可以取到字符串”pgpgout”。
    键值:要监控的数据键值,与之前配置的vmstat.[*]对应,以参数代替*。
    单位填B/s即可。
    自订倍数:因为读取出来的数据单位是kB,因此乘以1024是实际的Byte/s的值。
    数据更新间隔:每分钟获得一个数据点,即60秒。
    应用集:将其放到Disk中,也可以自订一个应用集。

  5. 添加成功后,过1分钟回到【监测中】——【最新数据】,就可以看到当前的数据和统计图:

总结

Zabbix自定义监控,如果数据可自定义获得方式,那么只需要按照上述方法,便可以添加自定义统计和监控数据,相关的监控还可以配置触发器和事件以便更加复杂的操作。

使用Epoll实现简单的Echo Server和Client,附带性能分析和性能数据(一)

背景介绍

IO多路复用以其高性能、高并发、低系统开销等特性成为了后端Server框架开发的必备技术。
本文给出一个使用Epoll的Echo Server实现,读者很容易就可以将其扩展到自己的模块中去。
同时本文也给出了使用Epoll的客户端实现,读者可将其改造为一个性能压测工具。相比于别的Epoll示例,本文客户端实现使用单线程IO多路复用,也支持多线程,只是在Echo Server本机测试这种IO压力不大的CPU压力场景下,由于CPU线程切换的开销存在,性能并不如单线程的好。
所有代码只保留最必要保留的部分,对其中一些操作做了详细的注释,避免踩坑。

代码地址

由于代码会更新,实际代码与本文用于测试的代码会有行数不一致的情况,性能差别应该不会太大。

https://github.com/godmoon/MoonLib/blob/master/src/CppNet.cpp
https://github.com/godmoon/MoonLib/blob/master/src/CppNet.h
https://github.com/godmoon/MoonLib/blob/master/gtest/src/CppNetTest.cpp

代码说明

代码在gtest测试框架下运行,实现功能如下:
主线程启动服务端线程和1个客户端线程(支持多个客户端线程),每个客户端线程和服务端线程都管理一个Epoll池。客户端线程中的Epoll池管理多个客户端连接。
每个客户端连接以网络序发送当前微秒级别UTC时间戳到服务端,每个请求8个字节,服务端收到包后将数值加1并且返回,客户端对比发送的数据和接收的数据,正确则进行成功计数,否则进行失败计数。
主线程每隔一秒打印上一秒成功和失败的请求数以及成功率,运行指定秒数后,通知客户端结束,所有客户端线程结束后,通知服务端线程结束,主线程结束。

测试结果

在我本地Ubuntu14.04虚拟机上测试30秒结果大约为17~20W/s的请求量:

压测过程中服务器状态

在压测过程中,要考虑到性能瓶颈在哪儿,在Linux下,3大性能指标:磁盘IO,CPU,网络,下面我们逐一看看性能瓶颈在哪儿。

  • 磁盘IO
    代码中并没有显式对磁盘进行操作,在压测过程中通过命令”dstat -cdlmnpsy”得到结果如下:

    查看-dsk/total-列,发现并没有大量的磁盘读写,磁盘很平静,符合理论预期。
  • CPU
    由于测试属于本机压测,网络不存在太大问题,猜测性能瓶颈很有可能出现在CPU,压测环境的CPU为4核i5-3470,虚拟机也分配了4个核,所以虚拟机理论上可以充分利用主机的CPU资源。
    通过命令”top”可以看到压测进程”MoonLibTest”的CPU占用保持在190%以上,如下所示:

    这是因为客户端和服务端各为一个线程,每个线程可以占用一个核的100%的资源,加起来最多占用200%,在top页面按下大写的”H”,或者shift+h,可以看到线程的资源占用情况:

    其中前2个占用CPU 99%左右的线程分别为客户端和服务端,最后一个线程52941为主线程,代码中为启动客户端线程的线程,目前阻塞在thread::join()处,不占用CPU。
    所以目前单进程情况下,客户端和服务端基本已经处于CPU满负荷状态。
  • 网络
    对于网络这块的分析,由于陷入了一些误解,导致用了好几个小时来解决问题。下面完整讲一下分析过程,前面的部分基本上都是错的,后面会逐渐纠正前面的认识错误。
    一开始,我错误地认为由于是本机压测,每个包收发各8个字节,20W请求,猜想网卡吞吐量应该是
    8Bytes*200000/s=1600000Bytes/s,差不多1.5MB/s
    结果通过命令”dstat -N lo”可以查到当前环路网卡lo的数据吞吐量如下所示:

    结果显示网卡流量居然有23MB/s,出乎意料,难道是dstat命令统计错了?再通过命令”sar -n DEV 1 100″得到如下结果:

    查看rxkB/s列和txkB/s列,果然是将近23MB/s。
    通过网络数据的原始值/proc/net/dev查看,执行命令”while true;do cat /proc/net/dev;sleep 1;done”,也得到一样的结果。
    通过命令”sudo tcpdump -i lo port 20000 -w ~/tmp/test.cap”抓包,才想起来我居然忘了包头,每个包有Mac帧,IP头,TCP头以及数据,这四部分分别占用字节数如下:
    Mac帧:14字节
    IP头:20字节(无选项)
    TCP头:20字节+选项12字节=32字节
    数据:8字节
    应答包和上述一样,所以请求包和应答包应该是各74字节,在计算1KB以上数据的大包时包头大小可忽略,但是8字节的小包,包头长度就是数据长度的好几倍,不可忽略包头大小。
    这样算下来吞吐量应该是
    74Bytes*200000/s=14800000Bytes/s,约为14MB/s
    但是以上结果仍然与实际测试值23MB/s有冲突,而且差距还不小。
    还有一个疑问出现在包量的统计上,通过上面sar命令可以看到包量接收和发送均为40W/s左右,基本上是测试结果的2倍,这个问题我思考了很久,最后才想到,程序中统计请求量,是客户端一次收发完整请求,算一次请求量,每秒20W请求量,而对于网卡,一次请求量有一收一发,也就是2个包,因此包量是40W/s,符合理论分析。
    那么流量呢?也一样,一次请求,一收一发,都是走的本地环回网卡lo,于是一次请求对应2个包,统计的流量应该是单个包的翻倍,那么发送和接收的吞吐量按照计算应该是14MB/s*2=28MB/s,然而和实际的23MB/s的数据仍然有冲突。
    在寻找原因的过程中,无意中使用到iptraf这个工具,通过这个工具,查看到包大小为60字节:

    一开始我并没有注意到60字节这个数据,只以为是统计误差,于是我手工telnet到本机的一个服务随便发了点小数据,通过抓包得到了3个包,查看Length字段得到3个包大小分别为158、181和66字节:

    理论上统计的lo网卡接收数据量应该增加:
    158+181+66=405字节
    通过测试前后得到的lo网卡接收字节数(也可以通过ifconfig命令查看到)


    发现统计的接收字节数却只增加了:
    1681288224-1681287861=363字节
    二者差值为:
    405-363=42字节
    发现什么了吗?3个包差距42字节,平均每个包统计少了14字节,而联系到前面的包大小实际74字节,iptraf显示60字节,也是少统计14字节,而14字节刚好是Mac帧长度。可以猜测是lo网卡的流量统计并未计算Mac帧的长度14字节。但我并未找到相关文档对此进行说明,也不确定非lo网卡是否会计算Mac帧,这个留到后面补充。
    至此,重新计算理论流量:
    60Bytes*200000/s*2=24000000Bytes/s,约为22.8MB/s,和实际值相符。
    性能瓶颈是否在网络上,暂时还不确定,因为还不知道本地环回网络处理的性能瓶颈是多少。可以通过提高数据包大小来测一下吞吐量是否提升。后面有方法了再补充。
  • 结论
    从上面的分析,可以得到瓶颈最可能出现在CPU上,如果提高服务端线程数,使服务端运行在多个CPU核,可能可以提高每秒请求量。
其他讨论
  • 在写本文之前,我也在网上找了很多Epoll的典型用法,但是很多文章存在以下问题:
    1. 很多文章都是复制粘贴的代码,并没有给出性能数据和性能数据的对比,也没有对一些参数的使用做详细解释。
    2. Epoll只应用在服务端,客户端使用多线程或者只是发送请求接收应答,没有体现出Epoll带来的性能提升。如果对于本文的服务端使用多线程阻塞方式客户端压测,大约只能达到4W/s的性能数据(我在这里曾经纠结了很久,一直怀疑是我的服务端写的有问题,完全没想到性能瓶颈在客户端,直到我用我的客户端代码去压测Redis只有4W/s左右的性能,而同样的环境下,官方基于Epoll的压测工具有12W/s左右的性能),典型值应该是10W/s以上。
  • 不同的线程、客户端数量对于测试结果的影响比较大,一般客户端数量从1开始递增,达到一定程度,由于服务端的性能受限,压测数据便不再增加,此时再增加客户端数量会导致额外开销从而使性能略微降低,比如本文中使用30个客户端能达到20W/s的性能数据,如果使用100个客户端则只有19W/s左右的数据,如果达到300个客户端,则性能数据降低到15W/s左右:

    客户端数量每秒请求量(万)
    11
    59
    1019
    10020
    30015
    50013
    100011
    50009
  • 本文测试的客户端为长连接方式,如果为短连接方式,每次请求会增加额外的TCP握手,性能会到多少呢?后面有时间补充。
  • 几个特别注意的点:
    1. 发送基础类型的数据,要使用网络序。本机测试可能没问题,不过哪天跨平台就可能产生服务端和客户端主机序不一致的问题,所以在发送网络数据时最好养成良好的习惯。遇到这种情况建议使用Protobuf等更加通用的协议。
    2. 服务端对于监听的端口要设置REUSEADDR,防止服务重启时端口处于TIME_WAIT状态而无法重新监听。
    3. 服务端要忽略或者手工处理SIGPIPE信号,防止客户端关闭后,服务端往Socket中写入数据导致服务端收到此信号导致服务挂掉。
    4. Epoll实现要监听EPOLLRDHUP:要考虑到对端断开连接的情况,对端断开连接的话,2.6.17及之后的内核版本会抛出一个EPOLLRDHUP|EPOLLIN事件,之前的版本会抛出EPOLLIN事件,此时通过read读取的返回值为0,所以可以用以下两种方式检测对端关闭:
      1. 2.6.17及之后的内核版本:监听EPOLLRDHUP和EPOLLIN事件同时发生,稳妥起见,可以在此事件中判断read()==0
      2. 2.6.17及之前的内核版本,在EPOLLIN事件中检测read()==0
    5. 服务端对于监听的端口以及链接端口要设置成非阻塞,避免服务端进程被阻塞,可能读者会有疑问所有的读写都是在Epoll告诉程序可读可写之后才进行的为什么还会阻塞呢?考虑这种情况:当写入缓冲区只剩下1K,而要写入的数据有2K的时候,阻塞状态会阻塞直到2K数据全部写完,而非阻塞状态只会写入1K并返回,所以非阻塞状态下的写入需要考虑写了一半数据的情况,本文中代码并未考虑这种情况。
    6. 由于本文提供的是多线程程序示例,客户端和服务端处于不同的线程中,最终压测的性能还受到CPU核心数的影响,可以采用设置CPU亲和力(Linux下使用taskset命令)的方法将不同的线程固定到不同的CPU核心上来提高性能,这个网上有一些资料,我后面再专门写一篇文章来表达一下我的看法。

如何:通过浏览器访问Linux下的Shell

背景介绍
因为公司无法直接访问外网,只能通过HTTP代理访问外网,所以就无法直接连接外网服务器的Shell,但是可以通过HTTP代理访问服务器的Web页面,所以有了需要通过浏览器的Http协议访问Linux下的Shell的需求。

环境介绍
Ubuntu 14.04
Apache2

操作步骤

  1. Apache2等安装方法这里就不介绍了,简单来说,在Ubuntu下执行以下命令即可:
    sudo apt-get install apache2
  2. 安装Shell in a box,安装完成后访问http://[IP或者域名]:4200应该可以直接访问shell了。
    sudo apt-get install shellinabox
    因为需求中只能访问80端口,后面将会使用Apache的80端口做转发,将请求转发到本机的4200端口。
  3. 修改Shell in a box配置,去除ssl依赖(此处如果可以使用HTTPS协议的话,也可以不做):
    sudo vim /etc/init.d/shellinabox
    在# Set some default values下添加下面一行,表示禁用SSL。
    SHELLINABOX_ARGS="--disable-ssl"
  4. 修改Apache配置
    1. 增加proxy模块:

       
    2. 创建shellinabox配置文件:/etc/apache2/conf-available/shellinabox.conf,添加转发命令,将/shell转发至http://127.0.0.1:4200/,即Shell in a box的默认监听端口。如果修改了默认端口,此处应该做相应修改。

       
    3. 启用shellinabox配置

       
  5. 重启Shell in a box服务和Apache服务:

     
  6. 检查看是否生效:访问http://[IP或者域名]/shell

 参考资料

  1. 介绍了有哪些开源的浏览器访问Shell的项目
  2. 本文使用的Shell in a box的项目主页
  3. Shell in a box的使用方法