Javax Transformer 在高并发环境下失败
Javax Transformer fails on high concurrent environment
我有一个带有 Singleton Bean 的平台,我在其中加载了一个 Transformer 实例(因为 xslt 太大以至于无法根据每个请求创建一个实例)。
在正常负载下一切正常,但是对大量并发请求进行压力测试时,转换器开始抛出 ArrayIndexOutOfBoundsException 并停止工作,因此我必须重新启动服务器实例或重新部署应用程序。
这是我在部署应用程序时创建实例的方式:
private Transformer createCFDI33TransformerInstance() {
InputStream in = new URL("http://www.sat.gob.mx/sitio_internet/cfd/3/cadenaoriginal_3_3/cadenaoriginal_3_3.xslt").openStream();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer
= factory.newTransformer(new StreamSource(in));
Logger.getLogger(PadeSingleton.class.getName()).log(Level.INFO, " Se ha cargado la instancia de XSLT");
return transformer;
} catch (TransformerConfigurationException | IOException ex) {
// Loading a remote instance was not possible so I will load a local instance
Logger.getLogger(PadeSingleton.class.getName()).log(Level.SEVERE, "No fue posible cargar una nueva instancia de cadena original, se usara la del sistema", ex);
InputStream in = CFDIv33Tools.class.getClassLoader()
.getResourceAsStream("com/soft/cadenaoriginal_3_3.xslt");
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer;
try {
transformer = factory.newTransformer(new StreamSource(in));
Logger.getLogger(PadeSingleton.class.getName()).log(Level.WARNING, "No fue posible cargar la instancia, se cargara una local");
} catch (TransformerConfigurationException ex1) {
Logger.getLogger(PadeSingleton.class.getName()).log(Level.SEVERE, null, ex1);
// Estamos en problemas
throw new Exception("Error critico");
}
return transformer;
}
}
所以在其他 bean 中我注入了我的 Singleton Bean 并调用了一个方法来获取我的 Transformer 实例。
public static String transform(String xml, Transformer instance) throws Exception {
StringWriter writer = new StringWriter();
try {
instance.transform(new StreamSource(new StringReader(xml)), new StreamResult(writer));
} catch (TransformerException e) {
throw new Exception("El comprobante contiene simbolos no permitidos o esta mal formado", e);
}
return writer.toString();
}
在这个方法中我得到了一个 ArrayIndexOutOfBoundsException
Caused by: javax.xml.transform.TransformerException: java.lang.ArrayIndexOutOfBoundsException: -1
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:746)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:351)
at com.icontech.pade.common.xml.tools.CFDIv33Tools.calcularCadenaOriginalV2(CFDIv33Tools.java:93)
... 138 more
我在进行压力测试时重复发生这种情况,当抛出此异常时,以下请求因相同的异常而失败。我想我的实例已损坏.. 或者什么的。
Transformer
不是线程安全的。您应该创建一个表示已编译样式表的 Templates
对象,然后为每个转换实例化一个新的 Transformer 对象。
(原则上,Transformer 是可连续重用的,因此一旦一个转换完成,您就可以在同一个线程中重用一个转换器。我不知道使用 Xalan 这样做是否有任何好处;在这种情况下萨克森,最好每次都创建一个新的变形金刚。)
我有一个带有 Singleton Bean 的平台,我在其中加载了一个 Transformer 实例(因为 xslt 太大以至于无法根据每个请求创建一个实例)。
在正常负载下一切正常,但是对大量并发请求进行压力测试时,转换器开始抛出 ArrayIndexOutOfBoundsException 并停止工作,因此我必须重新启动服务器实例或重新部署应用程序。
这是我在部署应用程序时创建实例的方式:
private Transformer createCFDI33TransformerInstance() {
InputStream in = new URL("http://www.sat.gob.mx/sitio_internet/cfd/3/cadenaoriginal_3_3/cadenaoriginal_3_3.xslt").openStream();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer
= factory.newTransformer(new StreamSource(in));
Logger.getLogger(PadeSingleton.class.getName()).log(Level.INFO, " Se ha cargado la instancia de XSLT");
return transformer;
} catch (TransformerConfigurationException | IOException ex) {
// Loading a remote instance was not possible so I will load a local instance
Logger.getLogger(PadeSingleton.class.getName()).log(Level.SEVERE, "No fue posible cargar una nueva instancia de cadena original, se usara la del sistema", ex);
InputStream in = CFDIv33Tools.class.getClassLoader()
.getResourceAsStream("com/soft/cadenaoriginal_3_3.xslt");
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer;
try {
transformer = factory.newTransformer(new StreamSource(in));
Logger.getLogger(PadeSingleton.class.getName()).log(Level.WARNING, "No fue posible cargar la instancia, se cargara una local");
} catch (TransformerConfigurationException ex1) {
Logger.getLogger(PadeSingleton.class.getName()).log(Level.SEVERE, null, ex1);
// Estamos en problemas
throw new Exception("Error critico");
}
return transformer;
}
}
所以在其他 bean 中我注入了我的 Singleton Bean 并调用了一个方法来获取我的 Transformer 实例。
public static String transform(String xml, Transformer instance) throws Exception {
StringWriter writer = new StringWriter();
try {
instance.transform(new StreamSource(new StringReader(xml)), new StreamResult(writer));
} catch (TransformerException e) {
throw new Exception("El comprobante contiene simbolos no permitidos o esta mal formado", e);
}
return writer.toString();
}
在这个方法中我得到了一个 ArrayIndexOutOfBoundsException
Caused by: javax.xml.transform.TransformerException: java.lang.ArrayIndexOutOfBoundsException: -1
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:746)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:351)
at com.icontech.pade.common.xml.tools.CFDIv33Tools.calcularCadenaOriginalV2(CFDIv33Tools.java:93)
... 138 more
我在进行压力测试时重复发生这种情况,当抛出此异常时,以下请求因相同的异常而失败。我想我的实例已损坏.. 或者什么的。
Transformer
不是线程安全的。您应该创建一个表示已编译样式表的 Templates
对象,然后为每个转换实例化一个新的 Transformer 对象。
(原则上,Transformer 是可连续重用的,因此一旦一个转换完成,您就可以在同一个线程中重用一个转换器。我不知道使用 Xalan 这样做是否有任何好处;在这种情况下萨克森,最好每次都创建一个新的变形金刚。)