BizTalk 环境的评估与改进





5.00/5 (1投票)
在我最近一次对 BizTalk 客户端环境的评估中,我们遇到了一些环境问题,例如速度慢、超时等。 我们对设置进行了一些更改,之后性能有所提高,故障/超时减少。 在这里,我们将看到所做的更改。
引言
本文讨论了最近在 BizTalk 客户端环境中进行的一次评估,以及为提高服务器性能而应用的一些技巧。
背景
这是一个 BizTalk Server 2010 服务器,带有 SQL Server 2008 数据库。 BizTalk 服务器使用业务流程和发送端口与多个外部系统交互。 业务流程作为 Web 服务公开,并由外部系统调用。 业务流程调用外部系统/Web 服务,然后将响应返回给调用系统。 业务流程将请求和响应文件存储在文件夹中,并将事务记录在 SQL Server 日志表中。
面临的主要问题是生产环境中的总体速度慢,以及某些 Web 服务调用中的应用程序超时,从而导致事务失败。
评估策略和改进步骤
为了对生产系统进行评估,第一步是使用 Message Box Viewer 进行分析。 我们在生产环境中运行该工具并分析了它生成的报告。 来自 Message Box Viewer 的一些主要发现
- DTA 跟踪数据库的存档和清除作业未成功运行,跟踪数据库的大小不断增加。
- 在生产环境中发现了大量挂起消息 (60K+),导致性能下降
- 在多个发送端口和业务流程中启用了跟踪,导致性能降低
- 在接收位置使用了 XML 接收和 XML 传输管道
- 接收位置使用 SOAP 协议,而不是 WCF-* 协议
- 只有一个主机实例
BizTalkServerApplication
用于执行业务流程、执行跟踪的发送端口等。
解决发现的问题
我们进行了以下更新以解决这些发现。
终止挂起消息的脚本:https://dl.dropboxusercontent.com/u/23405583/terminate.vbs' terminate.vbs
' Enter terminate.vbs with no arguments from a command prompt for usage
' This script needs to be run under a user account that is a member of the BizTalk Administrators
' group. This script needs to be run on a machine that is configured with BizTalk administration
' tools.
dim objBtsWmiNS, objMsg, svcinsts, inst, msg, ndx, size, savemessages
Dim aryClassIDs()
Dim aryTypeIDs()
Dim aryInstanceIDs()
Dim aryHostNames()
Dim aryObjQueues()
Dim aryHostBatchSize()
Dim strKey2Instance
Dim strQuery2Msg
Dim daysOld
maxBatchSize = 200 'Terminate in batches. Max supported batch size is 2K-1 (2047)
On Error Resume Next
Dim objNamedArgs: Set objNamedArgs = WScript.Arguments.Named
If objNamedArgs.Count = 0 OR objNamedArgs.Count > 3 Then
PrintUsage()
wscript.quit 0
End If
If Not objNamedArgs.Exists("Operation") Then
printUsage()
wscript.quit 0
End If
wmiQuery = ""
'ServiceStatus = 16 - 'Completed With Discarded Messages' in BizTalk Server 2004
'ServiceStatus = 32 - 'Suspended (not resumable)'
'ServiceStatus = 4 - 'Suspended (resumable)'
'ServiceClass = 64 - 'Routing Failure Report'
'ErrorId = "0xC0C01B4C" - is how 'Completed With Discarded Messages' are exposed in BizTalk Server 2009
If UCase(objNamedArgs("Operation")) = "Z" Then
wmiQuery = "select * from MSBTS_serviceinstance where ServiceStatus=16"
End If
If UCase(objNamedArgs("Operation")) = "A" Then
wmiQuery = "select * from MSBTS_serviceinstance where ServiceStatus=4 _
OR ServiceStatus=32 OR ServiceStatus=16 OR ErrorId='0xC0C01B4C' OR ServiceClass=64"
End If
If UCase(objNamedArgs("Operation")) = "SR" Then
wmiQuery = "select * from MSBTS_serviceinstance where ServiceStatus=4"
End If
If UCase(objNamedArgs("Operation")) = "SNR" Then
wmiQuery = "select * from MSBTS_serviceinstance where ServiceStatus=32"
End If
If UCase(objNamedArgs("Operation")) = "DIS" Then
wmiQuery = "select * from MSBTS_serviceinstance where ServiceClass=32 AND ServiceStatus=8"
'ServiceClass = 32 'Isolated Adapter
'ServiceStatus = 8 'Dehydrated
End If
If(wmiQuery = "") Then
PrintUsage()
wscript.quit 0
End If
argCount = 1
saveMessagesBeforeTermination = True
If objNamedArgs.Exists("NoSave") Then
If UCase(objNamedArgs("NoSave")) = "TRUE" Then
saveMessagesBeforeTermination = False
ElseIf UCase(objNamedArgs("NoSave")) <> "FALSE" Then
PrintUsage()
wscript.quit 0
End If
argCount = argCount + 1
End If
daysOld = 0
If objNamedArgs.Exists("DaysOld") Then
If IsNumeric(objNamedArgs.Item("DaysOld")) Then
If CLng(objNamedArgs.Item("DaysOld")) = CDbl(objNamedArgs.Item("DaysOld")) Then
daysOld = CLng(objNamedArgs.Item("DaysOld"))
Else
PrintUsage()
wscript.quit 0
End If
Else
PrintUsage()
wscript.quit 0
End If
argCount = argCount + 1
End If
If objNamedArgs.Count <> argCount Then
PrintUsage()
wscript.quit 0
End If
wscript.echo "-->Connecting to BizTalk WMI namespace"
Set objBtsWmiNS = GetObject("WinMgmts:{impersonationLevel=impersonate, _
(security)}\\.\root\MicrosoftBizTalkServer")
If Err <> 0 Then
CheckWMIError
wscript.quit 0
End If
wscript.echo "-->Getting BizTalk host collection"
Set hosts = objBtsWmiNS.ExecQuery("select * from MSBTS_HostSetting")
If Err <> 0 Then
CheckWMIError
wscript.quit 0
End If
hostCount = hosts.count
ReDim aryHostNames(hostCount - 1)
ReDim aryObjQueues(hostCount - 1)
ReDim aryHostBatchSize(hostCount - 1)
wscript.echo "-->Retrieve BizTalk host names and loading host queues"
ndx = 0
For Each host in hosts
wscript.echo "Found host " & host.Properties_("Name")
aryHostNames(ndx) = host.Properties_("Name")
Set aryObjQueues(ndx) = objBtsWmiNS.Get("MSBTS_HostQueue.HostName=""" _
& aryHostNames(ndx) & """")
If Err <> 0 Then
CheckWMIError
wscript.quit 0
End If
ndx = ndx + 1
Next
wscript.echo "-->Getting collection of service instances"
Set svcinsts = objBtsWmiNS.ExecQuery(wmiQuery)
ReDim aryClassIDs(hostCount, maxBatchSize-1)
ReDim aryTypeIDs(hostCount, maxBatchSize-1)
ReDim aryInstanceIDs(hostCount, maxBatchSize-1)
'Iterate through instances and save them in host-specific arrays.
'Terminate instances from host-specific array when array gets to a maxBatchSize
wscript.echo "-->Start iterating service instances"
totalCount = 0
saveMessages = saveMessagesBeforeTermination
For Each inst in svcinsts
sSuspendDate = inst.Properties_("SuspendTime")
sSuspendDay = Left(sSuspendDate,4) & "-" & _
Mid(sSuspendDate, 5, 2) & "-" & Mid(sSuspendDate, 7, 2)
dtSuspendDate = CDate(sSuspendDay)
If DateDiff("d", dtSuspendDate, Date()) >= daysOld Then
saveMessagesBeforeTermination = saveMessages
wscript.echo "Found suspended instance """ & _
inst.Properties_("ServiceName") & """ _
on host " & inst.Properties_("HostName")
'Resolve host index
For hostIdx = 0 To hostCount-1
If aryHostNames(hostIdx) = inst.Properties_("HostName") Then
Exit For
End If
Next
'16 is an internal service class that cannot be terminated
If 16 = inst.Properties_("ServiceClass") Then
wscript.echo "Skipping BizTalk internal service instances _
(they cannot be terminated anyway)"
Else
'64 is a routing failure report and doesn't have messages that can be saved
If 64 = inst.Properties_("ServiceClass") _
Or 16 = inst.Properties_("ServiceClass") Then
saveMessagesBeforeTermination = False
End If
errorCountSavingMessages = 0
If saveMessagesBeforeTermination Then
strQuery2Msg = "select * from MSBTS_MessageInstance _
where ServiceInstanceID=""" & _
inst.Properties_("InstanceId") & """"
Set msgInsts = objBtsWmiNS.ExecQuery(strQuery2Msg)
For Each msg in msgInsts
msg.SaveToFile "C:\Temp"
If Err <> 0 Then
CheckWMIError
wscript.echo "Failed to save MSBTS_MessageInstance"
wscript.echo Err.Description & Err.Number
errorCountSavingMessages = errorCountSavingMessages + 1
Else
wscript.echo "Saved message " & _
msg.Properties_("MessageInstanceID")
End If
Next
End If
If 0 = errorCountSavingMessages Then 'Only terminate when we had no problems saving messages
aryClassIDs(hostIdx, aryHostBatchSize(hostIdx)) = inst.Properties_("ServiceClassId")
aryTypeIDs(hostIdx, aryHostBatchSize(hostIdx)) = inst.Properties_("ServiceTypeId")
aryInstanceIDs(hostIdx, aryHostBatchSize(hostIdx)) = inst.Properties_("InstanceId")
aryHostBatchSize(hostIdx) = aryHostBatchSize(hostIdx) _
+ 1 'Keep track of newly added instace for that host
Else
wscript.echo "Skipping the instance since couldn't save its messages"
End If
totalCount = totalCount + 1
If(aryHostBatchSize(hostIdx) = maxBatchSize) Then
TerminateAccumulatedInstacesForHost hostIdx
End If
End If
End If
Next
' Delete whatever is left
For hostIdx = 0 To hostCount-1
If aryHostBatchSize(hostIdx) > 0 Then
TerminateAccumulatedInstacesForHost hostIdx
End If
Next
wscript.echo "SUCCESS> " & totalCount & _
" instances were found and attempted to be terminated"
Sub TerminateAccumulatedInstacesForHost(hostIdx)
wscript.echo "Sending termination request for host " _
& aryHostNames(hostIdx) & " service instances"
Dim aryClassIDs4Host()
Dim aryTypeIDs4Host()
Dim aryInstanceIDs4Host()
ReDim aryClassIDs4Host(aryHostBatchSize(hostIdx)-1)
ReDim aryTypeIDs4Host(aryHostBatchSize(hostIdx)-1)
ReDim aryInstanceIDs4Host(aryHostBatchSize(hostIdx)-1)
For i = 0 to aryHostBatchSize(hostIdx)-1
aryClassIDs4Host(i) = aryClassIDs(hostIdx, i)
aryTypeIDs4Host(i) = aryTypeIDs(hostIdx, i)
aryInstanceIDs4Host(i) = aryInstanceIDs(hostIdx, i)
Next
aryObjQueues(hostIdx).TerminateServiceInstancesByID aryClassIDs4Host, _
aryTypeIDs4Host, aryInstanceIDs4Host
CheckWMIError
aryHostBatchSize(hostIdx) = 0
End Sub
'This subroutine deals with all errors using the WbemScripting object.
'Error descriptions are returned to the user by printing to the console.
Sub CheckWMIError()
If Err <> 0 Then
On Error Resume Next
Dim strErrDesc: strErrDesc = Err.Description
Dim ErrNum: ErrNum = Err.Number
Dim WMIError : Set WMIError = CreateObject("WbemScripting.SwbemLastError")
If (TypeName(WMIError) = "Empty" ) Then
wscript.echo strErrDesc & " (HRESULT: " & Hex(ErrNum) & ")."
Else
wscript.echo WMIError.Description & "(HRESULT: " & Hex(ErrNum) & ")."
Set WMIError = nothing
End If
'wscript.quit 0
End If
End Sub
Sub PrintUsage()
wscript.echo "Usage:"
wscript.echo "cscript Terminate.vbs < /Operation:Z | _
A | DIS | SR | SNR > [/NoSave:true | false] [/DaysOld:n]"
wscript.echo
wscript.echo " Z terminates all ""Zombie"" _
instances (e.g. completed with discarded messages)"
wscript.echo " A terminates all suspended and zombie instances _
as well as all routing failure reports"
wscript.echo " SR terminates suspended resumable instances only"
wscript.echo " SNR terminates suspended non-resumable instances only"
wscript.echo " DIS terminates all dehydrated 'isolated adapter' instances"
wscript.echo " NoSave:true terminates instances without saving messages _
they reference. Default is false"
wscript.echo " DaysOld:n specifies number of days back to terminate. _
ie: ""/DaysOld:2"" terminates instances suspended more than 2 days ago."
wscript.echo
wscript.echo " Default action is to save instances to the C:\Temp folder on the local computer"
wscript.echo
wscript.echo " Ensure that the C:\Temp folder exists before _
running terminate if you want to save instances"
wscript.echo
wscript.echo " Example: cscript Terminate.vbs /Operation:z /NoSave:true /DaysOld:7"
wscript.echo
End Sub
- 创建了一个专用的跟踪主机和发送主机。 由于所有应用程序都使用
BizTalkServerApplication
,因此建议将一些发送端口移动到BizTalkSend
主机 - 为面临性能问题的应用程序创建和使用 64 位主机和应用程序池,用于某些接收位置
- 从生产环境中禁用了业务流程的开始和结束形状以及其他跟踪。 由于我们将事务记录在单独的表和文件中,因此跟踪对我们来说并不重要。
- DTA 清除和备份作业已启用以成功运行。 之前,它由于缺少磁盘空间来保存 DTA DB 的备份而失败。 现在我们清理了空间并再次运行它,并且它成功通过了。
- 对于系统中存在的众多挂起消息,我们清除了上周左右之前的消息。 找到了以下脚本,该脚本具有通过指定保留消息的天数来清除所有挂起消息的选项。 可以在计划中运行它,以清除早于 7 天左右的消息。
- 默认情况下,BizTalk 的最大并发传出连接数为 2。 因此,我们通过更改 BTSNtSvc.exe.config 文件来增加了一些 Web 服务调用的并发连接数(有选择地)。 其余服务器保持在现有的默认 2 个连接。
<system.net> <connectionManagement> <add address = "http://<external server>" maxconnection = "12" /> <add address = "*" maxconnection = "2" /> </connectionManagement> </system.net>
注意:不要将
maxconnection
设置为非常大的值,而应将其保持在 10-15 左右。 如果使用较大的值,可能会发生以下情况:如果 BizTalk Server 重新启动,则在启动后,BizTalk 将对目标服务器进行并发调用,以处理排队/未决的请求,这可能导致外部服务器关闭或将所有请求排队或导致超时。 因此,为了安全起见,请将其控制在 10-15 范围内。 - 更改管道以在某些使用 XML 接收/传输的位置使用
PassThruReceive
和PassThruTransmit
。 - 计划对安装的产品进行以下更新
- 服务器上未安装 BizTalk 累积更新 8
- 服务器上未安装 BizTalk 适配器包 2010 CU3
- DB 服务器上安装的 SQL Server 2008 R2 低于 SP2 - 要更新到 SP2
- SOAP 和 SQL 适配器将被基于 WCF 的适配器替换
诸如 7、8 和 9 之类的一些点尚未完成。 但是在完成前 6 个步骤之后,我们就面临了性能的提高和超时问题的急剧减少。