ab.exe介紹
ab.exe是apache server的一個組件,用於監測併發請求,並顯示監測數據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程序
一、數據訪問類(包含同步/異步訪問數據庫的方法)
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()),
};
}
}
二、數據庫對應實體類及輔助擴展方法
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);
}
}
}
三、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();
}
}
四、數據庫鏈接配置文件
<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個請求;