在 WebSphere Process Server V7.0 中使用存储与转发特性(一)

Phani Madgula, 软件开发人员, IBM

2010 年 8 月 09 日

本教程描述了 WebSphere® Process Server V7 中的新存储与转发特性,并使用几个应用程序场景来向您展示如何用它加快应用程序请求的处理。

简介

在 WebSphere Process Server(以下简称为 Process Server)以前的版本中处理异步请求时,如果出现运行时错误,将生成失败事件。在请求出错或服务不可用而抛出运行时错误时,恢复系统会持续生成失败事件。当运行时错误被处理后,管理员必须使用管理控制台或失败事件管理器(FEM)API 脚本重新提交失败事件。这是一个生成和重新提交失败事件的资源密集型操作。在 Process Server V7.0 中,新引入的存储与转发 特性对这一问题有较好的处理。

当一个组件调用一个服务、而该服务又不可用时,将抛出运行时错误。这些运行时错误只是对异步调用生成失败事件,而对于后续的服务调用不会生成失败事件。用户可以在适当的导入、导出或组件上配置存储与转发特性,使请求能被存储在相应的队列中。这些已存储的请求将不再被重新提交给错误的或不可用的服务。这避免了生成大量失败事件。当目标服务可用时,可以使用业务空间中的 Store and Forward 小部件 对这些请求进行中转。

关于本教程

本教程展示运行在 Process Server V7.0 上的应用程序的新特性的配置。呈现了各种应用程序示例场景,说明这些特性在运行时是如何工作的。同时介绍了与存储与转发特性相对应的业务空间小部件。

本文分为以下几部分:

  • 存储与转发特性简介
  • 各业务应用程序场景中的存储与转发特性
  • 存储与转发配置中的异常规范

先决条件

  • 对 J2EE 概念的深入理解。
  • 熟练使用 WebSphere Integration Developer(以下简称 Integration Developer)开发 WebSphere Process Server 应用程序以及在服务器环境中实施管理活动。
  • 具备 WebSphere MQ 及其工具相关知识。


系统需求

以下环境是运行此示例所必需的。

  • 一个 Windows® XP® Service Pack 2 桌面,至少 2 GB RAM
  • 带测试环境的 IBM® WebSphere Integration Developer V7.0.0.0 Fix Pack 1。您需要为测试环境应用临时补丁 IZ72101
  • IBM WebSphere Process Server V7.0.0.0 Fix Pack 1
  • WebSphere MQ V6.0

持续时间

3 小时

存储与转发特性简介

这一部分将介绍存储与转发特性,并解释如何在应用程序上配置该特性。这里也提供了配置限定符的指南。

存储与转发特性工作方式

您可以通过在异步调用的组件上配置存储与转发限定符 来在应用程序中启动存储与转发特性。该组件可以是一个导入、一个 SCA 导出或一个 SCA 组件。您可以将它配置在一个组件,一个指定界面或一个指定操作等所有界面上。作为配置的一部分,运行时异常也是指定的。您可以为每个存储与转发配置不同的运行时异常,这些存储与转发配置都是在组件的各个界面或操作上指定的,在业务空间小部件中形成服务控制点

当请求流中出现运行时异常时,存储与转发的理念是异常将会传播直至第一个异步点时停止。如果存储与转发被配置在异步点附近的组件上,后续请求将被存储在异步点上,而不是持续生成失败事件。运行时异常解决后,可以使用 Store and Forward 小部件重放已存储事件。异步点映射到服务控制点。

当将一个配置有存储与转发特性的应用程序部署到 Process Server、并启动运行时验证时,运行时验证器确认限定符已得到正确配置。如果有错误出现,应用程序的安装将会停止。如果有警告出现,会在日志文件显示,应用程序安装继续进行。限定符验证规则在 存储与转发运行时验证器 中有详细说明。

当配置存储与转发特性的组件异步调用一个服务、且服务抛出运行时错误时,将为请求生成一个失败事件。但是,这个失败事件也将为该组件启动存储。如果失败事件能在控制台中检索到,事件限定符这列将有一个 “initiated store” 值。在业务空间中 Store and Forward 小部件将指出存储在组件服务控制端点启动。服务控制点的状态将被设置为 “storing”。组件的后续请求将被存储在与组件对应的队列中。在目标服务可用时或运行时错误解决后,可在 Store and Forward 小部件中将服务控制点的状态设置为 “forwarding”。状态设置为 “forwarding” 时,已存储事件将被转发到目标服务进行处理。



 

