编程类开放书籍荟萃

关于开源图书有人在网络上做了大量整理,本文为大家刊载《免费的编程中文书籍索引》

书山有路勤为径,学海无涯苦作舟!

语言无关类

操作系统

智能系统

web服务器

版本控制

NoSQL

MySQL

项目相关

Web

大数据

编程艺术

其他

语言相关类

AWK

C/C++

CSS/HTML

Dart

Fortran

Java

JavaScript

PHP

iOS

Android

Python

Ruby

Shell

Go

Groovy

LaTeX

LISP

Lua

Haskell

R

Scala

Swift

Perl

Prolog

Vimscript

读书笔记及其它

读书笔记

测试相关

性能测试工具 wrk 安装与使用

今天给大家介绍一款开源的性能测试工具 wrk,简单易用,没有Load Runner那么复杂,他和 apache benchmark(ab)同属于性能测试工具,但是比 ab 功能更加强大,并且可以支持lua脚本来创建复杂的测试场景。

wrk 的一个很好的特性就是能用很少的线程压出很大的并发量, 原因是它使用了一些操作系统特定的高性能 I/O 机制, 比如 select, epoll, kqueue 等。 其实它是复用了 redis 的 ae 异步事件驱动框架. 确切的说 ae 事件驱动框架并不是 redis 发明的, 它来至于 Tcl的解释器 jim, 这个小巧高效的框架, 因为被 redis 采用而更多的被大家所熟知.

wrk GitHub 源码:https://github.com/wg/wrk

安装

wrk只能运行于 Unix 类的系统上,也只能在这些系统上便宜,所以我们需要一个Linux或者macOs。

不得不说,使用了 Win10之后方便很多。

必备条件:

  • Win10 RS及以上版本
  • 启用Ubuntu子系统

1、Win10 系统通过bash命令,切换到Ubuntu子系统。
然后需要安装一下编译工具,通过运行下面命令来安装工具:

# 安装 make 工具
sudo apt-get install make

# 安装 gcc编译环境
sudo apt-get install build-essential

安装 gcc 编译环境的时候最好挂一下VPN,速度会快些。
image

2、安装完成之后使用 git 下载 wrk 的源码到本地:

https://github.com/wg/wrk.git

3、切换到git的wrk目录,然后使用make命令:

cd /mnt/盘符/wrk目录

make

image

编译完成之后,目录下面会多一个 wrk 的文件。

image

测试

使用以下命令来测试一下:

./wrk -c 1 -t 1 -d 1 http://www.baidu.com

image

简单说一下wrk里面各个参数什么意思?

  • -t 需要模拟的线程数
  • -c 需要模拟的连接数
  • –timeout 超时的时间
  • -d 测试的持续时间

结果:

  • Latency:响应时间
  • Req/Sec:每个线程每秒钟的完成的请求数
  • Avg:平均
  • Max:最大
  • Stdev:标准差
  • +/- Stdev: 正负一个标准差占比

标准差如果太大说明样本本身离散程度比较高. 有可能系统性能波动很大.

如果想看响应时间的分布情况可以加上–latency参数
image

我们的模拟测试的时候需要注意,一般线程数不宜过多,核数的2到4倍足够了。 多了反而因为线程切换过多造成效率降低, 因为 wrk 不是使用每个连接一个线程的模型, 而是通过异步网络 I/O 提升并发量。 所以网络通信不会阻塞线程执行,这也是 wrk 可以用很少的线程模拟大量网路连接的原因。

在 wrk 的测试结果中,有一项为Requests/sec,我们一般称之为QPS(每秒请求数),这是一项压力测试的性能指标,通过这个参数我们可以看出应用程序的吞吐量。

总结

关于 wrk 已经介绍完毕了,之所以写这篇文章的目的是为了接下来对 ASP.NET Core做一个性能对比测试(Java,NodeJS,Python等)时需要用到该工具,敬请大家期待。


本文地址:http://www.cnblogs.com/savorboard/p/wrk.html
作者博客:Savorboard
欢迎转载,请在明显位置给出出处及链接


ASP.NET Core 性能对比评测(ASP.NET,Python,Java,NodeJS)

性能是我们日常生活中经常接触到的一个词语,更好的性能意味着能给我们带来更好的用户体检。比如我们在购买手机、显卡、CPU等的时候,可能会更加的关注于这样指标,所以本篇就来做一个性能评测。

性能也一直是我们开发人员一直追求的一个目标,我们在做语言选择,平台选择,架构选择的过程中都需要在性能之间做衡量。

同样性能对 .NET Core 团队来说也是至关重要的,一项新技术的诞生,除了对生产力的提高,还有技术团队对性能的追求。

今天,我们就来做一个对比测试,来看看微软的这样新技术性能到底怎么样,俗话说的好:“是骡子是马,拉出来溜溜”。

下面让我开始吧。

目录

测试目标

在测试之前,我们必须要明确我们本次测试想达到的一个目标。本次测试主要是测试应用程序的一个吞吐量。其中QPS,并发数,响应时间是我们衡量吞吐量的几个重要指标。

以下是本次对比测试的任务目标:

编号 对比方 系统环境 宿主环境 测试目标
1 ASP.NET Core vs ASP.NET Core Windows Kestrel vs IIS 相同平台不同宿主间性能差距
2 ASP.NET Core vs ASP.NET Windows IIS vs IIS 相同平台相同宿主不同框架间性能差距
3 ASP.NET Core vs ASP.NET Windows Kestrel vs IIS 相同平台不同宿主不同框架间性能差距
4 ASP.NET Core vs Python Django Linux Kestrel vs uwsgi 相同平台不同语言不同宿主不同框架间性能差距
5 ASP.NET Core vs Java Servlet Linux Kestrel vs Tomcat 相同平台不同语言不同宿主不同框架间性能差距
6-1 ASP.NET Core vs NodeJS Express Linux Kestrel vs self host 相同平台不同语言不同宿主不同框架间性能差距
6-2 ASP.NET Core vs NodeJS Koa Linux Kestrel vs self host 相同平台不同语言不同宿主不同框架间性能差距

测试工具

工欲善其事,必先利其器。

首先我们需要一个压力测试工具,本次我们使用 wrk,有关于wrk的介绍和使用,请查看我的 这篇博客

然后我们需要一个性能监控工具,因为wrk已经会给我们输出吞吐量相关指标,所以我们只需要一个监控CPU,内存等的工具即可。本次我们使用 Windows 自带的性能监视器。

Windows 性能监视器的打开方式:开始-->运行-->perfmon
PS: 在下面的监视器图中如果你发现cpu并没有100%,那是因为使用的虚拟机占用了一部分cpu,所以计算方式应该是虚拟机的cpu使用量+物理机cpu使用量。

环境准备

既然做测试,首先肯定是具有相同的运行环境,以下是本次测试使用到的软件和硬件环境。

  • 软硬件环境
名称 操作系统 职责 CPU 核心数 内存
物理机器1 Windows 10 RS1 Web Server && 负载生成 Intel Core i5-4590 4 16G
虚拟机器2 Ubuntu Server 16.04 Web Server Intel Core i5-4590 2 1G
虚拟机器3 Nano Server Web Server Intel Core i5-4590 2 1G

其中 虚拟机器2 为 “物理机器1” 使用 win 10 的 Hyper-v 技术搭建的一个虚拟机,所以有几个指标对于本次测试至关重要。

image

虚拟机设置为了2个虚拟核心,以便于在压力测试的过程中利用到多核特性。其中的虚拟机保留百分比,需要设置为100%,来分配两个物理cpu所有资源给它。占综系统资源百分比设置为50,也就是说虚拟机最多利用本地50%的CPU资源,虚拟机限制设置为100。

  • 源代码

AspNet 在 GitHub 有一个开源的性能测试项目叫benchmarks,之前新闻中23倍的性能也是出自于本测试项目, 为了客观,本次测试并不使用该项目,所有项目均我们自己新建,并且使用当前流行的框架,为了排除代码因素的干扰,我们使用最简单的 Hello World!。

如果你觉得本代码不够客观公正,欢迎在GitHub上Fork本项目,修改后给我提交PR,我会重新进行测试,并且更新本博客。

GitHub: https://github.com/yuleyule66/AspNetCoreBenchmarksCompare

开始测试

wkr命令参数:

wrk -t 2 -c 50 -d 20 --latency http://xxx

因为已经分配了2个核心给虚拟机使用,所以开的是双线程。使用这个参数是我经过多次测试,得到的一个最佳的模拟效果。

1 – ASP.NET Core vs ASP.NET Core(Kestrel vs IIS)

ASP.NET Core

  • 环境:物理机器1
  • OS:Windows 10 RS 1
  • Host:Kestrel
wrk -t 2 -c 50 -d 20 --latency http://localhost:5000

Running 20s test @ http://localhost:5000
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.49ms   21.72ms 358.18ms   98.99%
    Req/Sec    23.28k     1.98k   27.48k    92.13%
  Latency Distribution
     50%    0.00us
     75%    6.87ms
     90%   12.76ms
     99%   28.58ms
  913567 requests in 20.02s, 115.00MB read
Requests/sec:  45636.43
Transfer/sec:      5.74MB

ASP.NET Core

  • 环境:物理机器1
  • OS:Windows 10 RS 1
  • Host:IIS 10.0
wrk -t 2 -c 50 -d 20 --latency http://localhost:5001

Running 20s test @ http://localhost:5001
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.30ms    5.81ms  22.24ms   76.75%
    Req/Sec     7.61k   455.21     8.12k    90.00%
  Latency Distribution
     50%    3.14ms
     75%    9.02ms
     90%   15.62ms
     99%   17.17ms
  302880 requests in 20.02s, 44.77MB read
Requests/sec:  15130.97
Transfer/sec:      2.24MB

总结

QPS(Kestrel):45636.43
QPS(IIS):15130.97

这个结果难免令人诧异,程序部署在IIS上和使用Kestrel竟然差别如此之大,我们知道实际上即便部署在IIS上,实际上内部还是调用的Kestrel,但是测试结果告诉了我们答案。可能是由于IIS进一步的http封装导致的吧,毕竟IIS提供了那么多的其他功能。

以下是Windows的性能监视器,两个的曲线图差不多我就放一个了:
image

  • 红色:CPU使用率
  • 蓝色:内存使用率

2 – ASP.NET Core vs ASP.NET(IIS vs IIS)

ASP.NET Core

  • 环境:物理机器1
  • OS:Windows 10 RS
  • Host:IIS
wrk -t 2 -c 50 -d 20 --latency http://localhost:5001

Running 20s test @ http://localhost:5001
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.30ms    5.81ms  22.24ms   76.75%
    Req/Sec     7.61k   455.21     8.12k    90.00%
  Latency Distribution
     50%    3.14ms
     75%    9.02ms
     90%   15.62ms
     99%   17.17ms
  302880 requests in 20.02s, 44.77MB read
Requests/sec:  15130.97
Transfer/sec:      2.24MB

ASP.NET

  • 环境:物理机器1
  • OS:Windows 10 RS
  • Host:IIS
  • .NET Framework 4.6 + MVC5
wrk -t 2 -c 50 -d 20 --latency http://localhost:10280

Running 20s test @ http://localhost:10280
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.94ms    5.58ms  22.82ms   80.90%
    Req/Sec     9.10k   444.04     9.42k    95.00%
  Latency Distribution
     50%    3.00ms
     75%   10.10ms
     90%   13.57ms
     99%   16.45ms
  362177 requests in 20.00s, 89.80MB read
Requests/sec:  18104.50
Transfer/sec:      4.49MB

总结

QPS(ASP.NET Core + IIS):15130.97
QPS(ASP.NET + IIS):18104.50

看到这个结果的时候,其实我还是有一点小惊讶的,不仅仅是因为ASP.NET跑出了1.8K QPS这样的成绩,而是通过Stdev可以看出,ASP.NET 在应对高请求高并发的时候,还是相当的稳定的。这个结果说明了,在同样Windows+IIS环境中,ASP.NET是具有优势和竞争力的,可以预见 ASP.NET 应该还不会淘汰的太快。

Windows性能图我就不上了,基本上和上面一样 CPU 100% 的使用率。

3 – ASP.NET Core vs ASP.NET(Kestrel vs IIS)

ASP.NET Core

  • 环境:物理机器1
  • OS:Windows 10 RS 1
  • Host:Kestrel
wrk -t 2 -c 50 -d 20 --latency http://localhost:5000

Running 20s test @ http://localhost:5000
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.49ms   21.72ms 358.18ms   98.99%
    Req/Sec    23.28k     1.98k   27.48k    92.13%
  Latency Distribution
     50%    0.00us
     75%    6.87ms
     90%   12.76ms
     99%   28.58ms
  913567 requests in 20.02s, 115.00MB read
Requests/sec:  45636.43
Transfer/sec:      5.74MB

ASP.NET

  • 环境:物理机器1
  • OS:Windows 10 RS
  • Host:IIS
  • .NET Framework 4.6 + MVC5
