您的位置:时时app平台注册网站 > 编程知识 > volley源码学习时时app平台注册网站

volley源码学习时时app平台注册网站

2019-11-21 02:27

效果与利益表达

LongIntervalRetries是基于Quartz.Net的一个长日子间距重试的类库,其关键化解哪天推行以及施行结果上报的问题。

结论心得

经过全部的看了三次volley源码,心拿到了多少个相当重大的探究

  • 四个正是纯粹职责,每风度翩翩层都分开,担任每豆蔻梢头层应该有的效果与利益。相互解耦,底层不依附上层。比如互连网诉求Network那几个类,不依附于与上层的队列类,而现实封装底层网络央浼的BaseHttpStack也不依附上层Network。能够让network层很自在的转换底层网络央求库。

  • 政策形式的选拔,也让代码逻辑与政策分离,计策里面不依据具体逻辑。逻辑代码里面通过改造分歧计谋对象,来到达调控不一致政策的指标。

  • 面向接口的编制程序,整个volley种种模块都以因此接口互相之间调用,那样不依赖与实际达成,就将整个框架都搭建好了,以为深受启迪。

经过地点的剖释后,相信大家看代码都应该能精晓了。供给静心的点:

何以会构思基于Quartz.Net

无论是定时触发,还是作业政策,以及安装触发时间,那些都很引人瞩指标富有Job性情,而Quartz.Net自身便是贰个Job类库,并且其本身允许举行并发线程数量设置,假使依照它,显著大家得以毫不考虑线程相关的主题素材,那能够节省大家不小的专业量

volley源码学习

事先一直对于源码学习抱着意气风发种又爱又恨的激情。爱的是因为精晓源码有点专程好的设计思路,能够让本人前车之鉴,并且对于设计方式来说是最佳的实战地。那为啥还也许会恨呢,曾经很频仍下载了比非常多开源库的源码,但是看的看的就认为云里雾里,不知所踪。心中未有三个完完全全的框架,总感到看的细如牛毛,管窥蠡测。前几天又找时间翻出最简便易行的volley,打算伊始再看叁遍。没悟出收获累累,写下那篇作品,用来记录。

调用的代码示举个例子下:

类库相关

该类库在github上之处为:
该类库如今为v1.0.0版本,其nuget地址为:

volley专门的学问流程

首先先放一张google官方给的流程图,就算比较轻便,不过能够在心头有叁个大致的定义

时时app平台注册网站 1

1.png

率先我们从volley调用入手,找到调用入口,那样就足以刻舟求剑,一小点搜寻出全方位的流水生产线。

RequestQueue queue = Volley.newRequestQueue(this);
String url ="https://www.gaotenglife.com";

// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // Display the first 500 characters of the response string.
        mTextView.setText("Response is: "  response.substring(0,500));
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        mTextView.setText("That didn't work!");
    }
});
// Add the request to the RequestQueue.
queue.add(stringRequest);

从上边包车型大巴代码能够见见,大家率先通过newRequestQueue创制出三个requestqueue,也正是伸手队列。然后把一个央浼踏向到诉求队列之中。这里能够看出,当大家把stringrequest参加需要队列后,诉求便初阶了,所以大家便从add看起。

public <T> Request<T> add(Request<T> request) {
    // Tag the request as belonging to this queue and add it to the set of current requests.
    request.setRequestQueue(this);
    synchronized (mCurrentRequests) {
        mCurrentRequests.add(request);
    }

    // Process requests in the order they are added.
    request.setSequence(getSequenceNumber());
    request.addMarker("add-to-queue");

    // If the request is uncacheable, skip the cache queue and go straight to the network.
    if (!request.shouldCache()) {
        mNetworkQueue.add(request);
        return request;
    }
    mCacheQueue.add(request);
    return request;
 }

