[译] ASP.NET Core 性能优化最佳实践

标签: dev | 发表时间:2020-09-19 00:00 | 作者:
出处:http://itindex.net/relian

本文提供了 ASP.NET Core 的性能最佳实践指南。

译文原文地址: https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1

积极利用缓存

这里有一篇文档在多个部分中讨论了如何积极利用缓存。 有关详细信息,请参阅︰ https://docs.microsoft.com/en-us/aspnet/core/performance/caching/response?view=aspnetcore-3.1.

了解代码中的热点路径

在本文档中, 代码热点路径定义为频繁调用的代码路径以及执行时间的大部分时间。 代码热点路径通常限制应用程序的扩展和性能,并在本文档的多个部分中进行讨论。

避免阻塞式调用

ASP.NET Core 应用程序应设计为同时处理许多请求。 异步 API 可以使用一个小池线程通过非阻塞式调用来处理数以千计的并发请求。 线程可以处理另一个请求,而不是等待长时间运行的同步任务完成。

ASP.NET Core 应用程序中的常见性能问题通常是由于那些本可以异步调用但却采用阻塞时调用而导致的。 同步阻塞会调用导致 线程池饥饿和响应时间降级。

不要:

  • 通过调用 Task.WaitTask.Result来阻止异步执行。
  • 在公共代码路径中加锁。 ASP.NET Core 应用程序应设计为并行运行代码,如此才能使得性能最佳。
  • 调用 Task.Run并立即 await 。 ASP.NET Core 本身已经是在线程池线程上运行应用程序代码了,因此这样调用 Task.Run 只会导致额外的不必要的线程池调度。 而且即使被调度的代码会阻止线程, Task.Run 也并不能避免这种情况,这样做没有意义。

:

  • 确保 代码热点路径全部异步化。
  • 如在进行调用数据读写、I/O 处理和长时间操作的 API 时,存在可用的异步 API。那么务必选择异步 API 。 但是, 不要使用 Task.Run来包装同步 API 使其异步化。
  • 确保 controller/Razor Page actions 异步化。 整个调用堆栈是异步的,就可以利用 async/await模式的性能优势。
    使用性能分析程序 ( 例如 PerfView) 可用于查找频繁添加到 线程池的线程。 Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start事件表示新线程被添加到线程池。

使用 IEumerable<T>或 IAsyncEnumerable<T> 作为返回值

在 Action 中返回 IEumerable<T>将会被序列化器中进行同步迭代 。 结果是可能导致阻塞或者线程池饥饿。 想要要避免同步迭代集合,可以在返回迭代集合之前使用 ToListAsync使其异步化。

从 ASP.NET Core 3.0 开始, IAsyncEnumerable<T>可以用作为 IEumerable<T>的替代方法,以异步方式进行迭代。 有关更多信息,请参阅 Controller Action 的返回值类型

尽可能少的使用大对象

.NET Core 垃圾收集器在 ASP.NET Core 应用程序中起到自动管理内存的分配和释放的作用。 自动垃圾回收通常意味着开发者不需要担心如何或何时释放内存。 但是,清除未引用的对象将会占用 CPU 时间,因此开发者应最小化 代码热点路径中的分配的对象。 垃圾回收在大对象上代价特大 (> 85 K 字节 ) 。 大对象存储在 large object heap上,需要 full (generation 2) garbage collection 来清理。 与 generation 0 和 generation 1 不同,generation 2 需要临时暂挂应用程序。 故而频繁分配和取消分配大型对象会导致性能耗损。

建议 :

  • 考虑缓存频繁使用的大对象。 缓存大对象可防止昂贵的分配开销。
  • 使用 ArrayPool<T>作为池化缓冲区以保存大型数组。
  • 不要代码热点路径上分配许多短生命周期的大对象。

可以通过查看 PerfView中的垃圾回收 (GC) 统计信息来诊断并检查内存问题,其中包括:

  • 垃圾回收挂起时间。
  • 垃圾回收中耗用的处理器时间百分比。
  • 有多少垃圾回收发生在 generation 0, 1, 和 2.

有关更多信息,请参阅 垃圾回收和性能

优化数据操作和 I/O

与数据存储器和其他远程服务的交互通常是 ASP.NET Core 应用程序最慢的部分。 高效读取和写入数据对于良好的性能至关重要。

