Oracle 发送(异步/同步)请求

17

为实时监控Oracle11g数据库数据状态,使用触发器将新数据发送到程序接口进行展示处理。遇到程序接口未启动导致请求超时问题,应考虑使用异步请求以立即响应结果。记录研究成果以警示他人。

先说一下使用到的Oracle技术

  • UTL_HTTP(用于发送HTTP请求)

  • dbms_job(用户创建定时任务)

下面是两个核心的储存过程

SendRequests(发送请求,同步请求)

说明:这个储存过程是参考的网络资源,把参数直接通过url方式传入(http://ip/xx?param=xxx),加入异常捕获部分  

--发送网络请求
create or replace procedure SendRequests(url in VARCHAR2) is
begin
  declare
    req   UTL_HTTP.REQ;
    resp  UTL_HTTP.RESP;
    value VARCHAR2(1024);  -- URL to post to #这里所有参数的长度根据需求修改
    v_url VARCHAR2(4000) := url;
    v_param VARCHAR2(4000) := '1';
    v_param_length NUMBER := LENGTHB(v_param);
  begin
    DBMS_OUTPUT.ENABLE (buffer_size=>null);
    req := UTL_HTTP.BEGIN_REQUEST(url=> v_url, method => 'POST');
    UTL_HTTP.SET_BODY_CHARSET('UTF-8');
    UTL_HTTP.SET_HEADER(r =>  req, name => 'Content-Type', value => 'application/x-www-form-urlencoded');
    UTL_HTTP.SET_HEADER(req, 'Keep-Alive', 'timeout=1');
    UTL_HTTP.SET_HEADER(r => req, name => 'Content-Length', value => v_param_length);
    UTL_HTTP.WRITE_RAW (r => req, data => UTL_RAW.CAST_TO_RAW(v_param)); 
    resp := UTL_HTTP.GET_RESPONSE(req);
    loop
      UTL_HTTP.READ_LINE(resp, value, TRUE); --#这里请求发送完毕后释放内存
      DBMS_OUTPUT.PUT_LINE(value);
    END LOOP;
    UTL_HTTP.END_RESPONSE(resp);
    UTL_HTTP.END_REQUEST(req);
    EXCEPTION 
      WHEN OTHERS THEN --#此处捕获了所有异常 并且没有做处理,可以根据自己需求进行个别异常类型处理
      null;
  end;
end SendRequests;

AsyncSendRequests(发送异步请求)

说明:通过任务调度加SendRequests同步请求实现,参数和同步请求一样 dbms_job 是Oracle内置的任务调度工具,具体使用方式请自行了解,使用这种调用方式可立即执行任务且只执行一次(当任务出错后,重复指定数次自动停止任务,默认好像是16次),这样我们就实现了异步发送请求,无论请求是否成功,都会立即响应  

--异步发送网络请求
create or replace procedure AsyncSendRequests(url in VARCHAR2) is 
begin
  declare  
    job number;   
  begin
    dbms_job.submit(job, 'SendRequests('''|| url ||''');', sysdate, null);
  end;
end AsyncSendRequests;

触发器

触发器应该都会写了,这里只填一个例子

create or replace trigger FindingsOfAudit
  after insert or update
  on bak_is_flow 
  for each row
declare 
  --
begin
  --推送数据
  --SendRequests('http://nginx.guyubao.com/findings?jyjgbh='||:new.jyjgbh||'&hphm='||:new.hphm||'&lszt='||:new.lszt); --同步请求
  AsyncSendRequests('http://nginx.guyubao.com/findings?jyjgbh='||:new.jyjgbh||'&hphm='||:new.hphm||'&lszt='||:new.lszt); --异步请求
end FindingsOfAudit;

总结

无论使用哪种数据库,只要具有相应的API,都可以根据这个示例进行修改。如果支持异步线程,就可以避免使用任务调度。对于Oracle数据库,还可以通过直接调用Java类的方式发送请求。实现思路是使用触发器调用存储过程,存储过程再调用Java类。需要注意的是Oracle内置的JRE版本(例如,Oracle 11g内置JRE版本为1.5)。我并没有对这种方法进行测试,建议大家自行了解。