那其间根本的效应就是把网络诉求进入到相应的互连网诉求队列(mNetworkQueue),借使设置了央浼必要缓存的话,同有的时候间也步向到缓存队列中。这里比较奇怪,为什么只是意气风发味到场队列网络央求就能够发出去吗。于是大家随后向下看,最有希望是在mNetworkQueue.add的时候,发出诉求。于是我们继承深入,发掘mNetworkQueue也只是多个简易的线程安全的连串,也远非做过多的操作。其实那样也是创立的,队列不该富含越多的业务逻辑在里头。于是本人继续查找,是哪儿持有了这些队列。

 public void start() {
    stop();  // Make sure any currently running dispatchers are stopped.
    // Create the cache dispatcher and start it.
    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
    mCacheDispatcher.start();

    // Create network dispatchers (and corresponding threads) up to the pool size.
    for (int i = 0; i < mDispatchers.length; i  ) {
        NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                mCache, mDelivery);
        mDispatchers[i] = networkDispatcher;
        networkDispatcher.start();
    }
}

于是乎发掘八个地点接纳了这些mNetworkQueue队列,CacheDispatcher,NetworkDispatcher从名字是就能够收看,最有超大或许就是NetworkDispatcher里面进行互连网队列的拍卖了。于是大家看NetworkDispatcher,发掘那几个NetworkDispatcher原本正是多少个线程。

public void run() {
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    while (true) {
        try {
            processRequest();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                return;
            }
        }
    }
}

在它的run方法里面,不断循环调用processRequest,在processRequest里面从伏乞队列里抽出request

Request<?> request = mQueue.take();//取出请求
......


 // Perform the network request.
        NetworkResponse networkResponse = mNetwork.performRequest(request);//通过networt真正去请求网络

从那边看看,单风姿洒脱任务设计观念。队列只管理队列的逻辑,互联网只管理网络的逻辑。不交叉写到一同。

那便是说network里面是哪些真正诉求网络的吗,因为比较volley是包裹了切实网络央求库。
于是大家见到BasicNetwork里面,它里面有BaseHttpStack,而BaseHttpStack是个抽象类,它的子类,比方HurlStack是包裹了
HttpU奥德赛LConnection,而HttpClientStack是包装了httpcliet底层库。那样network就能够根据配置,选择大家必要的平底网络库。

那么央浼完事后,重临的结果怎样回调回去吗。这里又凭借ResponseDelivery,将诉求的结果发送到ui线程

 mDelivery.postResponse(request, response);

咱俩再进来ExecutorDelivery这里面

public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
    request.markDelivered();
    request.addMarker("post-response");
    mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}

然后通过mResponsePoster这里面调用handler,最终将结果发送给ui线程

 // Deliver a normal response or error, depending.
       if (mResponse.isSuccess()) {
            mRequest.deliverResponse(mResponse.result);
        } else {
            mRequest.deliverError(mResponse.error);
        }

那边最后调用了request里面安装的监听函数,最终把多少给了调用方。

到此大家的一个调用进程就分析完成了。是否挺轻易的。

