45、Web应用高级故障排除、维护及异步操作指南

Web应用高级故障排除、维护及异步操作指南

1. 高级故障排除与维护

在Web应用的管理和维护中,有许多关键任务需要处理,以确保系统的稳定运行和数据安全。

1.1 远程维护接口

建议创建基于Web的接口来执行特定的维护任务,如清除请求日志。这样可以通过HTTP远程运行这些任务,在无法现场操作时非常方便。

1.2 数据备份

数据备份是保障数据安全的重要措施。对于数据的定期(可能是每日)磁带备份,虽然有些备份系统声称能在Visual FoxPro表文件打开时进行可靠备份,但为了确保数据的完整性,建议在备份时关闭所有表。不过,如果数据存储在SQL Server或Oracle等后端数据库中,这个问题就不存在了。

为了实现备份时关闭表的操作,可以指定一个维护间隔,通过代码或INI文件设置来拒绝在这段时间内对数据表进行操作。以下是一个示例代码,可放置在主程序的 Process() 方法开头:

LOCAL lcStartMaint, lcEndMaint
lcStartMaint = "02:45"  && or read a value from INI
lcEndMaint = "03:15"
IF BETWEEN( TIME(), m.lcStartMaint, m.lcEndMaint)
  CLOSE DATA ALL
  LOCAL Response
  Response = CREATE( "wwResponseString")
  Response.StandardPage( "Maintenance Advisory", "Sorry, but we are " + ;
    "performing maintenance right now. Check back after " + ;
    m.lcEndMaint + " (eastern time). Thank you for your patience.")
    IF THIS.lCOMObject
      THIS.cOutput=Response.GetOutput()
    ELSE
      File2Var(THIS.oRequest.GetOutputFile(),Response.GetOutput())
    ENDIF
    RETURN  && avoid any further processing!
ENDIF

这个方法虽然可行,但可能并不总是有效,因为它依赖于在进入维护间隔后、备份设备启动前收到请求。另一种解决方案是在批处理文件中进行备份,在发出备份命令之前向应用程序发送HTTP请求,从而关闭表。但这是一个比较粗糙和不完善的方法。为了实现可靠的备份,要么确保备份期间应用程序不运行,要么将数据迁移到后端数据库。

如果使用Visual FoxPro数据且无法设置维护窗口(即应用程序必须24x7运行),可以使用Visual FoxPro的 COPY TO 命令制作数据的实时副本,然后对这些不需要打开的副本进行磁带备份。更多关于此方法的讨论可参考 相关链接

1.3 使用COM进行在线维护

如果使用COM而不是基于文件的消息传递,在执行维护任务时会有更多的灵活性。在COM模式下, admin.asp 页面有一个特殊链接,可选择“卸载除一个服务器外的所有服务器并暂停请求”。执行此操作后,一次只能处理一个请求,并且只接受管理员的请求(由 WC.INI 中的 AdminUser 设置决定)。这使得可以执行像 PACK REINDEX 这样需要独占使用的操作。完成后,释放“暂停”状态,其他用户就可以再次访问应用程序。在暂停期间,用户会看到一个(可自定义的)消息,提示正在进行维护。

要实现这个策略,可以创建Web Connection的 Process() 方法来执行维护任务,从而可以从Web页面调用这些方法。例如,一个打包表的方法可以如下:

FUNCTION PackCustomer
  CLOSE DATABASES
  IF OpenExclusive( "Customer")
    PACK
    USE
    THIS.StandardPage( "Customer table was packed.")
  ELSE
    THIS.ErrorMsg( "Could not get exclusive use of Customer table.")
  ENDIF
ENDFUNC  && PackCustomer

服务器操作员可以按照以下步骤执行维护任务:
1. 通过选择 admin.asp 中的“卸载除一个服务器外的所有服务器并暂停请求”链接(等效查询字符串为“_maintain~maintholdrequests”)将服务器置于维护模式。
2. 执行一个或多个维护功能,如前面所示的打包操作。理想情况下,可以创建一个作为管理员功能菜单的页面,并在 admin.asp 页面和维护菜单之间添加链接,以便轻松导航所有维护任务。
3. 完成所有任务后,通过选择 admin.asp 中的“从INI加载所有服务器”链接(等效查询字符串为“_maintain~load”)或点击Web Connection状态页面上的“切换”切换链接(等效查询字符串为“_maintain~HoldRequests”)释放暂停状态。

