openoffice是单线程的,如果并发操作,不管是通过java调用,还是命令调用,都会出问题,异常处理,可能会在openoffice服务器生成文件锁,导致命令一直执行不下去造成线程死锁;这种情况,tomcat线程被占满,tomcat默认连接数10000,等待执行队列数100,也就是服务器会接受10100个连接,服务器最多产生10100个文件句柄,所以,线程假死执行不下去,文件句柄会飙升;所以要防止应用假死,防止openoffice并发操作, 应用在负载均衡的时候,nginx必须对openoffice转换文件的请求指向同一台机器或者每个应用单独部署一个openoffice服务;同时程序里面应用的连接开启一个就行了,无需新建多个链接对象。
java版本针对openoffice单线程和可能线程假死的问题,我采取的方案有:
1、线程隔离转换文件,主线程异步关闭连接的;
2、转换接口采用jdk动态代理
3、openoffice连接采用单例。
代码如下:
转化器动态代理类:
/**
* DocumentConverter动态代理
* 线程隔离,openoffice文件转换操作,此步骤可能会导致宕机; 为了不影响主线程,转换过程采用线程隔离;
* 转换文件执行超时后,通过主线程关闭连接,同时输出日志
* @author lyq
* @date 2020-09-16
*
*/
public class DocumentConverterDynaProxy implements InvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(DocumentConverterDynaProxy.class);
private static ThreadPoolExecutor executor = null;
static{
//有限队列,缓存30个请求等待,线程池默认策略,缓存超出上限报错,此配置与tomcat max-threads(100) 值有关
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(30);
//初始化1个线程,最多1个线程
executor = new ThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS, workQueue,new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("openoffice-convert-thread");
return thread;
}
} );
}
private DocumentConverter documentConverter;
private Integer timeoutSec = 30;//转换超时时间,单位:秒
/**
* 绑定并获取代理对象
* @param documentConverter 转换器接口
* @param timeoutSec,执行转换超时时间,单位:秒
* @return DocumentConverter代理对象
* @author lyq
* @date 2020-09-16
*/
public Object bind(DocumentConverter documentConverter,Integer timeoutSec){
this.documentConverter = documentConverter;
this.timeoutSec = timeoutSec;
Class [] clzz = {DocumentConverter.class};
Object obj = Proxy.newProxyInstance(
this.documentConverter.getClass().getClassLoader(),clzz , this);
return obj;
}
/**
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ExecCallable runner = new ExecCallable(method, args);
Future future = null;
try{
// synchronized (executor) {
future = executor.submit(runner);
return future.get(timeoutSec, TimeUnit.SECONDS);
// }
}catch(TimeoutException e){
synchronized (runner.isExec) {
if(runner.isExec.get()){
if(OfficePatch.execExceptionCount.get()>3){
logger.error("openoffice执行"+timeoutSec+"秒转换超时了,请系统管理员检测openoffice服务器应用是否正常使用");
}else{
OfficePatch.execExceptionCount.incrementAndGet();
logger.error("openoffice执行"+timeoutSec+"秒转换超时,关闭连接。");
}
asynDisconnect();//断开连接线程才会跑下去;
}else{
runner.isExec.set(true);
logger.error("openoffice 缓存中等待"+timeoutSec+"秒超时,未执行转换,当前等待队列数量:"+executor.getQueue().size());
}
throw new OpenOfficeConvertException(e);
}
}catch(RejectedExecutionException e2){
//abort策略,缓存超出容量,报错。
logger.error("openoffice 超出等待执行缓存",e2);
throw new OpenOfficeConvertException(e2);
}finally{
// this.conn.disconnect();
if(future!=null){
future.cancel(true);
}
}
}
/**
*
* 异步关闭连接
* OpenOfficeConnection的disconnect跟StreamOpenOfficeDocumentConverter类的转换的
* 方法convertInternal会加同步锁,但是convertInternal转换过程会假死,造成线程永久不释放,
* 所以disconnect执行不了,通过异步关闭,避免tomcat线程被永久占用,最终造成应用崩溃。
* @author lyq
* @date 2020-09-16
*/
private void asynDisconnect() {
try{
XComponent component = (XComponent)readSuperPrivateField( OfficePatch.getConnection(), "bridgeComponent");
logger.debug("disconnecting");
writeSuperPrivateField(OfficePatch.getConnection(),"expectingDisconnection",false);
component.dispose();
}catch(Exception e2){
logger.error("openoffice关闭连接失败。",e2);
}
}
private static Object readSuperPrivateField(Object target,String filedname) {
if( target == null){
throw new RuntimeException("ClassUtils.readSuperPrivateField:属性target 为空");
}
if( target.getClass().getSuperclass() == Object.class){
throw new RuntimeException("readSuperPrivateField:属性target"+target.getClass().getName()+" 没有超类");
}
Field field = FieldUtils.getDeclaredField(target.getClass().getSuperclass(), filedname,true);
if (field == null) {
throw new RuntimeException("readSuperPrivateField:属性target"+target.getClass().getName()+" 超类 没有属性:"+ filedname);
}
Object value;
try {
value = FieldUtils.readField(field, target, true);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
return value;
}
private static Object writeSuperPrivateField(Object target,String filedname,Object value) {
if( target == null){
return new RuntimeException("readSuperPrivateField:属性target 为空");
}
if( target.getClass().getSuperclass() == Object.class){
return new RuntimeException("readSuperPrivateField:属性target "+target.getClass().getName()+" 没有超类");
}
Field field = FieldUtils.getDeclaredField(target.getClass().getSuperclass(), filedname,true);
if (field == null) {
return new RuntimeException("readSuperPrivateField:属性target "+target.getClass().getName()+" 超类 没有属性:"+ filedname);
}
try {
FieldUtils.writeField(field, target, value,true);
} catch (IllegalAccessException e) {
return new RuntimeException(e.getMessage());
}
return value;
}
class ExecCallable implements Callable{
private AtomicBoolean isExec = new AtomicBoolean(false);
private Method method = null;
private Object[] args = null;
public ExecCallable(Method method,Object[] args) {
// TODO Auto-generated constructor stub
this.method = method;
this.args = args;
}
@Override
public Object call() throws Exception {
// TODO Auto-generated method stub
synchronized (this.isExec) {
if(!isExec.get()){//未超时
isExec.set(true);
Object result = method.invoke(documentConverter, args);
return result;
}else{
return null;
}
}
}
}
}
外部调用类:
import java.net.ConnectException;
import java.util.concurrent.atomic.AtomicInteger;
import com.artofsolving.jodconverter.DocumentConverter;
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
/**
* 连接单例
* @author lyq
* 2020.9.17
*/
public class OfficePatch {
static AtomicInteger execExceptionCount = new AtomicInteger(0) ; //连接异常处理次数,重连后清0
private static OpenOfficeConnection conn = null;
private static String LOCK_ = "";
public static OpenOfficeConnection getConnection() throws ConnectException{
if(conn==null){
synchronized (LOCK_) {
if(conn==null){
conn = new SocketOpenOfficeConnection("ip", 端口);
System.out.println("openOffice正在连接。。");
conn.connect();
}
}
}
if(!conn.isConnected()){
synchronized (LOCK_) {
if(!conn.isConnected()){
System.out.println("openOffice正在重新连接。。");
execExceptionCount.set(0);
conn.connect();
}
}
}
return conn;
}
/**
* 绑定并获取代理转换对象
* @param converter openoffice转换器
* @param connection converter所调用的连接
* @param timeoutSec 超时时间,单位:秒
* @return
*/
public static DocumentConverter getDocmentConverterProxy(DocumentConverter converter,Integer timeoutSec){
DocumentConverterDynaProxy proxy = new DocumentConverterDynaProxy();
return (DocumentConverter)proxy.bind(converter,timeoutSec);
}
}
调用方法:
OpenOfficeConnection connection = OfficePatch.getConnection();
DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection);
converter = OfficePatch.getDocmentConverterProxy(converter, 100);
以上,完美处理openoffice宕机与单线程的问题。
分享到:
相关推荐
OpenOffice是一款开源的办公软件套件,它包含了与Microsoft Office相似的各种组件,如文字处理、电子表格、演示文稿制作、绘图以及数据库管理工具。这个压缩包包含了两个针对Linux操作系统的版本:OpenOffice_4.1.6_...
在实际应用中,需要注意异常处理,比如文件不存在、OpenOffice服务未启动等问题。此外,可以考虑并发转换以提高效率,但要注意OpenOffice可能对并行转换的数量有限制。 七、示例代码 下面是一个简单的C#代码片段,...
2. Web开发集成:OpenOffice可以作为服务器端的服务,处理Web应用中的文档操作。例如,用户可以在网页上提交一个文档,通过后端的OpenOffice服务进行格式转换、数据提取等处理,然后返回结果。这在B/S架构的应用中...
"实现openoffice文档转换在线预览"说明了主要的应用场景,即通过编程方式处理OpenOffice支持的文档格式(如ODT、DOC、XLS等),并生成预览。参考链接提供了具体的实现步骤和技术细节。 **标签解析** "openoffice ...
6. **性能优化**:由于启动OpenOffice进程可能会消耗较多资源,因此在处理大量文件转换时,可以考虑使用多线程或者异步处理,提高并发性能。 7. **安全问题**:在实际应用中,考虑到用户可能上传含有恶意代码的文件...
OpenOffice是一款开源的办公软件套件,它包含了字处理、电子表格、演示文稿和图形编辑等组件,可与Microsoft Office兼容。在处理文档时,可能会遇到文本显示为乱码的问题,这通常是因为缺少特定的字体导致的。针对这...
OpenOffice API是一个强大的工具,允许开发者通过编程方式与OpenOffice套件进行交互,...通过深入学习和实践,你可以构建出能够无缝集成OpenOffice功能的应用程序,无论是批量处理文档还是自定义办公自动化解决方案。
在IT领域,特别是开源软件社区,Apache OpenOffice(简称OpenOffice)是一款备受推崇的免费办公套件,它提供了包括文字处理、电子表格、演示文稿、数据库管理等在内的多种功能,是Microsoft Office的一个强有力的...
1. **打开OpenOffice Calc**:启动OpenOffice,选择“组件”中的“Calc”,这将是我们的电子表格应用程序。 2. **导入Excel文件**:点击菜单栏上的“文件”>“打开”,找到你的Excel文件(可能带有.xlsx或.xls扩展...
在Java开发环境中,如果需要与OpenOffice进行交互,例如自动化处理文档或数据转换,开发者通常会用到特定的Java库,这些库被打包成JAR文件。在给定的标题和描述中,提到的是用于集成OpenOffice功能的一系列jar包。 ...
Java作为一种广泛应用的编程语言,提供了多种库和工具来处理这样的需求。本话题主要关注如何使用OpenOffice API在Java环境中将Excel文件转换为PDF格式,同时确保图片等复杂元素能够完美保留。 OpenOffice是一个开源...
### OpenOffice应用:常见问题与解决方案深度解析 #### 开源软件OpenOffice的使用心得与技巧 OpenOffice作为一款开源的办公软件套装,以其强大的功能、跨平台的兼容性以及自由的许可证模式,受到了广大用户的喜爱...
《OpenOffice SDK开发文档中文版》主要面向希望开发OpenOffice应用程序的开发者,文档中详尽介绍了OpenOffice的开发接口以及编程模型,通过API函数详解、类和接口的介绍,以及实际的例子来帮助开发者更好地理解和...
OpenOffice是一款开源的办公软件套件,它包含了与Microsoft Office相似的应用程序,如Writer(文字处理)、Calc(电子表格)、Impress(演示文稿)和Draw(绘图)。这款软件的强大之处在于其跨平台的兼容性和对多种...
OpenOffice 4.1.8 是一款开源的办公软件套件,专为各种操作系统,包括Ubuntu,提供免费的文档处理、电子表格、演示文稿、绘图和数据库管理工具。这款软件是Apache软件基金会的项目之一,旨在替代商业化的Microsoft ...
OpenOffice是一款开源的办公软件套件,它包含了文字处理、电子表格、演示文稿、绘图等多种组件,可以与Microsoft Office兼容。OpenOffice提供了丰富的API和工具,允许开发者进行二次开发,创建自定义功能、插件或者...
OpenOffice是一款开源的办公软件套件,它包含了与Microsoft Office相似的各种组件,如文字处理、电子表格、演示文稿制作、绘图以及数据库管理等工具。这个名为"openoffice示例.rar"的压缩包文件很可能是为了提供...
OpenOffice是一款开源的办公软件套件,包含了文字处理、电子表格、演示文稿等多种应用程序,同时也提供了API供开发者使用。在IT行业中,OpenOffice的其中一个应用是实现文档的预览功能,尤其对于需要在线预览doc、...