所依,总计一下,大家必要:1卡塔 尔(阿拉伯语:قطر‎电磁打点计时器,用于依期施行;2卡塔尔国ActionInfo包装要重试的逻辑的连带消息;3卡塔 尔(英语:State of Qatar)内部存款和储蓄器队列,用于贮存ActionInfo;所以,代码如下:

干什么不应用波利

波莉是.NET基金会下的弹性和瞬态故障管理库,其消除的主题素材自然符合大家回调的业务场景,但为啥在这里间却不被应用呢,原因如下:

  • Polly的重试机制是个长时间内的体制,在其重试机制时间内,当前Task诚如是由此await卡住的,而对于大家的情况来讲,那明摆着是不适当的,大家的情景并不应有生出在如今线程内
  • Polly并不协助程序重启时的重试复苏,这点在大家的业务场景中及其主要,总无法服务珍视启后,大家尚未回调成功的业务就总体丢了啊

volley是什么

volley是三个包装好的网络库,是把httpclient或HttpU中华VLConnection又包装了风姿罗曼蒂克层,加上了线程,队列,缓存等体制,让网络央浼特别轻巧。

volley是google官方推出的三个开源项目,特地适用于android轻量的须要。可是对于大文件等支撑倒霉。

volley整个源码都以用了接口编制程序的想一想,那也相比切合设计情势的完美施行。

为此看volley源码会对于接口编制程序有较长远的掌握。

除外volley之外,还会有七个retrofit网络框架,是对okhttp的卷入,那些框架以往专程火,何况倘令你想领悟设计方式之美的话,那些retrofit源码必需看,等之后再写大器晚成篇剖判retrofit的稿子

透过自己的后生可畏对商量,小编发掘,要是二个新闻在有些阶段要被拍卖多少个步骤,且有些步骤之间有规范正视,例如唯有在第2步管理的结果是大功告成时,大家才有十分重要做后边的3步;平常状态,借使一切顺遂,那正是一步步从上往下的去做;然则因为思虑到别的一步恐怕都会出标题,并且大家期望在别的一步失利然后重试成功后,能三回九转持续的步骤。所以,基于那一个特点,作者感到我们得以设计意气风发种恍若回调函数的编写制定,当某些逻辑施行成功后,试行回调函数,大家得以在回调函数中存放接下去要做的逻辑;分明,小编认为我们需求某种递归的数据结构;为了协理方面那连串似回调函数的必要,作者设计了之类的三个数据结构:

发出的来由

归纳的说,大家提供了大器晚成多种的API供第三方调用,但因为实在API对应的事体管理时间较长,所认为了充实吞吐量,实际的政工逻辑并没包含在API服务器,而是分布在了不相同的服务器上举行管理,在事情管理完成后,再通过调用第三方提供的回调Url通知管理结果,为了有限支撑回调精确,那么我们必要好似此叁个国策:假诺回调失利,我们须要在指依期间间距之后再也回调,如此频仍直到回调成功依旧到达重复次数上限。上述回调方案是或不是很熟稔?嗯,好吧,直白的讲,回调这风流洒脱有的我们借鉴(抄袭卡塔尔了支付宝支付时的回调方案,所以大家要缓和的,就是经过代码来促成回调这有的的事体场景,于是也就有了LongIntervalRetries

缓存的兑现

在刚刚上面剖判代码的时候提起,mNetworkQueue被多少个类具备,三个正是大家已经解析过的NetworkDispatcher,还应该有三个正是大家那意气风发节要讲的CacheDispatcher。

public <T> Request<T> add(Request<T> request)
    ...
    ...
    mCacheQueue.add(request);

在上面队列add的时候,假若央求设置了索要缓存,request.shouldCache(),约等于以此为true,那么会先将以此request插足到缓存的行列之中。

而管理缓存队列的,正是CacheDispatcher。和NetworkDispatcher相似,那个CacheDispatcher也是三个线程,在线程的run方法里面,施行了processRequest这几个办法。那此中重要管理了缓存相关逻辑

第风姿罗曼蒂克先从缓存队列之准将缓存的request抽出来。

final Request<?> request = mCacheQueue.take();

....省略非主要代码
 // Attempt to retrieve this item from cache.
    Cache.Entry entry = mCache.get(request.getCacheKey());
    if (entry == null) {
        request.addMarker("cache-miss");
        // Cache miss; send off to the network dispatcher.
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            mNetworkQueue.put(request);
        }
        return;
    }

    // If it is completely expired, just send it to the network.
    if (entry.isExpired()) {
        request.addMarker("cache-hit-expired");
        request.setCacheEntry(entry);
        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            mNetworkQueue.put(request);
        }
        return;
    }

然后实行了七个决断,第一个正是先从缓存中获得那个request,若无缓存,则将央求直接步入到前边的互联网队列,实行网络诉求。假使有缓存,则再通过剖断entry.isExpired是还是不是过期,即使缓存的request已经晚点,则也投入到网络伏乞的系列中。

接下去,若是有缓存并且缓存未有过期,则从缓存中取到从前央浼过的数据,并进行剖析。如下

 Response<?> response = request.parseNetworkResponse(
            new NetworkResponse(entry.data, entry.responseHeaders));

