Java动态展现CGI执行进度

还是先介绍一下问题背景:公司产品中,Java应用服务器会调用CGI进行一些系统操作,比如系统的备份、恢复等,这时候Java相当于一个客户端去请求Apache服务器。

但有时候系统操作很费时间,比如备份一个很大的数据库,可能需要几个小时,这种情况下首先要避免http请求超时,其次还要动态地给客户端以进度信息,提高系统可用性。

系统可用性因人而异,暂且不提。可能有有同学想,在建立http连接的时候将timeout设置为0,也就是不超时,是不是就可以了呢?但是,apache服务器本身也有超时时间的限制,HTTP连接上长时间没有输入输出,服务器会回收连接资源,服务器超时后,会抛下面的异常:

java.io.IOException: Premature EOF
        at sun.net.www.http.ChunkedInputStream.readAheadBlocking(ChunkedInputStream.java:565)
        at sun.net.www.http.ChunkedInputStream.readAhead(ChunkedInputStream.java:609)
        at sun.net.www.http.ChunkedInputStream.read(ChunkedInputStream.java:696)
        at java.io.FilterInputStream.read(FilterInputStream.java:133)
        at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3053)
        at java.io.FilterInputStream.read(FilterInputStream.java:133)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.Reader.read(Reader.java:140)


在分析这个问题的时候,发现有两种方案:

1. Java端发出http请求,apache服务器收到请求后马上返回成功,然后继续进行系统操作,在执行过程中调用由Java服务器提供的REST API来刷新进度。这种方案问题之一是要实现REST API,另外一个问题是Perl的CGI模块是否支持这种方式也未可知。

2. Server Push的方案,apache服务器不时将系统操作执行进度推送给Java端。这种方案只需要改造一下Java端代码来支持动态更新进度。

对方案二进行了验证,发现可行。

严格来说,这不是一个典型的Server Push应用,因为并没有用到标准里提到的那些技术,如内置iframe、Ajax长轮询、XMLHttpRequest长轮询等,但是也利用了Server Push的基本特征:http长连接,服务器主动推送数据。

方案的本质其实非常简单,Http 1.1以后,连接方式默认都是长连接,客户端和服务器建立连接后,服务器可以在任意时间向这个连接中写数据,而客户端可以从这个连接的输入流中定期读取数据,获得服务器端的状态,然后更新进度。借用网上的一张图来说明:


也就是说,并不是一次请求一次响应这种传统的WEB模式,而是在连接建立后,服务器能够多次直接向客户端推送数据。


验证代码:

Java Code

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class TestServerPush {
    public static void main(String[] args) {
        HttpURLConnection conn = null;
        
        try {
            URL url = new URL("HTTP", "10.208.135.246", 8001, "/cgi-bin/testServerPush.pl");
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");

            conn.connect();
            int retCode = conn.getResponseCode();
            if (200 != retCode) {
                System.out.println("retCode is " + retCode);
                return;
            }

            InputStream content = (InputStream) conn.getContent();
            InputStreamReader rdr1 = new InputStreamReader(content);
            char[] cbuf = new char[200];
            
            int pos = rdr1.read(cbuf);
            
             while (-1 != pos) {
                 System.out.println(new String(cbuf));   // 这里更新执行进度
                 java.util.Arrays.fill(cbuf, '\0');      // 清空缓存
                 
                 Thread.sleep(1000);
                 pos = rdr1.read(cbuf);
             }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }
}

Perl Code

#!/usr/bin/perl
use CGI;
use CGI::Carp qw ( fatalsToBrowser warningsToBrowser );
use warnings;
use strict;

$| = 1;  ## turn autoflush on
$ENV{"PATH"}="/bin:/sbin:/usr/bin:/usr/sbin";
$<=$>;

my $q= new CGI;
print $q->header({-type=>'text/plain'});

my $count = 0;
while ($count < 5) {
   print "$count---".`date`;

   $count++;
   sleep(2);
}

先用CURL验证一下


可以看到每两秒服务器推送一次数据,静态的图片可能看不清楚,真正执行一下可以看出是定时推送的。

使用Java代码执行,类似的效果。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值