Java按時間梯度實現非同步回調介面

來源:互聯網
上載者:User

1. 背景

  在業務處理完之後,需要調用其他系統的介面,將相應的處理結果通知給對方,若是同步請求,假如調用的系統出現異常或是宕機等事件,會導致自身業務受到影響,事務會一直阻塞,資料庫連接不夠用等異常現象,可以通過非同步回調來防止阻塞,但非同步情況還存在一個問題,若調用一次不成功的話接下來怎麼處理?這個地方就需要按時間梯度回調,比如前期按10s間隔回調,回調3次,若不成功按30s回調,回調2次,再不成功按分鐘回調,依次類推……相當於給了對方系統復原的時間,不可能一直處於異常或宕機等異常狀態,若是再不成功可以再通過人工幹預的手段去處理了,具體業務具體實現。

2. 技術實現

  大體實現思路如,此過程用到兩個隊列,當前隊列和Next隊列,當前隊列用來存放第一次需要回調的資料對象,如果調用不成功則放入Next隊列,按照制定的時間策略再繼續回調,直到成功或最終持久化後人工接入處理。

 

  用到的技術如下:

  • http請求庫,retrofit2
  • 隊列,LinkedBlockingQueue
  • 調度線程池,ScheduledExecutorService

 

3. 主要代碼說明

3.1 回調時間梯度的策略設計

採用枚舉來對策略規則進行處理,便於代碼上的維護,該枚舉設計三個參數,層級、回調間隔、回調次數;