随后又经过了后生可畏层决断

 if (!entry.refreshNeeded()) {
        // Completely unexpired cache hit. Just deliver the response.
        mDelivery.postResponse(request, response);
    } else {
        // Soft-expired cache hit. We can deliver the cached response,
        // but we need to also send the request to the network for
        // refreshing.
        request.addMarker("cache-hit-refresh-needed");
        request.setCacheEntry(entry);
        // Mark the response as intermediate.
        response.intermediate = true;

        if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
            // Post the intermediate response back to the user and have
            // the delivery then forward the request along to the network.
            mDelivery.postResponse(request, response, new Runnable() {
                @Override
                public void run() {
                    try {
                        mNetworkQueue.put(request);
                    } catch (InterruptedException e) {
                        // Restore the interrupted status
                        Thread.currentThread().interrupt();
                    }
                }
            });
        } else {
            // request has been added to list of waiting requests
            // to receive the network response from the first request once it returns.
            mDelivery.postResponse(request, response);
        }
    }

也正是说,尽管缓存的数据须求刷新,那么还是供给将request发送给网络队列进行号令。假设数据没有须求刷新,则直接通过mDelivery将缓存的数码发送给ui线程。

这里面有多个概念,如下

  /** True if the entry is expired. */
    public boolean isExpired() {
        return this.ttl < System.currentTimeMillis();
    }

    /** True if a refresh is needed from the original data source. */
    public boolean refreshNeeded() {
        return this.softTtl < System.currentTimeMillis();
    }

ttl和softttl那三个是http协议里面通过header总括出来的五个值。详细能够查阅HttpHeaderParser那一个类。

当一连重试依然战败后,大家就能归入内部存款和储蓄器队列,然后定时重试了。那么哪些依期呢?日常用电磁打点计时器就能够;那准期多少啊?那几个近来自己也是拍脑袋的,近年来设定为5秒。为啥是5秒呢?首倘若五个思虑:1卡塔尔国为了不愿意太频仍的重试,因为太频仍的重试会占用更加多的系统能源,招致会潜濡默化框架中寻常的音信管理性能;2卡塔尔因为这种准时的重试对实时性日常不会异常高,便是说,例如当IO恢复生机后,我们常常不会必要及时就能够重试,过个几秒以至几分钟后再重试,也能选取。实际上,若无这种活动准期的重试机制,大家或者只好等到机重视启后本领重复被重试了,比较之下,已经丰富自动和即时了。

急迅利用

此间仅是简约的代码示例,后续会有详实的行使验证
首先大家须要表明Job

    public class SomeJob: IJob
    {
        public virtual Task Execute(IJobExecutionContext context)
        {
            return Task.FromResult(1);//默认LongIntervalRetries是通过Job是否产生异常来判断是否执行成功的
        }
    }

接下来我们得以将以此Job注册到LongIntervalRetries,同一时间安装重试攻略,以至注册事件监察和控制实行结果,完整的示范如下

var retry = new StdRetry();
//声明并注册重试规则
string simpleRuleName = "SimpleRepeatRetryRule";
var simpleRepeatRule = new SimpleRepeatRetryRule(simpleRuleName, 5, TimeSpan.FromSeconds(2));
retry.RuleManager.AddRule(simpleRepeatRule);
var registerInfo = new RetryJobRegisterInfo
{
    //指定要采用的重试规则,如果不设置,则默认使用已注册的第一项
    UsedRuleName = simpleRuleName,
    //需要传递给IJob的上下文数据
    JobMap = new Dictionary<string, object>
    {
        {"SomeKey","SomeValue" }
    },
    //开始执行时间,如果不指定则表示立刻执行
    StartAt = DateTimeOffset.UtcNow.AddSeconds(3),
};
//注册要执行的Job
retry.RegisterJob<SomeJob>(registerInfo);
//注册每次Job执行后的通知事件
retry.RegisterEvent<SomeJob>(e =>
{//Some code
});
retry.Start();//启动Quartz服务
//启动服务后仍可以RegisterJob、RegisterEvent

重试战略

看样子重试计谋的时候,笔者第意气风发自个儿想了下,假使要本身要好完毕重试计谋,笔者会咋办吗,很直白的的沉思就是,在互联网诉求败北的时候,剖断是还是不是有重试战略,然后在网络必要战败之处,重新发起网络央浼。

于是自身就径直根据这一个思路去探寻,能够在volley网路退步的地点,小编只找到了如下的代码

