ab.exe介紹
ab.exe是apache server的一個組件,用於監測並發請求,並顯示監測數據
具體使用及下載地址請參考:
http://www.cnblogs.com/gossip/p/4398784.html
本文的目的
通過webapi接口模擬100個並發請求下,同步和異步訪問數據庫的性能差異
創建數據庫及數據
--創建表結構
CREATE TABLE dbo.[Cars] (
Id INT IDENTITY(1000,1) NOT NULL,
Model NVARCHAR(50) NULL,
Make NVARCHAR(50) NULL,
[Year] INT NOT NULL,
Price REAL NOT NULL,
CONSTRAINT [PK_Cars] PRIMARY KEY CLUSTERED (Id) ON [PRIMARY]
) ON [PRIMARY];
GO
--創建存儲過程
CREATE PROCEDURE [dbo].[sp$GetCars]
AS
-- 存儲過程執行過程中等待一秒
WAITFOR DELAY '00:00:01';
SELECT * FROM Cars;
GO
--初始化數據
INSERT INTO dbo.Cars VALUES('Car1', 'Model1', 2006, 24950);
INSERT INTO dbo.Cars VALUES('Car2', 'Model1', 2003, 56829);
INSERT INTO dbo.Cars VALUES('Car3', 'Model2', 2006, 17382);
INSERT INTO dbo.Cars VALUES('Car4', 'Model3', 2002, 72733);
編寫webapi程序
1、數據訪問類(包含同步/異步訪問數據庫的方法)
public class GalleryContext
{
readonly string _spName = "sp$GetCars";
readonly string _connectionString =
ConfigurationManager.ConnectionStrings["TestDBConnStr"].ConnectionString;
/// <summary>
/// 同步獲取數據
/// </summary>
/// <returns></returns>
public IEnumerable<Car> GetCarsViaSP()
{
using (var conn = new SqlConnection(_connectionString))
{
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = _spName;
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
using (var reader = cmd.ExecuteReader())
{
return reader.Select(r => carBuilder(r)).ToList();
}
}
}
}
/// <summary>
/// 異步獲取數據
/// </summary>
/// <returns></returns>
public async Task<IEnumerable<Car>> GetCarsViaSPAsync()
{
using (var conn = new SqlConnection(_connectionString))
{
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = _spName;
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
using (var reader = await cmd.ExecuteReaderAsync()) //調用異步方法
{
return reader.Select(r => carBuilder(r)).ToList();
}
}
}
}
//private helpers
private Car carBuilder(SqlDataReader reader)
{
return new Car
{
Id = int.Parse(reader["Id"].ToString()),
Make = reader["Make"] is DBNull ? null : reader["Make"].ToString(),
Model = reader["Model"] is DBNull ? null : reader["Model"].ToString(),
Year = int.Parse(reader["Year"].ToString()),
Price = float.Parse(reader["Price"].ToString()),
};
}
}
2、數據庫對應實體類及輔助擴展方法
public class Car
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public float Price { get; set; }
}
public static class Extensions
{
public static IEnumerable<T> Select<T>(
this SqlDataReader reader, Func<SqlDataReader, T> projection)
{
while (reader.Read())
{
yield return projection(reader);
}
}
}
3、webapi Controller調用方法
//同步調用接口
public class SPCarsSyncController : ApiController
{
readonly GalleryContext galleryContext = new GalleryContext();
public IEnumerable<Car> Get()
{
return galleryContext.GetCarsViaSP();
}
}
//異步調用接口
public class SPCarsAsyncController : ApiController
{
readonly GalleryContext galleryContext = new GalleryContext();
public async Task<IEnumerable<Car>> Get()
{
return await galleryContext.GetCarsViaSPAsync();
}
}
4、數據庫連接配置文件
<connectionStrings>
<add name="TestDBConnStr"
connectionString="Server=你的數據庫地址;Database=TestDB;User Id=sa;Password=123;Integrated Security=false;Max Pool Size=500;Asynchronous Processing=True;"
providerName="System.Data.SqlClient" />
</connectionStrings>
其中:
Max Pool Size:表示連接池的最大數量
Asynchronous Processing=True;表示數據庫支持異步處理
Integrated Security=false;此處設為true或不設置會拋出異常
Login failed for user 'NT AUTHORITY\SYSTEM'
使用ab.exe模擬並發請求
模擬請求同步服務:

模擬請求異步服務:

結論:
訪問相同的數據表及數據,
使用同步方式共用時
20秒,平均每秒處理5個請求;
使用異步方式共用時
10秒,平均每秒處理
10個請求;