原文地址:C#中HttpClient使用注意:预热与长连接 最近在测试一个第三方API,准备集成在我们的网站应用中。API的调用使用的是.NET中的HttpClient,由于这个API会在关键业务中用到,对调用API的整体响应速度有严格要求,所以对HttpClient有了格外的关注。 开始测试的时候,只在客户端通过HttpClient用PostAsync发了一个http post请求。测试时发现,从创建HttpClient实例,到发出请求,到读取到服务器的响应数据总耗时在2s左右,而且多次测试都是这样。2s的响应速度当然是无法让人接受的,我们希望至少控制在100ms以内。于是开始追查这个问题的原因。 在API的返回数据中包含了该请求在服务端执行的耗时,这个耗时都在20ms以内,问题与服务端API无关。于是把怀疑点放到了网络延迟上,但ping服务器的响应时间都在10ms左右,网络延迟的可能性也不大。 当我们正准备换一个网络环境进行测试时,突然想到,我们的测试方式有些问题。我们只通过HttpClient发了一个PostAsync请求,假如HttpClient在第一次调用时存在某种预热机制(比如在EF中就有这样的机制),现在2s的总耗时可能大多消耗在HttpClient的预热上。 于是修改测试代码,将调用由1次改为100次,然后恍然大悟地发现——只有第1次是2s,接下来的99次都在100ms以内。果然是HttpClient的某种预热机制在搞鬼! 既然知道了是HttpClient预热机制的原因,那我们可以帮HttpClient进行热身,减少第一次请求的耗时。我们尝试了一种预热方式,在正式发http post请求之前,先发一个http head请求,代码如下:
1 2 3 4 |
_httpClient.SendAsync(new HttpRequestMessage { Method = new HttpMethod("HEAD"), RequestUri = new Uri(BASE_ADDRESS + "/") }) .Result.EnsureSuccessStatusCode(); |
经测试,通过这种热身方法,可以将第一次请求的耗时由2s左右降到1s以内(测试结果是700多ms)。 在知道第1次HttpClient请求耗时2s的真相之后,我们将目光转向了剩下的99次耗时100ms以内的请求,发现绝大部分请求都在50ms以上。有没有可能将之降至50ms以下?而且,之前一直有这样的纠结:每次调用是不是一定要对HttpClient进行Dispose()?是不是要将HttpClient单例或者静态化(声明为静态变量)?借此机会一起研究一下。 在HttpClient的背后,有一个对请求响应速度有着不容忽视影响的东东——TCP连接。一个HttpClient实例会关联一个TCP连接,在对HttpClient进行Dispose时,会关闭TCP连接(我们用Wireshark进行网络抓包也验证了这一点)。 在之前的测试中,我们每次用HttpClient发请求时,都是新建一个HttpClient实例,用完就对它进行Dispose,代码如下:
1 2 3 4 |
using (var httpClient = new HttpClient() { BaseAddress = new Uri(BASE_ADDRESS) }) { httpClient.PostAsync("/", new FormUrlEncodedContent(parameters)); } |
所以每次请求时都要经历新建TCP连接->传数据->关闭连接(也就是通常所说的短连接),而且雪上加霜的是请求用的是https,建立TCP连接时还需要一个基于公私钥加解密的key exchange过程:Client Hello -> Server Hello -> Certificate -> Client Key Exchange -> New Session Ticket。 如果我们想将请求响应时间降至50ms以下,就必须从这个地方下手——重用TCP连接(也就是通常所说的长连接)。要实现长连接,首先需要的就是在HttpClient第1次请求后不关闭TCP连接(不调用Dispose方法);而要让后续的请求继续使用这个未关闭的TCP连接,我们必须要使用同一个HttpClient实例;而要使用同一个HttpClient实例,就得实现HttpClient的单例或者静态化。之前的3 个问题,由于要解决第1个问题,后2个问题变成了别无选择。 为了实现长连接,我们将HttpClient的调用代码改为如下的样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class HttpClientTest { private static readonly HttpClient _httpClient; static HttpClientTest() { _httpClient = new HttpClient() { BaseAddress = new Uri(BASE_ADDRESS) }; //帮HttpClient热身 _httpClient.SendAsync(new HttpRequestMessage { Method = new HttpMethod("HEAD"), RequestUri = new Uri(BASE_ADDRESS + "/") }) .Result.EnsureSuccessStatusCode(); } public async Task<string> PostAsync() { var response = await _httpClient.PostAsync("/", new FormUrlEncodedContent(parameters)); return await response.Content.ReadAsStringAsync(); } } |
然后测试一下请求响应时间:
1 2 3 4 5 6 7 8 9 10 11 |
Elapsed:750ms Elapsed:31ms Elapsed:30ms Elapsed:43ms Elapsed:27ms Elapsed:29ms Elapsed:28ms Elapsed:35ms Elapsed:36ms Elapsed:31ms .... |
除了第1次请求,接下来的99次请求绝大多数都在50ms以内。TCP长连接的效果必须的! 通过Wireshak抓包也验证了长连接的效果: 这时,你也许会产生这样的疑问:将HttpClient声明为静态变量,会不会存在线程安全问题?我们当时也有这样的疑问,后来在stackoverflow上找到了答案:
1 2 3 4 5 6 7 8 9 10 |
As per the comments below (thanks @ischell), the following instance methods are thread safe (all async): CancelPendingRequests DeleteAsync GetAsync GetByteArrayAsync GetStreamAsync GetStringAsync PostAsync PutAsync SendAsync |
HttpClient的所有异步方法都是线程安全的,放心使用。 到这里,HttpClient的问题是不是可以完美收官了?。。。稍等,还有一个问题。 客户端虽然保持着TCP连接,但TCP连接是两口子的事,服务器端呢?你不告诉服务器,服务器怎么知道你要一直保持TCP连接呢?对于客户端,保持TCP连接的开销不大;但是对于服务器,则完全不一样的,如果默认都保持TCP连接,那可是要保持成千上万客户端的连接啊。所以,一般的Web服务器都会根据客户端的诉求来决定是否保持TCP连接,这就是keep-alive存在的理由。 所以,我们还要给HttpClient增加一个Connection:keep-alive的请求头,代码如下:
1 |
_httpClient.DefaultRequestHeaders.Connection.Add("keep-alive"); |
现在终于可以收官了。但是肯定不完美,分享的只是解决问题的过程。 from:https://www.cnblogs.com/JustYong/p/5872296.html
View Details
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
using Newtonsoft.Json; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; namespace w3cnet.Utils { /// <summary> /// HttpClient工具类 /// </summary> public class HttpClientUtil { /// <summary> /// GET /// </summary> /// <param name="url"></param> /// <param name="statusCode"></param> /// <returns></returns> public static string Get(string url, out string statusCode) { if (url.StartsWith("https")) ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = httpClient.GetAsync(url).Result; statusCode = response.StatusCode.ToString(); if (response.IsSuccessStatusCode) { string result = response.Content.ReadAsStringAsync().Result; return result; } return string.Empty; } /// <summary> /// GET /// </summary> /// <typeparam name="T"></typeparam> /// <param name="url"></param> /// <returns>指定对象</returns> public static T Get<T>(string url) where T : class, new() { if (url.StartsWith("https")) ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = httpClient.GetAsync(url).Result; T result = default(T); if (response.IsSuccessStatusCode) { var t = response.Content.ReadAsStringAsync(); var s = t.Result; result = JsonConvert.DeserializeObject<T>(s); } return result; } /// <summary> /// POST /// </summary> /// <param name="url"></param> /// <param name="postData"></param> /// <param name="statusCode"></param> /// <returns></returns> public static string Post(string url, string postData, out string statusCode) { if (url.StartsWith("https")) ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; var httpContent = new StringContent(postData); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json") { CharSet = "utf-8" }; var httpClient = new HttpClient(); var response = httpClient.PostAsync(url, httpContent).Result; statusCode = response.StatusCode.ToString(); if (response.IsSuccessStatusCode) { string result = response.Content.ReadAsStringAsync().Result; return result; } return null; } /// <summary> /// POST /// </summary> /// <typeparam name="T"></typeparam> /// <param name="url"></param> /// <param name="postData"></param> /// <returns>指定对象</returns> public static T Post<T>(string url, string postData) where T : class, new() { if (url.StartsWith("https")) ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; var httpContent = new StringContent(postData); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json") { CharSet = "utf-8" }; var httpClient = new HttpClient(); var response = httpClient.PostAsync(url, httpContent).Result; T result = default(T); if (response.IsSuccessStatusCode) { Task<string> t = response.Content.ReadAsStringAsync(); string s = t.Result; result = JsonConvert.DeserializeObject<T>(s); } return result; } } } |
from:https://www.cnblogs.com/louby/p/8021527.html
View Details利用HttpClient进行Http请求,基于此,简单地封装了下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Text; namespace ConsoleApplication2 { public class HTTPClientHelper { private static readonly HttpClient HttpClient; static HTTPClientHelper() { var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.None }; HttpClient = new HttpClient(handler); } /// <summary> /// get请求,可以对请求头进行多项设置 /// </summary> /// <param name="paramArray"></param> /// <param name="url"></param> /// <returns></returns> public static string GetResponseByGet(List<KeyValuePair<string,string>> paramArray, string url) { string result = ""; var httpclient = HTTPClientHelper.HttpClient; url = url + "?" + BuildParam(paramArray); var response = httpclient.GetAsync(url).Result; if (response.IsSuccessStatusCode) { Stream myResponseStream = response.Content.ReadAsStreamAsync().Result; StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8")); result = myStreamReader.ReadToEnd(); myStreamReader.Close(); myResponseStream.Close(); } return result; } public static string GetResponseBySimpleGet(List<KeyValuePair<string,string>> paramArray, string url) { var httpclient = HTTPClientHelper.HttpClient; url = url + "?" + BuildParam(paramArray); var result = httpclient.GetStringAsync(url).Result; return result; } public static string HttpPostRequestAsync(string Url, List<KeyValuePair<string, string>> paramArray, string ContentType = "application/x-www-form-urlencoded") { string result = ""; var postData = BuildParam(paramArray); var data = Encoding.ASCII.GetBytes(postData); try { using (HttpClient http = new HttpClient()) { http.DefaultRequestHeaders.Add("User-Agent", @"Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"); http.DefaultRequestHeaders.Add("Accept", @"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); HttpResponseMessage message = null; using (Stream dataStream = new MemoryStream(data ?? new byte[0])) { using (HttpContent content = new StreamContent(dataStream)) { content.Headers.Add("Content-Type", ContentType); var task = http.PostAsync(Url, content); message = task.Result; } } if (message != null && message.StatusCode == System.Net.HttpStatusCode.OK) { using (message) { result = message.Content.ReadAsStringAsync().Result; } } } } catch (Exception ex) { Console.WriteLine(ex.Message); } return result; } private static string Encode(string content, Encoding encode = null) { if (encode == null) return content; return System.Web.HttpUtility.UrlEncode(content, Encoding.UTF8); } private static string BuildParam(List<KeyValuePair<string, string>> paramArray, Encoding encode = null) { string url = ""; if (encode == null) encode = Encoding.UTF8; if (paramArray != null && paramArray.Count > 0) { var parms = ""; foreach (var item in paramArray) { parms += string.Format("{0}={1}&", Encode(item.Key, encode), Encode(item.Value, encode)); } if (parms != "") { parms = parms.TrimEnd('&'); } url += parms; } return url; } } } |
有关更多的Http请求,请看这里:https://github.com/wangqiang3311/HttpRequestDemo from:http://www.cnblogs.com/wangqiang3311/p/8991214.html
View Details一、 System.Net.Http.HttpClient简介 System.Net.Http 是微软.net4.5中推出的HTTP 应用程序的编程接口, 微软称之为“现代化的 HTTP 编程接口”, 主要提供如下内容: 1. 用户通过 HTTP 使用现代化的 Web Service 的客户端组件; 2. 能够同时在客户端与服务端同时使用的 HTTP 组件(比如处理 HTTP 标头和消息), 为客户端和服务端提供一致的编程模型。 个人看来是抄袭apache http client ,目前网上用的人好像不多,个人认为使用httpclient最大的好处是:不用自己管理cookie,只要负责写好请求即可。 由于网上资料不多,这里借登录博客园网站做个简单的总结其get和post请求的用法。 查看微软的api可以发现其属性方法:http://msdn.microsoft.com/zh-cn/library/system.net.http.httpclient.aspx 由其api可以看出如果想设置请求头只需要在DefaultRequestHeaders里进行设置 创建httpcliet可以直接new HttpClient() 发送请求可以按发送方式分别调用其方法,如get调用GetAsync(Uri),post调用PostAsync(Uri, HttpContent),其它依此类推。。。 二、实例(模拟post登录博客园) 首先,需要说明的是,本实例环境是win7 64位+vs 2013+ .net 4.5框架。 1.使用vs2013新建一个控制台程序,或者窗体程序,如下图所示: 2.必须引入System.Net.Http框架,否则将不能使用httpclient 3.实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace ClassLibrary1 { public class Class1 { private static String dir = @"C:\work\"; /// <summary> /// 写文件到本地 /// </summary> /// <param name="fileName"></param> /// <param name="html"></param> public static void Write(string fileName, string html) { try { FileStream fs = new FileStream(dir + fileName, FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.Default); sw.Write(html); sw.Close(); fs.Close(); }catch(Exception ex){ Console.WriteLine(ex.StackTrace); } } /// <summary> /// 写文件到本地 /// </summary> /// <param name="fileName"></param> /// <param name="html"></param> public static void Write(string fileName, byte[] html) { try { File.WriteAllBytes(dir + fileName, html); } catch (Exception ex) { Console.WriteLine(ex.StackTrace); } } /// <summary> /// 登录博客园 /// </summary> public static void LoginCnblogs() { HttpClient httpClient = new HttpClient(); httpClient.MaxResponseContentBufferSize = 256000; httpClient.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36"); String url = "http://passport.cnblogs.com/login.aspx"; HttpResponseMessage response = httpClient.GetAsync(new Uri(url)).Result; String result = response.Content.ReadAsStringAsync().Result; String username = "hi_amos"; String password = "密码"; do { String __EVENTVALIDATION = new Regex("id=\"__EVENTVALIDATION\" value=\"(.*?)\"").Match(result).Groups[1].Value; String __VIEWSTATE = new Regex("id=\"__VIEWSTATE\" value=\"(.*?)\"").Match(result).Groups[1].Value; String LBD_VCID_c_login_logincaptcha = new Regex("id=\"LBD_VCID_c_login_logincaptcha\" value=\"(.*?)\"").Match(result).Groups[1].Value; //图片验证码 url = "http://passport.cnblogs.com" + new Regex("id=\"c_login_logincaptcha_CaptchaImage\" src=\"(.*?)\"").Match(result).Groups[1].Value; response = httpClient.GetAsync(new Uri(url)).Result; Write("amosli.png", response.Content.ReadAsByteArrayAsync().Result); Console.WriteLine("输入图片验证码:"); String imgCode = "wupve";//验证码写到本地了,需要手动填写 imgCode = Console.ReadLine(); //开始登录 url = "http://passport.cnblogs.com/login.aspx"; List<KeyValuePair<String, String>> paramList = new List<KeyValuePair<String, String>>(); paramList.Add(new KeyValuePair<string, string>("__EVENTTARGET", "")); paramList.Add(new KeyValuePair<string, string>("__EVENTARGUMENT", "")); paramList.Add(new KeyValuePair<string, string>("__VIEWSTATE", __VIEWSTATE)); paramList.Add(new KeyValuePair<string, string>("__EVENTVALIDATION", __EVENTVALIDATION)); paramList.Add(new KeyValuePair<string, string>("tbUserName", username)); paramList.Add(new KeyValuePair<string, string>("tbPassword", password)); paramList.Add(new KeyValuePair<string, string>("LBD_VCID_c_login_logincaptcha", LBD_VCID_c_login_logincaptcha)); paramList.Add(new KeyValuePair<string, string>("LBD_BackWorkaround_c_login_logincaptcha", "1")); paramList.Add(new KeyValuePair<string, string>("CaptchaCodeTextBox", imgCode)); paramList.Add(new KeyValuePair<string, string>("btnLogin", "登 录")); paramList.Add(new KeyValuePair<string, string>("txtReturnUrl", "http://home.cnblogs.com/")); response = httpClient.PostAsync(new Uri(url), new FormUrlEncodedContent(paramList)).Result; result = response.Content.ReadAsStringAsync().Result; Write("myCnblogs.html",result); } while (result.Contains("验证码错误,麻烦您重新输入")); Console.WriteLine("登录成功!"); //用完要记得释放 httpClient.Dispose(); } public static void Main() { LoginCnblogs(); } } |
代码分析: 首先,从Main函数开始,调用LoginCnblogs方法; 其次,使用GET方法:
1 2 |
HttpResponseMessage response = httpClient.GetAsync(new Uri(url)).Result; String result = response.Content.ReadAsStringAsync().Result; |
再者,使用POST方法:
1 2 3 4 5 |
List<KeyValuePair<String, String>> paramList = new List<KeyValuePair<String, String>>(); paramList.Add(new KeyValuePair<string, string>("__EVENTTARGET", "")); .... response = httpClient.PostAsync(new Uri(url), new FormUrlEncodedContent(paramList)).Result; result = response.Content.ReadAsStringAsync().Result; |
最后,注意其返回值可以是string,也可以是byte[],和stream的方式,这里看你需要什么吧。 4.登录成功后的截图 1).使用浏览器登录后的截图: 2).使用Httpcliet登录后的截图: 总结,可以发现C#中HttpClient的用法和Java中非常相似,所以,说其抄袭确实不为过。 from:https://www.cnblogs.com/amosli/p/3918538.html
View Details一般有两种办法 第一种handler.UseCookies=true(默认为true),默认的会自己带上cookies,例如
1 2 3 4 5 6 7 8 9 10 11 12 |
var handler = new HttpClientHandler() { UseCookies = true }; var client = new HttpClient(handler);// { BaseAddress = baseAddress }; client.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0"); client.DefaultRequestHeaders.Add("Connection", "Keep-Alive"); client.DefaultRequestHeaders.Add("Keep-Alive", "timeout=600"); var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("email", "xxxx"), new KeyValuePair<string, string>("password", "xxxx"), }); var result = await client.PostAsync("https://www.xxxx.com/cp/login", content); result.EnsureSuccessStatusCode(); |
这种情况post请求登陆成功后,重定向到别的页面,也会自动带上cookies。如果把handler.UseCookies设置为false,登陆后重定向的话不会自动带上cookies,则又会跳转到登陆页面。 第二种设置 handler.UseCookies = false时,则需要手动给headers上加入cookies.
1 2 3 4 5 6 |
var handler = new HttpClientHandler() { UseCookies = false}; var client = new HttpClient(handler);// { BaseAddress = baseAddress }; var message = new HttpRequestMessage(HttpMethod.Get, url); message.Headers.Add("Cookie", "session_id=7258abbd1544b6c530a9f406d3e600239bd788fb"); var result = await client.SendAsync(message); result.EnsureSuccessStatusCode(); |
如果使用场景是:抓取需要登陆后才能看到的网页数据,建议使用第一种,不需要设置任何cookies,httpclient会自动把登陆后的cookies放置到后面的请求中。 原贴 : http://www.cnblogs.com/xiaozhu39505/p/8033108.html from:https://www.cnblogs.com/refuge/p/8060142.html
View Details有了上一篇《.NET Core玩转机器学习》打基础,这一次我们以纽约出租车费的预测做为新的场景案例,来体验一下回归模型。 场景概述 我们的目标是预测纽约的出租车费,乍一看似乎仅仅取决于行程的距离和时长,然而纽约的出租车供应商对其他因素,如额外的乘客数、信用卡而不是现金支付等,会综合考虑而收取不同数额的费用。纽约市官方给出了一份样本数据。 确定策略 为了能够预测出租车费,我们选择通过机器学习建立一个回归模型。使用官方提供的真实数据进行拟合,在训练模型的过程中确定真正能影响出租车费的决定性特征。在获得模型后,对模型进行评估验证,如果偏差在接受的范围内,就以这个模型来对新的数据进行预测。 解决方案 创建项目 看过上一篇文章的读者,就比较轻车熟路了,推荐使用Visual Studio 2017创建一个.NET Core的控制台应用程序项目,命名为TaxiFarePrediction。使用NuGet包管理工具添加对Microsoft.ML的引用。 准备数据集 下载训练数据集taxi-fare-train.csv和验证数据集taxi-fare-test.csv,数据集的内容类似为:
1 2 3 4 5 6 7 |
vendor_id,rate_code,passenger_count,trip_time_in_secs,trip_distance,payment_type,fare_amount VTS,1,1,1140,3.75,CRD,15.5 VTS,1,1,480,2.72,CRD,10.0 VTS,1,1,1680,7.8,CSH,26.5 VTS,1,1,600,4.73,CSH,14.5 VTS,1,1,600,2.18,CRD,9.5 ... |
对字段简单说明一下: 字段名 含义 说明 vendor_id 供应商编号 特征值 rate_code 比率码 特征值 passenger_count 乘客人数 特征值 trip_time_in_secs 行程时长 特征值 trip_distance 行程距离 特征值 payment_type 支付类型 特征值 fare_amount 费用 目标值 在项目中添加一个Data目录,将两份数据集复制到该目录下,对文件属性设置“复制到输出目录”。 定义数据类型和路径 首先声明相关的包引用。
1 2 3 4 5 6 7 8 9 |
using System; using Microsoft.ML.Models; using Microsoft.ML.Runtime; using Microsoft.ML.Runtime.Api; using Microsoft.ML.Trainers; using Microsoft.ML.Transforms; using System.Collections.Generic; using System.Linq; using Microsoft.ML; |
在Main函数的上方定义一些使用到的常量。
1 2 3 4 |
const string DataPath = @".\Data\taxi-fare-train.csv"; const string TestDataPath = @".\Data\taxi-fare-test.csv"; const string ModelPath = @".\Models\Model.zip"; const string ModelDirectory = @".\Models"; |
接下来定义一些使用到的数据类型,以及和数据集中每一行的位置对应关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public class TaxiTrip { [Column(ordinal: "0")] public string vendor_id; [Column(ordinal: "1")] public string rate_code; [Column(ordinal: "2")] public float passenger_count; [Column(ordinal: "3")] public float trip_time_in_secs; [Column(ordinal: "4")] public float trip_distance; [Column(ordinal: "5")] public string payment_type; [Column(ordinal: "6")] public float fare_amount; } public class TaxiTripFarePrediction { [ColumnName("Score")] public float fare_amount; } static class TestTrips { internal static readonly TaxiTrip Trip1 = new TaxiTrip { vendor_id = "VTS", rate_code = "1", passenger_count = 1, trip_distance = 10.33f, payment_type = "CSH", fare_amount = 0 // predict it. actual = 29.5 }; } |
创建处理过程 创建一个Train方法,定义对数据集的处理过程,随后声明一个模型接收训练后的结果,在返回前把模型保存到指定的位置,以便以后直接取出来使用不需要再重新训练。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public static async Task<PredictionModel<TaxiTrip, TaxiTripFarePrediction>> Train() { var pipeline = new LearningPipeline(); pipeline.Add(new TextLoader<TaxiTrip>(DataPath, useHeader: true, separator: ",")); pipeline.Add(new ColumnCopier(("fare_amount", "Label"))); pipeline.Add(new CategoricalOneHotVectorizer("vendor_id", "rate_code", "payment_type")); pipeline.Add(new ColumnConcatenator("Features", "vendor_id", "rate_code", "passenger_count", "trip_distance", "payment_type")); pipeline.Add(new FastTreeRegressor()); PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = pipeline.Train<TaxiTrip, TaxiTripFarePrediction>(); if (!Directory.Exists(ModelDirectory)) { Directory.CreateDirectory(ModelDirectory); } await model.WriteAsync(ModelPath); return model; } |
评估验证模型 创建一个Evaluate方法,对训练后的模型进行验证评估。
1 2 3 4 5 6 7 8 9 |
public static void Evaluate(PredictionModel<TaxiTrip, TaxiTripFarePrediction> model) { var testData = new TextLoader<TaxiTrip>(TestDataPath, useHeader: true, separator: ","); var evaluator = new RegressionEvaluator(); RegressionMetrics metrics = evaluator.Evaluate(model, testData); // Rms should be around 2.795276 Console.WriteLine("Rms=" + metrics.Rms); Console.WriteLine("RSquared = " + metrics.RSquared); } |
预测新数据 定义一个被用于预测的新数据,对于各个特征进行恰当地赋值。
1 2 3 4 5 6 7 8 9 10 11 12 |
static class TestTrips { internal static readonly TaxiTrip Trip1 = new TaxiTrip { vendor_id = "VTS", rate_code = "1", passenger_count = 1, trip_distance = 10.33f, payment_type = "CSH", fare_amount = 0 // predict it. actual = 29.5 }; } |
预测的方法很简单,prediction即预测的结果,从中打印出预测的费用和真实费用。
1 2 3 |
var prediction = model.Predict(TestTrips.Trip1); Console.WriteLine("Predicted fare: {0}, actual fare: 29.5", prediction.fare_amount); |
运行结果 到此我们完成了所有的步骤,关于这些代码的详细说明,可以参看《Tutorial: Use ML.NET to Predict New York Taxi Fares (Regression)》,只是要注意该文中的部分代码有误,由于使用到了C# 7.1的语法特性,本文的代码是经过了修正的。完整的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
using System; using Microsoft.ML.Models; using Microsoft.ML.Runtime; using Microsoft.ML.Runtime.Api; using Microsoft.ML.Trainers; using Microsoft.ML.Transforms; using System.Collections.Generic; using System.Linq; using Microsoft.ML; using System.Threading.Tasks; using System.IO; namespace TaxiFarePrediction { class Program { const string DataPath = @".\Data\taxi-fare-train.csv"; const string TestDataPath = @".\Data\taxi-fare-test.csv"; const string ModelPath = @".\Models\Model.zip"; const string ModelDirectory = @".\Models"; public class TaxiTrip { [Column(ordinal: "0")] public string vendor_id; [Column(ordinal: "1")] public string rate_code; [Column(ordinal: "2")] public float passenger_count; [Column(ordinal: "3")] public float trip_time_in_secs; [Column(ordinal: "4")] public float trip_distance; [Column(ordinal: "5")] public string payment_type; [Column(ordinal: "6")] public float fare_amount; } public class TaxiTripFarePrediction { [ColumnName("Score")] public float fare_amount; } static class TestTrips { internal static readonly TaxiTrip Trip1 = new TaxiTrip { vendor_id = "VTS", rate_code = "1", passenger_count = 1, trip_distance = 10.33f, payment_type = "CSH", fare_amount = 0 // predict it. actual = 29.5 }; } public static async Task<PredictionModel<TaxiTrip, TaxiTripFarePrediction>> Train() { var pipeline = new LearningPipeline(); pipeline.Add(new TextLoader<TaxiTrip>(DataPath, useHeader: true, separator: ",")); pipeline.Add(new ColumnCopier(("fare_amount", "Label"))); pipeline.Add(new CategoricalOneHotVectorizer("vendor_id", "rate_code", "payment_type")); pipeline.Add(new ColumnConcatenator("Features", "vendor_id", "rate_code", "passenger_count", "trip_distance", "payment_type")); pipeline.Add(new FastTreeRegressor()); PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = pipeline.Train<TaxiTrip, TaxiTripFarePrediction>(); if (!Directory.Exists(ModelDirectory)) { Directory.CreateDirectory(ModelDirectory); } await model.WriteAsync(ModelPath); return model; } public static void Evaluate(PredictionModel<TaxiTrip, TaxiTripFarePrediction> model) { var testData = new TextLoader<TaxiTrip>(TestDataPath, useHeader: true, separator: ","); var evaluator = new RegressionEvaluator(); RegressionMetrics metrics = evaluator.Evaluate(model, testData); // Rms should be around 2.795276 Console.WriteLine("Rms=" + metrics.Rms); Console.WriteLine("RSquared = " + metrics.RSquared); } static async Task Main(string[] args) { PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = await Train(); Evaluate(model); var prediction = model.Predict(TestTrips.Trip1); Console.WriteLine("Predicted fare: {0}, actual fare: 29.5", prediction.fare_amount); } } } |
不知不觉我们的ML.NET之旅又向前进了一步,是不是对于使用.NET Core进行机器学习解决现实生活中的问题更有兴趣了?请保持关注吧。 from:http://www.cnblogs.com/BeanHsiang/p/9017618.html
View DetailsML.NET 专门为.NET开发者提供了一套跨平台的开源的机器学习框架。 ML.NET支持.NET开发者不需要过度专业的机器学习开发经验,就能轻松地训练自己的模型,并且嵌入到自己的应用中。一切尽在.NET之中。ML.NET早期是由Microsoft Research开发,近十年来逐步集成到一个大体系中被众多Microsoft产品使用,如大家熟知的Windows、Bing、PowerPoint、Excel之类。 ML.NET的第一个预览版提供了分类器(如文本分类、情感分析)和回归(如价格预测)等实用的机器学习模型。第一版发布后在既有功能之上又新增了关于训练模型的.NET API,使用这些模型进行预测,就像框架中算法、转换、数据结构一类核心组件一样的开发体验。 接下来用个示例,一起进入快速上手的实践中来。 安装.NET SDK 为了创建一个.NET应用,首先下载 .NET SDK。 创建应用 使用如下命令初始化项目,创建一个控制台应用程序,目标为myApp:
1 2 |
dotnet new console -o myApp cd myApp |
安装ML.NET包 使用如下命令安装Microsoft.ML包:
1 |
dotnet add package Microsoft.ML |
下载数据集 假设我们使用机器学习来预测鸢尾花的类型,比如有setosa、versicolor、virginica三种,基于特征有四种:花瓣长度、花瓣宽度, 萼片长度、萼片宽度。 去UCI Machine Learning Repository: Iris Data Set下载一个现成的数据集,复制粘贴其中的数据到任何一个文本编辑器中,然后保存命名为iris-data.txt到myApp目录中。 粘贴完文本内容应该是如下格式,每一行表示不同鸢尾花的样本,数值的部分从左到右依次是萼片长度、萼片宽度、花瓣长度、花瓣宽度,最后是鸢尾花的类型。
1 2 3 4 |
5.1,3.5,1.4,0.2,Iris-setosa 4.9,3.0,1.4,0.2,Iris-setosa 4.7,3.2,1.3,0.2,Iris-setosa ... |
如果是使用了Visual Studio,将iris-data.txt添加至项目中,需要进行如下配置确保运行时数据集文件在输出的目录中。 编写代码 打开Program.cs文件,输入以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
using System; using Microsoft.ML; using Microsoft.ML.Runtime.Api; using Microsoft.ML.Trainers; using Microsoft.ML.Transforms; using Microsoft.ML.Data; namespace myApp { class Program { // STEP 1: Define your data structures // IrisData is used to provide training data, and as // input for prediction operations // - First 4 properties are inputs/features used to predict the label // - Label is what you are predicting, and is only set when training public class IrisData { [Column("0")] public float SepalLength; [Column("1")] public float SepalWidth; [Column("2")] public float PetalLength; [Column("3")] public float PetalWidth; [Column("4")] [ColumnName("Label")] public string Label; } // IrisPrediction is the result returned from prediction operations public class IrisPrediction { [ColumnName("PredictedLabel")] public string PredictedLabels; } static void Main(string[] args) { // STEP 2: Create a pipeline and load your data var pipeline = new LearningPipeline(); // If working in Visual Studio, make sure the 'Copy to Output Directory' // property of iris-data.txt is set to 'Copy always' string dataPath = "iris.data.txt"; pipeline.Add(new TextLoader(dataPath).CreateFrom<IrisData>(separator:',')); // STEP 3: Transform your data // Assign numeric values to text in the "Label" column, because only // numbers can be processed during model training pipeline.Add(new Dictionarizer("Label")); // Puts all features into a vector pipeline.Add(new ColumnConcatenator("Features", "SepalLength", "SepalWidth", "PetalLength", "PetalWidth")); // STEP 4: Add learner // Add a learning algorithm to the pipeline. // This is a classification scenario (What type of iris is this?) pipeline.Add(new StochasticDualCoordinateAscentClassifier()); // Convert the Label back into original text (after converting to number in step 3) pipeline.Add(new PredictedLabelColumnOriginalValueConverter() { PredictedLabelColumn = "PredictedLabel" }); // STEP 5: Train your model based on the data set var model = pipeline.Train<IrisData, IrisPrediction>(); // STEP 6: Use your model to make a prediction // You can change these numbers to test different predictions var prediction = model.Predict(new IrisData() { SepalLength = 3.3f, SepalWidth = 1.6f, PetalLength = 0.2f, PetalWidth = 5.1f, }); Console.WriteLine($"Predicted flower type is: {prediction.PredictedLabels}"); Console.ReadLine(); } } } |
运行应用 使用如下命令行运行程序:
1 |
dotnet run |
在最后一行将输出对花的预测结果,你可以修改传给Predict函数各种鸢尾花的特征值看看有什么不同的结果。 恭喜,你已经跨入使用ML.NET进行机器学习的门槛了! from:http://www.cnblogs.com/BeanHsiang/p/9010267.html
View Details最近公司有些小项目要用asp.net core尝试一下,局域网内建了内部的nuget服务。今天搞好.net core类库后发布过程一切顺利。 但在具体的项目中还原nuget包后直接提示: 警告 NU1701 已使用“.NETFramework,Version=v4.6.1”而不是项目目标框架“.NETCoreApp,Version=v2.1”还原包“DBCore 1.0.8”。此包可能与项目不完全兼容。 首先,怀疑是不是引用了.net core不支持的类库,然后把依赖全删除,不行。 然后,还是怀疑自己的类库有问题,就新建了一个空白的.net core类库,还原后还是提示。 然后,拿redis的nuget包发布上去,还原了一下,还是提示。 最后发现redis的类库是以目标框架作为文件夹的!!!然后在lib下增加了文件夹netcoreapp2.1,问题解决。
View Details一、前言 我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构。如果我们想使用以前版本中的HttpContext.Current的话,目前是不可用的,因为ASP.NET Core中是并没有这个API的。 当然我们也可以通过在Controller中访问HttpContext,但是某些情况下,这样使用起来还是不如HttpContext.Current方便。 二、IHttpContextAccessor 利用ASP.NET Core的依赖注入容器系统,通过请求获取IHttpContextAccessor接口,我们拥有模拟使用HttpContext.Current这样API的可能性。但是因为IHttpContextAccessor接口默认不是由依赖注入进行实例管理的。我们先要将它注册到ServiceCollection中:
1 2 3 4 5 6 |
public void ConfigureServices(IServiceCollection services) { services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); // Other code... } |
来模拟一个HttpContext.Current吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public static class HttpContext { public static IServiceProvider ServiceProvider; public static Microsoft.AspNetCore.Http.HttpContext Current { get { object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor)); Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext; return context; } } } |
其实说到HttpContext.Current就不得不提到多线程问题,在以前的ASP.NET版本中,如果遇到多线程环境很有可能HttpContext.Current为空的情况。说到这个问题以前就是有解决方案的,那就是CallContext; CallContext 是类似于方法调用的线程本地存储区的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽。数据槽不在其他逻辑线程上的调用上下文之间共享。当 CallContext 沿执行代码路径往返传播并且由该路径中的各个对象检查时,可将对象添加到其中。 当使用ASP.NET的时候,虽然线城池里的线程是复用的,但是CallContext并不在一个线程的多次使用中共享。因为CallContext是针对逻辑线程的TLS,线程池中被复用的线程是操作系统中的内核对象而不是托管对象。就像数据库连接池中保存的是非托管资源而不是托管资源。因此,先后执行的两个托管线程可能在底层复用了一个物理线程(内核对象),但并不能共享同一组CallContext数据槽。就像先后new的两个SqlConnection对象可能在底层使用了同一个物理连接,但是托管对象的属性已经被重置。 与此对照的是ThreadStaticAttribute,标记上这个特性的静态字段是往物理线程的TLS中保存数据(根据MSDN的描述猜的。具体没试过),因此如果两个托管线程对象内部使用的是同一个物理线程,则这个字段会复用(在两个线程通过这一字段访问同一个数据槽)。 在.NET Core中,也有新的API选择,AsyncLocal<T>。 三、HttpContextAccessor 我们来看看ASP.NET Core中的IHttpContextAccessor接口实现吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class HttpContextAccessor : IHttpContextAccessor { #if NET451 private static readonly string LogicalDataKey = "__HttpContext_Current__" + AppDomain.CurrentDomain.Id; public HttpContext HttpContext { get { var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle; return handle?.Unwrap() as HttpContext; } set { CallContext.LogicalSetData(LogicalDataKey, new ObjectHandle(value)); } } #elif NETSTANDARD1_3 private AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>(); public HttpContext HttpContext { get { return _httpContextCurrent.Value; } set { _httpContextCurrent.Value = value; } } #endif } |
最后我只能说在ASP.NET Core中是万物皆DI啊,其实Core中的实现早就为我们想好了这些功能,只是改变了使用方式。 GitHub:https://github.com/maxzhang1985/YOYOFx 如果觉还可以请Star下, 欢迎一起交流。 .NET Core 开源学习群: 214741894 from:https://www.cnblogs.com/maxzhang1985/p/6186455.html
View Details在我们用来获取客户端IP地址的传统ASP.NET中Request.UserHostAddress。但是这不适用于ASP.NET Core 2.0。我们需要一种不同的方式来检索HTTP请求信息。 1.在你的MVC控制器中定义一个变量
1 |
private IHttpContextAccessor _accessor; |
2. DI进入控制器的构造函数 public SomeController(IHttpContextAccessor accessor) { _accessor = accessor; } 3.回传IP地址
1 |
_accessor.HttpContext.Connection.RemoteIpAddress.ToString() |
这RemoteIpAddress是在类型IPAddress,而不是string。它包含了IPv4,IPv6等信息,它不像经典的ASP.NET,对我们来说更有用。 from:https://blog.csdn.net/yzj_xiaoyue/article/details/79200714
View Details