private static void attemptRetryOnException(String logPrefix, Request<?> request,
        VolleyError exception) throws VolleyError {
    RetryPolicy retryPolicy = request.getRetryPolicy();
    int oldTimeout = request.getTimeoutMs();

    try {
        retryPolicy.retry(exception);
    } catch (VolleyError e) {
        request.addMarker(
                String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
        throw e;
    }
    request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}

以此函数是持有网络有有不行的时候,会调用的。可是本人发觉那此中除了安装了重试战术的有个别本性,其余未有做网络诉求操作。那就意外了。难道互联网央浼会自个儿发起。这让作者大惑不解。

于是乎作者又一次一次放了网络央求的代码,终于开掘了端倪

原先在networkdispater调用Network举行互联网央浼的时候,network里面居然写了一个while循环

 @Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
    long requestStart = SystemClock.elapsedRealtime();
    while (true) {
    .....
    .....
    }
}

那般也就会表达了,那个循环会平昔尝试去央浼互连网,直到不满足重试攻略之后,退出循环。相当于说,那么些重试战术未有正真参加具体了重试逻辑。而只是保存了万众一心的重试状态,真正的重试逻辑还是网络央浼去保障。
走访这里,小编陡然以为到写这么代码的人,真是思路别具风姿浪漫格。而且那样的平价简来讲之,你可以重写重试战略,而没有必要再行改过网络重试的逻辑。那也正是设计情势里面攻略情势相比好的利用吧。

接下去,大家需求构思,如何设计八个重试服务。经过地点的深入分析,我们假设,大家的重试服务须要四个首要功能:1卡塔 尔(英语:State of Qatar)对某段逻辑三番两次重试钦点次数;2卡塔尔将某段逻辑放入重试队列定期重试;对于第二个功用须求,比较简单,直接设计八个递归函数就能够,代码如下:

兼备思路及演变

后生可畏开端大家的布署思路特别轻便,正是怎么准期触发回调那一个业务代码,但未来察觉,为啥大家要只限于回调呢?回调只是三个事务场景,但大家完全有十分的大希望有其余业务场景,适逢其时大家也真的存在这里么的事体场景,我们供给向第三方服务商实行部分须求,该须求同样耗费时间较长,该地方是或不是很熟稔?只但是与大家作为服务商区别,该服务商居然没提供回调方案,它须求大家团结如期去回调!而同等是以此第三方,其业务央浼参数具有非常的定制性,其供给大家先行做过多业务性的预管理,也正是索要做一些顺序性的行事后,大家本领得到完全的呼吁参数。
于是我们的希图思路初叶调节,最终得出该包装应当具备的效果与利益点:

  • 当中间应该封装掉什么按期触发以此功用
  • 其应该有所同有的时候候辅助多样思想政治工作政策(计策格局卡塔尔国
  • 其理应允许设置什么样时候来触发要实践的计划
  • 其相应扶持服务运维时自行回复未结束职责的力量
  • 其应有享有终极实行结果通报的能力

品类开源地址:

通过剖析,大家开掘整整enode框架中须要重试的点是可怜多的,比如command产生的event要发送到队列时,假如退步那须求重试;举个例子event长久化时失利了,也须要重试,等等。所以,一句话来说,大家理应设计叁个得以被圈定的重试服务,提供对少数特定的重试场景的扶植。

上意气风发篇随笔,简要介绍了enode框架中国国投息队列的设计思路,本文介绍一下enode框架中提到音信的重试机制的宏图思路。

从地方的代码,大家能够清晰的观看,大家兼备了一个简约的数据结构,用来含有要实行的逻辑,该逻辑实施时所供给的参数音讯,以至该逻辑推行成功后要做的下二个逻辑;通过上边那个数据结构,大家早已为完结地点的重试要求做好了数据结构方面包车型地铁预备;

再有叁个标题,假诺二个音信在某贰遍重试时成功了,可是我们期望在成功后继续对该音信做继续的步骤,该怎么落实啊?这么些难题初想一想感觉相比麻烦,因为大家大概早就远非了该音信的局地上下文情形。最体贴的是,大家怎么样知道该音信重试成功后接下去该做什么呢?何况正是知道接下去要做哪些了,不过倘若大家在做这些下一步的手续时,假如又倒闭了吗?是还是不是也要重试呢?所以,大家开采此处很入眼。

    /// <summary>一个数据结构,封装了一段要执行的逻辑以及一些相关的上下文信息
    /// </summary>
    public class ActionInfo
    {
        /// <summary>表示某个Action的名字
        /// </summary>
        public string Name { get; private set; }
        /// <summary>表示某个Action,封装了一段逻辑
        /// </summary>
        public Func<object, bool> Action { get; private set; }
        /// <summary>表示Action执行时所需要的数据信息
        /// </summary>
        public object Data { get; private set; }
        /// <summary>表示Action执行成功后,要执行的下一个Action的信息,这里体现出递归
        /// </summary>
        public ActionInfo Next { get; private set; }

        public ActionInfo(string name, Func<object, bool> action, object data, ActionInfo next)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }
            Name = name;
            Action = action;
            Data = data;
            Next = next;
        }
    }