wrk -t 2 -c 50 -d 20 --latency http://localhost:10280

Running 20s test @ http://localhost:10280
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.94ms    5.58ms  22.82ms   80.90%
    Req/Sec     9.10k   444.04     9.42k    95.00%
  Latency Distribution
     50%    3.00ms
     75%   10.10ms
     90%   13.57ms
     99%   16.45ms
  362177 requests in 20.00s, 89.80MB read
Requests/sec:  18104.50
Transfer/sec:      4.49MB

总结

QPS(ASP.NET Core + Kestrel):45636.43

QPS(ASP.NET + IIS):18104.50

这个结果应该是在预料之中的,大概是3倍的性能差距吧。但是我觉得和之前微软宣传的23倍的性能,是有很大差距的。

4 – ASP.NET Core vs Python Django

注意,以下我们开始使用到虚拟机器2了,我们要在Windows性能监控器里面查看CPU使用率,还需要再添加2个计数器。

物理处理器 \Hyper-V Hypervisor Logical Processor(*) \ %Total Run Time

虚拟处理器 \Hyper-V Hypervisor Virtual Processor(*) \ %Guest Run Time

ASP.NET Core

  • 环境:虚拟机器2
  • OS:Linux
  • Host:Kestrel
wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:5000/
Running 20s test @ http://192.168.2.48:5000/
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.39ms    5.33ms  33.05ms   77.20%
    Req/Sec    13.43k     1.32k   17.95k    74.75%
  Latency Distribution
     50%    2.00ms
     75%    8.15ms
     90%   13.75ms
     99%   15.80ms
  534787 requests in 20.01s, 67.32MB read
Requests/sec:  26730.83
Transfer/sec:      3.37MB

image


Python Django

  • 环境:虚拟机器2
  • OS:Linux
  • Host:uwsgi
  • Python 2.7.12 + Django 1.10.2

服务端宿主运行命令:

sudo uwsgi --http :8000 --file HelloWorldWebApp/wsgi.py --processes=2 --threads==2 --daemonize=/var/log/django.log

结果:

wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:8000
Running 20s test @ http://192.168.2.48:8000
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    23.40ms   12.23ms  78.13ms   74.81%
    Req/Sec   792.64    143.13     1.25k    67.10%
  Latency Distribution
     50%   21.16ms
     75%   31.25ms
     90%   38.26ms
     99%   53.75ms
  31591 requests in 20.09s, 3.01MB read
  Socket errors: connect 0, read 31591, write 0, timeout 0
Requests/sec:   1572.64
Transfer/sec:    153.67KB

image


总结

QPS(ASP.NET Core + Kestrel):26730.83

QPS(Python Django + Kestrel ):1572.64

不知道是我运行的方式不对还是怎么,这个差距还是蛮大的,大概是17倍的差距。看来Python Web 在做针对于做大请求并发情况下,还是弱了一点。

5 – ASP.NET Core vs Java Servlet

C# 和 JAVA 一直是两大阵营的开发人员喜欢讨论的话题,为了避免有阵营偏见,JAVA的源代码是我委托我们一个JAVA同事编写的,并且委托由他部署的,并且已经交代了他避免使用jsp,由Servlet直接输出。

ASP.NET Core

  • 环境:虚拟机器2
  • OS:Linux
  • Host:Kestrel
wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:5000/
Running 20s test @ http://192.168.2.48:5000/
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.39ms    5.33ms  33.05ms   77.20%
    Req/Sec    13.43k     1.32k   17.95k    74.75%
  Latency Distribution
     50%    2.00ms
     75%    8.15ms
     90%   13.75ms
     99%   15.80ms
  534787 requests in 20.01s, 67.32MB read
Requests/sec:  26730.83
Transfer/sec:      3.37MB

image


Java Servlet

  • 环境:虚拟机器2
  • OS:Linux
  • Host:Tomcat 7.0 + jdk 1.7
wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:8080/j2eeWebApp/hello
Running 20s test @ http://192.168.2.48:8080/j2eeWebApp/hello
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.93ms    6.17ms  68.17ms   81.53%
    Req/Sec     9.22k     1.01k   14.06k    70.50%
  Latency Distribution
     50%    1.75ms
     75%    9.91ms
     90%   14.39ms
     99%   22.10ms
  367733 requests in 20.05s, 93.70MB read
Requests/sec:  18338.73
Transfer/sec:      4.67MB

image


总结

QPS(ASP.NET Core + Kestrel):26730.83

QPS(Java Servlet + Tomcat):18338.73

通过这个结果我们可以看出,在性能上 ASP.NET Core 已经超越了Java。不说太多了,怕被喷…

6 – ASP.NET Core vs NodeJS

ASP.NET Core

  • 环境:虚拟机器2
  • OS:Linux
  • Host:Kestrel
wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:5000/
Running 20s test @ http://192.168.2.48:5000/
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.39ms    5.33ms  33.05ms   77.20%
    Req/Sec    13.43k     1.32k   17.95k    74.75%
  Latency Distribution
     50%    2.00ms
     75%    8.15ms
     90%   13.75ms
     99%   15.80ms
  534787 requests in 20.01s, 67.32MB read
Requests/sec:  26730.83
Transfer/sec:      3.37MB

NodeJS

  • 环境:虚拟机器2
  • OS:Linux
  • Host:self host
wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:1337
Running 20s test @ http://192.168.2.48:1337
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.40ms    5.23ms  31.25ms   79.47%
    Req/Sec    10.32k     0.88k   11.37k    90.25%
  Latency Distribution
     50%    2.08ms
     75%    8.32ms
     90%   13.19ms
     99%   15.93ms
  410902 requests in 20.02s, 61.13MB read
Requests/sec:  20522.89
Transfer/sec:      3.05MB

image

***************更新1:NodeJS 添加Web框架*******

Express框架,cluster模式

wrk -t 2 -c 30 -d 20 --latency http://192.168.2.48:1337
Running 20s test @ http://192.168.2.48:1337
  2 threads and 30 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.97ms    1.45ms  32.23ms   83.97%
    Req/Sec     7.83k     0.90k    8.82k    91.50%
  Latency Distribution
     50%    2.00ms
     75%    2.50ms
     90%    3.50ms
     99%    6.00ms
  311896 requests in 20.01s, 66.03MB read
Requests/sec:  15583.99
Transfer/sec:      3.30MB

Koa框架,cluster模式

wrk -t 2 -c 30 -d 20 --latency http://192.168.2.48:1337
Running 20s test @ http://192.168.2.48:1337
  2 threads and 30 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.74ms    0.86ms  13.59ms   86.65%
    Req/Sec     8.79k   804.39     9.98k    87.75%
  Latency Distribution
     50%    1.99ms
     75%    2.00ms
     90%    2.96ms
     99%    4.83ms
  349856 requests in 20.02s, 53.38MB read
Requests/sec:  17478.73
Transfer/sec:      2.67MB

从测试结果可以看出,Koa框架性能略高于Express框架。

**************End***********


总结

QPS(ASP.NET Core + Kestrel):26730.83

QPS(NodeJS):20522.89 (非cluster模式)
QPS(NodeJS Express):15583.99 (cluster模式)
QPS(NodeJS Koa):17478.73 (cluster模式)

这个结果着实让我吃了一惊,NodeJS性能竟然如此惊人,比JAVA要快10%。作为一个解释性语言这个性能可以说达到了极致,虽然在测试之前知道NodeJS采用的是异步IO,但还是被测试结果震惊了。

===========更新1=========

NodeJS 在加入了Web框架之后,性能仍然不弱。


不知道是不是因为NodeJS没有经过什么Web框架,直接输出的结果。所以我需要再加测一个ASP.NET Core 通过中间件直接输入结果的性能,这次我要使用微软的测试项目benchmarks。

wrk -t 2 -c 50 -d 20 --latency http://192.168.2.48:5000/plaintext
Running 20s test @ http://192.168.2.48:5000/plaintext
  2 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.69ms    5.03ms  18.30ms   80.38%
    Req/Sec    25.06k     4.14k   29.19k    83.33%
  Latency Distribution
     50%  806.00us
     75%    6.82ms
     90%   12.62ms
     99%   15.63ms
  1002476 requests in 20.10s, 126.20MB read
Requests/sec:  49874.57
Transfer/sec:      6.28MB

My God !!!

总结

以下是测试结果的汇总统计:

编号 对比方 系统环境 宿主环境 测试结果(QPS)
1 ASP.NET Core vs ASP.NET Core Windows Kestrel vs IIS 45.6k vs 15.2k
2 ASP.NET Core vs ASP.NET Windows IIS vs IIS 15.2k vs 18.2k
3 ASP.NET Core vs ASP.NET Windows Kestrel vs IIS 45.6k vs 18.2k
4 ASP.NET Core vs Python Django Linux Kestrel vs uwsgi 26.7k vs 1.57k
5 ASP.NET Core vs Java Servlet Linux Kestrel vs Tomcat 26.7k vs 18.3k
6-1 ASP.NET Core vs NodeJS Express Linux Kestrel vs self host 26.7k vs 15.6k
6-2 ASP.NET Core vs NodeJS Koa Linux Kestrel vs self host 26.7k vs 17.5k

image

作为微软的下一代 ASP.NET 框架,ASP.NET Core没有让我们失望,通过本次测试,我们大概对ASP.NET Core的性能心里有底了。一个圈子的良好发展需要社区的共同参与,也希望大家共同为.NET Core社区贡献自己的力量,同时也希望看到本篇文章的CTOs们以后在平台和框架选择的过程中考虑一下ASP.NET Core,因为她真的很优秀。

如果你觉得本篇博客对您有帮助的话,感谢您的【推荐】,如果你对.NET Core感兴趣可以关注我,我会定期在博客分享关于.NET Core的学习心得。


========更新1 :2016-10-17 感谢园友“幻天芒” 关于NodeJS的贡献======

有园友反应NodeJS项目没有使用web mvc框架,所以特更新,同时感谢 “幻天芒” 在github向nodeJS项目提交的PR。

1、添加node 多核cpu cluster 模式
2、添加node koa框架和express框架测试

更新测试结果。


========更新2 :2016-10-19 添加ASP.NET Core 在Windows Nano Server的测试结果======

环境:虚拟机器3,和Linux硬件一样

wrk -t 2 -c 30 -d 20 --latency http://192.168.2.52:8000
Running 20s test @ http://192.168.2.52:8000
  2 threads and 30 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.08ms  709.98us  31.25ms   77.30%
    Req/Sec    13.98k     1.38k   15.80k    87.75%
  Latency Distribution
     50%    1.00ms
     75%    1.03ms
     90%    2.00ms
     99%    3.45ms
  556354 requests in 20.03s, 70.04MB read
Requests/sec:  27780.50
Transfer/sec:      3.50MB

这个测试结果和微软的测试结果是一致的,Nano Server大概比在Linux上高出5-10%的性能。


========更新3 :2016-12-30 添加 WebListener 测试 ======

WebListener 是基于 Http.sys 实现的非跨平台只能运行于 Windows 的一个 web 服务器,其目的我觉得是为了替代iis的性能不足问题。

引用自QQ群 Lyrics:我的理解是这样的,Kestrel是跨平台的,定义了一套通用的 feature,然而目前在windows平台上,Kestrel所具备的feature 并没有 http.sys 提供的那么强大,为了使老系统能顺利迁移到core上面,微软不得已搞了一个能支持所有http.sys 的web server,就是weblistener, weblistener 能完整的利用 http.sys 的特性,在windows上功能完整。

测试结果:

Windows ASP.NET Core Kestrel :35.5k

Windows ASP.NET Core WebListener:27.9k

Kestrl 大概比 WebListener 高出 5-10%的性能。


本文地址:http://www.cnblogs.com/savorboard/p/dotnet-benchmarks.html
作者博客:Savorboard
欢迎转载,请在明显位置给出出处及链接


ASP.NET Core: 全新的ASP.NET !

背景

最新版本的 ASP.NET 叫做 ASP.NET Core (也被称为 ASP.NET 5)   它颠覆了过去的 ASP.NET。

什么是 ASP.NET Core?

ASP.NET Core 1.0 是一个开源跨平台的开发框架,用于构建基于云的现代 Web 应用 。它是从底层开始重新构建来提供性能优良的Web应用开发框架,可以部署在云上或者本地服务器上。另外,它使得 ASP.NET 应用更加精简和模块化(可以根据你的应用需要向里面添加其他模块),跨平台(你可以很容易的在 Windows, Mac or Linux 上开发和部署你的应用),云优化(你可以在云上在云上部署和调试你的应用)。

以前的版本

对于使用 ASP.NET 旧版本的我们来说,这意味着什么?

如果你正在使用旧版本的 ASP.NET 或者你有 WebForms 的开发背景,那么你将会认识到 ASP.NET Core 有多完美,这感觉起来就像从古典的 ASP 时代来到全新的 ASP.NET 的世界。

1

现在,让我们来一探究竟

下面列出 ASP.NET Core 1.0 的核心变化.

跨平台的运行时