需要注意的是,有些维护任务可能会超过标准Web浏览器的超时时间,这些任务不应该从基于Web的维护页面同步执行。

1.4 查找表和元数据的在线维护

对于需要在线维护的查找表和元数据,通常需要开发自己的基于Web的数据输入表单和 Process() 方法。从Web Connection 3.65版本开始, wwShowCursor 类有一个 EditTable() 方法,可用于快速轻松地编辑表,这对于维护功能非常有用。可以参考 wwDemo.PRG 中的 TableEditor() 方法示例,也可以在Web Connection安装的演示应用程序中运行这个示例。

2. 异步和定时操作

随着Web应用的成熟和复杂度的增加,除了对Web请求的简单响应外,还会出现一些新的需求,主要分为异步操作和定时操作两类。

2.1 异步操作的必要性

当一些传入请求处理时间过长,无法及时返回结果时,就需要异步操作。例如,长时间运行的查询,可能是在大型数据库上进行文本搜索或多次表连接的查询。当处理时间超过浏览器或Web服务器的超时值时,就绝对需要异步操作。但实际上,由于用户在长时间请求运行时没有得到任何反馈,可能会采取一些不良行为,如重新提交请求或离开网站,因此任何可能需要超过几秒才能完成的请求都应考虑迁移到异步操作。

以下是长时间请求可能导致的问题:
| 问题类型 | 具体描述 |
| ---- | ---- |
| 浏览器超时 | 客户端浏览器超时,用户收到无法控制的通用消息,不知道服务器是否收到请求、网站是否关闭或自己是否出错。 |
| 服务器超时 | Web服务器或Web Connection DLL放弃并超时,向用户发送错误页面,指示没有生成输出。在基于文件的消息传递下,应用程序会完成处理,但响应无法送达;在COM消息传递下,应用程序可能在足够长的超时后终止。 |
| 重复请求 | 用户习惯了网站其他页面的快速响应,会认为自己操作有误而再次提交请求,导致第一个进程完成后被放弃,相同请求重复执行,响应时间更长,网站资源被加倍占用。 |
| 用户离开 | 用户认为网站无响应而放弃,可能会导航到竞争对手的网站。 |
| 不良印象 | 即使用户在超时时间内得到响应,但在等待期间没有任何反馈,可能会对网站质量产生不良印象。 |

2.2 异步请求的工作原理

异步请求的解决方案是将请求处理分为两部分。首先,快速返回一个消息,表明请求正在处理,同时通过某种消息传递机制安排一个单独的应用程序来执行实际处理。具体步骤如下:

graph LR
    A[Web Connection方法接收请求并分析需求] --> B[将请求记录到队列]
    B --> C{选择架构}
    C -->|启动后端应用| D[使用RUN命令或ShellExecute API启动后端应用]
    C -->|后台轮询| E[创建后台运行的后端应用,持续轮询队列]
    B --> F[返回请求正在处理的响应,包含HTML刷新指令]
    F --> G[浏览器根据刷新指令定期轮询应用程序]
    D --> H[后端应用处理请求,更新队列状态]
    E --> H
    H --> I{处理完成?}
    I -->|否| J[Web Connection应用返回状态更新页面]
    I -->|是| K[Web Connection应用返回最终结果]
    J --> G
  1. 典型的Web Connection方法接收传入请求并分析需求(表单变量、URL参数等)。
  2. Web Connection方法不处理完整请求,而是将请求记录到队列中,由后端应用程序处理。这个队列可以简单地是一个作为自由表创建的单个DBF文件。
  3. 根据选择的架构,Web Connection方法可以使用Visual FoxPro的 RUN 命令或 ShellExecute API 启动后端应用程序。如果这种需求不频繁,可以选择这种方法;或者创建一个在后台运行并持续轮询队列中新条目的后端应用程序,这样可以提高效率,但需要确保后端应用程序始终运行并在需要时可用,例如将其包含在Windows启动组中。
  4. Web Connection方法立即返回一个响应,表明请求正在处理,并包含一个HTML刷新指令(使用META标签),使浏览器每隔几秒轮询Web Connection应用程序,查看处理是否完成。
  5. 后端应用程序在处理过程中更新队列中请求的状态,并在处理完成后插入最终响应。
  6. 在此期间,由于每个响应中包含的META刷新标签,Web Connection应用程序会收到客户端浏览器的重复轮询请求。每次请求时,方法会检查队列中项目的状态,要么返回状态更新页面(带有进一步的刷新指令),要么在处理完成后返回最终结果。
  7. 可以添加额外的代码来处理超时、用户取消等情况。