总结:

对于三个EDA架构为幼功的框架,大旨就是音讯使得,然后根据最终风度翩翩致性的尺度。所以,特别关键的有些是,要是音讯二回实践不成功,这该怎么做?笔者能体会通晓的攻略性就是新闻的重试。小编发觉,那篇作品相比难写,因为以为要把纷纭的作业清晰的表达出来,认为真的不便于。提起重试,那怎么是音讯的重试呢?怎么重试呢?笔者那边涉及的重试是指,三个信息,从新闻队列抽出来后,要管理,然而管理退步了,然后要再度尝试再管理该消息;怎么重试?这些标题相比复杂,不可能用轻便的后生可畏两句话来验证。

当大家要重试时,大家首先调用retryService的TrtAction方法,该方法正是用来支撑“对某段逻辑的钦点次数的连天重试”。该措施的首先个参数是三个字符串,表示要施行的逻辑的名号,这么些名称没什么实际用场,只是援助大家分别当前在执行的逻辑是哪段逻辑,该名称会在记录日志时行使,方便大家后续通过日记剖判到底是何地出错了,可能重试过了;然后第二个参数表示要重试的某部委托;当然,因为大家要了解该信托内部的逻辑是还是不是管理成功,所以须要三个布尔类型的再次回到值;倒数参数则是钦点须求接二连三重试多少次,上边的身体力行代码表示:先举行钦赐逻辑,假设失利,则总是重试3次;所以,借使老是都战败,也正是总共会试行4次;下面的代码应该简单驾驭,就不多深入分析了;

咱俩先来想转手,大家愿意有何的重试成效。以“event长久化时退步”为例,假若这一步退步,大家希望登时对那些手续重试三回,例如3次,假若3次内成功了,那就瓜熟蒂落了,继续往下做下边包车型客车逻辑;借使依旧败诉了呢?大家难道就放弃了吧?实际上,大家不可能舍弃,因为日常只要事件持久化战败很有相当大或然是由于互连网难点或eventstore有怎么样难点,何况大器晚成旦大家就这么吐弃了,那很可能所有事情逻辑的流水生产线就被暂停了,这样就不可能造成数量的末尾风华正茂致性了。所以,因为这种有的时候的IO难点产生的退步,大家不能忽视就甩掉重试,应该在品尝五遍重试仍战败时接收供给的花招,能够在IO复苏时,能自动再管理该新闻;可是大家又不能够动用当前线程无界定的重试下去,因为这么就导致不能够管理任何的音信了;所以我们当然就能够想到:大家理应在信息重试五回仍败北时,将该音信归入二个特意的重试队列,然后有其它多个独门的线程会依期从该队列收取要重试的消息,然后重试那个音信;那样,当IO恢复时,那些音讯就能便捷被成功拍卖了;