建议 :

  • 以异步方式调用所有数据访问 API 。
  • 不要读取不需要的数据。 编写查询时,仅返回当前 HTTP 请求所必需的数据。
  • 考虑缓存从数据库或远程服务检索的频繁访问的数据 ( 如果稍微过时的数据是可接受的话 ) 。 根据具体的场景,可以使用 MemoryCacheDistributedCache。 有关更多信息,请参阅 https://docs.microsoft.com/en-us/aspnet/core/performance/caching/response?view=aspnetcore-3.1.
  • 尽量减少网络往返。 能够单次调用完成就不应该多次调用来读取所需数据。
  • 在 Entity Framework Core 访问数据以用作只读情况时, 使用 no-tracking方式查询。 EF Core 可以更高效地返回 no-tracking 查询的结果。
  • 使用过滤器和聚集 LINQ 查询 (例如, .Where.Select.Sum语句) ,以便数据库执行过滤提高性能 。
  • 考虑 EF Core 可能在客户端解析一些查询运算符,这可能导致查询执行效率低下。 有关更多信息,请参阅 客户端计算相关的性能问题
  • 不要在集合上使用映射查询,这会导致执行 “N + 1” SQL 查询。 有关更多信息,请参阅 优化子查询

请参阅 EF 高性能专题以了解可能提高应用性能的方法:

在代码提交之前,我们建议评估上述高性能方法的影响。 编译查询的额外复杂性可能无法一定确保性能提高。

可以通过使用 Application Insights或使用分析工具查看访问数据所花费的时间来检测查询问题。 大多数数据库还提供有关频繁执行的查询的统计信息,这也可以作为重要参考。

通过 HttpClientFactory 建立 HTTP 连接池

虽然 HttpClient实现了 IDisposable接口,但它其实被设计为可以重复使用单个实例。 关闭 HttpClient实例会使套接字在短时间内以 TIME_WAIT状态打开。 如果经常创建和释放 HttpClient对象,那么应用程序可能会耗尽可用套接字。 在 ASP.NET Core 2.1 中,引入了 HttpClientFactory作为解决这个问题的办法。 它以池化 HTTP 连接的方式从而优化性能和可靠性。

建议 :

确保公共代码路径快若鹰隼

如果你想要所有的代码都保持高速, 高频调用的代码路径就是优化的最关键路径。 优化措施包括:

  • 考虑优化应用程序请求处理管道中的 Middleware ,尤其是在管道中排在更前面运行的 Middleware 。 这些组件对性能有很大影响。
  • 考虑优化那些每个请求都要执行或每个请求多次执行的代码。 例如,自定义日志,身份认证与授权或 transient 服务的创建等等。

建议 :

在 HTTP 请求之外运行长时任务

对 ASP.NET Core 应用程序的大多数请求可以由调用服务的 controller 或页面模型处理,并返回 HTTP 响应。 对于涉及长时间运行的任务的某些请求,最好使整个请求-响应进程异步。

建议 :

  • 不要把等待长时间运行的任务完成,作为普通 HTTP 请求处理的一部分。
  • 考虑使用 后台服务Azure Function处理长时间运行的任务。 在应用外执行任务特别有利于 CPU 密集型任务的性能。
  • 使用实时通信,如 SignalR,以异步方式与客户端通信。

缩小客户端资源

复杂的 ASP.NET Core 应用程序经常包含很有前端文件例如 JavaScript, CSS 或图片文件。 可以通过以下方法优化初始请求的性能:

  • 打包,将多个文件合并为一个文件。
  • 压缩,通过除去空格和注释来缩小文件大小。

建议 :

  • 使用 ASP.NET Core 的 内置支持用于打包和压缩客户端资源文件的组件。
  • 考虑其他第三方工具,如 Webpack,用于复杂客户资产管理。

压缩 Http 响应

减少响应的大小通常会显着提高应用程序的响应性。 而减小内容大小的一种方法是压缩应用程序的响应。 有关更多信息,请参阅 响应压缩

使用最新的 ASP.NET Core 发行版