你可以在 OSX 和 Linux上运行 ASP.NET Core 应用,这对于 ASP.NET 来说,这具有跨时代的意义,也给 ASP.NET 开发者和设计师们带来了全新的体验。ASP.NET Core 具有两个运行时,这意味着你可以选择不同的运行环境来部署你的应用,使得你的应用将更加灵活。

ASP.NET Core 1.0 是一个 ASP.NET 的重构版本,它运行于最新的 .NET Core。它是模块化的,允许开发者以插件的形式添加应用所需要的模块,大多数的功能都将作为插件提供并通过 NuGet 程序包管理。这样做的一个好处就是你可以升级应用的一个模块,但丝毫不会影响其他模块;另外,.NET Core 是一个跨平台的运行时,因此你可以在 OSX 或 Linux 操作系统上部署你的应用;它也是一个云优化的运行时,用于在云上部署和调试应用;.NET Core 可以和你的应用程序一起被部署,当服务器上有多个 .NET Core 版本时, 你依旧可以运行 ASP.NET Core 应用。

你也可以创建只运行在 windows 下完整 .NET 框架的 ASP.NET Core 应用。

ASP.NET 4.6 是最新的完整 .NET Framework 的发布版本,它允许你可以利用所有的 .NET 组件并且具备向后兼容能力。如果你计划将应用迁移到 .NET core,那么你需要做适量的修改,因为 .NET Core 相对于完整 .NET Framework 来说有所限制。

需要明确的是,ASP.NET 4.6 更加成熟。它如今久经考验并且现已发布并可使用。ASP.NET Core 1.0 是1.0 发布版本,包含 Web API 和 MVC,但是现在还没有 SignalR 和 Web Pages。,它也不支持VB 和 F# 语言。

ASP.NET Core 不再只依赖Visual Studio

ASP.NET Core 的跨平台,让它不再只依赖 Visual Studio,开发者和设计师们可以在自己喜欢的环境上工作。比如 Sublime Text,WebStorm ,这真是太棒了!

新的工程解决方案结构

如果你使用 Visual Studio 创建了一个空的 ASP.NET Core 工程,那么你将会看到下面的惊喜。(除非你没有使用之前的 ASP.NET 创建过任何项目)

2

你感觉到惊喜了吗?新的工程结构完全不一样了, 工程模板焕然一新,包含以下的新文件:

· global.json: 你可以在这里放置解决方案的配置信息和工程之间的引用。

· Program.cs: 这个文件包含了 ASP.NET Core RC2 应用的 Main 方法,负责配置和启动应用程序。

· src folder: 包含组成你应用程序的全部项目代码。

· wwwroot: 你的静态文件将被放置在这个文件夹,它们都将作为资源直接提供给客户端,包含 HTML,CSS 和 JavaScript 文件。

· project.json: 包含项目设置。在 ASP.NET Core中,你可以通过使用 NuGet 程序包管理工具(NPM)添加 NuGet 包或者编辑这个文件来管理从属。你可以通过任何文本编辑器来编辑这个文件,如果你使用 Visual Studio 2015,,这将会更加 轻松,因为它的智能提示会帮助你找到合适的 NuGet 包作为从属。project.json 就像下面这样。

3

· startup.cs 这个主要放置你 ASP.NET Core 的 stratup 和 configuration 代码,下面就是 stratup 类的样子。

4

ConfigureServices 方法定义了你应用程序使用的服务,Configure 方法用来定义组成请求管道的中间件。

· References: 它包含了 .NETCoreApp 第一个版本运行时的引用。

WebForms

是的,WebForms 不再是 ASP.NET 5 的一部分,这真令人悲伤。你可以继续使用 VS2015 的 .NET 4.6 来构建 Web Forms 应用,但是却不能体会 ASP.NET 5 的新特性了。

我已经开发了很多年从小型到大型的企业级 Web Forms 应用。 我很喜欢 Web Forms,,事实上我还会继续支持在各种论坛使用 WebForms 的社区,比如 http://forums.asp.net。但是我们是时候进步了,去学习一些新东西。这是学习 ASP.NET MVC 最后的时间了,就像过去的许多事物,你要么去适应,要么被淘汰。

除了 WebForms, the .NET Core 也没有包含 Windows Forms, WCF, WPF, Silverlight 等等。

VB.NET and F#

目前,在当前 ASP.NET Core 1.0 RC2 版本中, VB.NET 和 F# 也不被支持。

MVC Core 统一架构

5


ASP.NET Core 将见证 MVC, Web API 和 Web Pages(可能包含)组合在一个架构中,它被称为 ASP.NET MVC Core。尽管当前发布版本中,还不支持 Web Pages and SignalR。

在之前的 ASP.NET MVC 中, MVC 控制器和 Web API 控制器是不同的。 一个 MVC 控制器使用基类 System.Web.MVC.Controller ,一个 Web API 控制器使用基类 System.Web.Http.ApiController 。 在 MVC Core 中,会为它们提供一个共同的基类,就是 Microsoft.AspNetCore.Mvc.Controller

对于 HTML Helpers 来说,MVC 和 Web Pages 的合并是非常有可能的。 Web Pages 编程模型对当前版本来说还不适用,所以我们还不能负责任地说下一步计划合并哪些特性。 但是我们可以预测到,传统的 MVC 模型绑定将会出现。

View Components

在之前 ASP.NET MVC 中,, Html.Action() 帮助方法一般用于调用一个 sub-controller。ASP.NET MVC Core 将会使用新的 View Components 用来代替使用Html.Action() 的部件。

View Components 支持完全异步,这允许你创建异步的视图组件。

下面是一个简单的视图组件的例子,根据身份会返回个人介绍。

using Microsoft.AspNetCore.Mvc;  
using MVC6Demo.Models;  
using System.Threading.Tasks;  
using System.Collections.Generic;  
  
namespace MVC6Demo.ViewComponents  
{  
    public class PersonListViewComponent : ViewComponent  
    {  
        public async Task<iviewcomponentresult> InvokeAsync(string status) {  
            string viewToUse = "Default";  
            bool isFiltered = false;  
  
            PersonModel model = new PersonModel();  
  
            if (status.ToLower().Equals("registered")) { 
                viewToUse = "Registered"; isFiltered = true; 
            }  
                  
            var p = await GetPersonAsync(status, isFiltered);  
            return View(viewToUse,p);  
        }  
          
        private Task<ienumerable<person>> GetPersonAsync(string status, bool isFiltered) {  
            return Task.FromResult(GetPerson(status,isFiltered));  
        }  
        private IEnumerable<person> GetPerson(string status, bool isFiltered) {  
            PersonModel model = new PersonModel();  
  
            if (isFiltered)  
                return model.GetPersonsByStatus(status);  
            else  
                return model.GetAll;  
  
        }  
    }  
}  </person>

下面是 View Component 的视图:

<h3>Person List</h3>  
<ul>  
    @foreach (var p in Model) {  
        <li>@string.Format("{0} {1}",p.FirstName,p.LastName)</li>  
    }  
</ul>

这里展示了如何在主视图中调用 View Components

<div>   
    @await Component.InvokeAsync("PersonList", new { type = "Registered" })
</div>

新指令: @inject, @using, @inherits

ASP.NET MVC Core 提供了少量新指令。 下面我们来看看如何使用 @inject。 @inject 指令允许你注入一个类中的方法到你的视图中。

这是一个简单的类,来展示一些异步的方法。

using System.Threading.Tasks;  
using System.Linq;  
  
namespace MVC6Demo.Models  
{  
    public class Stats  
    {  
        private PersonModel _persons = new PersonModel();  
  
        public async Task<int> GetPersonCount() {  
            return await Task.FromResult(_persons.GetAll.Count());  
        }  
  
        public async Task<int> GetRegisteredPersonCount() {  
            return await Task.FromResult(
                      _persons.GetAll.Where(o => o.Status.ToLower().Equals("registered")).Count());  
        }  
  
        public async Task<int> GetUnRegisteredPersonCount() {  
            return await Task.FromResult(
                     _persons.GetAll.Where(o => o.Status.ToLower().Equals("")).Count());  
        }  
    }  
}

现在我们就可以在视图中使用 @inject 指令来调用那些方法:

@inject MVC6Demo.Models.Stats Stats  
  
@{  
    ViewBag.Title = "Stats";  
}  
  
<div>

这是不是很酷?

查看我关于 ASP.NET MVC 新指令详细例子的文章: Getting Started with ASP.NET MVC Core

Tag Helpers

ASP.NET MVC Core 另外一个非常酷的东西就是 Tag Helpers。对于之前的 HTML Helpers,Tag Helpers 是可选的替代语法。

所以相比于以下代码:

@using (Html.BeginForm("Login", "Account", FormMethod.Post, 
        new { @class = "form-horizontal", role = "form" }))
            {
                @Html.AntiForgeryToken()
                <h4>Use a local account to log in.</h4>
                <hr />
                @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                <div class="form-group">
                    @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
                    <div class="col-md-10">
                        @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })
                    </div>
                </div>
}

你可以使用这些代码:

<form asp-controller="Account" asp-action="Login" method="post" class="form-horizontal" role="form">  
    <h4>Use a local account to log in.</h4>  
    <hr />  
    <div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>  
    <div class="form-group">  
        <label asp-for="UserName" class="col-md-2 control-label"></label>  
        <div class="col-md-10">  
            <input asp-for="UserName" class="col-md-2 control-label" />  
            <span asp-validation-for="UserName" class="text-danger"></span>  
        </div>  
    </div>  
</form>

ASP.NET Core 不止可以部署在IIS上

14年前,ASP.NET 平台基本只能部署在一种服务器上,那就是 IIS。几年之后,Visual Studio Development Web Server(也叫作“Cassini”)作为一种开发服务被使用,但是它们最终都是调用 System.Web 作为应用程序和 Web 服务器中间的主机层。System.Web 主机与 IIS 耦合度很高,所以要想运行在另一台主机上会非常困难。

后来 OWIN 作为应用程序和 Web 服务器中间的接口出现。 Microsoft 开发了 Katana 作为一个 OWIN 的实现,可以部署 ASP.NET Web API, SignalR 和其他第三方框架,这些框架可以在 IIS 和 IIS Express, Katana’s 自托管主机和自定义主机。

ASP.NET Core 是不强调主机的,它在 Katana 和 OWIN 上行为一致。ASP.NET Core 也可以部署在 IIS, IIS Express 或者自托管在你自己的进程里。另外,ASP.NET Core 也会包含一个叫做 Kestrel 的 Web 服务器,它建立在 libuv 上,主要用于 iOS 和 Linux 操作系统。

新的HTTP请求管道

ASP.NET Core 提供了一种更加模块化的 HTTP 请求管道, 你可以只添加你需要的组件。这个管道不再依赖 System.Web,通过降低管道中的开销,你的 app 性能更加优良,更好的调谐 HTTP 协议栈。新的管道基于 Katana 项目经验,同时支持 OWIN。

动态的Web开发

Visual Studio 2015 中另一个非常酷的特性就是支持动态编译。在过去的 ASP.NET 中,当我们修改了应用的后台代码,我们需要重新编译并且运行才能看到页面的变化。 在新版本的 Visual Studio 中,你不需要再做这些额外的步骤,仅仅是保存你的修改和刷新浏览器即可。

6

这是在刷新页面之后的输出:

7

Attribute Routing: [controller] 和 [action] 标记

在过去的 MVC 和 Web API 中,使用路由属性可能会导致一些问题,尤其是你正在做一些代码重构。这是因为路由必须设定为字符串类型,当你修改了控制器的名字,你就必须修改路由属性的字符串

MVC Core 提供了新的 [controller] 和 [action] 标记,它们可以解决这个问题。下面这篇文章重点说明了这些新标记的用法。 : ASP.NET MVC 6 Attribute Routing.

集成的依赖注入 (DI)

ASP.NET Core 内嵌了对依赖注入和 Service Locator 模式的支持,这意味着你不在需要通过第三方依赖注入框架 Ninject 或 AutoFac。

集成 Grunt, Gulp and Bower

Visual Studio 2015 内嵌了对流行开源 Web 开发工具的支持。 Grunt 和 Gulp 可以帮你自动化构建 Web 开发工作流, 你可以使用它们来编译和压缩 JavaScript 文件。Bower 是一个用于客户端库的管理工具,包含 CSS 和 JavaScript 库。

内置的AngularJs模板

AngularJs 是当前最流行的前端框架之一,用于构建单页面应用程序(SPAs)。Visual Studio 包含了用于创建 AngularJs 模块,控制器,指令和工厂。

8

对 GruntJS 的支持使得 ASP.NET 成为一个用于构建客户端 AngularJs 应用的优秀服务器端框架。 当完成一个版本,你可以自动合并和压缩全部 AngularJs 文件。查看我的关于开始在 ASP.NET 中使用 Angular 和 Angular2 的文章 。

· ASP.NET 5: Jump Start to AngularJS with MVC 6 Web API

· ASP.NET Core:Getting Started with AngularJS 2

SignalR 3