2.3 wwAsyncWebRequest类

从3.65版本开始,Web Connection包含一个新的 wwAsyncWebRequest 类,用于处理异步请求。这个类是一个实用类,由Web Connection应用程序和后端应用程序调用,通过消息表进行进度通信。Web Connection应用程序将新请求写入表中,并在收到新的轮询请求时检查表中的状态消息;后端应用程序检查表中要处理的新请求,并在处理请求时更新表中的请求状态。 wwAsyncWebRequest 类将这种通信抽象为一个易于部署的类。

2.4 异步处理示例

以下是一个简单的异步处理示例,基于TODO应用程序。TODO应用程序的 Class_Tsk 类有一个 ListTasks 方法,用于列出所有未完成的任务。假设未来未完成任务数量过多,这个请求无法及时完成,通过在处理过程中插入延迟来模拟这种情况。

以下是 SlowListTasks 方法的代码:

**************************************
FUNCTION SlowListTasks
**************************************
SET PROCEDURE TO wwAsyncWebRequest ADDITIVE  && not set in WCONNECT.PRG
LOCAL loAsync, lcAction, lcJobId, lnRefreshPeriod, ;
  lnRetries, lnTry, lcMessage
loAsync = CREATEOBJECT( "wwAsyncWebRequest")
* See if this is a new request or polling for a current request:
lcAction = Request.QueryString( 'Action')
lcAction = IIF( EMPTY( m.lcAction), 'New', PROPER( m.lcAction))
IF m.lcAction == 'New'
  lcJobId = loAsync.SubmitEvent( , "Slow Task List")
  * Optionally, fire up the process here. (Note: You
  * can also run a continuous background process that
  * polls for new requests.)
  LOCAL lcRunCmd
  lcRunCmd = [RUN /n4 TODOAsyncHandler.EXE ] + m.lcJobId
  &lcRunCmd
ELSE  && not new
  lcJobId = Request.QueryString( 'JobId')
ENDIF
* Set refresk periond (in seconds):
lnRefreshPeriod = 1
* How many polls before we give up:
lnRetries = 50
* Which retry are we on:
lnTry = 0
DO CASE
CASE m.lcAction == 'Check'
  LOCAL lnStatus
  lnStatus = loAsync.CheckForCompletion( m.lcJobId)
  * 0 means still running, negative numbers are error conditions
  DO CASE
  CASE m.lnStatus = -2  && bad ID
    THIS.ErrorMsg( "Invalid Request", ;
      "Sorry, your request was either invalid or has been lost." + ;
      " Please try again." )
    RETURN
  CASE m.lnStatus = -1  && cancel
    THIS.ErrorMsg( "Request Cancelled", ;
      "Your request has been cancelled." )
    RETURN
  CASE m.lnStatus = 1  && completed
    LOCAL lcResult, loReturnXML
    lcResult = loAsync.oEvent.ReturnData
    #IF .F.  && return XML
    Response.ContentTypeHeader( "text/xml")
    Response.Write( m.lcResult )
    #ELSE  && return HTML page
    loReturnXML = CREATEOBJECT( "wwXML")
    loReturnXML.XMLToCursor( m.lcResult, "TaskList")
    Response.ExpandScript( Process.cHTMLPAGEPATH + ;
      "tasklist.wcs", 3, THIS.Header)
    USE IN TaskList
    #ENDIF
    RETURN
  ENDCASE
CASE m.lcAction == "Cancel"
  loAsync.CancelEvent( m.lcJobId)
  THIS.StandardPage( "Your request has been cancelled.")
  RETURN
