码迷,mamicode.com
首页 > Web开发 > 详细

HttpClientHelper

时间:2017-08-22 19:43:40      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:void   class   blog   dia   amp   cell   except   最大   syn   

//-------------------------------------------------------------------------- 
//  
//  Copyright (c) BUSHUOSX.  All rights reserved.  
//  
//  File: HttpClientHelper.cs 
//
//  Version:1.0.0.1
//
//  Datetime:20170815
// 
//-------------------------------------------------------------------------- 

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace BUSHUOSX.HTTP
{
    class HttpClientHelper
    {
        /// <summary>
        /// http task state
        /// </summary>
        class TASKSTATE
        {
            public object userState;
            public HttpClient httpClient;
            public TASKSTATE(object state,HttpClient client)
            {
                userState = state;
                httpClient = client;
            }
        }

        /// <summary>
        /// 不为Null时,任意http请求完成、取消、失败时回调
        /// </summary>
        Action<Task, object> _callbackOnAnyTaskComplited;
        /// <summary>
        /// 并发控制信号
        /// </summary>
        SemaphoreSlim _semaphore;
        /// <summary>
        /// 工作标记
        /// </summary>
        bool _isWorking;
        /// <summary>
        /// 共用httpclient。为Null时,为每个http请求new httpclient。
        /// </summary>
        HttpClient _httpClient;
        /// <summary>
        /// http request task 记录
        /// </summary>
        readonly ConcurrentDictionary<Task, TASKSTATE> _workingTasks = new ConcurrentDictionary<Task, TASKSTATE>();
        /// <summary>
        /// 启动的task计数。
        /// </summary>
        long _workingCount;//原子操作
        /// <summary>
        /// 任务取消标记
        /// </summary>
        CancellationTokenSource _cancelSource;
        /// <summary>
        /// 工作器监视线程
        /// </summary>
        Task _worker;
        /// <summary>
        /// 禁止自动启动任务
        /// </summary>
        bool _autoStartWorking = true;
        /// <summary>
        /// Http Request最大并发数
        /// </summary>
        public int MaxConcurrencyLevel { get; }
        /// <summary>
        /// Http Request超时设置,毫秒。小于等于0时,使用默认值。
        /// </summary>
        public int TimeoutMillisecond { get; }


        /// <summary>
        /// 可控制并发数的HttpClient
        /// </summary>
        /// <param name="concurrencyLevel">Http Request最大并发数</param>
        /// <param name="callbackOnAnyTaskComplited">不为Null时,任意http请求完成、取消、失败时回调。注意:如果使用,请保证方法的线程安全。</param>
        /// <param name="timeoutMillisecond">Http Request超时设置,毫秒。小于等于0时,使用默认值。</param>
        /// <param name="httpClient">留空则每个任务使用一个新的HttpClient对象</param>
        public HttpClientHelper(int concurrencyLevel, Action<Task, object> callbackOnAnyTaskComplited = null, int timeoutMillisecond = 0, HttpClient httpClient = null)
        {
            if (concurrencyLevel < 1)
            {
                throw new ArgumentOutOfRangeException("concurrencyLevel < 1");
            }
            MaxConcurrencyLevel = concurrencyLevel;
            _callbackOnAnyTaskComplited = callbackOnAnyTaskComplited;
            TimeoutMillisecond = timeoutMillisecond;
            _httpClient = httpClient;
        }

        /// <summary>
        /// 返回已发出的http请求数
        /// </summary>
        /// <returns></returns>
        public int GetWorkingTasksCount()
        {
            return _workingTasks.Count;
        }

        //public KeyValuePair<Task, object>[] GetTasks()
        //{
        //    return _workingTasks.ToArray();
        //}

        /// <summary>
        /// 停止工作器
        /// </summary>
        /// <param name="cancelPendingRequests">取消已发起的所有http请求</param>
        public void Stop(bool cancelPendingRequests = false)
        {
            _autoStartWorking = false;
            notifyEndWorker();
            _cancelSource.Cancel();
            if (_httpClient != null)
            {
                _httpClient.CancelPendingRequests();
            }
            else
            {
                foreach (var item in _workingTasks)
                {
                    if (item.Value.httpClient != null)
                    {
                        item.Value.httpClient.CancelPendingRequests();
                    }
                }
            }
            _workingTasks.Clear();
        }

        /// <summary>
        /// 重新启动工作器。stop之后使用。
        /// </summary>
        public void ReStart()
        {
            _autoStartWorking = true;
        }

        /// <summary>
        /// 阻塞直到所有http任务完成。
        /// </summary>
        public void WaitWorkComplited()
        {
            if (_worker != null)
            {
                _worker.Wait();
            }
        }

        /// <summary>
        /// 通过 http client 获取 string。超过并发数时会阻塞,直到有http request完成。
        /// </summary>
        /// <param name="address">http url string</param>
        /// <param name="state">用户状态。用于callbackOnAnyTaskComplited回调。</param>
        /// <returns></returns>
        public Task<string> GetString(string address, object state = null)
        {
            Interlocked.Increment(ref _workingCount);

            notifyStartWorker();

            if (!_isWorking)
            {
                Interlocked.Decrement(ref _workingCount);
                return null;
            }

            _semaphore.Wait(_cancelSource.Token);
            TASKSTATE tstate = new TASKSTATE(state, null);
            HttpClient client;
            try
            {
                if (_httpClient != null)
                {
                    client = _httpClient;
                }
                else
                {
                    client = new HttpClient();
                    if (TimeoutMillisecond > 0)
                    {
                        client.Timeout = TimeSpan.FromMilliseconds(TimeoutMillisecond);
                    }
                    tstate.httpClient = client;
                }
                var t = client.GetStringAsync(address);
                _workingTasks[t] = tstate;
                t.ContinueWith(anyTaskComplited, 
                    _cancelSource.Token,
                    TaskContinuationOptions.OnlyOnRanToCompletion & TaskContinuationOptions.NotOnRanToCompletion,
                    TaskScheduler.Current);
                return t;
            }
            catch (HttpRequestException e)
            {
                Interlocked.Decrement(ref _workingCount);
                _semaphore.Release();
                throw new HttpRequestException(e.Message, e.InnerException);
            }
        }

        /// <summary>
        /// 任意http task完成时回调
        /// </summary>
        /// <param name="task"></param>
        private void anyTaskComplited(Task task)
        {
            TASKSTATE tstate;
            _workingTasks.TryRemove(task, out tstate);
            //Debug.Assert(tstate.userState == state);

            _callbackOnAnyTaskComplited?.Invoke(task, tstate.userState);
            _semaphore.Release();

            Interlocked.Decrement(ref _workingCount);
        }

        /// <summary>
        /// 工作器初始化
        /// </summary>
        private void notifyStartWorker()
        {
            if (_isWorking) return;

            if (!_autoStartWorking) return;

            //初始化
            _isWorking = true;
            Debug.WriteLine("httpClientWorker启动……");
            _semaphore = new SemaphoreSlim(MaxConcurrencyLevel, MaxConcurrencyLevel);
            _cancelSource = new CancellationTokenSource();
            //_workingCount = 0;

            if (_httpClient != null && TimeoutMillisecond > 0)
            {
                _httpClient.Timeout = TimeSpan.FromMilliseconds(TimeoutMillisecond);
            }

            _worker = Task.Run(new Action(workerMonitor), _cancelSource.Token);

            _worker.ContinueWith(a=>{ notifyEndWorker(); },TaskContinuationOptions.NotOnRanToCompletion & TaskContinuationOptions.OnlyOnRanToCompletion);
        }

        /// <summary>
        /// 工作器结束清理工作
        /// </summary>
        private void notifyEndWorker()
        {
            if (_isWorking)
            {
                _isWorking = false;
                Debug.WriteLine("httpClientWorker结束……");
            }
        }

        /// <summary>
        /// 任务工作器监视器
        /// </summary>
        private void workerMonitor()
        {
            do
            {
                Thread.Sleep(1000);
            } while (_workingTasks.Count > 0 || Interlocked.Read(ref _workingCount) > 0);
        }

    }
}

 

HttpClientHelper

标签:void   class   blog   dia   amp   cell   except   最大   syn   

原文地址:http://www.cnblogs.com/bushuosx/p/7413175.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!