ASP.NET Core 也是以 SignalR 3 为基础,这使得你可以向云连接的应用程序添加实时功能。查看我之前的 SignalR 例子: ASP.Net SignalR: Building a Simple Real-Time Chat Application

Web.Config

在 ASP.NET Core 中,混乱的 web.config 文件被新的云就绪配置文件代替,它称作 “config.json”。微软希望开发人员更容易地在云中部署应用程序,并使得应用能够根据特殊环境自动的读取正确的配置参数。

这是一个新的配置文件的样子:

9

由于 ASP.NET Core 都是插件化的,你需要配置 Stratup 类的源代码,就像下面这样:

 public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath);

            builder.AddEnvironmentVariables();
            Configuration = builder.Build();
        }
        public IConfigurationRoot Configuration { get; }
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddTransient<MVC6Demo.Models.HeroStats>();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseDeveloperExceptionPage();


            app.UseMvc(m => {
                m.MapRoute(
                    name: "default",
                    template: "{controller}/{action}/{id?}",
                    defaults: new { controller = "Home", action="Index"});
            });
        }

xUnit.Net: .NET 新的单元测试工具

在之前的 ASP.NET MVC 中,默认的测试框架是 Visual Studio 单元测试框架(有时候也叫作mstest),这个框架使用 [TestClass] 和 [TestMethod] 特性来描述一个单元测试。

ASP.NET Core 使用 xUnit.net 作为它的单元测试框架。这个框架使用 [Fact] 特性来代替 [TestMethod] 特性,也消除了对 [TestClass] 属性的依赖。

绝对的免费和开源

是的,ASP.NET Core 被作为一个开源项目托管到 GitHub上, 你可以查看源代码,并下载并提交你的更改。

我认同开源的 .NET 会产生重大的意义,它产生了积极的商业意义和社区意义,十分感谢微软所做出的工作。

以上 ASP.NET Core 1.0 的新特性和新概念的介绍,是为了更好的帮助我们使用 ASP.NET Core 进行开发,同时在开发过程中,我们还可以借助一些好的工具来提高开发效率,并减少代码量,如 ComponentOne Studio for Asp.net MVC,它兼容 ASP.NET Core RC2 版本,是一款快速轻量级的控件来满足用户的所有需求。

 

文章来源:By Vincent Maverick Durano, 10 Jun 2016 

原文链接:http://www.codeproject.com/Articles/1104668/Introducing-ASP-NET-Core-The-New-ASP-NET-in-Town

葡萄城控件产品网站:http://www.gcpowertools.com.cn/
葡萄城企业软件网站:http://qy.grapecity.cn/
葡萄城技术支持论坛:http://gcdn.gcpowertools.com.cn/forum.php

58HouseSearch项目迁移到asp.net core

作者:李国宝
链接:https://zhuanlan.zhihu.com/p/22764927
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

前言

58HouseSearch这个项目原本是基于ASP.NET MVC 4写的,开发环境是Windows+VS2015,发布平台是linux+mono+jexus,这样看来整个项目基本已经满足跨平台的需求。

这样一来,本来我是没什么动力去做迁移的,好好的东西闲着没事干才迁移呢。

不过,这不国庆了么?穷人不是在家穷游天下么?所以…真的有点闲着没事干了。

迁移可行性探讨

项目迁移前,我们还是先来讨论一下迁移可行性。为嘛要进行可行性探讨呢?原因是.NET CORE是一个跨平台的框架,和上一代的.NET存在不兼容。

个人总结一下,迁移的主要的问题在于:代码不兼容、类库不兼容、严重依赖Windows API或者COM组件等。

代码不兼容

代码不兼容其实不算麻烦。毕竟代码是活的,你我也是活的,不就是一个改字罢了。花点时间慢慢改,总是能搞掂的。

类库不兼容

要不就弃用,要不就找替代品。

严重依赖Windows API或者COM组件

额?找替代品,找不到可用替代品的话。放弃吧,这个项目别考虑迁移了。

这个故事告诉我们,做跨平台项目的时候,少点用系统API或者组建。

回到58HouseSearch项目上面。

这个项目的代码基本都是我写的,所以重写代码没什么问题。
依赖的类库有下面几个:

AngleSharp是用来解析HTML的类库,用linq的方式来操作HTML,用起来实在爽快。

如果这货在.net core上不能跑,我应该立马放弃了。
不过,这个实在给力…


Newtonsoft.Json

在这个项目里面主要是用来记录PV数据的,非核心功能,可有可无。不过看了下nuget上的介绍,也是支持.net core的。

剩下log4net…嗯,并不支持log4net。不过这个就更加是非核心内容了,直接丢了。
PS:考虑后期加入Nlog替代log4net。

至于依赖Windows API之类的,在这个项目里面基本没有,所以略过…

准备工作

友情提示:

  1. Visual Studio Community 2015 with Update 3 下载镜像来安装。

错误操作如下:


正确打开方式:

  1. 安装.NET Core SDK和.NET Core之后再安装.NET Core 1.0.1 – VS 2015 Tooling Preview 2
  2. 安装.NET Core 1.0.1 – VS 2015 Tooling Preview 2 这货的可能会报错0x80072f8a未指定的错误

解决方案见下图:


详细见链接:安装DotNetCore.1.0.1-VS2015Tools.Preview2.0.2出现0x80072f8a未指定的错误

上面都弄好之后,理论上在VS2O15-新建项目里面可以看到ASP.NET CORE的模板了。

如下图:

项目迁移

新建空白ASP.NET CORE项目

新建好了之后如下图:

Nuget获取引用

NuGet Gallery

NuGet Gallery

添加Controllers文件夹

然后把之前项目的Controllers拷贝过来,改掉命名空间,去掉无用代码,添加相应引用。

添加Views文件夹

本项目直接把之前项目的Views拷贝过来是完全没有问题的。

静态文件处理

asp.net core MVC中的文件结构和asp.net mvc的文件结构略有不同。

asp.net core MVC在view中“IMG/Little/PaleGreen.png”对应的文件对应于“项目路径/webroot/IMG/Little/PaleGreen.png”;

asp.net mvc中,对应路径为“项目/IMG/Little/PaleGreen.png”。

因而,我们的所有静态文件都应该放到:webroot文件夹下。

上面的都做完了之后,项目结构如下:


接下来就是改代码了。

代码迁移

Startup.cs添加MVC1

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        //添加MVC框架
        services.AddMvc();

    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env,
    ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        //启用静态文件中间件
        app.UseStaticFiles();
        //启动MVC路由
        app.UseMvcWithDefaultRoute();
        //设置默认页面
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=House}/{action=Index}/{id?}"); 
        });


    }
}

改写GetHTMLByURL方法

之前的方法:


.net core重写了HttpWebRequest,变成了WebRequest,所以上面的代码废了。

重写如下:

public static string GetHTMLByURL(string Url, string type = "UTF-8")
{
    try
    {
        Url = Url.ToLower();

        System.Net.WebRequest wReq = System.Net.WebRequest.Create(Url);
        // Get the response instance.
        System.Net.WebResponse wResp = wReq.GetResponseAsync().Result;
        System.IO.Stream respStream = wResp.GetResponseStream();
        using (System.IO.StreamReader reader = new System.IO.
        StreamReader(respStream, Encoding.GetEncoding(type)))
        {
            return reader.ReadToEnd();
        }
    }
    catch (System.Exception ex)
    {

        return string.Empty;
    }

}

改写Controller代码

嗯,换了命名空间,别的一句都没改直接拉过来了…略过。

发布到ubuntu

Install for Ubuntu 14.04, 16.04 & Linux Mint 17

第一步

//Ubuntu 14.04 / Linux Mint 17
sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list'
sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
sudo apt-get update


//Ubuntu 16.04
sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
sudo apt-get update

第二步

sudo apt-get install dotnet-dev-1.0.0-preview2-003131

安装好了之后,输入 dotnet -v 应该能看到版本信息,如下图:


这样的下,一句完成了ubuntu 运行asp.net core的环境搭建了。

project.json里面隐藏的坑

dependencies

NET Core 1.0.1 – VS 2015 Tooling Preview 2模板的asp.net core 版本和ubuntu 的asp.net core 版本不一致。

根据微软爸给的教程,我们在ubuntu上安装的.NET Core 1.0.0,见上图。

然而我们创建项目的模板是.NET Core 1.0.1,见下图:


怎么办?要不升级ubuntu的asp.net core,要不降级。

由于没找到.NET Core 1.0.1 ubuntu的安装包,所以我选择了降级到.NET Core 1.0.0.

其中需要把Microsoft.NETCore.App version 、Microsoft.AspNetCore.Server.Kestrel、Microsoft.AspNetCore.Mvc 这三个节点都改成“1.0.0”。如下:

"dependencies": {
  "Microsoft.NETCore.App": {
    "version": "1.0.1",
    "type": "platform"
  },
  "Microsoft.AspNetCore.Diagnostics": "1.0.0",
  "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
  "Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
  "Microsoft.Extensions.Logging.Console": "1.0.0",
  "Microsoft.AspNetCore.Mvc": "1.0.1",
  "Microsoft.AspNetCore.StaticFiles": "1.0.0",
  "Newtonsoft.Json": "9.0.1",
  "AngleSharp": "0.9.8.1"
},

publishOptions

发布输出包括Views文件夹

"publishOptions": {
  "include": [
    "wwwroot",
    "web.config",
    "Views"
  ]
},

runtimes

runtimes 配置为模板运行平台。
详细见链接:Project.Json

"runtimes": { "ubuntu.14.04-x64": {} }

上面都弄好之后,跑一下看

dotnet restore

dotnet run

来个请求看看:

jexus转发/反向代理

ASP.NET Core “完整发布,自带运行时” 到jexus

Mac 软件推荐(续)之程序猿篇

在前面一篇文章“Mac 软件推荐续(!程序猿篇)” (文章取名装X失败, 悲伤)中, 我已经介绍了一些大众化的软件, 当然作为程序猿的你也应该参考参考.
本篇文章将介绍一些可以提高程序猿工作效率的一些软件和工具及相关配置.

Mac built-in

首先介绍的就是我觉得应该熟悉 Mac 内置的一些软件及配置.

trackpad 配置

  1. 启用 Tap to click: 在 System Preferences -> Trackpad 中启用, 用 tap 替换 click 的操作, 明明轻轻 tap 就可以完成的, 为何还要用力点击才 OK. 现在偶尔用其他人电脑非得用力 click 就太纠结了.
    同时, 还有 “右键”功能, Secondary click, 用两个手指 tap 弹出右键菜单.
    mac trackpad 设置
  2. 开启单词选词查询:
    选中某个中英文单词后, 三指 tab 会弹出词典释义. 这个在之前一篇文章中也有介绍.
  3. Scroll 方向: 这个道是自己习惯就好. 由于我刚开始从 Win 转向 Mac 的时候习惯用 Win 的那种方式, 于是就没有开启 Scroll direction: natural, 然后也一直沿用至今.
  4. 其他手势: 有必要熟悉一下, 比如知道在 Win 环境下用 win+d 可以显示桌面, 相应的功能在 Mac 下如何做.

快捷键

作为程序猿, 肯定离不开各种快捷键. 对于 Mac 内置的一些快捷键, 我们还是很有必要知道的. 基本的复制/粘贴就不说了, 常用的还有

空格键: 预览
cmd + ,: 设置
cmd + -/=: 缩小/放大
ctrl + u: 删除到行首(与zsh冲突, zsh中是删除整行)
ctrl + k: 删除到行尾
ctrl + p/n: 上/下移动一行或者前/后一个命令
ctrl + b/f: 光标前/后移char
esc + b/f: 光标前/后移word(蛋疼不能连续work)
ctrl + a/e: 到行首/行尾
ctrl + h/d: 删前/后字符
ctrl + y: 粘贴
ctrl + w: 删除前一个单词
esc + d: 删后一个单词
ctrl + _: undo
ctrl + r: bck-i-search/reverse-i-search, 输入关键字搜索历史命令

上面的这些快捷键特别是在敲命令时还是很有用的(可能有的确实是在命令行中才生效), 特别是结合 zsh 自动补全等功能. 比较 DT 的是就是 esc 一起用的时候, 不能连续使用. 举个例子, terminal 中输入了 git push origin source, 光标在末尾, 这时按住ctrl 不放, 按一下 w 即向前删除一个单词, 第一次按 w 删除 source, 再按 w 删除 origin. 而 esc + d 不能这样结合使用(如下 gif连续按就不 work), esc 必须中途释放再按才能 work.

bash自动补全

啥? 你说上面快捷键 ctrl + w 等不太好按? 按键特别别扭?
你需要做的就是将 caps lock 映射为 ctrl, Keyboard -> Modifier Keys修改, 目前我笔记本上的 ctrl 键无效. 不过, 一般情况下我用我的 HHKB, 这种映射方式正好符合 HHKB 的布局. (其实我是在买 HHKB 之前就修改的这个映射)