ASP.NET Core 的每个新发行版都包含性能改进。 .NET Core 和 ASP.NET Core 中的优化意味着较新的版本通常优于较旧版本。 例如, .NET Core 2.1 添加了对预编译的正则表达式的支持,并从使用 Span<T>改进性能。 ASP.NET Core 2.2 添加了对 HTTP/2 的支持。 ASP.NET Core 3.0 增加了许多改进,以减少内存使用量并提高吞吐量。 如果性能是优先考虑的事情,那么请升级到 ASP.NET Core 的当前版本。

最小化异常

异常应该竟可能少。 相对于正常代码流程来说,抛出和捕获异常是缓慢的。 因此,不应使用异常来控制正常程序流。

建议 :

  • 不要使用抛出或捕获异常作为正常程序流的手段,特别是在 代码热点路径中。
  • 在应用程序中包含用于检测和处理导致异常的逻辑。
  • 对意外的执行情况抛出或捕获异常。

应用程序诊断工具( 如 Application Insights ) 可以帮助识别应用程序中可能影响性能的常见异常。

性能和可靠性

下文将提供常见性能提示和已知可靠性问题的解决方案。

避免在 HttpRequest/HttpResponse body 上同步读取或写入

ASP.NET Core 中的所有 I/O 都是异步的。 服务器实现了 Stream接口,它同时具有同步和异步的方法重载。 应该首选异步方式以避免阻塞线程池线程。 阻塞线程会导致线程池饥饿。

不要使用如下操作: https://docs.microsoft.com/en-us/dotnet/api/System.IO.StreamReader.ReadToEnd。 它会阻止当前线程等待结果。 这是 sync over async的示例。

publicclassBadStreamReaderController:Controller      
{
[HttpGet("/contoso")]
publicActionResult<ContosoData>Get()
{
varjson =newStreamReader(Request.Body).ReadToEnd();

returnJsonSerializer.Deserialize<ContosoData>(json);
}
}

在上述代码中, Get采用同步的方式将整个 HTTP 请求主体读取到内存中。 如果客户端上载数据很慢,那么应用程序就会出现看似异步实际同步的操作。 应用程序看似异步实际同步,因为 Kestrel 支持同步读取。

应该采用如下操作: https://docs.microsoft.com/en-us/dotnet/api/System.IO.StreamReader.ReadToEndAsync,在读取时不阻塞线程。

publicclassGoodStreamReaderController:Controller      
{
[HttpGet("/contoso")]
publicasyncTask<ActionResult<ContosoData>> Get()
{
varjson =awaitnewStreamReader(Request.Body).ReadToEndAsync();

returnJsonSerializer.Deserialize<ContosoData>(json);
}

}

上述代码异步将整个 HTTP request body 读取到内存中。

[!WARNING] 如果请求很大,那么将整个 HTTP request body 读取到内存中可能会导致内存不足 (OOM) 。 OOM 可导致应用奔溃。 有关更多信息,请参阅 避免将大型请求主体或响应主体读取到内存中

应该采用如下操作:使用不缓冲的方式完成 request body 操作:

publicclassGoodStreamReaderController:Controller      
{
[HttpGet("/contoso")]
publicasyncTask<ActionResult<ContosoData>> Get()
{
returnawaitJsonSerializer.DeserializeAsync<ContosoData>(Request.Body);
}
}

上述代码采用异步方式将 request body 序列化为 C# 对象。

优先选用 Request.Form 的 ReadFormAsync

应该使用 HttpContext.Request.ReadFormAsync而不是 HttpContext.Request.FormHttpContext.Request.Form只能在以下场景用安全使用。

  • 该表单已被 ReadFormAsync调用,并且
  • 数据已经被从 HttpContext.Request.Form读取并缓存

不要使用如下操作:例如以下方式使用 HttpContext.Request.FormHttpContext.Request.Form使用了 sync over async,这将导致线程饥饿.