ENDCASE
* If we get this far, we want to return a status update page:
lnTry = loAsync.oEvent.ChkCounter
IF m.lnTry > m.lnRetries
  THIS.StandardPage( "Request Timeout", ;
    "Sorry, but your request could not be completed in a " + ;
    "timely fashion." + ;
    " Please try again later.")
  RETURN
ENDIF
lcMessage = 'Please wait. Your request is being processed. <br>' + ;
  [<table cellpadding=0 cellspacing=0 ] + ;
  [style="border: medium solid orange;" width=] + ;
  TRANSFORM( 5 * m.lnRetries) + [>] + ;
  [<tr><td style="background-color: blue;"] + ;
  [ width=] + TRANSFORM( 5 * m.lnTry) + ;
  [>&nbsp;</td><td>&nbsp;</td></tr></table>] + ;
  [<BR>] + loAsync.oEvent.Status
THIS.StandardPage( "Asynchronous Task List", m.lcMessage, , ;
  m.lnRefreshPeriod, ;
  'SlowListTasks.tsk?action=Check&JobId=' + m.lcJobId )
ENDFUNC  && SlowListTasks

后端应用程序的代码如下:

* ToDoAsyncMain.PRG
#INCLUDE WCONNECT.H
PARAMETERS lcJobId
IF EMPTY( m.lcJobId)
  RETURN
ENDIF
SET EXCLUSIVE OFF
SET TALK OFF
SET SAFETY OFF
DO wconnect
SET PROCEDURE TO wwasyncwebrequest ADDITIVE
SET PROCEDURE TO ToDoConfig ADDITIVE
SET PROCEDURE TO queryengine ADDITIVE
ON ERROR DO AsyncError IN ToDoAsyncMain WITH ;
  ERROR(), MESSAGE(), MESSAGE(1), SYS(16), LINENO()
DO PATH WITH '.\Data'
* Need some dummy objects to "simulate" the Server
* and Process objects in the TODO environment:
PRIVATE goWCServer, Process
goWCServer = CREATEOBJECT( "DummyServer")   && see below
Process    = CREATEOBJECT( "DummyProcess")  && see below
LOCAL loAsync, lcTitle
loAsync = CREATEOBJECT( "wwAsyncWebRequest")
IF NOT loAsync.LoadEvent( m.lcJobId)
  ON ERROR
  RETURN
ENDIF
loAsync.oEvent.Started = DATETIME()
loAsync.SaveEvent()
lcTitle = TRIM( loAsync.oEvent.Title)
DO CASE
CASE lcTitle == "Slow Task List"
  = DelayEvent( loAsync, m.lcJobId, 20)
  LOCAL loQuery, loXML, lcResult
  loQuery = CREATEOBJECT("QueryEngine")
  loQuery.Execute( "TASKLISTALL")
  loXML = CREATEOBJECT( "wwXML")
  loXML.nCreateDataStructure = 2 && include DTD
  lcResult = loXML.CursorToXML()
  loAsync.CompleteEvent( m.lcJobId, m.lcResult)
ENDCASE
ON ERROR
RETURN
* ----------------------------------------------------- *
FUNCTION AsyncError( lnErr, lcMess, lcCode, lcMethod, lnLine)
ON ERROR
WAIT WINDOW TIMEOUT 2 m.lcMess + [ (error ] + TRANS( m.lnErr) + [)]
IF NOT FILE( FULLPATH( ".\AsyncError.DBF"))
  SELECT 0
  CREATE TABLE AsyncError FREE ;
    ( Error I, Message C(80), Code M, Method C(50), Line I, MoreInfo M)
  USE
ENDIF
LOCAL lcLogString, lnLevel, lnPtr
lnLevel = PROGRAM(-1)
lcLogString = ""
lcLogString = m.lcLogString + "Program Stack" + CRLF + CRLF
FOR lnPtr = lnLevel TO 1 STEP -1
  lcLogString = m.lcLogString + ;
    TRANS( m.lnPtr) + [. ] + PROGRAM( m.lnPtr) + CRLF
ENDFOR
INSERT INTO AsyncError ;
    ( Error, Message, Code, Method, Line, MoreInfo) ;
    VALUES ;
    ( lnErr, lcMess, lcCode, lcMethod, lnLine, lcLogString )
ENDFUNC  && AsyncError
* ----------------------------------------------------- *
FUNCTION DelayEvent( loAsync, lcId, lnSecs)
DECLARE Sleep IN WIN32API INTEGER
LOCAL ii
FOR ii = 1 TO m.lnSecs
  Sleep( 1000)
  IF NOT loAsync.LoadEvent( m.lcID)
     LOOP
  ENDIF
  IF loAsync.oEvent.Cancelled
     RETURN
  ENDIF
  loAsync.oEvent.Status = TRANS( m.lnSecs - m.ii) + " seconds remaining."
  loAsync.SaveEvent()
ENDFOR
ENDFUNC  && DelayEvent
* ----------------------------------------------------- *
DEFINE CLASS DummyServer AS Custom
  oConfig = NULL
  cIniFile = "ToDo.INI"
  FUNCTION INIT
       THIS.SetEnvironment()
  ENDFUNC
  * ---------------------------------------------------- *
  FUNCTION SetEnvironment
       THIS.cIniFile = ADDBS( GetAppStartPath()) + THIS.cIniFile
       LOCAL lcStr
       THIS.oConfig = CREATE( "ToDoServerConfig")
       THIS.oConfig.cFileName = THIS.cIniFile
       THIS.oConfig.Load()
  ENDFUNC
ENDDEFINE
* ----------------------------------------------------- *
DEFINE CLASS DummyProcess AS Custom
  cTempPath = ""
  cShowPlanResults = ""
  * ---------------------------------------------------- *
  FUNCTION INIT
       THIS.cTempPath = goWcServer.oConfig.oTodo.cTempPath
  ENDFUNC
ENDDEFINE

在测试新的异步操作之前,需要构建后端可执行文件。创建一个名为 ToDoAsyncHandler 的新项目,并将上述程序作为主程序添加到项目中。该项目可以与主TODO应用程序放在同一文件夹中,然后将项目构建为可执行文件 ToDoAsyncHandler.EXE

完成上述步骤后,就可以测试系统了。启动TODO应用程序,然后在浏览器中输入 http://localhost/todo/SlowListTasks.tsk 。如果一切正常,在请求运行的延迟期间,应该会看到如预期的响应。

此外, wwAsyncWebRequest 类还支持在请求完成前取消长时间运行的请求。可以通过在 SlowListTasks 方法中构建消息的代码中添加以下内容来轻松实现:

lcMessage = m.lcMessage + ;
  [<p align=center>] + ;
  Response.Href( 'SlowListTasks.tsk?action=Cancel&JobId=' + m.lcJobId, ;
    "Cancel", .T.)

这样,在处理运行时,状态结果页面将显示取消链接,用户可以点击该链接取消请求。

通过以上的方法和示例,可以有效地进行Web应用的高级故障排除、维护以及处理异步操作,提高系统的性能和用户体验。

3. 定时操作的需求与实现思路

定时操作与异步操作不同,它并非对传入请求的响应,而是代表着需要定期执行的重复性任务。这些任务对于 Web 应用的正常运行和数据管理至关重要,以下是一些常见的定时操作示例:
| 操作类型 | 具体描述 |
| ---- | ---- |
| 发送排队消息 | 将排队的消息发送到邮件服务器,确保消息及时传递。 |
| 数据库实时备份 | 制作数据库的实时备份副本,保障数据的安全性和可恢复性。 |
| 发送错误日志 | 将错误日志通过电子邮件发送给管理员,方便及时发现和解决问题。 |
| 删除临时文件 | 清理系统中的临时文件,释放磁盘空间。 |

由于 Web 应用通常只响应传入的 Web 请求,无法自行执行这些定时任务。如果创建一个包含请求这些操作的 URL 页面,还需要员工及时触发每个操作,这既成本高昂又容易出错。因此,需要一种更自动化的解决方案来实现定时操作。

4. 异步与定时操作的结合与优化

在实际的 Web 应用中,异步操作和定时操作往往需要结合使用,以满足复杂的业务需求。以下是一些结合和优化的思路:

4.1 任务调度与异步处理

可以使用定时任务来触发异步操作。例如,定时任务可以定期检查是否有需要处理的长时间请求,并将这些请求放入异步处理队列中。这样可以确保系统资源的合理利用,避免长时间请求对正常业务造成影响。

4.2 错误处理与重试机制

在异步和定时操作中,错误处理和重试机制非常重要。当异步操作或定时任务失败时,系统应该能够捕获错误并进行相应的处理。可以设置重试次数和重试间隔,确保任务最终能够成功执行。

4.3 性能监控与优化

对异步和定时操作进行性能监控是优化系统的关键。可以记录每个操作的执行时间、成功率等指标,分析系统的性能瓶颈,并采取相应的优化措施。例如,如果发现某个异步操作的执行时间过长,可以考虑优化算法或增加系统资源。

5. 总结

Web 应用的高级故障排除、维护以及异步和定时操作是确保系统稳定运行和提供良好用户体验的重要环节。通过创建基于 Web 的维护接口、合理安排数据备份、利用 COM 进行在线维护等方法,可以有效地进行系统的维护和管理。对于长时间请求,采用异步操作可以避免用户的不良行为,提高系统的响应速度和资源利用率。同时,定时操作可以满足系统的周期性需求,保障数据的安全性和业务的正常运行。

在实际应用中,需要根据具体的业务需求和系统架构,选择合适的方法和工具来实现这些操作。通过不断地优化和改进,可以提高 Web 应用的性能和可靠性,为用户提供更好的服务。

以下是一个简单的定时任务示例,使用 Python 的 schedule 库来实现每天凌晨 2 点执行数据库备份的定时任务:

import schedule
import time
import subprocess

def backup_database():
    try:
        # 执行数据库备份命令
        subprocess.run(["python", "backup_script.py"])
        print("Database backup completed successfully.")
    except Exception as e:
        print(f"Database backup failed: {e}")

# 每天凌晨 2 点执行备份任务
schedule.every().day.at("02:00").do(backup_database)

while True:
    schedule.run_pending()
    time.sleep(1)

这个示例展示了如何使用 schedule 库来实现定时任务。在实际应用中,可以根据需要修改备份命令和执行时间。

通过以上的方法和示例,希望能够帮助开发者更好地处理 Web 应用的高级故障排除、维护以及异步和定时操作,提升系统的整体性能和稳定性。

演示了为无线无人机电池充电设计的感应电力传输(IPT)系统 Dynamic Wireless Charging for (UAV) using Inductive Coupling 模拟了为无人机(UAV)量身定制的无线电力传输(WPT)系统。该模型演示了直流电到高频交流电的转换,通过磁共振在气隙中无线传输能量,以及整流回直流电用于电池充电。 系统拓扑包括: 输入级:使用IGBT/二极管开关连接到全桥逆变器的直流电压源(12V)。 开关控制:脉冲发生器以85 kHz(周期:1/85000秒)的开关频率运行,这是SAE J2954无线充电标准的标准频率。 耦合级:使用互感和线性变压器块来模拟具有特定耦合系数的发射(Tx)和接收(Rx)线圈。 补偿:包括串联RLC分支,用于模拟谐振补偿网络(将线圈调谐到谐振频率)。 输出级:桥式整流器(基于二极管),用于将高频交流电转换回直流电,以供负载使用。 仪器:使用示波器块进行全面的电压和电流测量,用于分析输入/输出波形和效率。 模拟详细信息: 求解器:离散Tustin/向后Euler(通过powergui)。 采样时间:50e-6秒。 4.主要特点 高频逆变:模拟85 kHz下IGBT的开关瞬态。 磁耦合:模拟无人机着陆垫和机载接收器之间的松耦合行为。 Power GUI集成:用于专用电力系统离散仿真的设置。 波形分析:预配置的范围,用于查看逆变器输出电压、初级/次级电流和整流直流电压。 5.安装与使用 确保您已安装MATLAB和Simulink。 所需工具箱:必须安装Simscape Electrical(以前称为SimPowerSystems)工具箱才能运行sps_lib块。 打开文件并运行模拟。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值