另外, 借助之前介绍的Karabiner, 可以将一些常用的方向键(上下左右)重新映射一下, 比如我目前是 s + h/j/k/l 来表示方向, 手不用太移动就能直接按方向(HHKB 本身按方向太麻烦, Mac 内置键盘有方向键还需要大幅度移动手), 用起来方便多了.

Mac 内置的更多的快捷键列表可以参考 Mac 官网

其他还有一些常用的软件的快捷键, 可以用之前介绍的软件 cheetsheet, 长按 cmd, 可弹出当前 active 的软件的快捷键.

截图

这个从快捷键中单独列出来了, 就强调下这个功能.

cmd + shift + 3 截取整个屏幕.
cmd + shift + 4 部分窗口, 出现十字供选取, 若此时按空格键(这个技能得点赞), 会选取当前应用的窗口, 再 tap 即可完成截图.

上面快捷键是截图后以文件形式保存在桌面(默认是桌面, 当然你也可以自己修改保存位置), 再上面快捷键基础上再同时按 ctrl 就会把图片保存在内存/剪贴板中, 直接去相应窗口粘贴即可.

home brew

类似 centos 的 yum, ubuntu 的 apt-get, 能够方便管理安装软件包.
Mac 上类似的应用还有port, 我刚开始试用过 port, 貌似 brew 上的源会多一些.
brew-cask 是 brew 的一个加强版, 可以安装一些桌面应用, 例如 chrome 等等之类的.

这里就不多介绍了, 详情可以到官网查看.
brew
brew-cask

iTerm2

iTerm2官网有介绍功能. 以下是觉得可能常用的功能.

  1. 分屏功能
    • cmd + d 竖着分屏, cmd + shift + d 横着分屏
    • cmd + t 新建一个 tab, cmd + num 切换到第 num 个 tab
    • 当前窗口含有分屏时, 通过 cmd + [cmd + ] 来进行切换小的分屏
  2. 热键 设置一个热键, 比如我的是 alt + 空格, 弹出 iTerm2, 且以半透明的方式显示在当前 active 的窗口上面.
    iTerm2 hotkey
  3. 搜索
    • cmd + f搜索输入关键字后, 匹配的会黄色高亮, 此时按 tab 或者 shift + tab 会自动向后/前以word 的方式选中高亮的, 并自动 copy 到剪切板.
    • cmd + alt + e, 在所有的 tab 中全局搜索, 搜索出候选项后, 再选着你想要进入的 tab.

iTerm2 search

  1. 其他
    • 新版本的 iTerm2 还支持直接在控制台里 ls 图片文件(图片显示在控制台里).(如上图下半部分, 连 gif 都支持)
    • 自动识别控制台里的内容, 如含有链接或者本地文件路径可以用 cmd 加点击的方式直接打开链接或者文件(如下图上半部分). 这个功能很重要呢, 比如在编译过程中, 出现了 warning 或者 error, 一般会打印出具体文件路径, 此时直接从控制台就能打开文件进行 fix 了.
    • 自动补全, iTerm2 本身是支持自动补全的(cmd + ;), 不过建议直接结合后面的zsh使用. cmd + shift + h 剪贴板历史(下图最后一行).
    • 一些高级的功能目前可能处于测试版本, 你若用的稳定版是不支持的, 需要到官网下测试版. 还有更多的功能请到 iTerm2 官网探索吧.

iTerm2 imgcat

zsh

这个墙裂推荐啊. 结合 oh my zsh, 丰富的插件资源.

语法高亮, 自动补全等特别好, 在此推荐的几个插件或功能.

  1. git: 当前目录若是在一个 git repo 下面的话, 会自动显示当前的分支信息等等. 然后可以自己搞一些 alias, 简写命令, 比如我常用的一些.
alias gs=’git status’
alias gb=’git branch -va’
alias gco=’git checkout’
alias ga=’git add’
alias gc=’git commit -m’
alias gp=’git push’
alias gfom=’git fetch origin master’
alias gfod=’git fetch origin develop’
alias grod=’git rebase origin/develop’
alias grom=’git rebase origin/master’
  1. autojump: 这个也炒鸡赞. 会自动记录你 cd 过的目录, 下次你直接 j keyword 就会自动 cd 到以 keyword 匹配的目录. 输入 d会展示当前会话访问过的目录, 然后对应目录有标号, 接下来按标号即可跳转.
  2. osx: 举个最简单的例子, 比如你现在正在 finder 中浏览一个很深的目录, 现在突然想 cd 到这个目录去做一些命令操作. 如果你用xtrafinder 这样的软件的话道有这样的功能, 如果配上这个插件, 你直接输入 cdf (cd finder)就自动 cd 到 finder 打开的目录下.
  3. zsh-autosuggestions, 如下图所示, 我在 app-in-mac 这个目录下, 刚输入了 git, 此时光标还在 p 前面, zsh 就已经自动给我补全了 git push origin source, 此时我只要按 ctrl + e 跳转到行尾(所以熟悉上文中的快捷键很有必要啊), 回车即可执行命令了.

iTerm2 zsh plugins

更多的, 还是请到官网查看.

sublime text

文本编辑器, 也有丰富的插件支持, 直接官网看吧. 这个 App, 我用得也不是很多.

这里分享一个小的功能, 怎么在命令行用 sublime 打开特定的文件. 其实就是添加一个软链即可. (直接 open filename 会以文件默认关键的软件打开)

➜ app-in-mac git:(source) ✗ subl dungeon-game.cpp
➜ app-in-mac git:(source) ✗ which subl
/usr/local/bin/subl
➜ app-in-mac git:(source) ✗ ls -la /usr/local/bin/subl
lrwxr-xr-x 1 tanglei admin 62 1 24 2016 /usr/local/bin/subl -> /Applications/Sublime Text.app/Contents/SharedSupport/bin/subl

Vim

介绍 Vim 的文章也很多了. 这里就不详细展开了. 分享下我用的部分插件. (最近被 IntelliJ IDEA 搞得恶心了, 准备尝试抛弃),
为了让多台电脑同步我的 vim 配置/插件等, 我直接放 github 了(ref vimconfigs), 不同电脑只需要再建一个软链到github 中的 vimrc 即可.

vim 自动补全

  • Vundle/Pathogen: 插件管理, 我用的Pathogen, 直接将下面 github repo clone 到 ~/.vim/bundle/ 目录下即可
  • NERDTree: 文件目录树nerdtree github src
  • YouCompleteMe: YouCompleteMe github src 自动补全, 对C系列, 结合其他的可支持 Java/Python/Js 等, 跪求 Scala 支持
  • ctrlp.vim: 快速搜索文件
  • minibufexpl.vim: 会把最近打开的文件列出来方便跳转, github src
  • conque-term: shell 跑在 vim 里面, github src
  • ag: 代码搜索, 可结合 ctrlp.vim, 如果后者搜索太慢的话, github src
  • tagbar/taglist: 标签, 能显示类结构信息等, tagbar github src
  • vim-surround: 处理诸如(), "", [] 等配对信息, github src, ref
  • vim-easymotion: 快速跳转, 关键字后会给匹配到的标记, 再选标记并跳转(类似后文介绍 Chrome 插件的Vimium中的链接标记并跳转功能:按键 f 会将本文所有链接突出显示并用字母标记, 然后按相应的字母则会新开标签页打开). github src, ref
  • vim-powerline: 增强状态栏 github src
  • vim-indent-guides: 缩进可视化, github src

具体效果等配置方法可以参考下面的两篇文章, 插件具体用法可阅读具体插件的 doc.

Reference

Dash

其实介绍前文 介绍 Alfred 已经提到过, 这里再介绍一下. 程序猿应该必备啊. 内置各种语言, 各种环境的各种文档. 该 App 还提供各种 API 供其他工具交互使用. 例如 Vim(不是想象当中自动补全功能, 只是能够快捷地搜索 API), Sublime 等. (p.s 要是有人写了一个 Vim 插件, 能够支持调用 dash 的 API(如果有的话) 自动补全代码, 那应该会很受欢迎的)

dash

其他App

chrome

插件

  • AdBlock: 广告屏蔽;
  • EditThisCookie: 修改 cookie;
  • Evernote Web Clipper: 印象笔记;
  • JSONView and JSONLint for Google Chrome™: 请求返回的json进行beautify方便查看;
  • Markdown Here: 在富文本输入markdown, 渲染成 html;
  • Markdown Preview Plus: 渲染 .md 文件, 相当于 preview markdown;
  • Open Screenshot: 网页截图, 能够自动下拉截长图;
  • Postman: 请求伪造/抓包等, 也可以用curl;
  • Proxy SwitchySharp: proxy 切换;
  • RescueTime: 前文有介绍的RescueTime;
  • undirect: google/baidu 搜索结果, 点击直达网站, 这个貌似不太好用了. 征求替代品;
  • Vimium: 操作 vim 一样操作浏览器, 移动查找等功能, 还有前文提到的快速标记链接并跳转;

Charles

类 Windows 下 Fiddler 抓包应用.

相关命令 tcpdump

其他有用的命令行

一些好用的命令(基本的什么ls/cd/cp/rm之类的这里就直接忽略了), 我觉得作为程序猿还是应该了解, 至少只当某个场景下直接用相应的命令就能解决. 具体参数可以再 --help 或者 man commond 再看.

  • screen: 特别是 ssh 到登录远程时用以管理会话
  • curl: 网络请求, 相关的还有 traceroute, dig
  • find: 文件查找
  • grep/zgrep/zcat: 查看日志的时候用
  • awk: 这个本身就很强大了, 具体编程语法不用太掌握但可以了解一些基本的用法, 比如之前用到过给一个log文件, 能够取里面的参数拼接update 的sql(文件里有相应 update 的值和 where 条件值)
  • sed: 文本替换, 还有 tr, 注意 sed 的语法 Mac 和 一般 Linux 还有些不一样( 比如原文替换的时候 mac 里需要用参数 -i ""), 比如之前迁移 wordpress 到 jekyll 上的时候需要将一些链接整体替换成新的路径.
  • cut: 按列取数据, awk 也可以
  • sort: 这个就不多说了
  • uniq: 一般和 sort 一块用, 只能去重相邻的行
  • diff: 比较文件, 类似的还有 comm (输出3列, 分别是: 只在文件1, 只在文件2 和两个文件都在的行)
  • paste: 两个文件按列拼接
  • od: 以16/8/2进制查看文件
  • wc: 统计文件字节数/字数/行数

结合这些命令可能就能完成某些复杂的功能, 举个例子, 如线上的web 访问日志会记录 请求时间, 请求路径, 参数 等等. 现在需要统计 当天请求路径为 A, 排名前10的参数, 就可以 grep 路径A | cut 取出想要的数据列 | sort | uniq 之类的, 或者比如统计http 404 请求最多的10个路径. 再比如, 随机生成3个长度为8包含字母数字的字符串(偶尔会用到, 比如各种生产 secret key 的时候), 直接用如下命令即可

➜ _includes git:(source) ✗ cat /dev/urandom | sed ‘s/[^a-zA-Z0-9]//g’ | head -n 3 | cut -c 1-8
MaL6nEmZ
00m2Ub19
rsc4AOQm

其他的可能较少用, 但一旦用, 能省不少时间. 网上也有一些 online 的工具, 但哪有这个快准狠.

  • openssl sha1/aes-256-ecb/des/base64 等等: 比如当前我们开发用的 MVC 框架play framework用来加密 session 的算法, 可以方便算出 encoded 的 sessionid 进行 debug.
  • md5/base64: 常见的 md5, base64 编码
  • sips: scriptable image processing system 比如批量处理图片大小, 压缩等等

全文完, 关于 Mac 使用技巧和工具软件推荐, 一共如下3篇文章:

p.s 如果你觉得这文章对你有那么一点点收获, 请不要犹豫扫描下面二维码关注我的公众号, 如果你再能帮忙转发一下就更好了. 么么哒.

基于 Kafka 和 ElasticSearch,LinkedIn是如何构建实时日志分析系统的?

作者|李虓
编辑|祥南、小智
及时有效地搜索日志是 SRE  (Site Reliability Engineer) 日常工作的重要内容。LinkedIn 从使用 Splunk 到建立基于 ES 和 kafka 的日志分发、索引系统,为 SRE 提供了近似实时的搜索平台来检索超过 400 多个子系统的日志。在本文中,来自LinkedIn的李虓将和大家分享这套系统从无到有的一些技术架构经验。

本文整理自李虓在QCon上海2016的演讲,原题为:《LinkedIn 基于 Kafka 和 ElasticSearch 的实时日志分析系统》。回复关键词「领英」,下载完整版PPT。

写在前面

今天,和跟大家分享我们在用ElasticSearch和Kafka做日志分析的时候遇到的问题,系统怎么样一步一步演变成现在这个版本。你如果想拿ElasticSearch和Kafka来做日志分析的话,会有一些启发。全文主要包括以下几个Topic:

  1. 日志分析系统的基本需求;
  2. LinkedIn的日志系统演进过程;
  3. 我们的经验和教训。
为什么要做日志分析系统?

