您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
高性能Web Service数据库编程
 
作者:反正我信了 来源:博客园 发布于:2014-12-25
   次浏览      
 

简介:

ATL Server是性能很高的Web Application和Web Service的开发类库。到目前为止,我认为如果应用程序要和数据库交互,性能最高的办法是使用OLE DB,同时要启用ATL Server的Data Source Cache。

启用ATL Server的Data Source Cache支持

先来创建一个名为HighPerformance的工程,该工程将访问SQL Server数据库。在服务器选项中,选择“数据源缓存”。向导为我们创建了两个工程,一个是HighPerformance,一个是HighPerformanceIsapi。HighPerformance是我们的应用程序逻辑所在之处,我们所有的Web Service的可公开函数都在这里。HighPerformanceIsapi是我们的ISAP扩展服务工程,IIS将在第一次Web Service被调用期间加载它,并且一直缓存,直到很长一段时间没有人使用我们的Web Service才释放。因此我们可以利用这个特性将需要缓存的全局变量保存到HighPerformanceIsapi工程中,然后每个应用程序线程(HighPerformance工程)可以通过调用QueryService来获得缓存的全局变量,并且调用全局变量的成员函数。

HighPerformanceIsapi工程内部维护了一个线程池,每个线程对象内部都拥有自己的数据源缓存对象,请看代码:

typedef CDataSourceCache<> ds_cache_type;
     CComObjectGlobal m_dsCache;

CComObjectGlobal用于保存在m_dsCache对象的生存期内,HighPerformanceIsapi服务器不会被卸载掉。CDataSourceCache类实现了IDataSourceCache接口。

当HighPerformance工程中的应用程序逻辑通过QueryService请求全局对象时,只要传递

__uuidof(IDataSourceCache)即可。QueryService的实现代码如下:

HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, 

              REFIID riid, void** ppvObject)

     {

         if (InlineIsEqualGUID(guidService, __uuidof(IDataSourceCache)))

         {

              CIsapiWorker *pWorker = GetThreadWorker();

              if (pWorker)

              {

                   CDataSourceCache<> *pCache = NULL;
                   if (pWorker->GetWorkerData(_DATASOURCE_CACHE, (void **)&pCache))
                   {
                       *ppvObject = static_cast(pCache);
                       return S_OK;
                   }
              }
         }
         return baseISAPI::QueryService(guidService, riid, ppvObject);
     }

实现数据库的交互

创建ATL OLEDB使用者类

我们使用向导连接SQL SERVER (服务器名为GAO、用户名和密码为sa、数据库名为测试数据库、数据表为学生表),并支持属性化,生成支持写操作的OLE DB命令类。具体向导使用这里不叙述,请参考MSDN。

我们的OLE DB类名为CDataRowset。属性db_source是代表了连接信息,后面我将通过udl文件替代之。db_command属性中的SQL语句代表了查询表的操作,我们也可以使用Update或者insert语句代替之。

注意,我们的CDataRowset类创建在HighPerformance工程中,HighPerformanceIsapi工程中应该保存我们的全局数据源对象。

提供插入记录的能力

我们需要修改db_command属性中绑定的SQL语句,以便我们插入一条语句。db_command(L"INSERT INTO [学生表] ([姓名],[年龄],[性别],[地址]) VALUES (?,?,?,?)")

将db_column属性修改为db_param属性,顺序请对应INSERT语句的四个?号。

为了使我们的Web Service有更好的性能,我在工程中设置了使用UNICODE编码。

<!DOCTYPE HTML>[
     db_command(L"INSERT INTO [学生表] ([姓名],[年龄],[性别],[地址]) VALUES (?,?,?,?)")
]
class CDataRowset
{
public:
     [ db_param(1,DBPARAMIO_INPUT)] TCHAR m_column0[20];
     [ db_param(2,DBPARAMIO_INPUT)] TCHAR m_column1[20];
     [ db_param(3,DBPARAMIO_INPUT)] TCHAR m_column2[20];
     [ db_param(4,DBPARAMIO_INPUT)] TCHAR m_column3[20];
     void GetRowsetProperties(CDBPropSet* pPropSet)
     {
         pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
         pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
         pPropSet->AddProperty(DBPROP_IRowsetChange, true, DBPROPOPTIONS_OPTIONAL);
         pPropSet->AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE 
| DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
     }
};

编写CAddRecord类,该类从CDataRowset类派生,这里只能使用继承而不是直接修改CDataRowset类,是因为db_command属性导致了CDataRowset类引入了一些隐藏类,这些隐藏类会导致一些意外行为。

CAddRecord类将提供设置成员变量和清除成员变量的方法。代码如下:

void CAddRecord::Clear(void)

{

     //set member to zero

     m_column0[0]=0;

     m_column1[0]=0;

     m_column2[0]=0;

     m_column3[0]=0;

}
 

void CAddRecord::SetStudentInfo(BSTR Name,BSTR Age,BSTR Gender,BSTR Address)

{

     wcscpy(m_column0,Name);

     wcscpy(m_column1,Age);

     wcscpy(m_column2,Gender);

     wcscpy(m_column3,Address);

}

创建数据源连接对象

删除CDataRowset类的db_source属性,该属性帮助我们创建CDataSource对象,我们这里不需要。

定义连接字符串宏

#define CONN_STRINGW L"Provider=SQLOLEDB.1;Password=sa;
Persist Security Info=True;User ID=sa;Initial Catalog=
/x6d4b/x8bd5/x6570/x636e/x5e93;Data Source
=GAO;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;
Workstation ID=FREEBIRD;Use Encryption for Data=False;Tag with column collation when possible=False"

修改CHighPerformance类的代码:

HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)

{

         if (HTTP_SUCCESS != CSoapHandler::InitializeHandler(pRequestInfo, pProvider))
              return HTTP_FAIL;
         if (S_OK != GetDataSource(  pProvider, 
                                          CONN_STRINGW,
                                          CONN_STRINGW, &m_dc ))
              return HTTP_FAIL;
         return HTTP_SUCCESS;
}    

m_dc是该类的私有成员变量,类型为CDataConnection。

[ soap_method ]

     HRESULT AddNewStudent(BSTR Name,BSTR Age,BSTR Gender,BSTR Address)

     {

         m_rec.SetStudentInfo(Name,Age,Gender,Address);

         HRESULT hr=m_rec.OpenRowset(m_dc);

         return hr;

     }

     m_rec定义为CAddRecord m_rec;

使用UDL文件代替连接字符串

从UDL文件中获取连接字符串,需要创建MSDAINITIALIZE对象,并调用IDataInitialize接口的LoadStringFromStorage方法才行。这种动作我并不想放到InitializeHandler函数中反复做。唯一的办法是放到ISAPI扩展DLL中。

我们应该在类CHighPerformanceExtension中维护一个成员变量,当我们在应用程序DLL中通过QueryService查询某个接口(这里名叫IUdl)时,我们可以获得该变量的实现的IUdl接口,通过IUdl的方法GetConnectString获取连接字符串。这样我们就可以将该连接字符串作为参数传递给GetDataSource函数,从而取代宏。

实现IUdl接口的对象将在创建时读取c:/gao.udl文件,并将连接字符串保存,IUdl::GetConnectString方法将返回连接字符串。

这里涉及到几个知识点,如何在ATL SERVER中开发一个COM-LIKE 接口,并且将接口暴露为服务;如通过OLE DB组件读取UDL文件。源代码如下,请参考:

#pragma once

//interface IUdl

__interface __declspec(uuid("F4FA35D6-D26D-4b5a-9544-A69F66E927B8"))
IUdl:public IUnknown
{
     HRESULT GetConnectString(BSTR* pConString);
};
#pragma once
//#include 
#include "../CommonServiceDefinition.h"
#include 
using namespace std;
class CUdl :
     public IUdl
{
public: 
     CUdl()
     {
         CComPtr spDataInitialize;
        HRESULT hr = spDataInitialize.CoCreateInstance( __uuidof(MSDAINITIALIZE));
        if (SUCCEEDED(hr))
              hr=spDataInitialize->LoadStringFromStorage(CComBSTR("c://gao.udl"), &m_ConnString);
     }
     ULONG        STDMETHODCALLTYPE  AddRef()        {return 1;} 
     ULONG        STDMETHODCALLTYPE Release()        {return 1;} 
     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) 
     { 
         if( !ppvObject ) 
              return E_POINTER; 
         if( IsEqualIID( riid, IID_IUnknown)) 
*ppvObject = static_cast<IUnknown*>(this); 
         else if( IsEqualIID( riid, __uuidof(IUdl))) 
*ppvObject = static_cast<IUdl*>(this); 
         else 
              return E_NOINTERFACE; 
         AddRef(); 
         return S_OK; 
     }
     HRESULT GetConnectString(BSTR* pConString)
     { 
         if( pConString==NULL) 
         {
              return E_INVALIDARG;
         }
         CComBSTR bstr(m_ConnString);
         *pConString=bstr.Detach();
         return S_OK;
     } 
private:
     CComHeapPtr m_ConnString;
};

类CHighPerformanceExtension中声明变量CUdl m_udl;并且改写QueryService方法

HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, 
REFIID riid, void** ppvObject)

     {

         if (InlineIsEqualGUID(guidService, __uuidof(IDataSourceCache)))

         {

              CIsapiWorker *pWorker = GetThreadWorker();

              if (pWorker)

              {

                   CDataSourceCache<> *pCache = NULL;
                   if (pWorker->GetWorkerData(_DATASOURCE_CACHE, (void **)&pCache))
                   {
                       *ppvObject = static_cast(pCache);
                       return S_OK;
                   }
              }
         }
         if(InlineIsEqualGUID(guidService,__uuidof(IUdl)) && 
              InlineIsEqualGUID(riid,__uuidof(IUdl)) )
         {
              return m_udl.QueryInterface(riid,ppvObject);
         }
         return baseISAPI::QueryService(guidService, riid, ppvObject);
}

我们的应用程序dll中的代码如下:

// HighPerformance.h : 定义 ATL Server 请求处理程序类

//

#pragma once

#include "addrecord.h"
#include "../CommonServiceDefinition.h"
namespace HighPerformanceService
{
// webservice 的所有 struct、enum 和 typedefs 应进入命名空间
// IHighPerformanceService - Web 服务接口声明
//
[
     uuid("EECB2E3E-9CA8-4E07-8DE9-81A21E99E707"), 
     object
]
__interface IHighPerformanceService
{
     [id(1)] HRESULT AddNewStudent([in]BSTR Name,[in]BSTR Age,[in]BSTR Gender,[in]BSTR Address);
};
// HighPerformanceService - Web 服务实现
//
[
    request_handler(name="Default", sdl="GenHighPerformanceWSDL"),
     soap_handler(
         name="HighPerformanceService", 
         namespace="urn:HighPerformanceService",
         protocol="soap"
     )
]

class CHighPerformanceService :
     public IHighPerformanceService
{
public
     HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
     {
         if (HTTP_SUCCESS != CSoapHandler::InitializeHandler(pRequestInfo, pProvider))
              return HTTP_FAIL;
         CComPtr spUdl;
         HRESULT hr=pProvider->QueryService(__uuidof(IUdl),&spUdl);
         if(hr!=S_OK)
              return HTTP_FAIL;
         CComBSTR connectstring;
         hr=spUdl->GetConnectString(&connectstring);
         if(hr!=S_OK)
         {
              return HTTP_FAIL;
         }
         if (S_OK != GetDataSource(  pProvider, 
                                          connectstring,
                                          connectstring, &m_dc ))
         {
              return HTTP_FAIL;
         }
         return HTTP_SUCCESS;
     }    
     [ soap_method ]
     HRESULT AddNewStudent(BSTR Name,BSTR Age,BSTR Gender,BSTR Address)
     {
         m_rec.SetStudentInfo(Name,Age,Gender,Address);
         HRESULT hr=m_rec.OpenRowset(m_dc);
         return hr;
     }
private:
     CDataConnection m_dc;
     CAddRecord m_rec;
     // TODO: 在此添加其他 Web 服务方法
}; // 类 CHighPerformanceService
} // 命名空间 HighPerformanceService

Web Service调试

MSDN提供一个例子---SOAPDebugApp 示例:在客户端内存空间中调试 XML Web services

地址:ms-help://MS.MSDNQTR.2003FEB.2052/vcsample/html/vcsamsoapdebugappsample.htm

这种方式不同真正的WEB调用,而是通过直接调用应用程序DLL,这种方式很好,但是有局限性,由于我们的InitializeHandler要在每次调用WEB方法之前执行,并且每次都要从ISAPI扩展DLL线程池中打交道,而这种模拟调用不会导致IIS加载ISAPI扩展DLL。所以,InitializeHandler会失败,我们就没有办法对WEB 方法进行断点跟踪。解决之道有两种,一是用文件输出的方式,而是先不用扩展DLL缓存,当确信应用程序DLL已经没有问题后,再进行修改。但这两种方法都比较麻烦,不知道还有没有跟好的办法?

另外,调试的时候IIS容易出现不正常,请用iisreset命令重启。

性能评测

我编写客户端调用AddNewSutdent方法,一切成功。三个客户端同时各自写入2万笔纪录,总数6万,好极了。性能感觉非常好。

   
次浏览       
相关文章

基于EA的数据库建模
数据流建模(EA指南)
“数据湖”:概念、特征、架构与案例
在线商城数据库系统设计 思路+效果
 
相关文档

Greenplum数据库基础培训
MySQL5.1性能优化方案
某电商数据中台架构实践
MySQL高扩展架构设计
相关课程

数据治理、数据架构及数据标准
MongoDB实战课程
并发、大容量、高性能数据库设计与优化
PostgreSQL数据库实战培训
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]

MySQL索引背后的数据结构
MySQL性能调优与架构设计
SQL Server数据库备份与恢复
让数据库飞起来 10大DB2优化
oracle的临时表空间写满磁盘
数据库的跨平台设计
更多...   


并发、大容量、高性能数据库
高级数据库架构设计师
Hadoop原理与实践
Oracle 数据仓库
数据仓库和数据挖掘
Oracle数据库开发与管理


GE 区块链技术与实现培训
航天科工某子公司 Nodejs高级应用开发
中盛益华 卓越管理者必须具备的五项能力
某信息技术公司 Python培训
某博彩IT系统厂商 易用性测试与评估
中国邮储银行 测试成熟度模型集成(TMMI)
中物院 产品经理与产品管理
更多...