В докладе будут рассмотрены принципы и лучшие практики создания гибких масштабируемых приложений на базе Microsoft .NET Core в связке с сервисами Microsoft Azure. Будет рассмотрен ряд полезных подходов, инструментов и библиотек, которые сильно упростят разработку, конфигурирование и развертывание приложений. Также будет уделено внимание некоторым “подводные камням”, с которыми может столкнуться человек, использующий .NET Core.
4. Киев 2017
Analytics says…
.NET Core will be as popular as Ruby and
NodeJS.
.NET Core and NodeJS will be the most popular
platforms for back-end solution compete on the
market.
In few years .NET Core (not Java) will be
number one choice for enterprise-level
applications.
Architecture & Development of .NET Core Applications
.Net Core, Java, NodeJS…
6. Киев 2017
.NET Core :: Few Things
• Many mechanisms such as authentication, security,
component interactions now changed comparing to .Net
Framework.
• Many components and platforms (for example for
Desktop applications) are missing.
• Nevertheless, .NET Core provides corporate-level
software benefits for small projects.
Architecture & Development of .NET Core Applications
7. Киев 2017
.Net Core vs .Net Framework
• There are cross-platform needs.
• Application architecture is based
on microservices.
• Scalability and high performance
are the must. Need to get as
much as possible out of the box.
• Need to use both Linux and
Windows containers.
• You are running multiple .NET
versions side-by-side.
• Opensource framework is
required.
Architecture & Development of .NET Core Applications
• Application currently uses .NET
Framework and has strong
dependencies on Windows.
• Need to use Windows APIs that
are not supported by .NET Core.
• Need to use third-party libraries
or NuGet packages that are not
available for .NET Core.
• Need tools, technologies or
platforms not supported by .NET
Core.
.NET Core .Net Framework
8. Киев 2017
.Net Core vs .Net Framework
Architecture & Development of .NET Core Applications
Architecture / App Type Linux containers Windows Containers
Microservices on containers .NET Core .NET Core
Monolithic app .NET Core .NET Framework,
.NET Core
Best-in-class performance and scalability .NET Core .NET Core
Windows Server legacy app (“brown-field”)
migration to containers
-- .NET Framework
New container-based development (“green-field”) .NET Core .NET Core
ASP.NET Core .NET Core .NET Core (recommended)
.NET Framework
ASP.NET 4 (MVC 5, Web API 2, and Web Forms) -- .NET Framework
SignalR services .NET Core .NET Framework
.NET Core
WCF, WF, and other legacy frameworks Limited WCF
support in .NET
Core
.NET Framework
Limited WCF support in
.NET Core
Consumption of Azure services .NET Core .NET Framework,
.NET Core
10. Киев 2017
Microservices :: Pros and Cons
• Each microservice is relatively
small—easy to manage and evolve.
• It is possible to scale out individual
areas of the application.
• You can divide the development
work between multiple teams.
• Issues are more isolated.
• You can use the latest technologies.
Architecture & Development of .NET Core Applications
• Distributed application adds
complexity for developers.
• Deployment complexity.
• Atomic transactions usually are
not possible.
• Usually increase hardware
resource needs.
• Communication complexity.
• Correct system decomposition
complexity.
Benefits Disadvantages
11. Киев 2017
.Net Core Apps :: Hosting
Architecture & Development of .NET Core Applications
Feature App
Service
Service
Fabric
Virtual
Machine
Near-Instant Deployment X X
Scale up to larger machines without redeploy X X
Instances share content and configuration; no need to
redeploy or reconfigure when scaling
X X
Multiple deployment environments (production, staging) X X
Automatic OS update management X
Seamless switching between 32/64 bit platforms X
Deploy code with Git, FTP X X
Deploy code with WebDeploy X X
Deploy code with TFS X X X
Host web or web service tier of multi-tier architecture X X X
Access Azure services like Service Bus, Storage, SQL
Database
X X X
Install any custom MSI X X
16. Киев 2017
API :: Direct Communication
Architecture & Development of .NET Core Applications
Back-End
Microservice 1
Microservice 2
Microservice N
…
Client Apps
17. Киев 2017
API :: API Gateway
Architecture & Development of .NET Core Applications
Back-End
Microservice 1
Microservice 2
Microservice N
…
Client Apps
API Gateway
18. Киев 2017
API :: Azure API Management
Architecture & Development of .NET Core Applications
Back-End
Microservice 1
Microservice 2
Microservice N
…
Client Apps
Azure API
Management
19. Киев 2017
API :: Swagger
• Automatically generates API documentation
• Supports Client API generation and discoverability
• Provides ability to automatically consume and integrate
APIs
Architecture & Development of .NET Core Applications
20. Киев 2017
API :: Swagger Configuration
Architecture & Development of .NET Core Applications
public void ConfigureServices(IServiceCollection services)
{
// API documentation configuration
var swaggerConfigurationInfo = new SwaggerConfigurationInfo() {
Title = “My Application User API ",
Description = "Service provides all user specific information and management api.",
TermsOfService = “None”,
SecuritySchemas = new List<SecurityScheme> {
// Define the OAuth2.0 scheme (i.e. Implicit Flow), for access_token the user of
// Swagger will be redirected to Auth0 Login hosted page to input credentials
new OAuth2Scheme { Type = "oauth2",
Flow = "implicit",
AuthorizationUrl =
m_identityProviderSettings.Auth0Authorize.AbsoluteUri,
Scopes = new Dictionary<string, string>
{ { "openid profile email", "Security API" }}
}}};
// Add Framework API services(API versioning, swagger, etc.)
services.AddApiServices(swaggerConfigurationInfo);
}
21. Киев 2017
API :: AutoRest
Architecture & Development of .NET Core Applications
{autorest-location}autorest -Input http://{webapiname}.azurewebsites.net/swagger/
public async void InvokeTest()
{
UserApiClient client = new UserApiClient(...);
await client.IdentityUserActivatePostAsync(
new ActivateUserModel
{
ExternalReferenceId = "1354687252",
Password = "Qwerty123"
});
}
22. Киев 2017
API :: Versioning
Architecture & Development of .NET Core Applications
Back-End
V 1.0
API Gateway
V N.M
…
New Client Apps
Old Client Apps
23. Киев 2017
API Versioning :: Business Rules
• API versioning shall be applicable for any API endpoint.
• Old versions has to be supported as long as you agreed
with our clients.
• Old API versions should work the same way they worked
before new version was introduced.
• Old APIs shall be marked as deprecated.
• All versions has to be testable via unit/integration tests.
• Best practice is to apply versioning to external API only.
Architecture & Development of .NET Core Applications
24. Киев 2017
API Versioning :: Versioning in URI
Architecture & Development of .NET Core Applications
• URI Path
https://mywebportal.com/api/v2/getUsers
• Query String
https://mywebportal.com/api/getUsers?v=2.0
25. Киев 2017
Versioning with Headers
Architecture & Development of .NET Core Applications
GET /api/camps HTTP/1.1
Host: localhost:43333
Content-Type: application/json
X-version: 2.0
GET /api/camps HTTP/1.1
Host: localhost:43333
Content-Type: application/json
Accept: application/json;version=2.0
26. Киев 2017
Versioning with Content Type
Architecture & Development of .NET Core Applications
GET /api/camps HTTP/1.1
Host: localhost:43333
Content-Type: application/vnd.myapplication.v1+json
Accept: application/vnd.myapplication.v1+json
27. Киев 2017
Versioning in Action
Architecture & Development of .NET Core Applications
• Use Microsoft ASP.NET API Versioning NuGet package
• Each controller should be marked with API version:
• Old versions of API controllers should be marked as
deprecated:
• Each API version should be stored in Version folder
• Each controller should be placed in specific namespace
• Unit and integration tests should be also stored separately
29. Киев 2017
EF Core :: Resilient Connections
Architecture & Development of .NET Core Applications
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(10),
errorNumbersToAdd: null);
});
});
}
30. Киев 2017
Resiliency and Transactions
Architecture & Development of .NET Core Applications
System.InvalidOperationException: The configured execution strategy
'SqlServerRetryingExecutionStrategy' does not support user initiated
transactions. Use the execution strategy returned by
'DbContext.Database.CreateExecutionStrategy()' to execute all the operations
in the transaction as a retriable unit.
// Use of resiliency strategy within an explicit transaction
var strategy = dbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () => {
using (var transaction = dbContext.Database.BeginTransaction()) {
dbContext.Users.Update(user);
await dbContext.SaveChangesAsync();
await eventLogService.SaveEventAsync(userChangedEvent);
transaction.Commit();
}
});
SOLUTION
31. Киев 2017
EF Core :: Seeding
Architecture & Development of .NET Core Applications
public class Startup
{
// Other Startup code...
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
// Other Configure code...
// Seed data through our custom class
DatabaseSeed.SeedAsync(app).Wait();
// Other Configure code...
}
}
32. Киев 2017
EF Core :: Seeding
Architecture & Development of .NET Core Applications
public class DatabaseSeed
{
public static async Task SeedAsync(IApplicationBuilder applicationBuilder)
{
var context = (AppDbContext)applicationBuilder.
ApplicationServices.GetService(typeof(AppDbContext));
using (context)
{
context.Database.Migrate();
if (!context.Users.Any())
{
context.Users.AddRange(...);
await context.SaveChangesAsync();
}
if (!context.Departments.Any())
{
context.Departments.AddRange(...);
await context.SaveChangesAsync();
}
}
}
}
33. Киев 2017
EF Core :: Seeding Improvement
Architecture & Development of .NET Core Applications
• Use standard migration mechanism.
• Create base class(es) for seed migrations.
• Specify data context via attribute.
34. Киев 2017
EF Core :: Seeding Improvement
Architecture & Development of .NET Core Applications
/// <summary>
/// Seed Roles, RolesClaims and UserRoles
/// </summary>
[DbContext(typeof(MyContext))]
[Migration("SEED_201709121256_AddRolesClaimsUserRoles")]
public class AddRolesClaimsUserRoles : EmptyDbSeedMigration
{
/// <summary>
/// <see cref="SeedMigrationBase.PopulateData"/>
/// </summary>
protected override void PopulateData()
{
...
}
}
35. Киев 2017
EF Core :: InMemory Databases
Architecture & Development of .NET Core Applications
public class Startup
{
// Other Startup code ...
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
// DbContext using an InMemory database provider
services.AddDbContext<AppDbContext>(opt =>
opt.UseInMemoryDatabase());
}
// Other Startup code ...
}
37. Киев 2017
.NET Core Apps :: Health Cheks
Architecture & Development of .NET Core Applications
• https://github.com/aspnet/HealthChecks
• src/common
• src/Microsoft.AspNetCore.HealthChecks
• src/Microsoft.Extensions.HealthChecks
• src/Microsoft.Extensions.HealthChecks.SqlServer
• src/Microsoft.Extensions.HealthChecks.AzureStorage
38. Киев 2017
.NET Core Apps :: Health Checks
Architecture & Development of .NET Core Applications
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseHealthChecks("/hc)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
39. Киев 2017
.NET Core Apps :: Health Checks
Architecture & Development of .NET Core Applications
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Add health checks here.
services.AddHealthChecks(checks => {
checks.AddUrlCheck(“URL Check" )
.AddHealthCheckGroup("servers",
group => group
.AddUrlCheck("https://myserviceurl::8010")
.AddUrlCheck("https://tmysecondserviceurl:7777"))
}
services.AddMvc();
}
}
41. Киев 2017
.NET Core Apps :: Health Checks
Architecture & Development of .NET Core Applications
checks.AddHealthCheckGroup("memory",
group => group.AddPrivateMemorySizeCheck(1)
.AddVirtualMemorySizeCheck(2)
.AddWorkingSetCheck(1), CheckStatus.Unhealthy)
.AddCheck("thrower", Func<IHealthCheckResult>)
(() => { throw new DivideByZeroException(); }))
.AddCheck("long-running", async cancellationToken => {
await Task.Delay(10000, cancellationToken);
return HealthCheckResult.Healthy("I ran too long"); })
.AddCheck<CustomHealthCheck>("custom");
42. Киев 2017
Service Fabric Health Monitoring
Architecture & Development of .NET Core Applications
fabric:/System
43. Киев 2017
Service Fabric Health Hierarchy
Architecture & Development of .NET Core Applications
Cluster
Nodes Applications
Deployed
Applications
Deployed Service
Packages
Services
Partitions
Replicas
44. Киев 2017
Cluster Health Policy
Architecture & Development of .NET Core Applications
<FabricSettings>
<Section Name="HealthManager/ClusterHealthPolicy">
<Parameter Name="ConsiderWarningAsError" Value="False" />
<Parameter Name="MaxPercentUnhealthyApplications" Value=“10" />
<Parameter Name="MaxPercentUnhealthyNodes" Value=“10" />
<Parameter Name="ApplicationTypeMaxPercentUnhealthyApplications-
YourApplicationType" Value="0" />
</Section>
</FabricSettings>
• Consider Warning as Error
• Max Percent Unhealthy Applications
• Max percent Unhealthy Nodes
• Application Type Health Policy Map
45. Киев 2017
Service Fabric :: Health Reports
Architecture & Development of .NET Core Applications
private static Uri ApplicationName = new Uri("fabric:/MyApplication");
private static string ServiceManifestName = “MyApplication.Service";
private static string NodeName = FabricRuntime.GetNodeContext().NodeName;
private static Timer ReportTimer = new Timer(new TimerCallback(SendReport), null, 3000, 3000);
private static FabricClient Client = new FabricClient(new FabricClientSettings() {
HealthOperationTimeout = TimeSpan.FromSeconds(120),
HealthReportSendInterval = TimeSpan.FromSeconds(0),
HealthReportRetrySendInterval = TimeSpan.FromSeconds(40)});
public static void SendReport(object obj) {
// Test whether the resource can be accessed from the node
HealthState healthState = TestConnectivityToExternalResource();
var deployedServicePackageHealthReport = new DeployedServicePackageHealthReport(
ApplicationName, ServiceManifestName, NodeName,
new HealthInformation("ExternalSourceWatcher", "Connectivity", healthState));
Client.HealthManager.ReportHealth(deployedServicePackageHealthReport);
}
46. Киев 2017
Service Fabric + App Insights
Architecture & Development of .NET Core Applications
https://github.com/DeHeerSoftware/Azure-Service-Fabric-Logging-And-Monitoring
Serilog.Sinks.ApplicationInsights