上边包车型地铁代码是在一个command推行到位后对于发出的事件,框架要提交该聚合根爆发的风浪;通过BuildEvents方法拿到聚合根上发出的平地风波,然后我们接下去是尝试将该事件发送到二个事件队列,可是因为该事件队列在信息入队时会长久化音讯,相当于会有IO操作,所以就也许停业,所以大家先品尝实践一次,假如失利则即时延续尝试重试3次,若是那4次中恣意一回中标了,则做成功的逻辑,上例是调用FinishExecution方法;假诺这4次都未果,则跻身else的逻辑,即放入队列准期重试,然而大家期待在放入队列重试时只要某叁回重试成功了也要求确定保证能调用FinishExecution方法,所以也定义了三个回调的ActionInfo。最终,为了尽大概让各种ActionInfo所供给的参数消息语义分明,避免语言层面包车型客车闭包等繁缛难知晓的标题,我们尽量将ActionInfo中的Action所要求的参数信息明确的设置到ActionInfo上,实际不是从外层的函数中拿,从外围的函数中拿,如果再十二线程时,轻巧并发难题,而且也便于孳生代码修改招致的不便检查出来的闭包难题;当然,这里,我们能够见到自家利用了无名对象,小编是偷懒了,假若指望质量更加高,则足以彰显定义一个类来封装要求的参数音讯;

        private void CommitAggregate(AggregateRoot dirtyAggregate, ICommand command, IMessageQueue<ICommand> queue)
        {
            var eventStream = BuildEvents(dirtyAggregate, command);

            if (_retryService.TryAction("TrySendEvent", () => TrySendEvent(eventStream), 3))
            {
                FinishExecution(command, queue);
            }
            else
            {
                _retryService.RetryInQueue(
                    new ActionInfo(
                        "TrySendEvent",
                        (obj) => TrySendEvent(obj as EventStream),
                        eventStream,
                        new ActionInfo(
                            "SendEventSuccessAction",
                            (obj) =>
                            {
                                var data = obj as dynamic;
                                var currentCommand = data.Command as ICommand;
                                var currentQueue = data.Queue as IMessageQueue<ICommand>;
                                FinishExecution(currentCommand, currentQueue);
                                return true;
                            },
                            new { Command = command, Queue = queue },
                            null)));
            }
        }

volley源码学习时时app平台注册网站。接下去深入分析一下第贰个必要“将某段逻辑归入重试队列依期重试”:

上边大家看一个轻便易行的调用示例吧:

本文通过代码加思路的法门大约介绍了enode框架中有关音信重试的规划思路。但是本人一直不介绍enode中到底什么点会用到重试机制,有众多,起码五两个地点吗。但本人以为那不是首要了,入眼是地方作者深入分析的有个别思路,具体需求重试的场馆是偏业务属性了,涉及到enode框架中从command开头拍卖到结尾event被发布到query端的漫天进程中的每种重点的环节。小编以为通过本文的剖判,能够援助想看代码的爱侣更便于明白enode中有关重试方面包车型客车代码,那样就够了;关于重试方面,还应该有三个点并没有说,就是command的重试,关于那一点,和本文提到的重试有一点点不一致,作者希图专门写黄金时代篇小说介绍一下吗。

这这种特意的重试队列须求多少个呢?理论上大家可以为种种须要重试的点都规划一个重试队列来支撑,不过这么一方面过于复杂,而且线程多了还大概会耳濡目染系统的品质;所以大家必要衡量一下,只对同叁个阶段中要做的保有的事体设计一个重试队列,该阶段中那一个要做的政工中有别的一步败北,就都放到该阶段对应的重试队列里;

  1. 本身用了BlockingCollection,那是一个支撑并发且协理窒碍的依据publish-consumer格局的集合,而且这里,该集结内部封装了ConcurrentQueue,所以,他也是一个行列;那样设计的功利是,在队列中绝非成分的时候,线程会被打断,进而不会浪费财富;唯有当队列中有成分时,才会在当天timer周期到来时,能够从队列抽出要重试的ActionInfo,然后实行重试操作。
  2. volley源码学习时时app平台注册网站。Timer的周期暗许设置为5秒,那么,大家为了制止同不时刻,有多少个ActionInfo在被同临时间管理,小编加了叁个标志位_looping,当当前有ActionIno正在被管理时,则该标识位为true,否则为false。通过该标记位,我们能保障队列中的成分会多个个按梯次被管理,那样就不会混杂,以致莫明其妙的bug现身;
  3. 从地点的RetryAction方法中,我们可以看见,当当前的ActionInfo管理成功后,固然下一个ActionInfo存在(Next属性不等于空),则把下一个ActionInfo归入重试队列,等待被拍卖;通过那样的设计,我们能够以特别统生龙活虎的不二等秘书诀重试顾客愿意重试的ActionInfo以致那几个ActionInfo重试成功后的回调ActionInfo。其它,假若当前ActionInfo推行倒闭,则照旧将最近ActionInfo再放回队列,继续重试;