为应用程序配置存储与转发特性

Integration Developer 用于配置存储与转发限定符。在配置存储与转发限定符时注意以下几点:

  • 限定符可以配置在以下组件上:
    • SCA Export(不可以配置在其他导出上)
    • Imports
    • SCA Component
  • 此外,在上面提到的每个组件上,可将限定符独立配置在所有界面以及界面的操作上。
  • 每个配置都提供有一个名称,且形成一个服务控制点。所有服务控制点将在业务空间的存储与转发小部件中显示。参见图 1 中的 “Configuration name” 字段。
  • 为每个存储与转发配置都提供异常规范。它会指出哪个异常规范触发了存储。如果组件接收的异常不同于指定的那个,存储将不能被触发。默认情况下,异常是 ServiceUnavailableException。更多信息,参见 Service Runtime Exception handling。见图 1 插图。
  • 异常规范包括已配置的异常如何解决由该组件接收的包装异常。通常,在异常传播中,根异常被包装在更高级的异常中,组件接收这个包装异常,其中包含一个系列异常。在图 1 中 “Exception chain” 复选框指定了其值。
  • 异常规范包括一个匹配接收异常的消息。为了触发存储,接收异常中的消息同指定的消息相匹配。在配置客户运行时异常时这是很有用的。参见图 1 中的 “Message to match” 字段。
  • 如果收到的异常是指定异常的一个子类,异常规范也可以指定触发存储。这在配置客户运行时异常时是很有用的。参见图 1 “Match types that inherit from this type” 字段。
  • 图 1 提供了各种在配置存储与转发限定符时可用的选项(如上所述)。在组装图中单击 EmployeeServiceComponent 并导航到底部的 Properties > Details 选项卡。

    图 1. 配置存储与转发特性
    配置存储与转发特性

注意,您不能使用存储与转发特性进行同步调用。关于存储与转发限定符的更多信息,参见信息中心的 存储与转发限定符 主题。

存储与转发特性应用程序场景

这一部分介绍可以使用存储与转发特性的各种业务应用程序场景。每个场景中的特性行为将通过一个示例应用程序予以说明,您可以将这些示例部署到 Process Server 环境中。所有应用程序以 Project Interchange (PI) 形式提供,使它们可以导入 Integration Developer 并在测试环境中运行。

场景 1: 异步导入(JMS、MQ、MQJMS、GenericJMS)上的存储与转发

该部分讨论存储与转发特性如何使用消息绑定导入。业务场景和示例应用程序如下所述。

XYZ 计算机制造公司有一个组装单元和一个库存部门。库存部门提供关于 CPU 可用性信息。组装单元请求库存部门提供某个特定 CPU 的可用性以及数量。库存部门响应请求的 CPU 数量是否充足。如果数量不够,它会就何时达到所请求数量作出响应。库存部门通过 WebSphere MQ 传输接受请求。客户必须发送请求给 WebSphere MQ 上的一个指定队列,随后检索响应。因此请求流是一个单向操作。组装图如图 2 所示。


图 2. 库存服务组装图
库存服务组装图

从上面组装图中可以看出,CPUInventory 组件被作为 CPUInventoryExport 导出,并附有 MQJMS 导出绑定。AssemblyUnit 组件通过 CPUInventoryImport 和 MQJMS 导入绑定调用 CPUInventory 组件。存储与转发限定符配置在 CPUInventoryImport 上。图 3 显示了配置在导入上的存储与转发限定符。


图 3. CPUInventoryImport 上的存储与转发限定符
CPUInventoryImport 上的存储与转发限定符

有时候会有不受接纳的停机情况,这使得 WebSphere MQ 服务不可用。这种情况下,如果 AssemblyUnit 通过 CPUInventoryImport 调用 CPUInventory 组件,导入将未能与 WebSphere MQ 连接,从而生成失败事件。由于存储与转发配置在导入上,第一个失败事件将启动存储。来自导入的后续请求将被存储在导入队列中。这就是说,无论 WebSphere MQ 传输是否可用,都将生成请求。一旦 WebSphere MQ 服务恢复联机,存储的请求通过业务空间中的 Store and Forward 小部件被转发。

运行示例:

  1. 下载 Example1.zip(在 samples.zip 文件中)。PI 有两个模块,AssemblyService 和 InventoryService,以及一个库。
  2. 将示例导入到 Integration Developer。
  3. 创建一个名为 Test_QM 的队列管理器。创建一个名为 CHANNEL1 的服务器连接通道,并在 WebSphere MQ 中配置监听端口为1414。使用客户端绑定将导入连接到队列管理器上。在 Test_QM 中创建一个名为 inventory_export_queue 的队列。您可以使用 MQ Explorer 来完成这些活动。观察 Integration Developer 中的导入和导出属性,注意 CPUInventoryImport 将请求放置在 CPUInventoryExport 监听的 inventory_export_queue,这些值配置在 Import 和 Export 上。在 Import 和 Export 的 Security attributes 部分为 J2C authentication data entry 设置适当的值。关于 MQJMS 绑定的更多信息,参见 WebSphere MQ JMS 主题。
  4. 在测试服务器上部署示例。
  5. 使用 Integration Developer 测试客户端测试一个请求流。右键单击 AssemblyUnit 组件,选择 Test component 选项。输入以下值:
    CPU_item_no:1
    Quantity:5

  6. 检查 Systemout.log 文件查看以下输出:
    Status=Available
    Available_date=Today
    CPU_details=Intel PENTIUM

  7. 使用 MQ Explorer 停止队列管理器 Test_QM。发送如下请求:
    CPU_item_no:2
    Quantity:12

  8. 生成失败事件,如图 4 所示。使用管理控制台查看失败事件。注意 Event 限定符的值是 Initiated store

    图 4. 队列管理器不可用时,生成失败事件
    队列管理器不可用时,生成失败事

  9. 发送另外两个使用以下输入值的请求:
    CPU_item_no:3
    Quantity:16 and
    CPU_item_no:3
    Quantity:29

  10. 查看并确保无失败事件生成。请求将被存储到导入队列,如图 5 所示。使用管理控制台中的 Service Integration Bus Explorer 来查看 SCA-SYSTEM 总线上的队列点。

    图 5. 存储在 Import 队列的请求
    存储在 Import 队列的请求

  11. 启动业务空间并创建一个名为 StoreAndForward 的空间,向空间添加 Store and Forward 小部件并保存。如图 6 所示小部件状态显示为 “Storing”。注意小部件中的服务控制点,这里仅有一个服务控制点,名字同存储与转发配置的 Configuration name 一样(参见 图 3)。在服务器视图中右键单击 Server 启动业务空间,并在 Integration Developer 中选择 Launch > Business Space 选项。

    图 6. 业务空间中的 Store and Forward 小部件
    业务空间中的 Store and Forward 小部件

  12. 启动队列管理器 Test_QM。在 Store and Forward 小部件上选择存储复选框,并单击 Forward 按钮。这将所有已存消息转发到队列管理器。CPUInventory 服务将处理请求。检查 Systemout.log 文件查看输出消息,最初生成的失败事件必须通过管理控制台手动提交
  13. 如图 7 所示,Store and Forward 小部件显示存储状态为 “Forwarding”。

    图 7. Store and Forward 小部件显示存储状态为 “Forwarding”
    Store and Forward 小部件显示存储状态为 “Forwarding”


 

场景 2: 同步导入(WebServices)上的存储与转发特性

在这个场景中,存储与转发限定符配置在一个带有 Web 服务绑定的导入上。然而,Web 服务导入是异步调用的。这在 Web 服务抛出运行时错误时(它不可用时)将会触发消息存储。消息存储将是为导入创建的队列。业务场景和示例应用程序如下所述。

ABC 计算机制造公司有一个装配部门,装配计算机显示器。装配部门联系供应商购买计算机显示器,供应商提供一个客户能够提交订单的 Web 服务。订单由显示器项目号和数量组成。Web 服务对是否请求的订单能够立即提供,以及何时库存可用做出响应,具体装配细节如图 8 所示。


图 8. Vendor Web 服务装配图
Vendor Web 服务装配图

