并发问题 - Saxon 9.6 on .NET、XSLT 和自定义 XmlResolver 和 CollectionUriResolvers
Concurrency issue - Saxon 9.6 on .NET, XSLT and custom XmlResolver and CollectionUriResolvers
我想验证一下我对 Saxon 的 XSLT 对象和并发性的理解。
基本上,我需要自定义解析器来 return 请求特定数据进行转换,目前,我正在为每个请求的每个转换创建新的解析器实例。我已经收到了来自一个请求的数据在另一个请求中使用的早期报告,这是一个严重的问题。
在 .NET 4.6 (C#) 上使用 Saxon 9.6.0.6 HE,Windows 7/Server 2012。
我的代码可以为许多并发请求执行许多不同的共享转换。 Saxon 的已编译 XSLT 是性能所必需的。目前,代码是多线程的(在适当的地方使用 TPL 和异步),并且不使用锁定(并且希望尽可能避免这种情况)。
我偶尔会收到关于转换输出中跨请求数据不正确 'leaked' 的报告(即,可能是并发问题)。我不确定这是否与自定义 XmlResolver 或自定义 CollectionUriResolver 的行为有关。我在等待更多信息。我还不能重现这个问题(仍在处理这个问题,如果可以的话,我会 post 更新)。
我们的转换同时使用 fn:doc 和 fn:collection。
代码在应用程序启动时预编译所有可能的转换。这些可执行文件是共享的。
对于事务中的给定转换,我的代码通过编译的可执行文件的 .Load() 调用创建了一个 XsltTransformer 对象。这似乎创建了一个查看 9.6 HE 代码的新对象(这是我所期望的)。
接下来,我的代码创建自定义 XmlResolver 和 CollectionUriResolver 的新实例(尚未移至 CollectionFinder,但认为这可能以相同的方式运行)并且这些实例填充了适当的特定请求 docs/values/etc 以输入转换。
这两个解析器只有一次 XSLT 执行的生命周期 - 它们不会被重复使用。
我们以我所知道的唯一方式将解析器与 XsltTransform 对象相关联:
Saxon.Api.XsltTransformer transform = executable.Load();
transform.InitialContextNode = sourceData;
transform.Implementation.getConfiguration().
setCollectionURIResolver(collectionResolver);
transform.InputXmlResolver = inputResolver;
在 Saxon 代码中,看起来 输入解析器是基于实例的,因此不共享(最终似乎存在于控制器 class 中,如下所示,当 XsltTransformer 通过 Load() 创建时,它本身就是一个新实例。
public XmlResolver InputXmlResolver
{
set
{
controller.setURIResolver(new DotNetURIResolver(value));
}
}
但是,我担心配置数据可能被共享,并且在配置对象上设置集合解析器(CollectionFinder 似乎是相同的)我们可能会遇到并发问题。
实现我所追求的结果的正确方法是什么 - 让我们的自定义解析器响应特定于请求的行为?我可以在每个转换中使用一对带有请求特定数据的实例,还是必须跨请求共享解析器(可能将请求 ID 注入转换以形成传递给解析器的 URI 的一部分)?
稍作更新
看来您可以直接在控制器 ('Implementation') 上或在配置上设置 CollectionURIResolver,它们在内存中是截然不同的对象:
transform.Implementation.setCollectionURIResolver(collectionResolverOne);
transform.Implementation.getConfiguration().
setCollectionURIResolver(collectionResolverTwo);
但是,在运行时,调用的是配置的解析器(在上述情况下为 collectionResolverTwo)。我不确定控制器副本的用途。
此外,配置数据似乎确实是共享的,因为如果我从同一个可执行文件创建第二个转换器并在配置级别设置它的集合解析器,这会更新第一个转换器使用的解析器。
所以 - 我想我已经找到了我的问题 - 我现在需要知道在我需要集合解析器为每个请求(例如,一个请求可能在特定集合中有五个条目,另一个可能有两个)。
我认为在解释你的问题时你基本上已经自己解决了。 Saxon 中的 Configuration 对象(它在 API 级别支持 Processor)是共享的,并且在任何初始化之后强烈建议不要使用 setURIResolver() 等方法更改其状态,因为此类更改会影响工作-以未定义的方式取得进展。
Saxon 有 API 个一一对应的对象和内部对象,内部对象并不是 100% 封装的,因为一些用户需要访问更私密的功能。在 Java 世界中,还有概念上相似的 JAXP 类,但仅限于 XSLT 1.0 功能。对应关系是:
关于整个撒克逊环境的共享信息:
API:处理器内部:配置JAXP:TransformerFactory
包含用于编译样式表的选项的可重用 XSLT 编译器:
API: XsltCompiler Internal: CompilerInfo JAXP: 没有等价物
编译好的样式表,可以重复执行(多线程并发)
API:XsltExecutable 内部:Executable/PreparedStylesheet JAXP : 模板
单个转换,使用一个样式表转换一个源文档:
API:XsltTransformer 内部:控制器JAXP:Transformer
Saxon 中的某些配置选项仅在配置级别可用,例如,可用的排序规则 URI 集是在此级别定义的,并且不能从一个转换到另一个转换。
但是,URIResolvers 通常可以在 XsltTransformer/Controller 级别定义。它们也可以在 Processor/Configuration 上设置,但如果您想在整个过程中使用相同的设置,那只是默认设置。在您的情况下,您应该在控制器级别设置它们。
我想验证一下我对 Saxon 的 XSLT 对象和并发性的理解。
基本上,我需要自定义解析器来 return 请求特定数据进行转换,目前,我正在为每个请求的每个转换创建新的解析器实例。我已经收到了来自一个请求的数据在另一个请求中使用的早期报告,这是一个严重的问题。
在 .NET 4.6 (C#) 上使用 Saxon 9.6.0.6 HE,Windows 7/Server 2012。
我的代码可以为许多并发请求执行许多不同的共享转换。 Saxon 的已编译 XSLT 是性能所必需的。目前,代码是多线程的(在适当的地方使用 TPL 和异步),并且不使用锁定(并且希望尽可能避免这种情况)。
我偶尔会收到关于转换输出中跨请求数据不正确 'leaked' 的报告(即,可能是并发问题)。我不确定这是否与自定义 XmlResolver 或自定义 CollectionUriResolver 的行为有关。我在等待更多信息。我还不能重现这个问题(仍在处理这个问题,如果可以的话,我会 post 更新)。
我们的转换同时使用 fn:doc 和 fn:collection。
代码在应用程序启动时预编译所有可能的转换。这些可执行文件是共享的。
对于事务中的给定转换,我的代码通过编译的可执行文件的 .Load() 调用创建了一个 XsltTransformer 对象。这似乎创建了一个查看 9.6 HE 代码的新对象(这是我所期望的)。
接下来,我的代码创建自定义 XmlResolver 和 CollectionUriResolver 的新实例(尚未移至 CollectionFinder,但认为这可能以相同的方式运行)并且这些实例填充了适当的特定请求 docs/values/etc 以输入转换。
这两个解析器只有一次 XSLT 执行的生命周期 - 它们不会被重复使用。
我们以我所知道的唯一方式将解析器与 XsltTransform 对象相关联:
Saxon.Api.XsltTransformer transform = executable.Load();
transform.InitialContextNode = sourceData;
transform.Implementation.getConfiguration().
setCollectionURIResolver(collectionResolver);
transform.InputXmlResolver = inputResolver;
在 Saxon 代码中,看起来 输入解析器是基于实例的,因此不共享(最终似乎存在于控制器 class 中,如下所示,当 XsltTransformer 通过 Load() 创建时,它本身就是一个新实例。
public XmlResolver InputXmlResolver
{
set
{
controller.setURIResolver(new DotNetURIResolver(value));
}
}
但是,我担心配置数据可能被共享,并且在配置对象上设置集合解析器(CollectionFinder 似乎是相同的)我们可能会遇到并发问题。
实现我所追求的结果的正确方法是什么 - 让我们的自定义解析器响应特定于请求的行为?我可以在每个转换中使用一对带有请求特定数据的实例,还是必须跨请求共享解析器(可能将请求 ID 注入转换以形成传递给解析器的 URI 的一部分)?
稍作更新 看来您可以直接在控制器 ('Implementation') 上或在配置上设置 CollectionURIResolver,它们在内存中是截然不同的对象:
transform.Implementation.setCollectionURIResolver(collectionResolverOne);
transform.Implementation.getConfiguration().
setCollectionURIResolver(collectionResolverTwo);
但是,在运行时,调用的是配置的解析器(在上述情况下为 collectionResolverTwo)。我不确定控制器副本的用途。
此外,配置数据似乎确实是共享的,因为如果我从同一个可执行文件创建第二个转换器并在配置级别设置它的集合解析器,这会更新第一个转换器使用的解析器。
所以 - 我想我已经找到了我的问题 - 我现在需要知道在我需要集合解析器为每个请求(例如,一个请求可能在特定集合中有五个条目,另一个可能有两个)。
我认为在解释你的问题时你基本上已经自己解决了。 Saxon 中的 Configuration 对象(它在 API 级别支持 Processor)是共享的,并且在任何初始化之后强烈建议不要使用 setURIResolver() 等方法更改其状态,因为此类更改会影响工作-以未定义的方式取得进展。
Saxon 有 API 个一一对应的对象和内部对象,内部对象并不是 100% 封装的,因为一些用户需要访问更私密的功能。在 Java 世界中,还有概念上相似的 JAXP 类,但仅限于 XSLT 1.0 功能。对应关系是:
关于整个撒克逊环境的共享信息:
API:处理器内部:配置JAXP:TransformerFactory
包含用于编译样式表的选项的可重用 XSLT 编译器:
API: XsltCompiler Internal: CompilerInfo JAXP: 没有等价物
编译好的样式表,可以重复执行(多线程并发)
API:XsltExecutable 内部:Executable/PreparedStylesheet JAXP : 模板
单个转换,使用一个样式表转换一个源文档:
API:XsltTransformer 内部:控制器JAXP:Transformer
Saxon 中的某些配置选项仅在配置级别可用,例如,可用的排序规则 URI 集是在此级别定义的,并且不能从一个转换到另一个转换。
但是,URIResolvers 通常可以在 XsltTransformer/Controller 级别定义。它们也可以在 Processor/Configuration 上设置,但如果您想在整个过程中使用相同的设置,那只是默认设置。在您的情况下,您应该在控制器级别设置它们。