publicclassBadReadController:Controller      
{
[HttpPost("/form-body")]
publicIActionResultPost()
{
varform = HttpContext.Request.Form;

Process(form["id"], form["name"]);

returnAccepted();
}

应该使用如下操作:使用 HttpContext.Request.ReadFormAsync异步读取表单正文。

publicclassGoodReadController:Controller      
{
[HttpPost("/form-body")]
publicasyncTask<IActionResult>Post()
{
varform =awaitHttpContext.Request.ReadFormAsync();

Process(form["id"], form["name"]);

returnAccepted();
}

避免将大型 request body 或 response body 读取到内存中

在 .NET 中,大于 85 KB 的对象会被分配在大对象堆 ( LOH)。 大型对象的开销较大,包含两方面:

  • 分配大对象内存时需要对被分配的内存进行清空,这个操作成本较高。 CLR 会保证清空所有新分配的对象的内存。(将内存全部设置为 0)
  • LOH 只会在内存剩余不足时回收。 LOH 需要在 full garbage collection或者 Gen2 collection进行回收。

博文很好描述了该问题:

当分配大对象时,它会被标记为 Gen 2 对象。 而不像是 Gen 0 那样的小对象。 这样的后果是,如果你在使用 LOH 时耗尽内存, GC 会清除整个托管堆,而不仅仅是 LOH 部分。 因此,它将清理 Gen 0, Gen 1 and Gen 2 ( 包括 LOH ) 。 这称为 full garbage collection,是最耗时的垃圾回收。 对于很多应用,这是可以接受的。 但绝对不适用于高性能 Web 服务器,因为高性能 Web 服务器需要更多的内存用于处理常规 Web 请求 ( 从套接字读取,解压缩,解码 JSON 等等 )。

天真地将一个大型 request 或者 response body 存储到单个 byte[]string中:

  • 这可能导致 LOH 的剩余空间快速耗尽。
  • 因此产生的 full GC 可能会导致应用程序的性能问题。

使用同步 API 处理数据

例如使用仅支持同步读取和写入的序列化器/反序列化器时 ( 例如, JSON.NET):

  • 将数据异步缓冲到内存中,然后将其传递到序列化器/反序列化器。

[!WARNING] 如果请求较大,那么可能导致内存不足 (OOM) 。 OOM 可导致应用奔溃。 有关更多信息,请参阅 避免将大型请求主体或响应主体读取到内存

ASP.NET Core 3.0 默认情况下使用 https://docs.microsoft.com/en-us/dotnet/api/system.text.json进行 JSON 序列化,这将带来如下好处。 https://docs.microsoft.com/en-us/dotnet/api/system.text.json:

  • 异步读取和写入 JSON 。
  • 针对 UTF-8 文本进行了优化。
  • 通常比 Newtonsoft.Json更高的性能。

不要将 IHttpContextAccessor.HttpContext 存储在字段中

IHttpContextAccessor.HttpContext返回当前请求线程中的 HttpContext. IHttpContextAccessor.HttpContext** 不应该 ** 被存储在一个字段或变量中。

不要使用如下操作:例如将 HttpContext存储在字段中,然后在后续使用该字段。

publicclassMyBadType      
{
privatereadonlyHttpContext _context;
publicMyBadType(IHttpContextAccessor accessor)
{
_context = accessor.HttpContext;
}

publicvoidCheckAdmin()
{
if(!_context.User.IsInRole("admin"))
{
thrownewUnauthorizedAccessException("The current user isn't an admin");
}
}
}

以上代码在构造函数中经常得到 Null 或不正确的 HttpContext

应该采用如下操作:

publicclassMyGoodType      
{
privatereadonlyIHttpContextAccessor _accessor;
publicMyGoodType(IHttpContextAccessor accessor)
{
_accessor = accessor;
}

publicvoidCheckAdmin()
{
varcontext = _accessor.HttpContext;
if(context !=null&& !context.User.IsInRole("admin"))
{
thrownewUnauthorizedAccessException("The current user isn't an admin");
}
}
}

不要尝试在多线程下使用 HttpContext

HttpContext 不是线程安全的。 从多个线程并行访问 HttpContext可能会导致不符预期的行为,例如线程挂起,崩溃和数据损坏。

不要使用如下操作:以下示例将发出三个并行请求,并在 HTTP 请求之前和之后记录传入的请求路径。 请求路径将被多个线程 ( 可能并行 ) 访问。

publicclassAsyncBadSearchController:Controller      
{
[HttpGet("/search")]
publicasyncTask<SearchResults>Get(stringquery)
{
varquery1 = SearchAsync(SearchEngine.Google, query);
varquery2 = SearchAsync(SearchEngine.Bing, query);
varquery3 = SearchAsync(SearchEngine.DuckDuckGo, query);

awaitTask.WhenAll(query1, query2, query3);

varresults1 =awaitquery1;
varresults2 =awaitquery2;
varresults3 =awaitquery3;

returnSearchResults.Combine(results1, results2, results3);
}

privateasyncTask<SearchResults>SearchAsync(SearchEngine engine,stringquery)
{
varsearchResults = _searchService.Empty();
try
{
_logger.LogInformation("Starting search query from {path}.",
HttpContext.Request.Path);
searchResults = _searchService.Search(engine, query);
_logger.LogInformation("Finishing search query from {path}.",
HttpContext.Request.Path);
}
catch(Exception ex)
{
_logger.LogError(ex,"Failed query from {path}",
HttpContext.Request.Path);
}

returnawaitsearchResults;
}

应该这样操作:以下示例在发出三个并行请求之前,从传入请求复制下文需要使用的数据。

publicclassAsyncGoodSearchController:Controller      
{
[HttpGet("/search")]
publicasyncTask<SearchResults>Get(stringquery)
{
stringpath = HttpContext.Request.Path;
varquery1 = SearchAsync(SearchEngine.Google, query,
path);
varquery2 = SearchAsync(SearchEngine.Bing, query, path);
varquery3 = SearchAsync(SearchEngine.DuckDuckGo, query, path);

awaitTask.WhenAll(query1, query2, query3);

varresults1 =awaitquery1;
varresults2 =awaitquery2;
varresults3 =awaitquery3;

returnSearchResults.Combine(results1, results2, results3);
}

privateasyncTask<SearchResults>SearchAsync(SearchEngine engine,stringquery,
stringpath)
{
varsearchResults = _searchService.Empty();
try
{
_logger.LogInformation("Starting search query from {path}.",
path);
searchResults =await_searchService.SearchAsync(engine, query);
_logger.LogInformation("Finishing search query from {path}.", path);
}
catch(Exception ex)
{
_logger.LogError(ex,"Failed query from {path}", path);
}

returnawaitsearchResults;
}

请求处理完成后不要使用 HttpContext

HttpContext只有在 ASP.NET Core 管道处理活跃的 HTTP 请求时才可用。 整个 ASP.NET Core 管道是由异步代理组成的调用链,用于处理每个请求。 当 Task从调用链完成并返回时, HttpContext就会被回收。

不要进行如下操作:以下示例使用 async void,这将使得 HTTP 请求在第一个 await时处理完成,进而就会导致:

  • 在 ASP.NET Core 应用程序中, 这是一个 完全错误的做法
  • 在 HTTP 请求完成后访问 HttpResponse
  • 进程崩溃。
publicclassAsyncBadVoidController:Controller      
{
[HttpGet("/async")]
publicasyncvoidGet()
{
awaitTask.Delay(1000);

// The following line will crash the process because of writing after the
// response has completed on a background thread. Notice async void Get()

awaitResponse.WriteAsync("Hello World");
}
}

应该进行如下操作:以下示例将 Task返回给框架,因此,在操作完成之前, HTTP 请求不会完成。

publicclassAsyncGoodTaskController:Controller      
{
[HttpGet("/async")]
publicasyncTaskGet()
{
awaitTask.Delay(1000);

awaitResponse.WriteAsync("Hello World");
}
}

不要在后台线程中使用 HttpContext

不要使用如下操作:以下示例使用一个闭包从 Controller属性读取 HttpContext。 这是一种错误做法,因为这将导致:

  • 代码运行在 Http 请求作用域之外。
  • 尝试读取错误的 HttpContext
[HttpGet("/fire-and-forget-1")]      
publicIActionResultBadFireAndForget()
{
_ = Task.Run(async() =>
{
awaitTask.Delay(1000);

varpath = HttpContext.Request.Path;
Log(path);
});

returnAccepted();
}

应该采用如下操作:

  • 在请求处理阶段将后台线程需要的数据全部进行复制。
  • 不要使用 controller 的所有引用
[HttpGet("/fire-and-forget-3")]      
publicIActionResultGoodFireAndForget()
{
stringpath = HttpContext.Request.Path;
_ = Task.Run(async() =>
{
awaitTask.Delay(1000);

Log(path);
});

returnAccepted();
}

后台任务最好采用托管服务进行操作。 有关更多信息,请参阅 采用托管服务运行后台任务

不要在后台线程获取注入到 controller 中的服务

不要采用如下做法:以下示例使用闭包从 controller获取 DbContext进行操作。 这是一个错误的做法。 这将导致代码云在请求的作用域之外。 而 ContocoDbContext是基于请求作用域的,因此这样将引发 ObjectDisposedException

[HttpGet("/fire-and-forget-1")]      
publicIActionResultFireAndForget1([FromServices]ContosoDbContext context)
{
_ = Task.Run(async() =>
{
awaitTask.Delay(1000);

context.Contoso.Add(newContoso());
awaitcontext.SaveChangesAsync();
});

returnAccepted();
}

应该采用如下操作:

[HttpGet("/fire-and-forget-3")]      
publicIActionResultFireAndForget3([FromServices]IServiceScopeFactory
serviceScopeFactory)
{
_ = Task.Run(async() =>
{
awaitTask.Delay(1000);

using(varscope = serviceScopeFactory.CreateScope())
{
varcontext = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

context.Contoso.Add(newContoso());

awaitcontext.SaveChangesAsync();
}
});

returnAccepted();
}

以下高亮的的代码说明:

  • 为后台操作创建新的作用域,并且从中获取需要的服务。
  • 在正确的作用域中使用 ContocoDbContext,即只能在请求作用域中使用该对象。
[HttpGet("/fire-and-forget-3")]      
publicIActionResultFireAndForget3([FromServices]IServiceScopeFactory
serviceScopeFactory)
{
_ = Task.Run(async() =>
{
awaitTask.Delay(1000);

using(varscope = serviceScopeFactory.CreateScope())
{
varcontext = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

context.Contoso.Add(newContoso());

awaitcontext.SaveChangesAsync();
}
});

returnAccepted();
}

不要在响应正文已经开始发送时尝试修改 status code 或者 header

ASP.NET Core 不会缓冲 HTTP 响应正文。 当正文一旦开始发送:

  • Header 就会与正文的数据包一起发送到客户端。
  • 此时就无法修改 header 了。

不要使用如下操作:以下代码尝试在响应启动后添加响应头:

app.Use(async(context, next) =>      
{
awaitnext();

context.Response.Headers["test"] ="test value";
});

在上述的代码中,如果 next()已经开始写入响应,则 context.Response.Headers["test"] = "test value";将会抛出异常。

应该采用如下操作:以下示例检查 HTTP 响应在修改 Header 之前是否已启动。

app.Use(async(context, next) =>      
{
awaitnext();

if(!context.Response.HasStarted)
{
context.Response.Headers["test"] ="test value";
}
});

应该采用如下操作:以下示例使用 HttpResponse.OnStarting来设置 Header,这样便可以在响应启动时将 Header 一次性写入到客户端。

通过这种方式,响应头将在响应开始时调用已注册的回调进行一次性写入。 如此这般便可以:

  • 在恰当的时候进行响应头的修改或者覆盖。
  • 不需要了解管道中的下一个 middleware 的行为。
app.Use(async(context, next) =>      
{
context.Response.OnStarting(() =>
{
context.Response.Headers["someheader"] ="somevalue";
returnTask.CompletedTask;
});

awaitnext();
});

如果已开始写入响应主体,则请不要调用 next()

仅当后续组件能够处理响应或时才调用它们,因此如果当前已经开始写入响应主体,后续操作就已经不再需要,并有可能引发异常情况。

托管于 IIS 应该使用 In-process 模式

使用 in-process 模式托管, ASP.NET Core 应用程序将与 IIS 工作进程在同一进程中运行。 In-process 模式拥有比 out-of-process 更加优秀的性能表现,因为这样不需要将请求通过回环网络适配器进行代理中转。 回环网络适配器是将本机发送的网络流量重新转回本机的的网络适配器。 IIS 进程管理由 Windows Process Activation Service (WAS)来完成。

在 ASP.NET Core 3.0 和更高版本中的默认将采用 in-process 模式进行托管。

有关更多信息,请参阅 在 Windows 上使用 IIS 托管 ASP.NET Core


Newbe.Translations

您所阅读的当前文章源自于 Newbe.Translations 项目参与的翻译贡献,您可以通过右侧链接一同参与该项目: https://www.newbe.pro/Newbe.Translations/Newbe.Translations/

翻译内容具有一定的时效性,不会随着原文内容实时更新,如果内容存在一定过时,您也可以联系我们。

相关 [asp net core] 推荐:

Debugging .NET Core on Linux with LLDB | RayDBG

- -
The LLDB debugger is conceptually similar to the native Windows debugging tools in that it is a low level and command live driven debugger. Part of the reason the .NET Core team chose the LLDB debugger was for its extensibility points that allowed them to create the SOS plugin which can be used to debug .NET core applications.

Profiling a .NET Core Application on Linux | All Your Base Are Belong To Us

- -
In the same vein of  my previous post on analyzing core dumps of .NET Core applications on Linux, let’s take a look at what it takes to do some basic performance profiling.

.Net Core 全局性能诊断工具

- - IT瘾-dev
现在.NET Core 上线后,不可避免的会出现各种问题,如内存泄漏、CPU占用高、接口处理耗时较长等问题. 这个时候就需要快速准确的定位问题,并解决. 这时候就可以使用.NET Core 为开发人员提供了一系列功能强大的诊断工具. 接下来就详细了解下:.NET Core 全局诊断工具. dotnet-counters 是一个性能监视工具,用于初级运行状况监视和性能调查.

Analyzing a .NET Core Core Dump on Linux | All Your Base Are Belong To Us

- -
I thought this walkthrough might be useful if you find yourself in the same boat, because, to be quite honest, I didn’t find it trivial.. A lot of distros will have something preconfigured, but the simplest approach is to just put a file name in the /proc/sys/kernel/core_pattern file:.

Debugging .NET Core app from a command line on Linux - Dots and Brackets: Code Blog

- -
Million years ago, way before the ice age, I was preparing small C++ project for “Unix Programming” university course and at some point had to debug it via command line.

.Net Core in Docker - 在容器内编译发布并运行 - Agile.Zhou - 博客园

- -
Docker可以说是现在微服务,DevOps的基础,咱们.Net Core自然也得上Docker. .Net Core发布到Docker容器的教程网上也有不少,但是今天还是想来写一写. 你搜.Net core程序发布到Docker网上一般常见的有两种方案:. 1、在本地编译成Dll文件后通过SCP命令或者WinSCP等工具上传到服务器上,然后构建Docker镜像再运行容器.

为什么 web 开发人员需要迁移到. NET Core, 并使用 ASP.NET Core MVC 构建 web 和 webservice/API - 张善友 - 博客园

- -
2018 .NET开发者调查报告: .NET Core 是怎么样的状态,这里我们看到了还有非常多的.net开发人员还在观望,本文给大家一个建议. 这仅代表我的个人意见, 我有充分的理由推荐.net 程序员使用. 有些人可能不同意我的观点, 但是分享想法和讨论它是好的. .net 程序员或他们所在的团队总有各种理由说他们的系统还在使用旧系统, 这显然是企业开发人员的事情.

【实验手册】使用Visual Studio Code 开发.NET Core应用程序 - 张善友 - 博客园

- -
开源和跨平台开发是Microsoft 的当前和将来至关重要的策略. .NET Core已开源,同时开发了其他项来使用和支持新的跨平台策略. .NET Core 2.0 目前已经正式发布,是适用于针对 Web 和云构建跨平台应用程序的最新开源技术,可在 Linux、Mac OS X 和 Windows 上运行.

KISSY Core 预览版

- MArCoRQ - 岁月如歌
KISSY 是淘宝新一代前端 UI 类库,陆陆续续经过大半年的开发,终于完成了核心部分. KISSY 借鉴了 YUI3 的代码组织思想,尝试融合 jQuery/YUI2/ExtJS 等类库的优点. 目前才刚起步,下面是相关话题:. 请先看个 ppt, 或许能解答你的疑惑:前端_UI_类库_KISSY_赛马竞标书.pptx.

是否该用 Core Data?

- kezhuw - jjgod / blog
Core Data 是 Cocoa 里面一套非常受欢迎的框架,从 Mac OS X 10.4 提供以来,在 10.5 中引入了完善的 schema 迁移机制,再到 iPhone OS 3.0 时被引入 Cocoa Touch,这套完善的框架都被认为是管理大量结构化数据所首选的 Cocoa 框架,尤其是因为使用 Core Data 能大大减少需要手工编写的代码量,就使它更受开发者欢迎了.