在图 8 中,ComputerMonitorVendor 组件使用 WebService 绑定被导出。AssemblyDivision 组件通过 ComputerMonitorVendorImport 发出订单,调用 Web 服务。订单请求是一个带有回调函数的异步请求响应。存储与转发限定符配置在 ComputerMonitorVendorImport 上,如图 9 所示。


图 9. 配置在 Web 服务导入上的存储与转发特性
配置在 Web 服务导入上的存储与转发特性

在供应商环境中可能存在一些网络或其他方面的问题,使 Web 服务不可用。由于存储与转发限定符是配置在 ComputerMonitorVendorImport 上的,而且是从 AssemblyDivision 组件异步调用,订单存储在导入队列中。只有第一个订单(失败的)创建失败事件,随后的订单都存储在导入队列中,这样一来,装配部门就可以继续生成订单请求,不管供应商 Web 服务是否可用。当 Web 服务联机后,存储的订单将使用业务空间中 Store and Forward 小部件被转发。这些请求由 ComputerMonitorVendor 部件处理,响应将被推向 AssemblyDivision 组件。

运行示例:

  1. 下载 Example2.zip(在 samples.zip 文件中)。PI 有两个模块,AssemblyService 和 ComputerMonitorVendorService,以及一个库。
  2. 将示例导入到 Integration Developer 中。将 Web 服务端点的端口号改为合适的值。
  3. 部署示例并使用 Integration Devloper 测试客户端测试 AssemblyDivision 组件。
  4. 在 Integration Developer 中停止 ComputerMonitorVendorService 模块并发送一个请求。将生成一个事件限定符值为 “Initiated store” 的失败事件。
  5. 发送另外两个订单请求。这两个请求将不生成失败事件。请求被存储在 Web 服务导入队列中,如图 10 所示。

    图 10. 存储在 Web 服务导入队列中的订单请求
    存储在 Web 服务导入队列中的订单请求

  6. 在业务空间中打开 Store and Forward 小部件,注意服务控制点(ComputerMonitorVendorService_Unavailable)的状态是否为 “Storing” 。参考 场景 1 的 11 步,获取关于如何打开业务空间以及查看 Store and Forward 小部件的更多信息。
  7. 启动 ComputerMonitorVendorService 模块并在 Store and Forward 小部件中将状态设置回 “Forward”。
  8. 重新提交最初生成的失败事件。
  9. 在 SystemOut.log 文件中观察输出消息。


 

场景 3: SCA 导出上的存储与转发

到目前为止,我们已经展示了消息绑定导入和 Web 消息绑定上的存储与转发特性。类似地,您可以在 SCA 导出上(不能在其他的导出上)配置存储与转发限定符。在 SCA 导出上配置之后,如果导出同步调用一个 SCA 组件,且组件抛出一个运行时异常,第一个失败的请求将产生一个失败事件,后续请求将存储在 SCA 导出队列中。图 11 显示了示例应用程序场景以及一系列启动存储的调用。注意,默认情况下,SCA 导入和导出的首选交互样式是同步的。但是为了启动存储,必须将 SCA 导入或导出的首选交互样式设置为 “异步”。您可以通过以下类似于前面场景的步骤来轻松地模拟这个场景。


图 11. SCA 导出上的存储与转发限定符
SCA 导出上的存储与转发限定符

图 11 中通过第 6 步将请求存储在导出队列中。


 

场景 4: 带有异步导出(JMS、MQ、MQJMS、GenericJMS)的存储与转发

消息导出异步调用与其相对应的组件。因此,当存储与转发限定符配置在组件上、且组件出现了运行时错误时,存储被触发且消息存储在组件队列中。图 12 展示了一个示例应用程序场景以及一系列启动存储的步骤。也可以通过上述场景提供的类似步骤轻松地模拟这个场景。


图 12. SCA 组件上的存储与转发限定符
SCA 组件上的存储与转发限定符

在图 12 中,通过第 4 步将消息存储在组件队列中


 

场景 5: 带有多个界面和引用的存储与转发特性

在这个场景中,存储与转发限定符配置在一个 SCA 组件上,同步调用两个服务。调用的服务是 Web 服务。如果其中一个中断,组件接收到一个运行时错误并生成错误事件。后续的请求将被存储在组件队列中。注意,即使只有一个服务中断,另一个仍然可用,组件的所有请求也要被存储。业务场景和示例如下所述。