说明:

轻松易行说美素佳儿(Friso卡塔尔(Nutrilon卡塔尔下:

public class DefaultRetryService : IRetryService
    {
        private const long DefaultPeriod = 5000;
        private BlockingCollection<ActionInfo> _retryQueue = new BlockingCollection<ActionInfo>(new ConcurrentQueue<ActionInfo>());
        private Timer _timer;
        private ILogger _logger;
        private bool _looping;

        public DefaultRetryService(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.Create(GetType().Name);
            _timer = new Timer(Loop, null, 0, DefaultPeriod);
        }

        public void Initialize(long period)
        {
            _timer.Change(0, period);
        }
        public void RetryInQueue(ActionInfo actionInfo)
        {
            _retryQueue.Add(actionInfo);
        }

        private void Loop(object data)
        {
            try
            {
                if (!_looping)
                {
                    _looping = true;
                    RetryAction();
                    _looping = false;
                }
            }
            catch (Exception ex)
            {
                _logger.Error("Exception raised when retring action.", ex);
                _looping = false;
            }
        }
        private void RetryAction()
        {
            var actionInfo = _retryQueue.Take();
            if (actionInfo != null)
            {
                var success = false;
                try
                {
                    success = actionInfo.Action(actionInfo.Data);
                    _logger.InfoFormat("Executed action {0} from queue.", actionInfo.Name);
                }
                catch (Exception ex)
                {
                    _logger.Error(string.Format("Exception raised when executing action {0}.", actionInfo.Name), ex);
                }
                finally
                {
                    if (success)
                    {
                        if (actionInfo.Next != null)
                        {
                            _retryQueue.Add(actionInfo.Next);
                        }
                    }
                    else
                    {
                        _retryQueue.Add(actionInfo);
                    }
                }
            }
        }
    }

上边谈到,要是音讯管理失利要再重试,其实是叁个不会细小的答问。因为比方一个音信在管理的时候一齐有5个步骤,倘若前2步都成功,不过第3步战败了,那重试的时候,前2步还须求再试行呢?笔者的主见是,在能办成的事态下,就无须再做前2步操作了,而是平昔从第3步初步重试。所以说,这种做法相当于是“哪个地方跌倒,哪里继续”;

    if (_retryService.TryAction("TrySendEvent", () => TrySendEvent(eventStream), 3))
    {
        FinishExecution(command, queue);
    }

其余三个标题,那这种非常的重试队列必要扶植消息长久化吗?不用,我们只供给内存队列就能够了,因为当贰个音信还未有被全然成功拍卖前,是不会从message store删除的;所以,尽管机器重启了,该音讯还是能够在该机器重启后被拍卖的;而当该机器没重启时,该特地重试的内部存款和储蓄器队列会不断地以单独的线程准时重试该音讯;

        public bool TryAction(string actionName, Func<bool> action, int maxRetryCount)
        {
            return TryRecursively(actionName, (x, y, z) => action(), 0, maxRetryCount);
        }
        private bool TryRecursively(string actionName, Func<string, int, int, bool> action, int retriedCount, int maxRetryCount)
        {
            var success = false;
            try
            {
                success = action(actionName, retriedCount, maxRetryCount);
                if (retriedCount > 0)
                {
                    _logger.InfoFormat("Retried action {0} for {1} times.", actionName, retriedCount);
                }
            }
            catch (Exception ex)
            {
                _logger.Error(string.Format("Exception raised when tring action {0}, retrid count {1}.", actionName, retriedCount), ex);
            }

            if (success)
            {
                return true;
            }
            else if (retriedCount < maxRetryCount)
            {
                return TryRecursively(actionName, action, retriedCount   1, maxRetryCount);
            }
            else
            {
                return false;
            }
        }

这就是说怎么重试呢?

本文由时时app平台注册网站发布于编程知识,转载请注明出处:volley源码学习时时app平台注册网站

关键词: