简介:... 1
启用ATL Server的Data Source Cache支持... 1
实现数据库的交互... 2
创建ATL OLEDB使用者类... 2
提供插入记录的能力... 2
创建数据源连接对象... 3
使用UDL文件代替连接字符串... 4
Web Service调试... 4
性能评测... 5
ATL Server是性能很高的Web Application和Web Service的开发类库。到目前为止,我认为如果应用程序要和数据库交互,性能最高的办法是使用OLE DB,同时要启用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<ds_cache_type> 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<IDataSourceCache *>(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编码。
[
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<CHighPerformanceService>::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 <stdafx.h>
#include "../CommonServiceDefinition.h"
#include <fstream>
using namespace std;
class CUdl :
public IUdl
{
public:
CUdl()
{
CComPtr<IDataInitialize> 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<OLECHAR> 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<IDataSourceCache *>(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<CHighPerformanceService>::InitializeHandler(pRequestInfo, pProvider))
return HTTP_FAIL;
CComPtr<IUdl> 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
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万,好极了。性能感觉非常好。
分享到:
相关推荐
### 开发高性能的Web Service应用 #### WebService性能概述 在开发高性能的Web Service应用时,首先需要理解WebService性能的基本概念及其重要性。性能不仅关乎用户体验,还直接影响到系统的稳定性和资源消耗。...
理解Web Service的基本原理,熟悉PB11的API和工具,以及遵循良好的编程和设计原则,将有助于提升开发效率和应用质量。 总结,PB11提供了强大的Web Service开发功能,让开发者能够轻松地构建和整合分布式系统。通过...
Java Web Service性能监视是针对基于Java的Web服务应用程序的性能监控...通过这些工具,可以有效地监控Java Web Service的性能,从而提高整体服务质量,减少响应时间,避免资源浪费,并确保系统的高可用性和稳定性。
【WebService客户端调用服务器数据库】是一个关键的IT技术主题,主要涉及如何通过Web服务接口与远程服务器数据库进行交互。在互联网应用中,这种技术经常用于实现不同系统间的数据共享和功能整合。以下是对这一主题...
最后,考虑到实际应用中的安全性问题,书中还会涵盖如何处理SQL注入、事务管理、连接池配置以及错误处理等内容,这些都是开发安全、高性能的JSP数据库应用不可或缺的部分。 总之,《JSP数据库编程指南》将深入探讨...
- **Chap12**:可能探讨了更高级的Web Service话题,如安全性、事务处理和性能优化。 - **Chap09**:可能涵盖了Delphi6和Kylix2中的SOAP客户端开发。 - **Chap10**:可能详细介绍了如何创建和部署SOAP服务器端...
6. **ASP.NET Cache Service**:缓存服务提高了应用程序性能,通过存储常用数据,减少对数据库或其他资源的重复访问。 7. **ASP.NET Deployment**:ASP.NET简化了部署流程,例如使用XCOPY部署,使得只需复制文件...
SQL Server 2000是一个功能强大、可扩展、高性能的关系型数据库管理系统,它被广泛应用于公司和企业中。通过存储技术,SQL Server 2000能够提供稳定、高效的数据存储解决方案。 3. Web Service技术: Web Service是...
XFire(后来被Apache CXF吸收)是另一个强大的Web服务框架,以其高性能和轻量级特性受到欢迎。XFire支持多种协议和数据格式,包括RESTful服务、JAXB数据绑定以及MTOM(消息传输优化机制)。其API简洁易用,使得开发...
《高性能网站建设进阶指南》是一本专为WEB开发者设计的性能优化手册,旨在帮助开发者们构建更快、更稳定、更高效的网站。这本书深入探讨了JavaWeb和Java在网站开发中的应用,结合实际案例,提供了丰富的优化策略和...
Delphi Web Service是一种在Delphi开发环境中创建网络服务的技术,它允许...通过掌握Delphi Web Service技术,开发者可以充分利用Delphi的强大编程能力,构建高效、可扩展的网络服务,满足现代企业对跨平台通信的需求。
6. **高性能SOAP栈**:XFire设计了高效的SOAP栈,进一步优化了Web Service的通信效率。 #### 三、XFire与Spring的协同效应 XFire与Spring的结合,为开发者提供了更为强大的工具集,特别是在以下方面展现出巨大潜力...
尽管XML Web服务通常用于跨网络的松耦合通信,但.NET Remoting可以提供更高的性能和更低的延迟,适用于内部企业应用间的通信。 **08 使用ADO.NET .ppt** ADO.NET是.NET框架中的数据访问组件,可以用来连接数据库并...
8. **Oracle连接池**:在JAVA WEB应用中,连接池如C3P0、HikariCP或Apache DBCP,用于管理和复用数据库连接,减少资源消耗并提高性能。 9. **JPA和Hibernate**:对于更高级别的数据访问,可以使用Java Persistence ...
### Web Service开发知识点详解 ...通过掌握关键技术、工具和技术,开发者可以有效地构建出高性能、安全可靠的Web Service应用。无论是企业内部还是不同企业间的集成场景,Web Service都是不可或缺的一部分。
20120102 ServiceMobileQuery net web service 是一个专为实现这一功能而设计的.NET Web服务,它集成了C#编程语言、ASP.NET技术、SQL数据库以及DBA数据库管理技能,为开发者提供了一个高效、稳定的解决方案。...
### 深入浅出 Web Service #### 一、什么是 Web Service? Web Service 是一种通信方式,它允许两个电子设备在网络之间进行数据交换。根据维基百科定义:“Web Service 是一种通过网络,遵循 Web 协议来提供服务的...
Web Service 使用标准的HTTP和XML协议,允许来自不同平台和编程语言的应用程序相互操作。JAX-RPC(Java API for XML-based RPC)是Java中用于实现Web Service的一种技术,它提供了基于XML的远程过程调用机制。JAXR...
【ASP.NET与Web Service实例剖析】是一份关于...总的来说,这份PPT是针对ASP.NET和Web Service的一份深度学习资源,涵盖了从基础知识到实践应用的多个层面,对于想深入了解这两种技术的开发者来说具有很高的参考价值。
以上只是Java Web编程技术的一小部分,实际开发中还需要了解版本控制(如Git)、持续集成/持续部署(CI/CD)工具、性能调优、安全性以及Web标准等。随着技术的发展,Java Web开发不断演变,例如Spring Boot的出现...