首先,什么是日志?简单的说日志就是一个结构化的数据+时间戳。计算机开始日志就已经存在,从那时候就有各种各样的工具来帮我们分析、解析或者查找日志。

一开始做这个东西的时候,很多团队觉得不是很需要,工程师登录到服务器上面做一些cat或者grep和简单的表达式处理就够了,可以找到需要的信息。如果不够的话,比如在很多台机器上的话,有mssh、cssh等工具。

还不够的话,可以自己写工具,有一次我发现在我们的生产服务器上面,有一个SRE写了一套系统,从自己的台式机,做了一个ssh tunnel到实际的生产系统里面做了远程代码调用,远程把那些文件拿回来,这是一个一级的安全生产事故,是非常不负责任的事情,但是这也就暴露了我们确实有这个需求。

当我们有五万台服务器,五百多个微服务的时候,你不可能指望每个人都非常熟练的去解决这样的事情。开发或者运维经常会遇到这样的需求,比如拿某两个时间点之间的所有的日志,只需要看WARN或者ERROR或者FATAL的消息,并且有十几个错误是已知的,要忽略。

这个服务是跑在好几个数据中心几百台服务器上面,还需要关心有没有新的错误,这个错误是不是由于某个特定的用户造成的,或者某些特定的用户行为造成的,比如说他post了什么,或者request的长度超过一个固定长度;这个服务器上的错误信息有没有和其他服务器上的错误信息相关联。给我30分钟我有可能写出来一个四五行的grep命令去几百台服务器上把日志拉下来,但如果在凌晨三点钟,这就是一个不太可能的任务。

日志分析系统需要满足以下基本的要求:

  1. 对于重要的日志,满足索引,检索、排序、分类,并且提供一定程度的可视化、分析日志的功能;
  2. 可根据数据规模横向扩展。因为互联网的发展非常非常的快,我们希望找到一个解决方案,不要过了一年甚至半年,当服务器或者用户数量加倍以后,解决方案就完全不可用。需要找到一个方案,当用户数量加倍时,简单的加几台机器或者服务器就可以继续使用;
  3. 这套系统能够很轻易的扩展,因为很多公司已经有了很多的报警或者监控系统。可以方便的通过API或者通过扩展接入到已经有的监控、报警,或者其他系统里面。

还有一些其他扩展性的需求,包括日志采样,提高安全性、保护日志里面包含的信息,也是后面着重谈的一个问题。

LinkedIn日志系统演进

回到四年前,从LinkedIn成立到2012年我们有一个系统叫Splunk,非常好用,只有一个问题,太贵了。2012年的时候,我们当时生产环境有三、四千台的服务器,续签第二年的合约时他们的报价是2000万美元一年。这实在是不可以接受的,并且那个时候是2012年,我们现在的服务器台数、用户请求数已经翻了差不多十倍,当时的价格如果是2000万,现在更多,因为它是根据数据量来算license的。

从2012年到2014年,因为我们当时决定不用Splunk,就进入了一个混沌期,这段时间非常痛苦,大家都有需求,但是没有人有方法,很多人开始搞自己的小动作,做些小工具。我之前看了一下内部工具库,里面有二、三十个用python或者shell写的小工具,这些小工具是用来找一个时间段内的log或者特定用户的log,但是有很大的浪费,很多工具重复的写,也非常难维护。

2014年到2015年这一年多的时间我们痛下决心,一定要做一套能够横跨LinkedIn所有log的系统,并且推广到整个LinkedIn。当时选择了ELK,它的优点就是:开源,发布周期非常快,当然也有缺点,它非常的新,所以有很多小毛病。

相信大家很多人已经知道ELK是什么——ElasticSearch、Logstash、Kibana。ElasticSearch就是基于Lucene的储存,索引,搜索引擎;Logstash是提供输入输出以及转换处理插件的日志标准化管道;Kibana提供可视化和查询ES的用户界面。

LinkedIn日志系统演进V1

每个人花30分钟就可以在自己的电脑或者生产环境上搭一个这样的东西,Log通过Logstash读出来,放到ElasticSearch里,然后Kibana去读。这步做完以后其实就能达到非常好的效果。LinkedIn所有的业务群都会要求有一个异常面板,比如说支付系统业务群,它大概有十个左右不同的小服务。

当报警系统发现支付系统有了各种各样的问题之后,我们第一步就是到异常面板来看Log里面有没有什么东西,可以根据时间线上看,有没有哪些新的服务在近期有了新的log或者error log数量不一样。并且它会根据不同的exception/java stack拿出来做count,这也给分析带来很大的帮助,还可以写出很多复杂的query。

第一个版本非常简单,我们只把它应用到了一两个非常关键的系统上。这套系统做完之后我们做了一个对比,平均故障解决时间从以前的五十几分钟缩短到小于30分钟。我们的线上系统一般最快会花5到10分钟才有一个不是很关键的警报出来,如果能很快发现问题在哪里,解决这个问题,比如说一个简单的rollback操作,在几百台机器上也会花5到10分钟的时间,真正留给人根据log去判断问题在哪里的时间也只有短短的5到10分钟。

如果不是有一个异常面板能看到所有的信息,比如有没有哪个服务器的异常比其他服务器多,有没有一个异常是突然今天出了很多次的,或者有没有一个服务器今天出了很多的异常,最起码要花二十到三十分钟的时间手工的看log。

第一个版本有几个问题,第一是Logstash Agent的维护不是很理想,Logstash是基于Ruby的,启动它就要有几十兆内存被jvm吃掉,我们想避免在每个机器上都要起一个Logstash。并且在我们使用过程中Logstash非常不稳定,莫名其妙就死掉了,还需要一个守护进程守护它。第二个是log标准化,也是非常头疼的问题。

第一个Logstash Agent的问题通过引入Kafka解决,引入Kafka后不需要每个host上有agent。LinkedIn内部有专门的SRE团队维护Kafka,很便宜,不需要花钱去维护。对于Log标准化,我们花了非常多的时间看,LinkedIn有99%的服务都是java service,有15种以上log,最重要的是access log和application log。我们做的一件事情是通过java Container logger标准化直接写入Kafka。有的程序直接写到kafka,不上磁盘,有的程序还要同时写到磁盘里面,这是可配置的。

这些是通过LinkedIn standard container一起rollout到所有的service上。开发人员什么都不用管,只要在程序里写logger.info/logger.error,那些信息就会直接进到Kafka。对于程序日志,默认警告以上的级别进入Kafka,可以在线通过jmx控制。对于访问日志,我们有10%采样,可以通过ATS入口动态控制。

LinkedIn日志系统演进V2

这是第二个版本,可以看到在生产环境的Java service那边,Host上已经没有Logstash,所有的log直接写入Kafka,Logstash从Kafka里消费这些日志,写到ElasticSearch里面去。

第二个版本还会出现一些问题,比如说一个服务出现问题的时候会影响整个ELK cluster。我们出现过这样的情况,一个团队写了一个新的服务,把所有的log的级别都定义成error,整个ElasticSearch就被它冲垮了。很多时候还会出现网络饱和的情况。

很简单,第二步非常简单的决定就是把它进行拆分优化:

  1. 首先按照业务功能拆分ELK cluster,比如说负责支付的,跟钱有关的系统用一个集群;所有跟用户登陆有关的系统,安全有关的系统用一个集群;
  2. 将Logstash和ElasticSearch分开运行。ElasticSearch是磁盘密集的操作,Logstash是CPU密集的操作,我们当时想把他们放到一个物理机上,但是试下来相互影响还是挺大的,所以决定把Logstash跟其他的系统混用,与ElasticSearch分开运行;
  3. 对于每个Kafka话题,Logstash数量不少于话题partition数量。LinkedIn有500多个服务,每个服务会产生访问日志、程序日志两个Kafka topic。Logstash消费Kafka的时候,如果consumer数量少于partition数量,会触发Kafka一个隐藏的漏洞,导致Kafka partition不均匀,出现各种诡异的问题。我们的建议是,一般情况下,每个topic有四到八个partition,然后根据情况设置相应数量的Logstash。
LinkedIn日志系统演进V3

根据业务需求拆分,我们拆出来20几个这样的相同的集群,这个版本,还有一些问题。首先是跨业务分组集群的查询,虽然很多的时候,一个问题在一个业务分组里面就能找到,但是还有很多的时候要跨到另外一个集群里面才能找到问题到底是从哪开始的。

第二,千万不要跨数据中心做ElasticSearch的集群,非常非常差,根本跑不起来。即使两个数据中心非常近这样做也不好,尤其当数据量上来之后,会有一些非常诡异的问题。数据量非常大的ElasticSearch集群我们会要求它全部在一个机架上,如果有一个服务器在另一个机架上都会出现timeout的问题。

为了解决刚刚说的问题,我们引入Tribe,用下来的感觉可以用,但是这明显不是一个他们支持的功能。Tribe理念很好,但是它不支持分层,我们需要两种不同的Tribe,首先要能跨业务分组,还要跨数据中心。

最好的情况是做一个分层的结构,数据中心在最外面,业务分组在最里面,但是从设计理念上是另外一个概念,所以不行。只能退而求其次,在一个数据中心里面会有跨所有业务分组的一个Tribe,还会对相同的业务分组有一个跨数据中心的Tribe,有两个不同类型的Tribe进行查询。

LinkedIn日志系统演进V4

到这一步,基本上功能实现的差不多了,就开始慢慢的把500多个服务的日志打到Kafka里面,大概花了一两个月,发现consume跟不上,遇到了性能瓶颈。用ElasticSearch超过50或者100台服务器,就会遇到很多这样的瓶颈。我们花了三个月的时间,做了各种性能调优。

这一步算是最后一步,首先理解了一下自己的业务逻辑,我们要做的事情非常明白,非常单一,就是需要实时的,或者准实时的log来做在线的trouble shooting,基本上不会用到14天以前的数据,保留14天以前的数据就是为了看两周的周期而已。

今天的问题都解决不完,根本没有时间考虑几个月之前的问题,实际的业务状态是24小时之内的日志查询的最多,14天以前用的非常少,查询速度要求在15秒之内,超过30秒就timeout了。索引速度30秒以内可以接受,超过5分钟会触发警报。

LinkedIn日志系统演进V5

采用冷热分区的方式解决这个问题,我们测试了很多种不同的硬件,最后选定了在非常重要和数据量很大的集群上用ssd做热索引,24小时之内的索引全部上到SSD机器上,超过24小时就挪到普通的机器上。相当于在集群里边,有一个热的Cluster,数据全面走到热的cluster里面,24小时以后,会被挪到冷的cluster。做了这个之后,系统变得比较稳定,功能也正常,内部会根据需求保留7到14天的数据。

这步之后,我们把它推广到LinkedIn整个公司,第二天我接到法务和安全部的电话。当我们做了一个非常容易的系统,把所有的log展现给所有人的时候,如果大家能很轻易的把4亿用户的用户名、密码、邮箱等信息搜出来,这个事情就非常严重,所以我们又做了几个调整。

第一个是定期扫描所有的ES,根据特定的关键字来防止敏感信息进入日志,如果进入马上报警。还有用户隐私的问题,所有Elasticsearch的查询记录同样送入Kafka,并对敏感业务部门的访问进行隔离,所有访问日志定期审核。我们的做法是在前面加一个Nginx,在nginx上可以做访问控制,把用户所有的访问日志全部送回Kafka做定期审核,有一个扫描进程定期的扫描各种各样的关键字。

LinkedIn日志系统演进现状

这是我们现在生产系统里的状态,有20多个针对不同业务模块的ELK集群,1000+服务器,主要都是Elasticsearch。1分钟之内生产系统发生的log我们这边就可以搜索,所有的log保留7到14天。现在大概有500亿的索引文档,500到800T,之前测试时推到1500到2000T都是可以工作的。

因为我们是500多个service,20多个集群,没有办法很好的维护这么多集群,所以是每个业务模块的SRE团队维护自己的Elasticserach集群。Virtual Team模式确保ELK的及时更新。还有一点比较关键,需要保证你的ES不会被没有授权的用户访问,默认的情况下是不接受SSL连接的,SearchGuard和Sheild这两种解决方式都是可以解决这个问题的。

我想着重提一下采样方式,这个是比较有意思的,也是比较通用的方式。采样方式是10%+特定的用户,为什么要这么做?我们调过不同的比例,发现不影响,如果有问题,10%的采样就能发现。为什么还要特定用户呢?很多时候,有一些active的用户会经常给你报错,给你反馈意见,需要及时去看到底发生了什么事情。

对LinkedIn来说有大概百分之零点几的power user在网站上非常活跃,做很多事情,我们需要把他们的log加到sample里。所有的用户请求,都是通过Apache Traffic Server进入数据中心,在这层它会去访问LiX,询问是否要对当前用户打标签,如果打了标签就把这个标签放在InvocationContext里面。

从前台到后台所有的服务器只要touch到这个request,都会在InvocationContext里看到一个requestID。通过java container的bydefault得到requestID,把那条访问的日志发到Kafka里面,通过这样的方式做成sample。