一个计算机制造公司需要计算机显示器进行组装。它联系一个提供计算机显示器的供应商。供应商有两个服务执行订单处理。第一个服务是 ComputerMonitorVendorServicemputer,它接受订单并返回所需产品以及数量是否有库存的信息。如果产品和数量都有库存,供应商要求通过信用卡付账。支付服务由 PaymentGatewayService 提供。

两个服务都是通过 Web 服务导入。计算机制造公司首先必须使用 ComputerMonitorVendorService 核实所需产品及数量,然后联系 PaymentGatewayService 进行支付。计算机制造公司部署了 ComputerMonitorBuyerService,它依次异步调用 ComputerMonitorVendorService 和 PaymentGatewayService 来发送请求订单以及付款(如果产品和数量足够),图 13 是组装图。


图 13. ComputerMonitorBuyerService 组装图
ComputerMonitorBuyerService 的组装图

存储与转发限定符配置在 ComputerMonitorBuyer 组件上,如图 14 所示。


图 14. 组件上的存储与转发配置
组件上的存储与转发配置

如果 ComputerMonitorVendorService 或者 PaymentGatewayServicethen 其中一个是不可用的,那么所有 ComputerMonitorBuyer 请求都被存储。当不可用服务可用时,存储的请求在两个服务上依次重放。例如,如果 PaymentGatewayService 不可用 ,ComputerMonitorBuyer 将调用 ComputerMonitorVendorService 来查明是否订单的库存不足。

然而,如果 PaymentGatewayService 调用失败,将生成失败事件并启动存储。后续的订单请求将被存储。当 PaymentGatewayService 可用时,存储的请求将被重放。示例包含 Test.jsp ,您可以使用它来向 ComputerMonitorBuyer 组件发送订单请求。Test.jsp 通过订单请求异步调用 ComputerMonitorBuyer。该请求是单向操作。

带有长期运行业务流程的存储与转发

在这个例子中,如果 ComputerMonitorBuyer 是一个长期运行 BPEL 流程,那么存储与转发将不能 工作,这是因为长期运行业务流程不支持存储与转发。

运行示例:

  1. 下载 Example3.zip(在 samples.zip 文件中),并将其导入 Integration Developer。
  2. 将模块部署到测试服务器。适当修改 Web 服务终端的端口号。
  3. 访问 Test.jsp,它在 WebClient 项目中。右键单击并选择 Run as > Run on server。图 15 提供了输入值。

    图 15. 通过 Test.jsp 输入提供的值
    通过 Test.jsp 输入提供的值

  4. 在 SystemOut.logs 中,观察以下输出:
    Inventory status=Available
    Available date=Today
    Computer monitor details=IBM Monitor
    Payment status=PAYMENT DONE
    Payment details=CREDIT CARD ACCEPTED

  5. 停止 PaymentGatewayService 模型。
  6. 重发图 15 所示的请求。
  7. 生成失败事件,启动存储。
  8. 发送图 16 所示的另一个请求。

    图 16. 通过 Test.jsp 输入提供的值
    通过 Test.jsp 输入提供的值

  9. 发送图 17 所示的请求。

    图 17. 通过 Test.jsp 输入提供的值
    通过 Test.jsp 输入提供的值

  10. 观察并确保无失败事件生成,但请求存储在组件队列中,如图 18 所示。

    图 18. 存储在组件队列中的请求
    存储在组件队列中的请求

  11. 找到 Store and Forward 小部件,会发现 ComputerMonitorVendorService_Unavailable 服务控制点的状态为 “Storing”。
  12. 启动 PaymentGatewayService 模块并在 Store and Forward 小部件中将其状态设置回 “Forwarding”。
  13. 重新提交最初创建的失败事件。

这一部分将讨论存储与转发配置中的异常规范,参见 为应用程序配置存储与转发特性 了解更多异常规范信息。

EmployeeServiceComponent 调用 EmployeeDetailsServiceComponent,目的是通过员工号(Empno)来检索某个员工的详细信息。服务调用是一个通过 SCA 导入和 SCA 导出的同步调用,您可以在 SystemOut.log 文件中查看输出。存储与转发限定符配置在 EmployeeServiceComponent 上。EmployeeService 模块同自定义 RuntimeException1 类( java.lang.RuntimeException 的一个子类)一起打包。

