1- مقدمه
پارتیشن بندی در بانک اطلاعاتی SQL Server، از ویژگیهایی است که از نسخه 2005، به این محصول اضافه شده است. بکارگیری این قابلیت که با Split کردن، محتوای یک جدول و قرار دادن آنها در چندین فایل، برای جداول حجیم، به ویژه جداولی که دادههای آن حاوی مقادیر تاریخچهای است، بسیار سودمند است.سادگی در مدیریت دادهها و شاخصهای موجود یک جدول (از قبیل اندازه فضای ذخیره سازی و استراتژی جدید Back up گیری)، اجرای سریعتر کوئری هایی که روی یک محدوده از دادهها کار میکنند و سهولت در آرشیو دادههای قدیمی یک جدول، از قابلیتهایی است که استفاده از این ویژگی بوجود میآورد.
محدوده استفاده از این ویژگی روی یک بانک اطلاعاتی و در یک Instance است. بنابراین مباحث مرتبط با معماری Scalability را پوشش نمیدهد و صرفاً Solution ایی است که در یک Instance بانک اطلاعاتی استفاده میشود.
2- Data File و Filegroup
هر بانک اطلاعاتی در حالت پیش فرض، شامل یک فایل دادهای (MDF.) و یک فایل ثبت تراکنشی (LDF.) میباشد. میتوان جهت ذخیره سطرهای دادهای از فایلهای بیشتری تحت نام فایلهای ثانویه (NDF.) استفاده نمود. به همان طریق که در فایل سیستم، فایلها به پوشهها تخصیص داده میشوند، میتوان Data File را به Filegroup تخصیص داد. چنانچه چندین Data File به یک Filegroup تخصیص داده شوند، دادهها در تمامی Data Fileها به طریق Round-Robin توزیع میشوند.3- Partition Function
مطابق با مقادیر تعریف شده در بدنه دستور، محدوده دادهای (پارتیشنها) با استفاده از Partition Function ایجاد میشود. با در نظر گرفتن ستونی که به عنوان Partition Key انتخاب شده، این تابع یک Data Type را به عنوان ورودی دریافت میکند. در هنگام تعریف محدوده برای پارتیشنها، به منظور مشخص کردن محدوده هر پارتیشن از Right و Left استفاده میشود.Left نمایش دهندهی حد بالای هر محدوده است و به طور مشابه، Right برای مشخص کردن حد پائین آن محدوده استفاده میشود. به منظور درک بهتر، به شکل زیر توجه نمائید:
همانطور که مشاهده میشود، همواره نیاز به یک Filegroup اضافهتری از آنچه مورد نظرتان در تعریف تابع است، میباشد. بنابراین اگر Function دارای n مقدار باشد، به n+1 مقدار برای Filegroup نیاز است.
همچنین هیچ محدودیتی برای اولین و آخرین بازه در نظر گرفته نمیشود. بنابراین جهت محدود کردن مقادیری که در این بازهها قرار میگیرند، میتوان از Check Constraint استفاده نمود.
3-1- Right or Left
یک سوال متداول اینکه از کدام مورد استفاده شود؟ در پاسخ باید گفت، به چگونگی تعریف پارتیشن هایتان وابسته است. مطابق شکل، تنها تفاوت این دو، در نقاط مرزی هر یک از پارتیشنها میباشد. در بیشتر اوقات هنگام کار با دادههای عددی میتوان از Left استفاده نمود و بطور مشابه هنگامیکه نوع دادهها از جنس زمان است، میتوان از Right استفاده کرد.
4- Partition Schema
گام بعدی پس از ایجاد Partition Function، تعریف Partition Schema است، که به منظور قرار گرفتن هر یک از پارتیشنهای تعریف شده توسط Function در Filegroupهای مناسب آن استفاده میشود.
5- Partition Table
گام پایانی ایجاد یک جدول، استفاده از Partition Schema است، که دادهها را با توجه به رویه درون Partition Function مورد استفاده، ذخیره میکند. همانطور که میدانید هنگام ایجاد یک جدول، میتوان مکان ذخیره شدن آنرا مشخص نمود.
Create Table <name> (…) ON …
در هنگام ایجاد یک جدول، معمولاً جدول در Filegroup پیش فرض که PRIMARY است، قرار میگیرد. میتوان با نوشتن نام Partition Schema و همچنین Partition Key که پیشتر ذکر آن رفت، بعد از بخش ON، برای جدول مشخص نمائیم که دادههای آن به چه ترتیبی ذخیره شوند. ارتباط این سه به شرح زیر است:
توجه شود زمانیکه یک Primary Key Constraint به یک جدول اضافه میشود، یک Unique Clustered Index نیز همراه با آن ساخته میشود. چنانچه Primary Key شامل یک Clustered Index باشد، جدول با استفاده از این ستون (ستونهای) شاخص ذخیره خواهد شد، در حالیکه اگر Primary Key شامل یک Non Clustered Index باشد، یک ساختار ذخیره-سازی اضافی ایجاد خواهد شد که دادههای جدول در آن قرار خواهند گرفت.
6- Index & Data Alignment
به عنوان یک Best Practice هنگام ایجاد یک Partition Table به منظور پارتیشن بندی، از ساختار Aligned Index استفاده شود. بدین ترتیب که تعریف Index، شامل Partition Key (ستونی که معیاری برای پارتیشن بندی است) باشد. چنانچه این عمل انجام شود، دادههای ذخیره شده مرتبط با هر پارتیشن متناظر با همان شاخص، در فایل دادهای (NDF.) ذخیره خواهند شد. از این رو چنانچه کوئری درخواست شده از جدول روی یک محدوده باشد
Where [OrderDate] Between …
بدین ترتیب برای بهرمندی از این مزایا، استفاده از Aligned Index توصیه شده است.
7- Operations
از نیازمندیهای متداول در پارتیشنینگ میتوان به افزودن، حذف پارتیشنها و جابجایی محتوای یک پارتیشن که برای عملیات آرشیو استفاده میشود، اشاره کرد.
7-1- Split Partition
به منظور ایجاد یک محدوده جدید به پارتیشنها استفاده میشود. یک نکته مهم مادامی که عملیات انتقال دادهها به پارتیشن جدید انجام میگیرد، روی جدول یک قفل انحصاری قرار میگیرد و بدین ترتیب عملیات ممکن است زمانبر باشد.
به عنوان یک Best Practice همواره یک Partition خالی را Split نمائید و پس از آن اقدام به بارگذاری داده در آن نمائید.
به یاد داشته باشید پیش از انجام عملیات splitting روی Partition Function با تغییر در Partition Schema (و بکارگیری Next Used) مشخص نمائید چه محدودهای در این Filegroup جدید قرار خواهد گرفت.
7-2- Merge Partition
به منظور ادغام پارتیشنها استفاده میشود، چنانچه پارتیشن خالی نیست، برای عملیات ادغام مسائل Performance به علت اینکه در طول عملیات از Lock (قفل انحصاری) استفاده میشود، در نظر گرفته شود.
7-3- Switch Partition
چنانچه جدول و شاخصهای آن به صورت Aligned هستند، میتوانید از Switch in و Switch out استفاده نمائید. عملیات بدین ترتیب انجام میشود که بلافاصله محتوای یک پارتیشن یا جدول (Source) در یک پارتیشن خالی جدولی دیگر و یا یک جدول خالی (Target) قرار میگیرد. عملیات تنها روی Meta Data انجام میگیرد و هیچ داده ای منتقل نمیشود.
محدودیتهای بکارگیری به شرح زیر است:
- جدول یا پارتیشن Target باید حتماً خالی باشد.
- جداول Source و Target حتماً باید در یک Filegroup یکسان قرار داشته باشند.
- جدول Source باید حاوی Aligned Indexهای مورد نیاز Target و همچنین مطابقت در Filegroup را دارا باشد.
- چنانچه Target به عنوان یک پارتیشن است، اگر Source جدول است بایست دارای یک Check Constraint باشد در غیر این صورت چنانچه یک پارتیشن است باید محدوده آن در محدوده Target قرار گیرد.
8- بررسی یک سناریوی نمونه
در ابتدا یک بانک اطلاعاتی را به طریق زیر ایجاد میکنیم:
این بانک مطابق تصویر، شامل 3 عدد فایل گروپ (FG1، FG2 و FG3) و 3 عدد دیتا فایل (P1، P2 و P3) میباشد. Filegroup پیش فرض Primary است، که چنانچه در تعریف جداول به نام Partition Schema و Partition Key مرتبط اشاره نشود، به طور پیش فرض در Filegroup موسوم به Primary قرار میگیرد. چنانچه چک باکس Default انتخاب شود، همانطور که قابل حدس زدن است، آن Filegroup در صورت مشخص نکردن نام Filegroup در تعریف جدول، به عنوان مکان ذخیره سازی انتخاب میشود. چک باکس Read Only نیز همانطور که از نامش پیداست، چنانچه روی یک Filegroup تنظیم گردد، عملیات مربوط به Write روی دادههای آن قابل انجام نیست و برای Filegroup هایی که جنبه نگهداری آرشیو را دارند، قابل استفاده است.
چنانچه Filegroup ای را از حالت Read Only دوباره خارج کنیم، میتوان عملیات Write را دوباره برای آن انجام داد.
پس از ایجاد بانک اطلاعاتی، گام بعدی ایجاد یک Partition Function و پس از آن یک Partition Schema است. همانطور که مشاهده میکنید در Partition Function از سه مقدار استفاده شده، بنابراین در Partition Schema باید از چهار Filegroup استفاده شود، که در مثال ما از Filegroup پیش فرض که Primary است، استفاده شده است.
USE [PartitionDB] GO CREATE PARTITION FUNCTION pfOrderDateRange(DATETIME) AS RANGE LEFT FOR VALUES ('2010/12/31','2011/12/31','2012/12/31') GO CREATE PARTITION SCHEME psOrderDateRange AS PARTITION pfOrderDateRange TO (FG1,FG2,FG3,[PRIMARY]) GO
پس از طی گامهای قبل، به ایجاد یک جدول به صورت Aligned Index مبادرت ورزیده میشود.
CREATE TABLE Orders ( OrderID INT IDENTITY(1,1) NOT NULL, OrderDate DATETIME NOT NULL, OrderFreight MONEY NULL, ProductID INT NULL, CONSTRAINT PK_Orders PRIMARY KEY CLUSTERED (OrderID ASC, OrderDate ASC) ON psOrderDateRange (OrderDate) ) ON psOrderDateRange (OrderDate) GO
در ادامه برای بررسی درج اطلاعات در پارتیشن با توجه به محدوده آنها اقدام به افزودن رکوردهایی در جدول ساخته شده مینمائیم.
SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2010/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2011/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2012/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO
از طریق دستور Select زیر میتوان نحوه توزیع دادهها را در جدول مشاهده کرد.
USE [PartitionDB] GO SELECT OBJECT_NAME(i.object_id) AS OBJECT_NAME, p.partition_number, fg.NAME AS FILEGROUP_NAME, ROWS, au.total_pages, CASE boundary_value_on_right WHEN 1 THEN 'Less than' ELSE 'Less or equal than' END AS 'Comparition',VALUE FROM sys.partitions p JOIN sys.indexes i ON p.object_id = i.object_id AND p.index_id = i.index_id JOIN sys.partition_schemes ps ON ps.data_space_id = i.data_space_id JOIN sys.partition_functions f ON f.function_id = ps.function_id LEFT JOIN sys.partition_range_values rv ON f.function_id = rv.function_id AND p.partition_number = rv.boundary_id JOIN sys.destination_data_spaces dds ON dds.partition_scheme_id = ps.data_space_id AND dds.destination_id = p.partition_number JOIN sys.filegroups fg ON dds.data_space_id = fg.data_space_id JOIN (SELECT container_id, SUM(total_pages) AS total_pages FROM sys.allocation_units GROUP BY container_id) AS au ON au.container_id = p.partition_id WHERE i.index_id < 2
خروجی دستور فوق به شرح زیر است:
در ادامه به ایجاد یک Filegroup جدید میپردازیم.
/* Query 2-3- Split a partition*/ -- Add FG4: ALTER DATABASE PartitionDB ADD FILEGROUP FG4 Go ALTER PARTITION SCHEME [psOrderDateRange] NEXT USED FG4 GO ALTER PARTITION FUNCTION [pfOrderDateRange]() SPLIT RANGE('2013/12/31') GO -- Add Partition 4 (P4) to FG4: GO ALTER DATABASE PartitionDB ADD FILE ( NAME = P4, FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\P4.NDF' , SIZE = 1024KB , MAXSIZE = UNLIMITED, FILEGROWTH = 10%) TO FILEGROUP [FG4] -- GO
و در ادامه به درج اطلاعاتی برای بررسی نحوه توزیع دادهها در Filegroup هایمان میپردازیم.
SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2013/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2012/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO
جهت ادغام پارتیشنها به طریق زیر عمل میشود:
/* Query 2-4- Merge Partitions */ ALTER PARTITION FUNCTION [pfOrderDateRange]() MERGE RANGE('2010/12/31') Go
به منظور آرشیو نمودن اطلاعات به طریق زیر از Switch استفاده میکنیم. ابتدا یک جدول موقتی برای ذخیره رکوردهایی که قصد آرشیو آنها را داریم، ایجاد میکنیم. همانگونه که در تعریف جدول مشاهده میکنید، نام Filegroup ای که برای ساخت این جدول استفاده میشود، با Filegroup ای که قصد آرشیو اطلاعات آنرا داریم، یکسان است.
در ادامه میتوان مثلاً با ایجاد یک Temporary Table به انتقال این اطلاعات بدون توجه به Filegroup آنها پرداخت.
/* Query 2-5- Switch Partitions */ USE [PartitionDB] GO CREATE TABLE [dbo].[Orders_Temp]( [OrderID] [int] IDENTITY(1,1) NOT NULL, [OrderDate] [datetime] NOT NULL, [OrderFreight] [money] NULL, [ProductID] [int] NULL, CONSTRAINT [PK_OrdersTemp] PRIMARY KEY CLUSTERED ([OrderID] ASC,[OrderDate] ASC)ON FG2 ) ON FG2 GO USE [tempdb] GO CREATE TABLE [dbo].[Orders_Hist]( [OrderID] [int] NOT NULL, [OrderDate] [datetime] NOT NULL, [OrderFreight] [money] NULL, [ProductID] [int] NULL, CONSTRAINT [PK_OrdersTemp] PRIMARY KEY CLUSTERED ([OrderID] ASC,[OrderDate] ASC) ) GO USE [PartitionDB] GO ALTER TABLE [dbo].[Orders] SWITCH PARTITION 1 TO [dbo].[Orders_Temp] GO INSERT INTO [tempdb].[dbo].[Orders_Hist] SELECT * FROM [dbo].[Orders_Temp] GO DROP TABLE [dbo].[Orders_Temp] GO SELECT * FROM [tempdb].[dbo].[Orders_Hist]
- تنظیمات برنامه را از چندین منبع مختلف خوانده و آنها را یکی کنید.
- تنظیمات را بر اساس تنظیمات مختلف محیطی برنامه، بارگذاری کنید.
- امکان نگاشت اطلاعات خوانده شدهی از فایلهای کانفیگ به کلاسها پیش بینی شدهاست.
- امکان بارگذاری مجدد فایلهای کانفیگ درصورت تغییر، بدون ریاستارت کل برنامه وجود دارد.
- امکان تزریق وابستگیهای تنظیمات برنامه، به قسمتهای مختلف آن پیش بینی شدهاست.
نصب پیشنیاز خواندن تنظیمات برنامه از یک فایل JSON
برای شروع به کار با خواندن تنظیمات برنامه در ASP.NET Core، نیاز است ابتدا بستهی نیوگت Microsoft.Extensions.Configuration.Json را نصب کنیم.
برای این منظور بر روی گره references کلیک راست کرده و گزینهی manage nuget packages را انتخاب کنید. سپس در برگهی browse آن Microsoft.Extensions.Configuration.Json را جستجو کرده و نصب نمائید:
البته همانطور که در تصویر مشاهده میکنید، اگر صرفا Microsoft.Extensions.Configuration را جستجو کنید (بدون ذکر JSON)، بستههای مرتبط با خواندن فایلهای کانفیگ از نوع XML و یا حتی INI را هم خواهید یافت.
انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{ "dependencies": { //same as before "Microsoft.Extensions.Configuration.Json": "1.0.0" },
افزودن یک فایل کانفیگ JSON دلخواه
بر روی پروژه کلیک راست کرده و از طریق منوی add->new item یک فایل خالی جدید را به نام appsettings.json ایجاد کنید (نام این فایل دلخواه است)؛ با این محتوا:
{ "Key1": "Value1", "Auth": { "Users": [ "Test1", "Test2", "Test3" ] }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } }
<appSettings> <add key="Logging-IncludeScopes" value="false" /> <add key="Logging-Level-Default" value="verbose" /> <add key="Logging-Level-System" value="Information" /> <add key="Logging-Level-Microsoft" value="Information" /> </appSettings>
در فایل JSON فوق، نمونهای از key/valueها، آرایهها و اطلاعات چندین سطحی را مشاهده میکنید.
خواندن فایل تنظیمات appsettings.json در برنامه
پس از نصب پیشنیاز خواندن فایلهای کانفیگ از نوع JSON، به فایل آغازین برنامه مراجعه کرده و سازندهی جدیدی را به آن اضافه کنید:
public class Startup { public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json"); Configuration = builder.Build(); }
الف) این خواندن، در سازندهی کلاس آغازین برنامه و پیش از تمام تنظیمات دیگر باید انجام شود.
ب) جهت در معرض دید قرار دادن اطلاعات خوانده شده، آنرا به یک خاصیت عمومی انتساب دادهایم.
ج) متد SetBasePath جهت مشخص کردن محل یافتن فایل appsettings.json ذکر شدهاست. این اطلاعات را میتوان از سرویس توکار IHostingEnvironment و خاصیت ContentRootPath آن دریافت کرد. همانطور که ملاحظه میکنید، این تزریق وابستگی نیز به صورت خودکار توسط ASP.NET Core مدیریت میشود.
دسترسی به تنظیمات خوانده شده توسط اینترفیس IConfigurationRoot
تا اینجا موفق شدیم تا تنظیمات خوانده شده را به خاصیت عمومی Configuration از نوع IConfigurationRoot انتساب دهیم. اما ساختار ذخیره شدهی در این اینترفیس به چه صورتی است؟
همانطور که مشاهده میکنید، هر سطح از سطح قبلی آن با : جدا شدهاست. همچنین اعضای آرایه، دارای ایندکسهای 0: و 1: و 2: هستند. بنابراین برای خواندن این اطلاعات میتوان نوشت:
var key1 = Configuration["Key1"]; var user1 = Configuration["Auth:Users:0"]; var authUsers = Configuration.GetSection("Auth:Users").GetChildren().Select(x => x.Value).ToArray(); var loggingIncludeScopes = Configuration["Logging:IncludeScopes"]; var loggingLoggingLogLevelDefault = Configuration["Logging:LogLevel:Default"];
یک نکته: خاصیت Configuration، دارای متد GetValue نیز هست که توسط آن میتوان نوع مقدار دریافتی و یا حتی مقدار پیش فرضی را در صورت عدم وجود این key، مشخص کرد:
var val = Configuration.GetValue<int>("key-name", defaultValue: 10);
سرویس IConfigurationRoot قابل تزریق است
در قسمت قبل، سرویسها و تزریق وابستگیها را بررسی کردیم. نکتهی جالبی را که میتوان به آن اضافه کرد، قابلیت تزریق خاصیت عمومی
public class Startup { public IConfigurationRoot Configuration { set; get; }
الف) اعلام موجودیت IConfigurationRoot به IoC Container
اگر از استراکچرمپ استفاده میکنید، باید مشخص کنید، زمانیکه IConfigurationRoot درخواست شد، آنرا چگونه باید از خاصیت مرتبط با آن دریافت کند:
var container = new Container(); container.Configure(config => { config.For<IConfigurationRoot>().Singleton().Use(() => Configuration);
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddSingleton<IConfigurationRoot>(provider => { return Configuration; });
ب) فایل project.json کتابخانهی Core1RtmEmptyTest.Services را گشوده و وابستگی Microsoft.Extensions.Configuration.Abstractions را به آن اضافه کنید:
{ "dependencies": { //same as before "Microsoft.Extensions.Configuration.Abstractions": "1.0.0" }
ج) سپس فایل MessagesService.cs را گشوده و این اینترفیس را به سازندهی سرویس MessagesService تزریق میکنیم:
public interface IMessagesService { string GetSiteName(); } public class MessagesService : IMessagesService { private readonly IConfigurationRoot _configurationRoot; public MessagesService(IConfigurationRoot configurationRoot) { _configurationRoot = configurationRoot; } public string GetSiteName() { var key1 = _configurationRoot["Key1"]; return $"DNT {key1}"; } }
اکنون اگر برنامه را اجرا کنید، با توجه به اینکه میان افزار Run از این سرویس سفارشی استفاده میکند:
public void Configure( IApplicationBuilder app, IHostingEnvironment env, IMessagesService messagesService) { app.Run(async context => { var siteName = messagesService.GetSiteName(); await context.Response.WriteAsync($"Hello {siteName}"); }); }
خواندن تنظیمات از حافظه
الزاما نیازی به استفاده از فایلهای JSON و یا XML در اینجا وجود ندارد. ابتداییترین حالت کار با بستهی Microsoft.Extensions.Configuration، متد AddInMemoryCollection آن است که در اینجا میتوان لیستی از key/valueها را ذکر کرد:
var builder = new ConfigurationBuilder() .AddInMemoryCollection(new[] { new KeyValuePair<string,string>("the-key", "the-value"), });
var theValue = Configuration["the-key"];
امکان بازنویسی تنظیمات انجام شده، بسته به شرایط محیطی
در اینجا محدود به یک فایل JSON و یک فایل تنظیمات برنامه، نیستیم. برای کار با ConfigurationBuilder میتوان از Fluent interface آن استفاده کرد و به هر تعدادی که نیاز بود، متدهای خواندن از فایلهای کانفیگ دیگر را اضافه کرد:
public class Startup { public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddInMemoryCollection(new[] { new KeyValuePair<string,string>("the-key", "the-value"), }) .AddJsonFile("appsettings.json", reloadOnChange: true, optional: false) .AddJsonFile($"appsettings.{env}.json", optional: true); Configuration = builder.Build(); }
برای مثال در اینجا آخرین AddJsonFile تعریف شده، بنابر متغیر محیطی فعلی به appsettings.development.json تفسیر شده و در صورت وجود این فایل (با توجه به optional بودن آن) اطلاعات آن دریافت گردیده و اطلاعات مشابه فایل appsettings.json قبلی را بازنویسی میکند.
امکان دسترسی به متغیرهای محیطی سیستم عامل
در انتهای زنجیرهی ConfigurationBuilder میتوان متد AddEnvironmentVariables را نیز ذکر کرد:
var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables();
امکان نگاشت تنظیمات برنامه به کلاسهای متناظر
کار کردن با key/valueهای رشتهای، هرچند روش پایهای استفادهی از تنظیمات برنامه است، اما آنچنان refactoring friendly نیست. در ASP.NET Core امکان تعریف تنظیمات strongly typed نیز پیش بینی شدهاست. برای این منظور باید مراحل زیر طی شوند:
به عنوان نمونه تنظیمات فرضی smtp ذیل را به انتهای فایل appsettings.json اضافه کنید:
{ "Key1": "Value1", "Auth": { "Users": [ "Test1", "Test2", "Test3" ] }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } }, "Smtp": { "Server": "0.0.0.1", "User": "user@company.com", "Pass": "123456789", "Port": "25" } }
در این کتابخانهی جدید که محل نگهداری ViewModelهای برنامه خواهد بود، کلاس معادل قسمت smtp فایل config فوق را اضافه کنید:
namespace Core1RtmEmptyTest.ViewModels { public class SmtpConfig { public string Server { get; set; } public string User { get; set; } public string Pass { get; set; } public int Port { get; set; } } }
سپس به پروژهی Core1RtmEmptyTest مراجعه کرده و بر روی گره references آن کلیک راست کنید. در اینجا گزینهی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.ViewModels را انتخاب کنید، تا اسمبلی آنرا بتوان در پروژهی جاری استفاده کرد.
انجام اینکار معادل است با افزودن یک سطر ذیل به فایل project.json پروژه:
{ "dependencies": { // same as before "Core1RtmEmptyTest.ViewModels": "1.0.0-*" },
و سپس در کلاس آغازین برنامه و متد ConfigureServices آن، نحوهی نگاشت قسمت Smtp فایل کانفیگ را مشخص کنید:
public IServiceProvider ConfigureServices(IServiceCollection services) { services.Configure<SmtpConfig>(options => Configuration.GetSection("Smtp").Bind(options));
سپس برای استفاده از این تنظیمات strongly typed (برای نمونه در لایه سرویس برنامه)، ابتدا ارجاعی را به پروژهی Core1RtmEmptyTest.ViewModels به لایهی سرویس برنامه اضافه میکنیم (بر روی گره references آن کلیک راست کنید. در اینجا گزینهی add reference را انتخاب کرده و سپس Core1RtmEmptyTest.ViewModels را انتخاب کنید).
در ادامه نیاز است بستهی نیوگت جدیدی را به نام Microsoft.Extensions.Options به لایهی سرویس برنامه اضافه کنیم. به این ترتیب قسمت وابستگیهای فایل project.json این لایه چنین شکلی را پیدا میکند:
"dependencies": { "Core1RtmEmptyTest.ViewModels": "1.0.0-*", "Microsoft.Extensions.Configuration.Abstractions": "1.0.0", "Microsoft.Extensions.Options": "1.0.0", "NETStandard.Library": "1.6.0" }
public interface IMessagesService { string GetSiteName(); } public class MessagesService : IMessagesService { private readonly IConfigurationRoot _configurationRoot; private readonly IOptions<SmtpConfig> _settings; public MessagesService(IConfigurationRoot configurationRoot, IOptions<SmtpConfig> settings) { _configurationRoot = configurationRoot; _settings = settings; } public string GetSiteName() { var key1 = _configurationRoot["Key1"]; var server = _settings.Value.Server; return $"DNT {key1} - {server}"; } }
اکنون اگر برنامه را جرا کنید، این خروجی را میتوان مشاهده کرد (که در آن آدرس Server دریافت شدهی از فایل کانفیگ نیز مشخص است):
البته همانطور که در قسمت قبل نیز عنوان شد، این تزریق وابستگیها در تمام قسمتهای برنامه کار میکند. برای مثال در کنترلرها هم میتوان <IOptions<SmtpConfig را به همین نحو تزریق کرد.
نحوهی واکنش به تغییرات فایلهای کانفیگ
در نگارشهای قبلی ASP.NET، هر تغییری در فایل web.config، سبب ریاستارت شدن کل برنامه میشد که این مساله نیز خود سبب بروز مشکلات زیادی مانند از دست رفتن سشن تمام کاربران میشد.
در ASP.NET Core، برنامهی وب ما دیگر متکی به فایل web.config نبوده و همچنین میتوان چندین و چند نوع فایل config داشت. به علاوه در اینجا متدهای مرتبط معرفی فایلهای کانفیگ دارای پارامتر مخصوص reloadOnChange نیز هستند:
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
همانطور که مطلع هستید از ویندوز ویستا به بعد، فرمت قدیمی فایلهای راهنمای ویندوز (فایلهای hlp) منسوخ شده تلقی میشود و فرمت پیشنهادی، chm است. نرم افزارهای زیادی برای تهیه فایلهای compiled html help یا همان chm های معروف وجود دارند که معروفترین آنها برنامهی Help & Manual است.
اما تمام این برنامهها در حقیقت پوستهای هستند برای برنامهی رایگان html help work shop مایکروسافت و در نهایت از کامپایلر آن استفاده میکنند. بنابراین چرا از برنامهی رایگان اصلی استفاده نشود؟
اگر تا به حال با html help work shop کار نکردهاید، در ادامه مروری سریع بر آن خواهیم داشت:
الف) درست کردن فایلهای صفحات راهنما
برنامهی Help & Manual ایی که معرفی شد و تمام نمونههای مشابه آن، تنها کار مهمی را که انجام میدهند این است که شما را از یک html editor بینیاز میکنند. بنابراین زمانیکه میخواهیم از برنامهی اصلی html help work shop استفاده کنیم نیاز به یک html editor خارجی برای تهیه فایلهای راهنما خواهیم داشت. مثلا مرحوم front page یا نگارش جدید آن به نام Microsoft expression web یا Dreamweaver یا Aptana یا حتی notepad !
در اینجا تنها مشخص کردن نوع encoding نمایش صفحه برای صحیح نمایش داده شدن متون فارسی کافی است. اما این تمام ماجرا نیست.
ب) کامپایل کردن فایلهای راهنمای ایجاد شده
برای این منظور بجای آپلود بیش از 30 تصویر جهت توضیحات قدم به قدم نحوهی انجام اینکار، یک فایل ویدیویی درست کردهام که آنرا از آدرس زیر میتوانید دریافت کنید.
دریافت فایل
همانطور که در این فایل آموزشی مقدماتی هم تاکید شده، به صورت پیش فرض جستجوی فارسی کار نمیکند. همچنین اگر یک متن انگلیسی را جستجو کنید، صفحات یاد شده با عنوانی به هم ریخته نمایش داده میشوند. در صفحه افزودن به علاقمندیها نیز یک چنین مشکلی با عنوانها وجود دارد.
در قسمت دوم، نحوهی رفع این دو مشکل (مشکل جستجوی عبارات فارسی و مشکل عنوانهای به هم ریخته) را بررسی خواهیم کرد.
ادامه دارد ...
System.Web.dll System.Web.ApplicationServices.dll
C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\WebMatrix.Data.dll C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\WebMatrix.WebData.dll
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="DefaultConnection" connectionString="Data Source=.;Initial Catalog=SimpleMembershipProviderDB;Integrated Security=True;" providerName="System.Data.SqlClient"/> </connectionStrings> <system.web> <roleManager enabled="true" defaultProvider="SimpleRoleProvider"> <providers> <clear/> <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/> </providers> </roleManager> <membership defaultProvider="SimpleMembershipProvider"> <providers> <clear/> <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData"/> </providers> </membership> </system.web> </configuration>
using System; using System.Security.Principal; using System.Web.Security; using WebMatrix.WebData; namespace MemberShipConsoleApplication { class Program { static void Main(string[] args) { WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); AddUserAndRolSample(); Login(); if (System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated) RunApp(); } static void AddUserAndRolSample() { if (WebSecurity.UserExists("iman")) return; // No implements in SimpleMembershipProvider : // Membership.CreateUser("iman", "123"); WebSecurity.CreateUserAndAccount("iman", "123"); Roles.CreateRole("admin"); Roles.CreateRole("User"); Roles.AddUserToRole("iman", "admin"); } static void Login() { for (int i = 0; i < 3; i++) { Console.Write("UserName: "); var userName = Console.ReadLine(); Console.Write("Password: "); var password = Console.ReadLine(); if (Membership.ValidateUser(userName, password)) { var user = Membership.GetUser(userName); var identity = new GenericIdentity(user.UserName); var principal = new RolePrincipal(identity); System.Threading.Thread.CurrentPrincipal = principal; Console.Clear(); return; } Console.WriteLine("User Name Or Password Not Valid."); } } static void RunApp() { Console.WriteLine("Welcome To MemberShip. User Name: {0}", System.Threading.Thread.CurrentPrincipal.Identity.Name); if (System.Threading.Thread.CurrentPrincipal.IsInRole("admin")) Console.WriteLine("Hello Admin User!"); Console.Read(); } } }
InitializeDatabaseConnection(string connectionStringName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables)
به عنوان مثال:
- New
- Approved
- Committed
- Done
- Removed
در وضعیتهای فوق چنانچه یک Work item اشتباهاً به Backlog افزوده شود، تنها راه اینکه اعلام کنیم این Work item از دستور کار خارج شده است، اینست که آنرا در وضعیت Removed قرار دهیم.
در هیچ کجای رابط کاربری (تحت وب و Team explorer در ویژوال استدیو) امکانی برای حذف یک Work item در نظر گرفته نشده است.
در ادامه روش حذف فیزیکی یک Work item از TFS را خواهیم دید:
1- ابتدا وارد خط فرمان (Command Prompt) شده و به مسیر زیر بروید:
cd %programfiles(x86)%\Microsoft Visual Studio 12.0\Common7\IDE
witadmin destroywi /collection:CollectionURL /id:id
witadmin destroywi /collection:"https://mytfs.visualstudio.com/DefaultCollection " /id:357
چنانچه این قابلیت نظر شما را جلب کرد، توصیه میکنم نگاهی هم به سایر سوییچهای فرمان witadmin بیاندازید.
پیشنیازهای نصب Docker بر روی ویندوز
مطابق مستندات آن، برای نصب داکر بر روی ویندوز به حداقلهای زیر نیاز است:
- استفاده از ویندوز 10 نگارش enterprise، که شماره نگارش آن حداقل 1607 باشد (حداقل Anniversary Update باشد).
- مجازی سازی در BIOS فعال شده باشد.
البته مجازی سازی عموما به صورت پیشفرض فعال است. برای بررسی آن، taskmanager ویندوز را اجرا کرده و در برگهی Performance آن، جائیکه مشخصات CPU را نمایش میدهد، یک سطر به Virtualization اختصاص دارد که مقدار آن باید enabled باشد (تصویر زیر) و اگر نیست، برای فعال کردن آن باید به تنظیمات BIOS سیستم خود مراجعه کنید:
روش دیگر دریافت این اطلاعات، اجرای دستور systeminfo در خط فرمان، با دسترسی مدیریتی است. در خروجی آن، عبارت «Virtualization Enabled In Firmware» را جستجو کنید که باید مقدار آن yes باشد.
- داشتن CPU با قابلیت SLAT یا Second Level Address Translation.
برای یافتن این موضوع، برنامهی coreinfo را دریافت کرده و آنرا به صورت coreinfo -v اجرا کنید. خروجی آن سه سطر مرتبط با مجازی سازی را به همراه دارد. اگر قابلیتی موجود نباشد، جلوی آن یک خط تیره و اگر قابلیتی موجود باشد، روبروی آن یک ستاره را مشاهده خواهید کرد.
روش دیگر بررسی آن، اجرای دستور msinfo32 در قسمت run ویندوز و سپس enter است. در قسمت system summary، اطلاعات Second Level Address Translation قابل مشاهده هستند (اگر No باشد، امکان اجرای containerهای لینوکسی را بر روی ویندوز نخواهید داشت):
- داشتن حداقل 4 گیگابایت RAM.
- فعال بودن Hyper-V نیز برای اجرای Linux Containers بر روی ویندوز، ضروری است (نصاب Docker، اینکار را به صورت خودکار انجام میدهد).
دریافت نصاب Docker for Windows
برای دریافت نصاب داکر مخصوص ویندوز، به آدرس زیر مراجعه کنید:
https://store.docker.com/editions/community/docker-ce-desktop-windows
که بلافاصله با تصویر کریه زیر مواجه خواهید شد:
برای رفع این مشکل، میتوان از روش مطرح شدهی در مطلب «یک روش ساده برای دور زدن تحریمها!» استفاده کرد؛ یعنی تنظیم DNS به 178.22.122.100 به صورت زیر:
پس از این تغییر، چون IP قابل مشاهدهی سیستم شما توسط سایت داکر تغییر میکند، اینبار صفحهی دریافت Docker Community Edition for Windows به صورت زیر ظاهر میشود:
همانطور که مشاهده میکنید، عنوان کردهاست که لطفا لاگین کنید تا بتوانید این برنامه را دریافت کنید. به همین جهت بر روی لینک آن کلیک کرده، یک اکانت جدید را در سایت docker ایجاد کنید (با یک ایمیل واقعی که تائیدیه آنرا دریافت خواهید کرد). پس از آن، با این اکانت جدید به سایت داکر وارد شوید تا لینک دریافت فایل exe نصاب آنرا دریافت کنید.
در این حالت مرورگر و یا حتی دانلودمنیجر شما بدون مشکل میتوانند این فایل را دریافت کنند و همان تنظیم DNS فوق، مشکل عدم دسترسی را برطرف میکند.
نصب Docker for Windows
پس از اجرای نصاب آن و پایان عملیات نصب (که تنها کافی است در صفحهی ابتدایی آن تیک مربوط به Windows Containers را نیز قرار دهید)، نیاز دارد تا شما را یکبار از سیستم Logout و login کند. پس از ورود به سیستم، تنظیمات ابتدایی آن به صورت خودکار صورت گرفته و در صورت فعال نبودن Hyper-V، پیام زیر را مشاهده خواهید کرد:
بر روی OK کلیک کنید تا اینکار با موفقیت به پایان برسد. البته پس از آن، منتظر حداقل یکبار ریاستارت شدن خودکار سیستم، بدون اطلاع قبلی نیز باشید.
یک نکته: کاری که در قسمت فعالسازی Hyper-V به صورت خودکار انجام میشود، شامل اجرای سه دستور زیر، در کنسول پاور شل، با دسترسی مدیریتی و سپس ری استارت سیستم است:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All -Verbose Enable-WindowsOptionalFeature -Online -FeatureName Containers -All -Verbose bcdedit /set hypervisorlaunchtype Auto
C:\Users\Vahid>docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 18.06.1-ce OSType: windows
بررسی تنظیمات سوئیچ کردن بین Linux Containers و Windows Containers
پس از اتمام ریاستارتها، برای آزمایش فعال بودن Hyper-V، در قسمت Run ویندوز، عبارت Virtmgmt.msc را نوشته و enter کنید. اگر تصویر زیر را مشاهده نمیکنید:
یکبار بر روی آیکن Docker در قسمت Tray Icons ویندوز کلیک راست کرده و گزینهی switch to Linux containers را انتخاب کنید تا پس از مدتی، آیکن MobyLinuxVM در قسمت virtual machines (تصویر فوق) ظاهر شود.
اگر پس از انتخاب این گزینه، پیام زیر را دریافت کردید:
و یا اگر بر روی این ماشین مجازی کلیک راست کردید و گزینهی Start آنرا انتخاب کردید و پیام زیر ظاهر شد:
قسمت «پیشنیازهای نصب Docker بر روی ویندوز» را با دقت بررسی کنید (خصوصا قسمت BIOS و SLAT). نبود یکی از موارد ذکر شده، سبب بروز این مشکل میشود.
برای مثال اجرای دستور coreinfo -v بر روی سیستم من چنین خروجی را به همراه دارد:
E:\>coreinfo -v AuthenticAMD Microcode signature: 00000000 HYPERVISOR - Hypervisor is present SVM * Supports AMD hardware-assisted virtualization NP - Supports AMD nested page tables (SLAT)
همانطور که مشاهده میکنید، قابلیت SLAT در CPU این سیستم وجود ندارد. به همین جهت نمیتوان به Linux containers سوئیچ کرد. هرچند windows containers آن کار میکند.
روش دیگر مشاهدهی این خطا، مراجعهی به event viewer ویندوز است. در قسمت خطاهای سیستم، ممکن است خطای زیر را مشاهده کنید:
Hypervisor launch failed; Second Level Address Translation is required to launch the hypervisor.
آزمایش Docker نصب شده
پس از نصب docker، خط فرمان ویندوز را گشوده و دستور زیر را صادر کنید:
docker run hello-world
یک نکته: این image، یک image لینوکسی است. به همین جهت پیش از اجرای این دستور، همانطور که پیشتر نیز عنوان شد، یکبار بر روی آیکن Docker در قسمت Tray Icons ویندوز کلیک راست کرده و گزینهی switch to Linux containers را انتخاب کنید. سپس دستور docker run hello-world را اجرا نمائید.
و یا در همین حال دستور docker run -p 80:80 nginx را صادر کنید تا وب سرور لینوکسی nginx را بتوانید تحت ویندوز اجرا کنید. پس از خاتمهی عملیات دریافت و اجرای وب سرور، با توجه به تنظیم p 80:80-، پورت 80 میزبان (اولین عدد)، به پورت 80 کانتینر نگاشت شدهاست. به همین جهت تنها با اجرای دستور http://localhost، خروجی این وب سرور را میتوانید در مرورگر سیستم خود مشاهده کنید.
همانطور که مشاهده میکنید، با استفاده از داکر، پیش از آنکه بدانیم چگونه باید یک نرم افزار را نصب کرد، میتوان از آن استفاده کرد!
روش متوقف کردن Containers در حال اجرا
اگر دستور docker ps را در خط فرمان ویندوز اجرا کنید، لیست پروسههای اجرا شدهی توسط آن قابل مشاهده هستند. در این لیست container id در حال اجرا نیز مشخص است. برای خاتمهی کار آن، تنها کافی است دستور docker stop id را اجرا کنید.
یک نکته: ضرورتی به ذکر کامل id نیست. برای مثال ذکر سه حرف اول آن نیز کفایت میکند.
روش اجرای مجدد یک Container
فرض کنید میخواهیم سرور nginx را مجددا اجرا کنیم. یک روش آن، اجرای مجدد دستور docker run -p 80:80 nginx است که پیشتر آنرا انجام دادیم. در این حالت این image تبدیل به container شده و همانند روشهای متداول نصب نرم افزار، اکنون به عنوان یک نرم افزار نصب شده در دسترس است. برای مشاهدهی لیست آنها، دستور docker ps -a را اجرا کنید. این لیست تا این لحظه باید شامل containerهای nginx و hello-world باشد. متوقف کردن یک container، سبب تخریب یا حذف آن نمیشود. در این حالت در لیستی که توسط دستور docker ps -a نمایش داده شدهاست، باز هم container idها قابل مشاهده هستند. فقط کافی است برای اجرای یکی از آنها، دستور docker start id را اجرا کرد. به این صورت دیگر نیازی به ذکر دستور کامل docker run با تمام پارامترهای آن نیست. این id نیز همانطور که ذکر شد، میتواند سه حرف ابتدایی این id باشد تا حدی که نسبت به سایر idهای موجود، منحصربفرد شناخته شود. یا بجای container id میتوان container name نمایش داده شدهی در این لیست را استفاده کرد.
پس از اجرای nginx توسط دستور docker start id، دو روش برای بررسی در حال اجرا بودن آن وجود دارد:
الف) مرورگر را باز کنیم و آدرس http://localhost را بررسی کنیم.
ب) دستور docker ps را در خط فرمان اجرا کنیم، تا مشخص شود که آیا پروسهی nginx در حال اجرا است یا خیر؟
بنابراین دستور docker ps -a لیست تمام containers در حال اجرا و همچنین متوقف شده را نمایش میدهد. اما دستور docker ps تنها لیست containers در حال اجرا را نمایش خواهد داد.
روش حذف Containers از Docker
همانطور که در قسمت قبل نیز بحث شد، معادل نصب نرم افزار در اینجا، ایجاد یک container از یک image دریافتی از docker hub است. روش عکس آن، یعنی تخریب یک container، دقیقا معادل عزل نرم افزار از سیستم، در حالتهای متداول است. برای اینکار مجددا دستور docker ps -a را اجرا میکنیم تا لیست تمام containerهای در حال اجرا و همچنین متوقف شده نمایش داده شوند. لیستی که در اینجا نمایش داده میشود، شبیه به لیستی است که در قسمت add/remove programs ویندوز مشاهده میکنید. این لیست معادل لیست نرم افزارهای نصب شدهی بر روی سیستم است و یا برای مشاهدهی لیست imageهای دریافتی از docker hub میتوان دستور docker images را صادر کرد.
قبل از حذف یک container نیاز است آنرا متوقف کنیم. برای این منظور از دستور docker stop id استفاده میشود. سپس اجرای دستور docker rm id، سبب حذف کامل این container خواهد شد. برای آزمایش آن، مجددا دستور docker ps -a را اجرا کنید.
دستور docker rm چندین id را نیز میپذیرد. میتوان این idها و یا حتی سه حرف ابتدایی آنها را با فاصله در اینجا ذکر کرد. علاوه بر id، ذکر نام containers نیز مجاز است.
روش حذف Imageهای دریافتی از Docker Hub
دستور docker rm، فقط containers را از سیستم حذف میکند (نرم افزارهای نصب شده). اما خود imageهای اصلی دریافت شدهی از docker hub را حذف نمیکند (معادل همان فایلهای zip دریافت نرم افزار یا برنامههای نصاب، در حالت متداول و سنتی نصب نرم افزار). برای آزمایش آن دستور docker images را اجرا کنید. هنوز هم در لیست آن، تمام موارد دریافتی موجود هستند.
برای حذف یک image میتوان از دستور docker rmi id استفاده کرد (rmi بجای rm). این id نیز در لیست docker images ظاهر میشود و ذکر قسمتی از آن، تا حدی که نسبت به سایر idهای لیست شده منحصربفرد باشد، کافی است. در اینجا بجای id، از نام image نیز میتوان استفاده کرد. همچنین ذکر چندین id و یا نام نیز پس از دستور docker rmi، میسر است.
روش جستجوی imageها در Docker Hub توسط Docker CLI
فرض کنید میخواهیم image مربوط به راهنمای Docker را از Docker Hub دریافت کنیم. یک روش آن مراجعهی مستقیم به سایت آن است و استفاده از امکانات جستجوی فراهم شدهی در آن سایت. روش دیگر، استفاده از Docker CLI است. اگر دستور docker search docs را در خط فرمان اجرا کنیم، لیست تمام مخازن کدی که در آنها واژهی docs قرار دارد، نمایش داده میشود. البته پیش از نصب image آن بهتر است به برگهی tags مخزن کد آن نیز مراجعه کنید تا بتوانید حجم آنرا نیز مشاهده نمائید که حدود یک گیگابایت است. مخازن docker hub، حاوی imageهای نصاب containerهای متناظر هستند. برای دریافت و اجرای آن میتوان دستور docker run -p 4000:4000 docs/docker.github.io را اجرا کرد.
پس از دریافت یک گیگابایت مستندات، container آن بر روی پورت 4000 در سیستم ما (http://localhost:4000)، به صورت یک وب سایت استاتیک، قابل دسترسی خواهد بود. به این صورت میتوان به مستندات کامل داکر به صورت آفلاین دسترسی داشت.
مفهوم Interactive Terminal در Docker
زمانیکه دستور اجرای مستندات آفلاین را صادر میکنید، در انتهای آن عنوان میکند که وب سایت محلی آن بر روی پورت 4000 قابل دسترسی است. سپس در ذیل آن ذکر شدهاست که اگر ctrl+c را فشار دهید، اجرای آن به پایان میرسد. اما عملا اینطور نیست و اگر دستور docker ps را صادر کنید، هنوز container در حال اجرای آن را میتوان مشاهده کرد.
اما اگر اینبار دستور اجرای docker run را به همراه یک interactive terminal با سوئیچ it و نام docs صادر کنیم:
docker run -p 4000:4000 -it --name docs docs/docker.github.io
سوئیچ it یا interactive terminal سبب میشود تا یک container در foreground، بجای background اجرا شود. به این ترتیب دستور ctrl+c، سبب خاتمهی واقعی پروسهی درحال اجرای در container میشود.
روش دیگر خاتمهی این container، استفاده از نام ذکر شدهاست؛ یعنی اجرای دستور docker stop docs.
یک نکته: اگر میخواهید از terminal باز شده قطع شوید (مجددا به command prompt باز گردید) اما سبب خاتمهی container آن نشوید، از ترکیب ctrl+p+q استفاده کنید.
اجرای containerهای ویندوزی
در مورد نحوهی سوئیچ بین نوعهای مختلف containerهای ویندوزی و لینوکسی پیشتر توضیح دادیم. برای این منظور میتوان بر روی آیکن Docker در قسمت Tray Icons ویندوز کلیک راست کرده و گزینهی switch to Windows/Linux containers را انتخاب کرد. باید دقت داشت که پشتیبانی از containerهای ویندوزی، از ویندوز 10، نگارش 1607، یا همان Anniversary Update آن به بعد، به ویژگیهای ویندوز اضافه شدهاند که به صورت خودکار توسط docker فعالسازی میشوند:
اجرای IIS به عنوان یک Windows Container
تا اینجا imageهای دریافتی، لینوکسی بودند. اگر گزینهی Windows Containers را به روشی که گفته شد، فعال کنید، اینبار با اجرای دستورات docker ps و یا docker images، هیچ خروجی را دریافت نخواهید کرد. از این جهت که کانتینرهای ویندوزی و لینوکسی، به صورت کاملا ایزولهای از هم اجرا و مدیریت میشوند. علت آنرا هم در MobyLinuxVM که پیشتر با اجرای دستور Virtmgmt.msc بررسی کردیم، میتوان یافت. Containerهای لینوکسی، در داخل MobyLinuxVM اجرا میشوند.
در اینجا به عنوان مثال میتوان image رسمی مربوط به IIS را از docker hub دریافت و به صورت یک کانتینر ویندوزی اجرا کرد. البته پیش از اجرای دستورات آن بهتر است به برگهی tags آن مراجعه کرده و حجمهای نگارشهای مختلف آنرا بررسی کرد. اجرای دستور docker pull microsoft/iis به معنای دریافت tag ای به نام latest است (به حجم 6 گیگابایت!)؛ یعنی با دستور docker pull microsoft/iis:latest یکی است. بنابراین در اینجا بر اساس tagهای مختلف، میتوان دستور pull متفاوتی را صادر کرد. برای مثال اگر دستور docker pull microsoft/iis:nanoserver را صادر کردید، نگارش مخصوص nano server آنرا که فقط 449 مگابایت است، دریافت میکند. بنابراین از این پس به tagهای هر مخزن docker hub خوب دقت کنید و نگارش مختص به سیستم عامل خود را دریافت نمائید. عدم ذکر tag ای، همواره tag ویژهای را به نام latest، دریافت میکند.
با اجرای دستور زیر
docker run -p 81:80 -d --name iis microsoft/iis:nanoserver
یک نکته: مشکلی با اجرای IIS مخصوص نانوسرور بر روی ویندوز 10 به این صورت و توسط داکر نیست. بنابراین پس از اجرای دستور فوق، کار دریافت image و ساخت container و سپس اجرای آن به صورت خودکار انجام شده و بلافاصله به command prompt بازگشت داده میشویم (به علت استفادهی از پارامتر d). اکنون اگر دستور docker ps را صادر کنیم، مشاهده میکنیم که کانتینر IIS مخصوص نانوسرور، هم اکنون بر روی ویندوز 10 در حال اجرا است و در آدرس http://localhost:81 قابل دسترسی است.
جهت تکمیل این بحث، بهتر است image مخصوص nanoserver را نیز از docker hub دریافت و اجرا کنیم:
docker run microsoft/windowsservercore
تنظیمات کارت شبکهی Containers
هنگامیکه پروسهای درون یک container اجرا میشود، ایزوله سازیهای بسیاری نیز در مورد آن اعمال خواهد شد؛ به همین جهت گاهی از اوقات عدهای containerها را با ماشینهای مجازی نیز مقایسه میکنند. برای مثال کانتینرها به همراه network adapter خاص خود نیز هستند؛ درست مانند اینکه یک کامپیوتر مجزای از سیستم جاری میباشند و اگر این network adapter را ping کنیم، میتوان به این صورت نیز به آن کانتینر، دسترسی داشته باشیم.
برای یافتن آن، دستور docker inspect iis را صادر میکنیم. خروجی آن به همراه یک قسمت network نیز هست که داخل آن یک IP Address قابل مشاهده است. این IP است که مختص و منحصربفرد این container است. در ابتدا برای آزمایش آن، میتوان آنرا ping کرد؛ مانند ping 172.27.49.47. همچنین به تمام برنامههای داخل این container توسط این IP نیز میتوان دسترسی یافت. برای مثال فراخوانی http://172.27.49.47:81 در مرورگر، سبب نمایش صفحهی اول IIS میشود. البته اگر اینکار را انجام دهیم، کار نمیکند. علت اینجا است، نگاشت پورتی را که تعریف کردهایم (پورت 81)، به پورتی در کامپیوتر میزبان است و نه این IP ویژه. برنامهی اصلی IIS در داخل container، به پورت 80 بر روی این آدرس IP گوش فرا میدهد. اکنون اگر آدرس http://172.27.49.47:80 را در کامپیوتر میزبان فراخوانی کنیم، کار میکند.
بنابراین هرچند containerها به معنای نرم افزارهای از پیش نصب شدهی در حال اجرا هستند، اما ... به همراه ایزوله سازیهای قابل توجهی بر روی کامپیوتر میزبان اجرا میشوند؛ درست مانند یک کامپیوتر مجزای از آن.
کش توزیع شده در ASP.NET Core
Caching can help improve the performance of an ASP.NET Core application. Distributed caching is helpful when working with an ASP.NET application that’s deployed to a server farm or scalable cloud environment. Microsoft documentation contains examples of doing this with SQL Server or Redis, but in this post,I’ll show you an alternative.