这样做有两点好处,一点是如果有什么问题,只需要把他的memberID或者requestID拿到最上面一层Tribe里面,系统就会出现关于这条request的所有service的log。99.9%的情况可以一目了然的知道哪里出了问题。做微服务的大家都知道,dependence非常乱,我们LinkedIn的情况,一个request会touch二三十个service。所以说有这样一个方式是非常重要的。

日常运维之坑

我想聊一下我们遇到的几个坑,ES集群的默认名字是大坑,如果不改集群名字就把它放在自己的电脑上或者测试系统上跑,一旦相同子网的人加了一个新的node也没有改名字,就会自动加到你的集群里面。不管怎么样一定要改你的集群名字。

Master、Data、Client节点不要混用,使用单ES节点;JVM不要allocate超过30G的heap,我们测试发现30G左右的heap可以达到最好的performance,如果超过30G就没有办法完全使用;JVM版本的兼容性也是一个超级大的坑,它不是最新的兼容,也不是哪个版本以后,或者以前兼容,是有几个版本兼容,再几个版本不兼容,再几个版本兼容。有一些兼容性的问题一定要注意。

日常运维之硬件

讲一下刚才已经提到的硬件的选择,要根据数据量,业务的情况,索引和查询速度的要求决定,像我们的要求是一定要索引快,因为要做实时的故障排查,数据的类型我们只做两个,一个是访问log,一个是application log,这些决定了我们使用的硬件是12核/64G/2x1TB的硬盘,有八九百台都是这样的配置。

有一小部分是数据量非常大但是对查询的速度要求不是很高,比如做自动查询的那套,我们会把数据丢到JBODs里面,索引速度优先考虑的话,会用100台左右的SSD。

日常运维之集群

集群没有magic number,必须要用实际的生产数据,一遍一遍试,才能试出来用什么样的配置能达到最好的效果,这几个是我们的learning:

  1. 横向扩展,不要纵向扩展,不要试图用更大的memory或者最快的CPU,不会有太大的效果,我们大概测过四五个版本,基本上没怎么成功;
  2. 每个shard不要超过50G,只要超过50G我们的shard就会莫名其妙的失联;
  3. 关闭冗余,我们把replication关了是为了更快。关了之后如果出现硬件故障,从Kafka那边读,数据很快就能回来;
  4. 每天建新的索引,有一个集群因为数据量非常大,会做每个小时的索引,二三个数据量比较小的集群每周做一个索引;
  5. 只分析必要字段,IP字段、设备版本,这些字段完全没有必要做分析,只要能整字段查找就行了,把它关掉,可以省很多的空间,index速度也会快很多。
日常运维之工具

还有就是一些工具,刚刚提到主动扫描敏感信息的就是一个script在后台根据一些关键字扫描敏感信息;结合警报系统,相信每个公司都有自己的警报系统,ElasticAlert是一个开源的解决方案,我们自己也有内建的,根据读出来的信息的关键字做警报;循环删除index用的Curator;系统健康状态监控是自己做的监控系统,官方的Marvel也很好用。

提高代码质量

做完这套系统以后,我们并不是完全被动的状态,只能用这套系统来做故障排查,我们发现这套系统里面有些指标可以很好的反映开发人员的代码的质量。

  1. 请求数/日志总行数,对于每个service都不一样,但是大概有一个区间,如果出了这个区间,可能这个service就有一些问题;
  2. 请求数量/错误(异常)行数,这个也有一个区间,我们这边的区间是1000个请求,大概产生2000到5000行的日志,500到3000行的异常数量,这个是我们可以接受的,经常会有service超出这个范围10倍,通过这个就能发现异常有没有处理。

通过这些发现一些有意思的metric返回给开发人员,帮助他们提高代码的质量,这是一个比较有意思的发现。国外和国内大家做的东西很多很像,遇到的问题也很像,希望能给大家一些启发。

作者介绍

李虓(Li Xiao),LinkedIn SRE 团队高级技术经理。带领 SRE 团队负责 LinkedIn 在线支付系统,高级会员功能,公共 API 接口,视频上传和分享等系统的运维,同时负责搭建 LinkedIn 生产环境日志检索系统,Rest.Li 框架性能优化等跨功能项目。在加入 LinkedIn 之前有着丰富的 Java 开发和项目管理经验。四年前加入 LinkedIn SRE 团队,致力于让开发和运维团队紧密配合,在新功能上线和产品稳定性之间找到最佳平衡。


QCon是由InfoQ主办的全球顶级技术盛会,每年在伦敦、北京、东京、纽约、圣保罗、上海、旧金山召开。

QCon北京2017将于4月16日~18日在北京·国家会议中心举行,精心设计了支撑海量业务的互联网架构、大规模网关系统、微服务实践、快速进化的容器生态、智能化运维、互联网广告系统实践、大数据实时计算与流处理和金融科技转型与未来等30来个专题,涵盖架构、大数据、云计算、移动、前端、人工智能等热点领域,将邀请来自Google、Facebook、阿里巴巴、腾讯、百度、美团点评、爱奇艺等典型互联网公司的技术专家,分享技术领域最新成果。敬请期待。

具体详戳 「 阅读原文 」访问大会官网!

亿级用户PC主站的PHP7升级实践

伴随业务的增长,系统压力也在不断增加,再加上机房机架趋于饱和,无法更加有效应对各种突发事件。在这样的情况下,PC主站升级为PHP 7,有哪些技术细节可以分享?

背景

新浪微博在2016年Q2季度公布月活跃用户(MAU)较上年同期增长33%,至2.82亿;日活跃用户(DAU)较上年同期增长36%,至1.26亿,总注册用户达8亿多。PC主站作为重要的流量入口,承载部分用户访问和流量落地,其中我们提供的部分服务(如:头条文章)承担全网所有流量。

随着业务的增长,系统压力也在不断的增加。峰值时,服务器Hits达10W+,CPU使用率也达到了80%,远超报警阈值。另外,当前机房的机架已趋于饱和,遇到突发事件,只能对非核心业务进行降低,挪用这些业务的服务器来进行临时扩容,这种方案只能算是一种临时方案,不能满足长久的业务增长需求。再加上一年一度的三节(圣诞、元旦、春节),系统需预留一定的冗余来应对,所以当前系统面临的问题非常严峻,解决系统压力的问题也迫在眉急。

面对当前的问题,我们内部也给出两套解决方案同步进行。

  • 方案一:申请新机房,资源统一配置,实现弹性扩容。
  • 方案二:对系统进行优化,对性能做进一步提升。

针对方案一,通过搭建与新机房之间的专线与之打通,高峰时,运用内部自研的混合云DCP平台,对所有资源进行调度管理,实现了真正意义上的弹性扩容。目前该方案已经在部分业务灰度运行,随时能对重点业务进行小流量测试。

针对方案二,系统层面,之前做过多次大范围的优化,比如:

  • 将Apache升级至Nginx
  • 应用框架升级至Yaf
  • CPU计算密集型的逻辑扩展化
  • 弃用smarty
  • 并行化调用

优化效果非常明显,如果再从系统层面进行优化,性能可提升的空间非常有限。好在业界传出了两大福音,分别为HHVM和PHP7。

方案选型

在PHP7还未正式发布时,我们也研究过HHVM(HipHop Virtual Machine),关于HHVM更多细节,这里就不再赘述,可参考官方说明。下面对它提升性能的方式进行一个简单的介绍。

默认情况下,Zend引擎先将PHP源码编译为opcode,然后Zend解析引擎逐条执行。这里的opcode码,可以理解成C语言级的函数。而HHVM提升性能方式为替代Zend引擎将PHP代码转换成中间字节码(HHVM自己的中间字节码,通常称为中间语言),然后在运行时通过即时(JIT)编译器将这些字节码转换成x64的机器码,类似于Java的JVM。

HHVM为了达到最佳优化效果,需要将PHP的变量类型固定下来,而不是让编译器去猜测。Facebook的工程师们就定义一种Hack写法,进而来达到编译器优化的目的,写法类似如下:

<?hh 
class point {     
    public float $x, $y;     
    function __construct(float $x, float $y) {         
        $this->x = $x;         
        $this->y = $y;
     } 
     }

通过前期的调研,如果使用HHVM解析器来优化现有业务代码,为了达到最佳的性能提升,必须对代码进行大量修改。另外,服务部署也比较复杂,有一定的维护成本,综合评估后,该方案我们也就不再考虑。

当然,PHP7的开发进展我们也一直在关注,通过官方测试数据以及内部自己测试,性能提升非常明显。

令人兴奋的是,在去年年底(2015年12月04日),官方终于正式发布了PHP7,并且对原生的代码几乎可以做到完全兼容,性能方面与PHP5比较能提升达一倍左右,和HHVM相比已经是不相上下。

无论从优化成本、风险控制,还是从性能提升上来看,选择PHP7无疑是我们的最佳方案。

系统现状以及升级风险

微博PC主站从2009年8月13日发布第一版开始,先后经历了6个大的版本,系统架构也随着需求的变化进行过多次重大调整。截止目前,系统部分架构如下。

从系统结构层面来看,系统分应用业务层、应用服务层,系统所依赖基础数据由平台服务层提供。

从服务部署层面来看,业务主要部署在三大服务集群,分别为Home池、Page池以及应用服务池。

为了提升系统性能,我们自研了一些PHP扩展,由于PHP5和PHP7底层差别太大,大部分Zend API接口都进行了调整,所有扩展都需要修改。

所以,将PHP5环境升级至PHP7过程中,主要面临如下风险:

  • 使用了自研的PHP扩展,目前这些扩展只有PHP5版本,将这些扩展升级至PHP7,风险较大。
  • PHP5与PHP7语法在某种程度上,多少还是存在一些兼容性的问题。由于涉及主站代码量庞大,业务逻辑分支复杂,很多测试范围仅仅通过人工测试是很难触达的,也将面临很多未知的风险。
  • 软件新版本的发布,都会面临着一些未知的风险和版本缺陷。这些问题,是否能快速得到解决。
  • 涉及服务池和项目较多,基础组件的升级对业务范围影响较大,升级期间出现的问题、定位会比较复杂。

对微博这种数亿用户级别的系统的基础组件进行升级,影响范围将非常之大,一旦某个环节考虑不周全,很有可能会出现比较严重的责任事故。

PHP7升级实践

1. 扩展升级

一些常用的扩展,在发布PHP7时,社区已经做了相应升级,如:Memcached、PHPRedis等。另外,微博使用的Yaf、Yar系列扩展,由于鸟哥(laruence)的支持,很早就全面支持了PHP7。对于这部分扩展,需要详细的测试以及现网灰度来进行保障。

PHP7中,很多常用的API接口都做了改变,例如HashTable API等。对于自研的PHP扩展,需要做升级,比如我们有个核心扩展,升级涉及到代码量达1500行左右。

新升级的扩展,刚开始也面临着各式各样的问题,我们主要通过官方给出的建议以及测试流程来保证其稳定可靠。

官方建议

  • 在PHP7下编译你的扩展,编译错误与警告会告诉你绝大部分需要修改的地方。
  • 在DEBUG模式下编译与调试你的扩展,在run-time你可以通过断言捕捉一些错误。你还可以看到内存泄露的情况。

测试流程

  • 首先通过扩展所提供的单元测试来保证扩展功能的正确性。

  • 其次通过大量的压力测试来验证其稳定性。
  • 然后再通过业务代码的自动化测试来保证业务功能的可用性。

  • 最后再通过现网流量灰度来确保最终的稳定可靠。

整体升级过程中,涉及到的修改比较多,以下只简单列举出一些参数变更的函数。

(1)addassocstringl参数4个改为了3个。

//PHP5
 add_assoc_stringl(parray, key, value, value_len);
//PHP7
 add_assoc_stringl(parray, key, value);

(2)addnextindex_stringl 参数从3个改为了2个。

//PHP5
 add_assoc_stringl(parray, key, value, value_len);
//PHP7
 add_assoc_stringl(parray, key, value);

(3)RETURN_STRINGL 参数从3个改为了2个。

//PHP5 
RETURN_STRINGL(value, length,dup); 
//PHP7 
RETURN_STRINGL(value, length);

(4)变量声明从堆上分配,改为栈上分配。

//PHP5 
zval* sarray_l; 
ALLOC_INIT_ZVAL(sarray_l); 
array_init(sarray_l);  

//PHP7 
zval sarray_l; 
array_init(&sarray_l);

(5)zendhashgetcurrentkey_ex参数从6个改为4个。

//PHP5 
ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex (
     HashTable* ht,
    char** str_index,     
    uint* str_length,     
    ulong* num_index,     
    zend_bool duplicate,     
    HashPosition* pos);  
    
//PHP7 
ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(     
      const HashTable *ht,     
     zend_string **str_index,     
     zend_ulong *num_index,     
     HashPosition *pos);

更详细的说明,可参考官方PHP7扩展迁移文档:https://wiki.PHP.net/PHPng-upgrading。

2. PHP代码升级

