代理服务要点

代理服务是通过IServiceBroker获取的服务,并以 RPC 兼容的接口公开,使服务及其客户端能够存在于不同的应用程序域、进程,甚至在 Live Share 的情况下跨计算机。 代理服务可以由 Visual Studio 主进程或其一些辅助进程提供,并且可以通过 Visual Studio 扩展在其中任一个进程中使用。

更多(非中转)Visual Studio 服务可通过 IServiceProvider 界面获取,如 “使用和提供服务”中所述。 此类服务通常仅在主 Visual Studio 进程中可用,但公开的功能集比中转服务更大。

在 Live Share 来宾上运行的 Visual Studio 扩展可以通过访问 Live Share 主机提供的这些服务的子集来提供附加功能。 授权检查适用于 Live Share 连接,以降低可能行为不当的 Live Share 来宾对 Live Share 主机安全性的风险。 选择通过 Live Share 公开其服务的中转服务作者应注意实施授权检查,如 如何提供中转服务中所述。

服务代理

Visual Studio 具有一个全局IServiceBroker,类似于可用于检索其他服务的GlobalProvider。 也可以通过 MEF 检索它。

可能会有其他更加上下文特定的服务代理由某些特定的 Visual Studio 功能提供,这些功能希望将全局服务代理与他们自己的一个提供附加服务(或可能抑制某些服务)的代理进行聚合。

IServiceBroker 是一个有意作为黑匣子的组件,它允许客户端获取可能位于本地、在其他进程中或在其他计算机上的服务。 服务代理可以是一个或多个其他代理的集合,并应用了相关策略。

根据 Visual Studio 进程所处的上下文,此全局服务代理是由动态变化的一组其他服务代理组成的聚合。 进程中的上下文更改可能会更改可能激活的中转服务集。 例如,加载解决方案时,与活动解决方案专门相关的服务可能会可用。 相同的服务也可以在“文件夹打开视图”中可用,尽管使用了不同的后端实现。 服务实现中的更改对于该服务的客户端是透明的,因为这两个实现必须满足相同的协定,但需要客户端在此上下文更改(通过这些更改通知服务 AvailabilityChanged)中重新查询服务以获取新实例。

Service Broker 通常用于获取服务的代理。 也就是说,客户端不直接接收对服务对象的引用,而是接收一个存根,该存根会将所有方法调用转发给服务,并将结果或异常转发回客户端。 它还可能会将服务引发的事件转发到客户端。 在某些情况下,服务可能支持或要求客户端提供一个“目标对象”,该服务可以调用方法以回调客户端。

中转服务容器

必须将服务引入到 IBrokeredServiceContainer 中,才能从全球 IServiceBroker 获取。 此服务容器不仅负责向服务代理公开服务工厂,还负责控制哪些客户端可以访问该服务,并在这些客户端的访问权限发生变化时通知他们。

经纪服务的组成

中转服务由以下元素组成:

  • 声明服务功能并充当服务与其客户端之间的协定的接口。
  • 该接口的实现。
  • ServiceMoniker 用于为服务分配名称和版本。
  • 一个结合了ServiceMoniker,在必要时处理 RPC 行为的ServiceRpcDescriptor
  • 用于提供服务工厂的代码
  • 服务注册

服务接口

这可能是标准 .NET 接口(通常用 C# 编写)。 为了允许中转服务客户端和服务存在于不同的进程并通过 RPC 通信,此接口必须遵循服务将使用的ServiceRpcDescriptor所规定的限制。 这些限制通常包括不允许的属性和索引器,大多数或所有方法返回 Task 或其他异步兼容的返回类型。

代理服务代号和描述符

激活服务需要知道其名称。 由于名字对象包含在服务的描述符中,因此客户端通常只需处理ServiceRpcDescriptor。 描述符将添加在中转服务与其客户端之间建立 RPC 连接所需的行为,或者在需要时对来自或发送到 Stream 的 RPC 调用进行序列化。

Visual Studio 建议将 ServiceJsonRpcDescriptor 派生类型用于中转服务,当客户端和服务需要 RPC 进行通信时,该类型利用 StreamJsonRpc 库。 StreamJsonRpc 对服务接口应用某些限制,如 此处所述

描述符很少需要直接使用。 相反,它通常从VisualStudioServices或提供服务的库中获取,然后被用作GetProxyAsync的参数。

ServiceMonikerServiceJsonRpcDescriptor类都是不可变的,因此可以安全地作为static readonly字段或属性共享。 任何其他 ServiceRpcDescriptor派生类型都应不可变。

A ServiceMoniker 是可序列化的。 A ServiceJsonRpcDescriptor 无法序列化。

服务受众

每个代理服务都用来自 ServiceAudience 的标志进行注册。 这些标志控制代理服务将被公开给哪些客户端以及通过哪些连接。

典型的选择是 ServiceAudience.Local,它将服务公开给 Visual Studio 会话中的任何本地进程。 使用此设置时,即使实时共享会话处于活动状态,服务也始终在本地激活。

当添加ServiceAudience.LiveShareGuest标志时,请求该中转服务的Live Share来宾将通过与Live Share主机的远程连接获得该中转服务的代理。

定义的 ServiceAudience 标志的任意组合都是合法的。 可以仅设置LiveShareGuest标志,而不设置Local标志,例如,向 Live Share 来宾(来自 Live Share 主机)公开中转服务,并且永远不会在本地可用(客户端和服务在同一进程中)。

RemoteExclusiveClientRemoteExclusiveServer标志已被弃用。

当客户端请求中转服务时,它不需要知道该服务的ServiceAudience是什么,也不需要知道服务将在哪里激活。 值得注意的是,服务记录此值对开发人员来说很有帮助,因为这使他们能够了解服务可能在何处激活,从而预测在各种上下文中该服务可能产生的数据类型,以及服务何时可能可用。

中转客户端的构成

当客户端请求中转服务时,如果服务不可用,则返回null;如果服务激活失败,则引发ServiceActivationFailedException;如果服务可用,则返回其代理。 代理无论是在与客户端相同的进程中激活中转服务还是在不同的进程中激活,都可以使用。 此代理有助于协调本地和远程服务案例中的使用模式,以便客户端无需了解服务所在的位置。