有关后台处理Web服务的设计问题

时间:2019-02-09 19:04:39

标签: c# wcf

问题的标题可能不够清楚,请允许我在此处解释背景:

我想设计一个生成PDF并将其提交给打印机的Web服务,这是工作流程:

  1. 用户向Web服务提交了一个请求,该请求可能会被取消,这样用户就不会因为等待作业完成而受苦。用户可能会收到HTTP200并继续他们的工作。

  2. 一旦Web服务接收到请求,Web服务就会生成PDF,并将其提交给指定的打印机,此过程可能会花费一些时间和CPU资源。由于我不希望耗尽该服务器上的所有资源,因此我可以在此处使用生产者使用者模式,可能会有一个队列来排队客户端作业,并逐个处理它们。

我的问题是:

  1. 我是C#的新手,排队和处理它们的正确模式是什么?我应该使用ConcurrentQueue和ThreadPool对其进行存档吗?

  2. 向用户通知作业成功/失败的正确方法是什么?不是使用回调服务,异步是一种理想的方法吗?我担心的是,队列中可能有很多工作,我不希望客户等待完成。

  3. Web服务放置在负载均衡器的后面,如何在其中维护“进程队列”?我已经尝试过使用Hangfire,看来还可以,但是我正在寻找替代方案?

  4. 我如何知道队列中的作业数量/线程当前如何运行?该Web服务将部署在IIS上,是否可以通过本机方式对其进行存档,还是应该执行Web服务调用来获取它们?

任何帮助将不胜感激,谢谢!

1 个答案:

答案 0 :(得分:1)

WCF支持即发即弃方法的想法。您只需将合同接口方法标记为一种方法,就不必等待退货了:

[OperationContract( IsOneWay = true )]
void PrintPDF( PrintRequest request );

当然,唯一的缺点是,您不会从服务器收到任何有关请求成功或有效的通知。您必须进行某种定期轮询才能查看发生了什么。我猜您可以将Guid放入PrintRequest中,以便稍后可以查询该工作。

如果您不熟悉wcf,则可以考虑使用signalR ...那里有一个包含服务器和简单wpf客户端here的综合示例应用程序。建立连接后,任何一方都可以发起交换。

如果您需要坚持使用wcf,则可以执行dualHttp。客户端与端点连接以回调到...,然后服务器可以在工作完成时发布通知。您可以从this sample上感受一下。

signalR和wcf dualHttp都非常简单。我想我的偏爱将基于人们从事这项工作的经验。 signalR的优势是可以与基于浏览器的客户端很好地协作……如果您担心的话。

关于队列本身...并与wcf模型保持一致,您要确保您的请求可序列化...因此,如果需要,您可以排空队列并稍后重新启动。在wcf中,这通常意味着为队列项目订立数据合同。顺便说一句,我从不喜欢将大量参数传递给服务,而我更愿意为方法参数和返回类型订立数据协定。

数据协定通常只是简单的类型,标记有用于控制序列化的属性。 wcf方法具有在导线上对类型进行序列化/反序列化的魔力,而无需您进行过多思考。客户端发送一个whizzy,服务器接收一个whizzy作为其参数。

有一些警告...特别是,反序列化不会调用您的构造函数(我相信它反而使用MemberwiseClone)...因此您不能依赖于构造函数来初始化属性。为此,您必须记住,例如,不需要的集合类型可能需要延迟初始化。例如:

[DataContract]
public class ClientState
{
  private static object sync = new object( );

  //--> and then somewhat later...

  [DataMember( Name = "UpdateProblems", IsRequired = false, EmitDefaultValue = false )]
  List<UpdateProblem> updateProblems;
  /// <summary>Problems encountered during previous Windows Update sessions</summary>
  public List<UpdateProblem> UpdateProblems
  {
    get
    {
      lock ( sync )
      {
        if ( updateProblems == null ) updateProblems = new List<UpdateProblem>( );
      }
      return updateProblems;
    }
  }

  //--> ...and so on...

}

我经常要做的是将后备变量标记为可序列化的成员,因此反序列化不会调用属性逻辑。我发现这是一个重要的“技巧”。

生产者/消费者容易编写...而且容易出错。环顾StackOverflow ...您会发现很多示例。最好的之一是here。您可以使用ConcurrentQueue来做到这一点并避免锁,或者像示例中那样使用简单的Queue进行处理。

但是实际上...使用某种服务总线体系结构而不是自己滚动队列会更好得多。

在负载均衡器后面意味着您可能希望他们都调用服务实例来管理单个队列。您可以自己滚动,也可以让每个实例管理自己的队列。这可能比您要在服务器实例上进行的处理更多……这就是您的要求。使用wcf dual http,您可能需要将负载均衡器配置为具有客户端亲和性...以便可以进行面向会话的双向通信。 signalR支持由Sql Server,Redis或Azure Service Bus支持的消息总线,因此您不必担心与特定服务器实例的亲缘关系。 here中讨论了它的性能含义。

我想最明显的建议是...找出那里的东西,并尝试避免重新发明轮子。如果您处于刻录/学习模式并且有足够的时间来使用它,请务必这样做。但是,如果您获得报酬,请查找并学习该领域中已经存在的工具。

由于双方都使用.Net,因此您可以考虑将所有合同(服务合同和数据合同)写入在客户端和服务上都使用的.DLL。这样做的好处是,保持同步很容易,并且您不必使用通过WSDL发现或服务引用向导生成的(相当弱的)生成的数据协定类型,并且可以使用来启动客户端实例ChannelFactory<IYourServiceContract>