整体来讲,PHP7向前的兼容性正如官方所描述那样,能做到99%向前兼容,不需要做太多修改,但在整体迁移过程中,还是需要做一些兼容处理。

另外,在灰度期间,代码将同时运行于PHP5.4和PHP7环境,现网灰度前,我们首先对所有代码进行了兼容性修改,以便同一套代码能同时兼容两套环境,然后再按计划对相关服务进行现网灰度。

同时,对于PHP7的新特性,升级期间,也强调不允许被使用,否则代码与低版本环境的兼容性会存在问题。

接下来简单介绍下升级PHP7代码过程中,需要注意的地方。

(1)很多致命错误以及可恢复的致命错误,都被转换为异常来处理,这些异常继承自Error类,此类实现了 Throwable 接口。对未定义的函数进行调用,PHP5和PHP7环境下,都会出现致命错误。

undefine_function();

错误提示:

PHP Fatal error:  Call to undefined function 
undefine_function() in /tmp/test.PHP on line 4

在PHP7环境下,这些致命的错误被转换为异常来处理,可以通过异常来进行捕获。

try {      
     undefine_function(); 
     } 
     catch (Throwable $e) {      
     echo $e; 
}

提示:

Error: Call to undefined function undefine_function() in /tmp/test.PHP:5 Stack trace: 
#0 {main}

(2)被0除,PHP 7 之前,被0除会导致一条 E_WARNING 并返回 false 。一个数字运算返回一个布尔值是没有意义的,PHP 7 会返回如下的 float 值之一。

  • +INF
  • -INF
  • NAN

如下:

var_dump(42/0);  // float(INF)  + E_WARNING 
var_dump(-42/0); // float(-INF) + E_WARNING 
var_dump(0/0);   // float(NAN)  + E_WARNING

当使用取模运算符( % )的时候,PHP7会抛出一个 DivisionByZeroError 异常,PHP7之前,则抛出的是警告。

echo 42 % 0;

PHP5输出:

PHP Warning:  Division by zero in /tmp/test.PHP on line 4

PHP7输出:

PHP Fatal error:  Uncaught DivisionByZeroError: Modulo by zero in /tmp/test.PHP:4 Stack trace: #
0 {main} 
thrown in /tmp/test.PHP on line 4

PHP7环境下,可以捕获该异常:

try {     
    echo 42 % 0; 
    } catch (DivisionByZeroError $e) {    
    echo $e->getMessage(); 
}

输出:

Modulo by zero

(3)pregreplace() 函数不再支持 “\e” (PREGREPLACEEVAL). 使用 pregreplace_callback() 替代。

$content = preg_replace("/#([^#]+)#/ies", "strip_tags('#\\1#')", $content);

PHP7:

$content = preg_replace_callback("/#([^#]+)#/is", "self::strip_str_tags", $content); 
public static function strip_str_tags($matches){      
      return "#".strip_tags($matches[1]).'#'; 
}

(4)以静态方式调用非静态方法。

class foo { function bar() { echo ‘I am not static!’; } } foo::bar();

以上代码PHP7会输出:

PHP Deprecated:  Non-static method foo::bar() should not be called statically in /tmp/test.PHP on line 10 
I am not static!

(5)E_STRICT 警告级别变更。

原有的 ESTRICT 警告都被迁移到其他级别。 ESTRICT 常量会被保留,所以调用 errorreporting(EALL|E_STRICT) 不会引发错误。

关于代码兼容PHP7,基本上是对代码的规范要求更严谨。以前写的不规范的地方,解析引擎只是输出NOTICE或者WARNING进行提示,不影响对代码上下文的执行,而到了PHP7,很有可能会直接抛出异常,中断上下文的执行。

如:对0取模运行时,PHP7之前,解析引擎只抛出警告进行提示,但到了PHP7则会抛出一个DivisionByZeroError异常,会中断整个流程的执行。

对于警告级别的变更,在升级灰度期间,一定要关注相关NOTICE或WARNING报错。PHP7之前的一个NOTICE或者WARNING到了PHP7,一些报警级变成致命错误或者抛出异常,一旦没有对相关代码进行优化处理,逻辑被触发,业务系统很容易因为抛出的异常没处理而导致系统挂掉。

以上只列举了PHP7部分新特性,也是我们在迁移代码时重点关注的一些点,更多细节可参考官方文档http://PHP.net/manual/zh/migration70.PHP。

3. 研发流程变更

一个需求的开发到上线,首先我们会通过统一的开发环境来完成功能开发,其次经过内网测试、仿真测试,这两个环境测试通过后基本保证了数据逻辑与功能方面没有问题。然后合并至主干分支,并将代码部署至预发环境,再经过一轮简单回归,确保合并代码没有问题。最后将代码发布至生产环境。

为了确保新编写的代码能在两套环境(未灰度的PHP5.4环境以及灰度中的PHP7环境)中正常运行,代码在上线前,也需要在两套环境中分别进行测试,以达到完全兼容。

所以,在灰度期间,对每个环节的运行环境除了现有的PHP5.4环境外,我们还分别提供了一套PHP7环境,每个阶段的测试中,两套环境都需要进行验证。

4. 灰度方案

之前有过简单的介绍,系统部署在三大服务池,分别为Home池、Page池以及应用服务池。

在准备好安装包后,先是在每个服务池分别部署了一台前端机来灰度。运行一段时间后,期间通过错误日志发现了不少问题,也有用户投诉过来的问题,在问题都基本解决的情况下,逐渐将各服务池的机器池增加至多台。

经过前期的灰度测试,主要的问题得到基本解决。接下是对应用服务池进行灰度,陆续又发现了不少问题。前后大概经历了一个月左右,完成了应用服务池的升级。然后再分别对Home池以及Page池进行灰度,经过漫长灰度,最终完成了PC主站全网PHP7的升级。

虽然很多问题基本上在测试或者灰度期间得到了解决,但依然有些问题是全量上线后一段时间才暴露出来,业务流程太多,很多逻辑需要一定条件才能被触发。为此BUG都要第一时间同步给PHP7升级项目组,对于升级PHP引起的问题,要求必须第一时间解决。

5. 优化方案

(1)启用Zend Opcache,启用Opcache非常简单, 在PHP.ini配置文件中加入:

zend_extension=opcache.so 
opcache.enable=1 
opcache.enable_cli=1"

(2)使用GCC4.8以上的编译器来编译安装包,只有GCC4.8以上编译出的PHP才会开启Global Register for opline and execute_data支持。

(3)开启HugePage支持,首先在系统中开启HugePages, 然后开启Opcache的hugecodepages。

关于HugePage

操作系统默认的内存是以4KB分页的,而虚拟地址和内存地址需要转换, 而这个转换要查表,CPU为了加速这个查表过程会内建TLB(Translation Lookaside Buffer)。 显然,如果虚拟页越小,表里的条目数也就越多,而TLB大小是有限的,条目数越多TLB的Cache Miss也就会越高, 所以如果我们能启用大内存页就能间接降低这个TLB Cache Miss。

PHP7与HugePage

PHP7开启HugePage支持后,会把自身的text段, 以及内存分配中的huge都采用大内存页来保存, 减少TLB miss, 从而提高性能。相关实现可参考Opcache实现中的accel_move_code_to_huge_pages()函数。

开启方法

以CentOS 6.5为例, 通过命令:

sudo sysctl vm.nr_hugepages=128

分配128个预留的大页内存。

$ cat /proc/meminfo | grep Huge 
AnonHugePages:    444416 kB 
HugePages_Total:     128 
HugePages_Free:      128 
HugePages_Rsvd:        0 
HugePages_Surp:        0 
Hugepagesize:       2048 kB

然后在PHP.ini中加入

opcache.huge_code_pages=1

6. 关于负载过高,系统CPU使用占比过高的问题

当我们升级完第一个服务池时,感觉整个升级过程还是比较顺利,当灰度Page池,低峰时一切正常,但到了流量高峰,系统CPU占用非常高,如图:

系统CPU的使用远超用户程序CPU的使用,正常情况下,系统CPU与用户程序CPU占比应该在1/3左右。但我们的实际情况则是,系统CPU是用户CPU的2~3倍,很不正常。

对比了一下两个服务池的流量,发现Page池的流量正常比Home池高不少,在升级Home池时,没发现该问题,主要原因是流量没有达到一定级别,所以未触发该问题。当单机流量超过一定阈值,系统CPU的使用会出现一个直线的上升,此时系统性能会严重下降。

这个问题其实困扰了我们有一段时间,通过各种搜索资料,均未发现任何升级PHP7会引起系统CPU过高的线索。但我们发现了另外一个比较重要的线索,很多软件官方文档里非常明确的提出了可以通过关闭Transparent HugePages(透明大页)来解决系统负载过高的问题。后来我们也尝试对其进行了关闭,经过几天的观察,该问题得到解决,如图:

什么是Transparent HugePages(透明大页)

简单的讲,对于内存占用较大的程序,可以通过开启HugePage来提升系统性能。但这里会有个要求,就是在编写程序时,代码里需要显示的对HugePage进行支持。

而红帽企业版Linux为了减少程序开发的复杂性,并对HugePage进行支持,部署了Transparent HugePages。Transparent HugePages是一个使管理Huge Pages自动化的抽象层,实现方案为操作系统后台有一个叫做khugepaged的进程,它会一直扫描所有进程占用的内存,在可能的情况下会把4kPage交换为Huge Pages。

为什么Transparent HugePages(透明大页)对系统的性能会产生影响

在khugepaged进行扫描进程占用内存,并将4kPage交换为Huge Pages的这个过程中,对于操作的内存的各种分配活动都需要各种内存锁,直接影响程序的内存访问性能。并且,这个过程对于应用是透明的,在应用层面不可控制,对于专门为4k page优化的程序来说,可能会造成随机的性能下降现象。

怎么关闭Transparent HugePages(透明大页)

(1)查看是否启用透明大页。

[root@venus153 ~]# cat  /sys/kernel/mm/transparent_hugepage/enabled 
[always] madvise never

使用命令查看时,如果输出结果为[always]表示透明大页启用了,[never]表示透明大页禁用。

(2)关闭透明大页。

echo never > /sys/kernel/mm/transparent_hugepage/enabled 
echo never > /sys/kernel/mm/transparent_hugepage/defrag

(3)启用透明大页。

echo always >  /sys/kernel/mm/transparent_hugepage/enabled 
echo always > /sys/kernel/mm/transparent_hugepage/defrag

(4)设置开机关闭。

修改/etc/rc.local文件,添加如下行:

if test -f /sys/kernel/mm/redhat_transparent_hugepage/enabled; then     
     echo never > /sys/kernel/mm/transparent_hugepage/enabled     
    echo never > /sys/kernel/mm/transparent_hugepage/defrag 
fi

升级效果

由于主站的业务比较复杂,项目较多,涉及服务池达多个,每个服务池所承担业务与流量也不一样,所以我们在对不同的服务池进行灰度升级,遇到的问题也不尽相同,导致整体升级前后达半年之久。庆幸的是,遇到的问题,最终都被解决掉了。最让人兴奋的是升级效果非常好,基本与官方一致,也为公司节省了不少成本。

以下简单地给大家展示下这次PHP7升级的成果。

(1)PHP5与PHP7环境下,分别对我们的某个核心接口进行压测(压测数据由QA团队提供),相关数据如下:

同样接口,分别在两个不现的环境中进行测试,平均TPS从95提升到220,提升达130%。

(2)升级前后,单机CPU使用率对比如下。

升级前后,1小时流量情况变化:

升级前后,1小时CPU使用率变化:

升级前后,在流量变化不大的情况下,CPU使用率从45%降至25%,CPU使用率降低44.44%。

(3)某服务集群升级前后,同一时间段1小时CPU使用对比如下。

PHP5环境下,集群近1小时CPU使用变化:

PHP7环境下,集群近1小时CPU使用变化:

升级前后,CPU变化对比:

升级前后,同一时段,集群CPU平均使用率从51.6%降低至22.9%,使用率降低56.88%。

以上只简单从三个维度列举了一些数据。为了让升级效果更加客观,我们实际的评估维度更多,如内存使用、接口响应时间占比等。最终综合得出的结论为,通过本次升级,PC主站整体性能提升在48.82%,效果非常好。团队今年的职能KPI就算是提前完成了。

总结

整体升级从准备到最终PC主站全网升级完成,时间跨度达半年之久,无论是扩展编写、准备安装脚本、PHP代码升级还是全网灰度,期间一直会出现各式各样的问题。最终在团队的共同努力下,这些问题都彻底得到了解决。

一直以来,对社区的付出深怀敬畏之心,也是因为他们对PHP语言性能极限的追求,才能让大家的业务坐享数倍性能的提升。同时,也让我们更加相信,PHP一定会是一门越来越好的语言。

作者简介

侯青龙,微博主站研发负责人。2010年加入新浪微博,先后参与过微博主站V2版至V6版的研发,主导过主站V6版以及多机房消息同步系统等重大项目的架构设计工作。致力于提升产品研发效率以及优化系统性能。


感谢韩婷对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。