/** * 回調策略 */public enum CallbackType {    //等級1,10s執行3次    SECONDS_10(1, 10, 3),    //等級2,30s執行2次    SECONDS_30(2, 30, 2),    //等級3,60s執行2次    MINUTE_1(3, 60, 2),    //等級4,5min執行1次    MINUTE_5(4, 300, 1),    //等級5,30min執行1次    MINUTE_30(5, 30*60, 1),    //等級6,1h執行2次    HOUR_1(6, 60*60, 1),    //等級7,3h執行2次    HOUR_3(7, 60*60*3, 1),    //等級8,6h執行2次    HOUR_6(8, 60*60*6, 1);    //層級    private int level;    //回調間隔時間 秒    private int intervalTime;    //回調次數    private int count;}

3.2 資料轉送對象設計

聲明抽象父類,便於其他對象調用傳輸繼承。

/** * 訊息對象父類 */public abstract class MessageInfo {    //開始時間    private long startTime;    //更新時間    private long updateTime;    //是否回調成功    private boolean isSuccess=false;    //回調次數    private int count=0;    //回調策略    private CallbackType callbackType;}

要傳輸的對象,繼承訊息父類;

/** * 工單回調資訊 */public class WorkOrderMessage extends MessageInfo {    //車架號    private String vin;    //工單號    private String workorderno;    //工單狀態    private Integer status;    //工單原因    private String reason;    //操作使用者    private Integer userid;}

3.3 調度線程池的使用

//聲明線程池,大小為16private ScheduledExecutorService pool = Executors.newScheduledThreadPool(16);
...略while (true){ //從隊列擷取資料,交給定時器執行 try { WorkOrderMessage message = MessageQueue.getMessageFromNext(); long excueTime = message.getUpdateTime()+message.getCallbackType().getIntervalTime()* 1000; long t = excueTime - System.currentTimeMillis(); if (t/1000 < 5) {//5s之內將要執行的資料提交給調度線程池 System.out.println("MessageHandleNext-滿足定時器執行條件"+JSONObject.toJSONString(message)); pool.schedule(new Callable<Boolean>() { @Override public Boolean call() throws Exception { remoteCallback(message); return true; } }, t, TimeUnit.MILLISECONDS); }else { MessageQueue.putMessageToNext(message); } } catch (InterruptedException e) { System.out.println(e); } }

 

3.4 retrofit2的使用,方便好用。

具體可查看官網相關文檔進行瞭解,用起來還是比較方便的。http://square.github.io/retrofit/

retrofit初始化:

import retrofit2.Retrofit;import retrofit2.converter.gson.GsonConverterFactory;public class RetrofitHelper {    private static final String HTTP_URL = "http://baidu.com/";    private static Retrofit retrofit;    public static Retrofit instance(){        if (retrofit == null){            retrofit = new Retrofit.Builder()                    .baseUrl(HTTP_URL)                    .addConverterFactory(GsonConverterFactory.create())                    .build();        }        return retrofit;    }}

如果需要修改逾時時間,連線時間等可以這樣初始話,Retrofit採用OkHttpClient

import okhttp3.OkHttpClient;import retrofit2.Retrofit;import retrofit2.converter.gson.GsonConverterFactory;import java.util.concurrent.TimeUnit;public class RetrofitHelper {    private static final String HTTP_URL = "http://baidu.com/";    private static Retrofit retrofit;    public static Retrofit instance(){        if (retrofit == null){            retrofit = new Retrofit.Builder()                    .baseUrl(HTTP_URL)                    .client(new OkHttpClient.Builder()                            .connectTimeout(30, TimeUnit.SECONDS)//連線時間                            .readTimeout(30, TimeUnit.SECONDS)//讀時間                            .writeTimeout(30, TimeUnit.SECONDS)//寫時間                            .build())                    .addConverterFactory(GsonConverterFactory.create())                    .build();        }        return retrofit;    }}

Retrofit使用通過介面調用,要先聲明一個介面;

import com.alibaba.fastjson.JSONObject;import com.woasis.callbackdemo.bean.WorkOrderMessage;import retrofit2.Call;import retrofit2.http.Body;import retrofit2.http.POST;public interface WorkOrderMessageInterface {    @POST("/api")    Call<JSONObject> updateBatteryInfo(@Body WorkOrderMessage message);}

介面和執行個體對象準備好了,接下來就是調用;

private void remoteCallback(WorkOrderMessage message){        //執行個體介面對象        WorkOrderMessageInterface workOrderMessageInterface = RetrofitHelper.instance().create(WorkOrderMessageInterface.class);                //調用介面方法        Call<JSONObject> objectCall = workOrderMessageInterface.updateBatteryInfo(message);        System.out.println("遠程調用執行:"+new Date());        //非同步呼叫執行        objectCall.enqueue(new Callback<JSONObject>() {            @Override            public void onResponse(Call<JSONObject> call, Response<JSONObject> response) {                System.out.println("MessageHandleNext****調用成功"+Thread.currentThread().getId());                message.setSuccess(true);                System.out.println("MessageHandleNext-回調成功"+JSONObject.toJSONString(message));            }            @Override            public void onFailure(Call<JSONObject> call, Throwable throwable) {                System.out.println("MessageHandleNext++++調用失敗"+Thread.currentThread().getId());                //失敗後再將資料放入隊列                try {                    //對回調策略初始化                    long currentTime = System.currentTimeMillis();                    message.setUpdateTime(currentTime);                    message.setSuccess(false);                    CallbackType callbackType = message.getCallbackType();                    //擷取等級                    int level = CallbackType.getLevel(callbackType);                    //擷取次數                    int count = CallbackType.getCount(callbackType);                    //如果等級已經最高,則不再回調                    if (CallbackType.HOUR_6.getLevel() == callbackType.getLevel() && count == message.getCount()){                        System.out.println("MessageHandleNext-等級最高,不再回調, 線下處理:"+JSONObject.toJSONString(message));                    }else {                        //看count是否最大,count次數最大則增加level                        if (message.getCount()<callbackType.getCount()){                            message.setCount(message.getCount()+1);                        }else {//如果不小,則增加level                            message.setCount(1);                            level += 1;                            message.setCallbackType(CallbackType.getTypeByLevel(level));                        }                        MessageQueue.putMessageToNext(message);                    }                } catch (InterruptedException e) {                    e.printStackTrace();                    System.out.println("MessageHandleNext-放入隊列資料失敗");                }            }        });    }

3.5結果實現

4.總結

本次實現了按照時間梯度去相應其他系統的介面,不再導致本身業務因其他系統的異常而阻塞。請大佬們看實現有沒有不合理的地方,歡迎批評指正。

源碼:

相關文章

Alibaba Cloud 10 Year Anniversary

With You, We are Shaping a Digital World, 2009-2019

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。