如果 Empno 是『1,2,3,4,5,6』之外的数字,EmployeeDetailsServiceComponent 将抛出一个 “RuntimeException1”。Test.jsp 异步调用 EmployeeServiceComponent。我们将阐述存储与转发特性如何使用异常规范中的各选项。将 Example4.zip(在 samples.zip文件中)导入到 Integration Developer 并遵循以下说明。

存储与转发限定符上的 ServiceRuntimeException

  1. 下载的应用程序将在 EmployeeServiceComponent 上配置存储与转发限定符,如图 19 所示。

    图 19. 为存储与转发指定的 ServiceRuntimeException
    为存储与转发指定的 ServiceRuntimeException

  2. 在测试服务器上部署应用程序,并使用 Test.jsp 调用请求流,Test.jsp 包装在 EmployeeServiceWebClient 项目中。右键点击 JSP file 并选择 Run as > Run on server。验证请求流成功运行。
  3. 停止 EmployeeDetailsService 模块并触发一个请求。一个失败事件生成,后续事件被存储。
  4. 启动 EmployeeDetailsService 并使用 Store and Forward 小部件转发请求,重新提交失败事件。
  5. EmployeeDetailsService 模块启动之后,通过 Empno 为 10 的 Test.jsp 触发一个请求。EmployeeDetailsService 将抛出一个 “RuntimeException1”。RuntimeException1 包装在 ServiceRuntimeException 中,配置在存储与转发限定符上。因此一个失败事件生成,后续请求被存储。


 

存储与转发限定符上的 RuntimeException1

  1. 使用 RuntimeException1 修改存储与转发限定符,如图 20 所示。

    图 20. 为存储与转发指定的 RuntimeException1
    为存储与转发指定的 RuntimeException1

  2. 使用上述修改重新部署应用程序并使用 Test.jsp 成功测试请求流。停止 EmployeeDetailsService 模块并触发一个请求流。失败事件生成,但它不会触发存储。这是因为 RuntimeException1 是为存储与转发限定符配置的。然而,不可用的 EmployeeDetailsService 抛出一个 ServiceRuntimeException,这并不是为存储与转发配置的。
  3. 启动 EmployeeDetailsService 模块并重新提交失败事件。
  4. 使用值为 11 的 Empno 触发一个请求。这将使 EmployeeDetailsServiceComponent 抛出一个 RuntimeException1,生成一个失败事件,之后存储被触发。


 

存储与转发限定符上的 RuntimeException1 和异常消息

  1. 修改存储与转发限定符,如图 21 所示。

    图 21. 存储与转发上的异常和异常消息
    存储与转发上的异常和异常消息

  2. 注意 “Message to match” 属性。已经为它提供了一个值 “This is RuntimeException1”,这个值事实上是 EmployeeDetailsServiceComponent 中抛出的 RuntimeException1 中包含的消息文本。
  3. 部署应用程序并使用 Test.jsp 触发一个请求。测试请求是否成功运行。使用值为 11 的 Empno 触发一个请求,这将生成一个失败事件,并且存储将被触发,这是因为异常和异常消息匹配。
  4. 将 “Message to match” 属性修改为 RuntimeException1 Message。重新部署应用程序并使用值为 11 的 Empno 触发一个请求。失败事件生成,但是不会触发存储,这是因为异常消息与在存储与转发限定符上指定的异常不匹配。


 

场景 4: 存储与转发限定符上的异常链配置

  1. 修改 EmployeeServiceComponent 上的存储与转发限定符,如图 22 所示。

    图 22. 存储与转发上的异常链配置
    存储与转发上的异常链配置

  2. 注意异常链值。它被设置为 “Match only the top level exception”。异常依然是 RuntimeException1。
  3. 使用值为 11 的 Empno 触发一个请求。这将使 EmployeeServiceComponent 抛出一个 RuntimeException1。但是 RuntimeException1 包装在顶级异常 ServiceRuntimeException 中,如图 23 所示。由于 存储与转发配置被设置为仅匹配顶级异常,失败事件生成,但是存储没有被触发。类似地,您可以测试其他异常链的值,您也可以使用一个示例应用程序测试继承自该选项类型的匹配类型。

    图 23.失败事件中的异常栈跟踪
    失败事件中的异常栈跟踪

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14789789/viewspace-671486/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/14789789/viewspace-671486/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值