เมื่อออกแบบโซลูชันตามสถาปัตยกรรมไมโครเซอร์วิส เรามักจะพบกับข้อกำหนดสำหรับการจัดการทั้งระบบที่รวดเร็วและง่ายดาย ซึ่งเป็นระบบอัตโนมัติที่สูงที่สุดเท่าที่จะเป็นไปได้ โดยไม่ต้องปรับเปลี่ยนส่วนประกอบแต่ละส่วนที่จำเป็น
นี่เป็นความท้าทายอย่างแท้จริง และนั่นคือสาเหตุที่ฉันตัดสินใจเตรียมบทช่วยสอนที่สาธิตวิธีสร้างสถาปัตยกรรมบริการขนาดเล็กในวิธีที่ง่ายที่สุดเท่าที่จะเป็นไปได้ ซึ่งสามารถปรับขนาดและปรับให้เข้ากับความต้องการของลูกค้าได้อย่างรวดเร็วและง่ายดาย
ฉันไม่ต้องการยุ่งเกี่ยวกับโค้ดและการตั้งค่าของแต่ละบริการ แต่ต้องการควบคุมระบบด้วยคอนเทนเนอร์ที่จัดเตรียมใน Docker เท่านั้น
ผลลัพธ์ที่ได้คือสถาปัตยกรรมบริการไมโครที่เรียบง่ายที่สามารถปรับขนาดได้อย่างง่ายดายด้วยการเปลี่ยนแปลงการตั้งค่าคอนเทนเนอร์เพียงไม่กี่ครั้ง Ocelot จัดเตรียมทุกอย่างอื่นๆ ไว้เป็นเกตเวย์/โหลดบาลานเซอร์ และกงสุลเป็นตัวแทนการค้นหาบริการ
สถาปัตยกรรมดังกล่าวช่วยให้เราสามารถปรับใช้บริการเดียวได้โดยไม่ต้องประสานการปรับใช้ภายในบริการอื่นๆ บริการที่ปรับใช้ใหม่จะถูกลงทะเบียนโดยอัตโนมัติเมื่อค้นพบบริการ และพร้อมใช้งานทันทีผ่านเกตเวย์ คุณคงจินตนาการได้เลยว่าสิ่งนี้จะช่วยเพิ่มประสิทธิภาพให้กับทีมพัฒนาทุกทีมได้ขนาดไหน!
แน่นอนว่าการใช้บริการเกตเวย์เดียวจะกลายเป็นจุดเดียวของความล้มเหลวในสถาปัตยกรรมของเรา ดังนั้นเราจึงจำเป็นต้องปรับใช้อย่างน้อยสองอินสแตนซ์เพื่อให้มีความพร้อมใช้งานสูง แต่ฉันจะทิ้งปัญหานั้นไว้ให้คุณเล่นด้วย
ในการสาธิตครั้งก่อน ฉันแสดงให้เห็นวิธีการใช้ Ocelot เป็นเกตเวย์บริการและโหลดบาลานเซอร์ร่วมกับ Eureka เพื่อการค้นพบบริการ ยูเรก้าสาธิตนี้ใช้กงสุลในการค้นหาบริการแทน
Consul เป็นโซลูชันโครงข่ายบริการที่ให้ส่วนควบคุมที่มีคุณลักษณะครบถ้วนพร้อมฟังก์ชันการค้นหาบริการ การกำหนดค่า และการแบ่งส่วน คุณสมบัติแต่ละอย่างเหล่านี้สามารถใช้แยกกันได้ตามต้องการ หรือใช้ร่วมกันเพื่อสร้างบริการแบบตาข่ายเต็มรูปแบบ กงสุลต้องการระนาบข้อมูลและรองรับทั้งโมเดลการรวมพร็อกซีและเนทิฟ กงสุลจัดส่งพร็อกซีในตัวที่เรียบง่ายเพื่อให้ทุกอย่างใช้งานได้ทันที แต่ยังรองรับการผสานรวมพร็อกซีบุคคลที่สาม เช่น Envoy
คุณสมบัติที่สำคัญของกงสุลคือ:
การค้นพบบริการ : ลูกค้าของกงสุลสามารถลงทะเบียนบริการ เช่น api หรือ mysql และลูกค้ารายอื่นๆ สามารถใช้กงสุลเพื่อค้นหาผู้ให้บริการของบริการที่กำหนดได้ การใช้ DNS หรือ HTTP ทำให้แอปพลิเคชันสามารถค้นหาบริการที่พึ่งพาได้อย่างง่ายดาย
การตรวจสอบสภาพ : ลูกค้ากงสุลสามารถให้การตรวจสุขภาพจำนวนเท่าใดก็ได้ ไม่ว่าจะเกี่ยวข้องกับบริการที่กำหนด ("เว็บเซิร์ฟเวอร์ส่งคืน 200 ตกลง") หรือกับโหนดในเครื่อง ("คือการใช้หน่วยความจำต่ำกว่า 90%") ผู้ดำเนินการสามารถใช้ข้อมูลนี้เพื่อตรวจสอบความสมบูรณ์ของคลัสเตอร์ และส่วนประกอบการค้นพบบริการจะใช้เพื่อกำหนดเส้นทางการรับส่งข้อมูลออกจากโฮสต์ที่ไม่ดี
ร้านค้า KV : แอปพลิเคชันสามารถใช้การจัดเก็บคีย์/ค่าแบบลำดับชั้นของกงสุลเพื่อวัตถุประสงค์หลายประการ รวมถึงการกำหนดค่าแบบไดนามิก การตั้งค่าสถานะคุณลักษณะ การประสานงาน การเลือกตั้งผู้นำ และอื่นๆ HTTP API แบบธรรมดาทำให้ใช้งานง่าย
การสื่อสารบริการที่ปลอดภัย : กงสุลสามารถสร้างและแจกจ่ายใบรับรอง TLS สำหรับบริการเพื่อสร้างการเชื่อมต่อ TLS ร่วมกัน ความตั้งใจสามารถใช้เพื่อกำหนดบริการที่ได้รับอนุญาตให้สื่อสารได้ การแบ่งส่วนบริการสามารถจัดการได้อย่างง่ายดายด้วยความตั้งใจที่สามารถเปลี่ยนแปลงได้แบบเรียลไทม์ แทนที่จะใช้โทโพโลยีเครือข่ายที่ซับซ้อนและกฎไฟร์วอลล์แบบคงที่
Multi Datacenter : กงสุลรองรับศูนย์ข้อมูลหลายตัวตั้งแต่แกะกล่อง ซึ่งหมายความว่าผู้ใช้กงสุลไม่ต้องกังวลกับการสร้างเลเยอร์นามธรรมเพิ่มเติมเพื่อขยายไปยังหลายภูมิภาค
กงสุลได้รับการออกแบบมาให้เป็นมิตรกับทั้งชุมชน DevOps และนักพัฒนาแอปพลิเคชัน ทำให้เหมาะสำหรับโครงสร้างพื้นฐานที่ทันสมัยและยืดหยุ่น
ที่มา: การแนะนำกงสุล
ส่วนสำคัญของบทช่วยสอนนี้คือการใช้กงสุลเพื่อค้นหาจุดสิ้นสุดของบริการแบบไดนามิก เมื่อลงทะเบียนบริการกับกงสุลแล้ว จะสามารถค้นพบบริการได้โดยใช้ DNS ทั่วไปหรือ API ที่กำหนดเอง
กงสุลจะจัดให้มีการตรวจสุขภาพในกรณีบริการเหล่านี้ หากอินสแตนซ์บริการหรือตัวบริการตัวใดตัวหนึ่งไม่มีประสิทธิภาพหรือไม่ผ่านการตรวจสอบสภาพ รีจิสทรีจะทราบเรื่องนี้และจะหลีกเลี่ยงการส่งคืนที่อยู่ของบริการ งานที่โหลดบาลานเซอร์จะทำจะได้รับการจัดการโดยรีจิสทรีในกรณีนี้
เนื่องจากเราใช้ บริการเดียวกันหลายอินสแตนซ์ กงสุลจึงสุ่มส่งการรับส่งข้อมูลไปยังอินสแตนซ์ที่แตกต่างกัน ดังนั้นจึงช่วยปรับสมดุลภาระระหว่างอินสแตนซ์ของบริการ
กงสุลจัดการกับความท้าทายในการตรวจจับความล้มเหลวและการกระจายโหลดไปยังบริการหลายอินสแตนซ์โดยไม่จำเป็นต้องปรับใช้โหลดบาลานเซอร์แบบรวมศูนย์
โดยจะจัดการรีจิสทรีโดยอัตโนมัติ ซึ่งได้รับการอัปเดตเมื่อมีการลงทะเบียนอินสแตนซ์ใหม่ของบริการและพร้อมให้รับการรับส่งข้อมูล สิ่งนี้ช่วยให้เราขยายขนาดบริการได้อย่างง่ายดาย
ก่อนที่จะลงรายละเอียดการดำเนินการเกี่ยวกับวิธีดำเนินการลงทะเบียนด้วยตนเองกับกงสุล มาดูกันว่าการค้นพบบริการด้วยการลงทะเบียนด้วยตนเองทำงานอย่างไรจริงๆ
ในขั้นตอนแรก อินสแตนซ์บริการจะลงทะเบียนตัวเองกับบริการค้นหาบริการโดยระบุชื่อ ID และที่อยู่ หลังจากที่เกตเวย์นี้สามารถรับที่อยู่ของบริการนี้ได้โดยการสอบถามบริการกงสุลที่ค้นพบด้วยชื่อ/รหัส
สิ่งสำคัญที่ควรทราบที่นี่ คืออินสแตนซ์บริการได้รับการลงทะเบียนด้วย รหัสบริการที่ไม่ซ้ำกัน เพื่อแยกความกำกวมระหว่างอินสแตนซ์ของบริการที่ทำงานบนตัวแทนบริการกงสุลเดียวกัน บริการทั้งหมดจำเป็นต้อง มี ID ที่ไม่ซ้ำกันต่อโหนด ดังนั้นหากชื่ออาจขัดแย้งกัน (กรณีของเรา) จะต้องระบุ ID ที่ไม่ซ้ำกัน
มาดูกันว่าเราจะใช้การลงทะเบียนด้วยตนเองในแอปพลิเคชัน .NET ได้อย่างไร ขั้นแรก เราต้องอ่านการกำหนดค่าที่จำเป็นสำหรับการค้นหาบริการจากตัวแปรสภาพแวดล้อมที่ถูกส่งผ่านไฟล์ docker-compose.override.yml
public static class ServiceConfigExtensions
{
public static ServiceConfig GetServiceConfig ( this IConfiguration configuration )
{
ArgumentNullException . ThrowIfNull ( configuration ) ;
ServiceConfig serviceConfig = new ( )
{
Id = configuration . GetValue < string > ( "ServiceConfig:Id" ) ,
Name = configuration . GetValue < string > ( "ServiceConfig:Name" ) ,
ApiUrl = configuration . GetValue < string > ( "ServiceConfig:ApiUrl" ) ,
Port = configuration . GetValue < int > ( "ServiceConfig:Port" ) ,
ConsulUrl = configuration . GetValue < Uri > ( "ServiceConfig:ConsulUrl" ) ,
HealthCheckEndPoint = configuration . GetValue < string > ( "ServiceConfig:HealthCheckEndPoint" ) ,
} ;
return serviceConfig ;
}
}
หลังจากอ่านการกำหนดค่าที่จำเป็นในการเข้าถึงบริการค้นหาบริการแล้ว เราสามารถใช้เพื่อลงทะเบียนบริการของเราได้ รหัสด้านล่างนี้ถูกนำมาใช้เป็น งานเบื้องหลัง (บริการที่โฮสต์) ซึ่ง จะลงทะเบียน บริการใน กงสุล โดยการแทนที่ข้อมูลก่อนหน้าเกี่ยวกับบริการ หากมีอยู่ หากบริการถูกปิด บริการจะ ถูกยกเลิกการลงทะเบียนจากสำนักทะเบียนกงสุลโดยอัตโนมัติ
public class ServiceDiscoveryHostedService (
ILogger < ServiceDiscoveryHostedService > logger ,
IConsulClient client ,
ServiceConfig config )
: IHostedService
{
private AgentServiceRegistration _serviceRegistration ;
/// <summary>
/// Registers service to Consul registry
/// </summary>
public async Task StartAsync ( CancellationToken cancellationToken )
{
_serviceRegistration = new AgentServiceRegistration
{
ID = config . Id ,
Name = config . Name ,
Address = config . ApiUrl ,
Port = config . Port ,
Check = new AgentServiceCheck ( )
{
DeregisterCriticalServiceAfter = TimeSpan . FromSeconds ( 5 ) ,
Interval = TimeSpan . FromSeconds ( 15 ) ,
HTTP = $ "http:// { config . ApiUrl } : { config . Port } /api/values/ { config . HealthCheckEndPoint } " ,
Timeout = TimeSpan . FromSeconds ( 5 )
}
} ;
try
{
await client . Agent . ServiceDeregister ( _serviceRegistration . ID , cancellationToken ) . ConfigureAwait ( false ) ;
await client . Agent . ServiceRegister ( _serviceRegistration , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( Exception ex )
{
logger . LogError ( ex , $ "Error while trying to deregister in { nameof ( StartAsync ) } " ) ;
}
}
/// <summary>
/// If the service is shutting down it deregisters service from Consul registry
/// </summary>
public async Task StopAsync ( CancellationToken cancellationToken )
{
try
{
await client . Agent . ServiceDeregister ( _serviceRegistration . ID , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( Exception ex )
{
logger . LogError ( ex , $ "Error while trying to deregister in { nameof ( StopAsync ) } " ) ;
}
}
}
เมื่อเราลงทะเบียนบริการของเราในบริการค้นหาบริการแล้ว เราก็สามารถเริ่มใช้งาน Gateway API ได้
Ocelot ต้องการให้คุณจัดเตรียมไฟล์การกำหนดค่าที่มีรายการ เส้นทาง (การกำหนดค่าที่ใช้ในการแมปคำขออัปสตรีม) และ GlobalConfiguration (การกำหนดค่าอื่นๆ เช่น QoS การจำกัดอัตรา ฯลฯ) ในไฟล์ ocelot.json ด้านล่าง คุณสามารถดูวิธีที่เราส่งต่อคำขอ HTTP เราต้องระบุประเภทของโหลดบาลานเซอร์ที่เราจะใช้ ในกรณีของเราคือ “RoundRobin” ซึ่งจะวนซ้ำบริการที่มีอยู่และส่งคำขอไปยังบริการที่มีอยู่ สิ่งสำคัญคือต้องตั้งค่ากงสุลเป็นบริการค้นหาบริการใน GlobalConfiguration สำหรับ ServiceDiscoveryProvider
{
"Routes" : [
{
"Servicename" : " ValueService " ,
"DownstreamPathTemplate" : " /{everything} " ,
"DownstreamScheme" : " http " ,
"UpstreamPathTemplate" : " /{everything} " ,
"UpstreamHttpMethod" : [ " GET " ],
"UseServiceDiscovery" : true ,
"RouteIsCaseSensitive" : false ,
"LoadBalancerOptions" : {
"Type" : " RoundRobin "
},
"QoSOptions" : {
"ExceptionsAllowedBeforeBreaking" : 3 ,
"DurationOfBreak" : 5000 ,
"TimeoutValue" : 2000
}
}
],
"GlobalConfiguration" : {
"RequestIdKey" : " OcelotRequestId " ,
"UseServiceDiscovery" : true ,
"ServiceDiscoveryProvider" : {
"Host" : " consul " ,
"Port" : 8500 ,
"Type" : " PollConsul " ,
"PollingInterval" : 100
}
}
}
ต่อไปนี้เป็นคำอธิบายที่จำเป็นสำหรับการตั้งค่า ServiceDiscoveryProvider ในส่วน GlobalConfiguration :
หลังจากที่เรากำหนดการกำหนดค่าของเราแล้ว เราก็สามารถเริ่มใช้งาน API Gateway ได้ ด้านล่างนี้เราจะเห็นการใช้งานบริการ Ocelot API Gateway ซึ่งใช้ไฟล์การกำหนดค่า ocelot.json และ กงสุล เป็นรีจิสทรีบริการ
IHostBuilder hostBuilder = Host . CreateDefaultBuilder ( args )
. UseContentRoot ( Directory . GetCurrentDirectory ( ) )
. ConfigureWebHostDefaults ( webBuilder =>
{
webBuilder . ConfigureServices ( services =>
services
. AddOcelot ( )
. AddConsul < MyConsulServiceBuilder > ( )
. AddCacheManager ( x =>
{
x . WithDictionaryHandle ( ) ;
} )
. AddPolly ( ) ) ;
webBuilder . Configure ( app =>
app . UseOcelot ( ) . Wait ( ) )
. ConfigureAppConfiguration ( ( hostingContext , config ) =>
{
config
. SetBasePath ( hostingContext . HostingEnvironment . ContentRootPath )
. AddJsonFile ( "appsettings.json" , false , true )
. AddJsonFile ( $ "appsettings. { hostingContext . HostingEnvironment . EnvironmentName } .json" , true , true )
. AddJsonFile ( "ocelot.json" , false , true )
. AddEnvironmentVariables ( ) ;
} )
. ConfigureLogging ( ( builderContext , logging ) =>
{
logging . ClearProviders ( ) ;
logging . AddConsole ( ) ;
logging . AddDebug ( ) ;
} ) ;
} ) ;
IHost host = hostBuilder . Build ( ) ;
await host . RunAsync ( ) ;
ดังที่ได้กล่าวไว้ก่อนหน้านี้ เราจะรวมบริการทั้งหมดเข้ากับ Docker รวมถึง Consul โดยใช้ การกระจาย GNU/Linux แบบ Lightweight สำหรับคอนเทนเนอร์
ไฟล์ docker-compose.yml พร้อมการตั้งค่าสำหรับคอนเทนเนอร์ทั้งหมดมีลักษณะดังนี้:
services :
services :
consul :
image : hashicorp/consul
container_name : consul
command : consul agent -dev -log-level=warn -ui -client=0.0.0.0
hostname : consul
networks :
- common_network
valueservice1.openapi :
image : valueservice.openapi:latest
container_name : valueservice1.openapi
restart : on-failure
hostname : valueservice1.openapi
build :
context : .
dockerfile : src/ValueService.OpenApi/Dockerfile
networks :
- common_network
valueservice2.openapi :
image : valueservice.openapi:latest
container_name : valueservice2.openapi
restart : on-failure
hostname : valueservice2.openapi
build :
context : .
dockerfile : src/ValueService.OpenApi/Dockerfile
networks :
- common_network
valueservice3.openapi :
image : valueservice.openapi:latest
container_name : valueservice3.openapi
restart : on-failure
hostname : valueservice3.openapi
build :
context : .
dockerfile : src/ValueService.OpenApi/Dockerfile
networks :
- common_network
services.gateway :
image : services.gateway:latest
container_name : services.gateway
restart : on-failure
hostname : services.gateway
build :
context : .
dockerfile : src/Services.Gateway/Dockerfile
networks :
- common_network
networks :
common_network :
driver : bridge
โปรดทราบว่าบริการของเราไม่มีไฟล์การกำหนดค่าใดๆ เพื่อจุดประสงค์นั้น เราจะใช้ไฟล์ Docker-compose.override.yml :
services :
consul :
ports :
- " 8500:8500 "
valueservice1.openapi :
# Swagger UI: http://localhost:9100/index.html
# http://localhost:9100/api/values
environment :
- ASPNETCORE_ENVIRONMENT=Development
- ServiceConfig__ApiUrl=valueservice1.openapi
- ServiceConfig__ConsulUrl=http://consul:8500
- ServiceConfig__HealthCheckEndPoint=healthcheck
- ServiceConfig__Id=ValueService.OpenApi-9100
- ServiceConfig__Name=ValueService
- ServiceConfig__Port=8080
ports :
- 9100:8080
depends_on :
- consul
valueservice2.openapi :
# Swagger UI: http://localhost:9200/index.html
# http://localhost:9200/api/values
environment :
- ASPNETCORE_ENVIRONMENT=Development
- ServiceConfig__ApiUrl=valueservice2.openapi
- ServiceConfig__ConsulUrl=http://consul:8500
- ServiceConfig__HealthCheckEndPoint=healthcheck
- ServiceConfig__Id=ValueService.OpenApi-9200
- ServiceConfig__Name=ValueService
- ServiceConfig__Port=8080
ports :
- 9200:8080
depends_on :
- consul
valueservice3.openapi :
# Swagger UI: http://localhost:9300/index.html
# http://localhost:9300/api/values
environment :
- ASPNETCORE_ENVIRONMENT=Development
- ServiceConfig__ApiUrl=valueservice3.openapi
- ServiceConfig__ConsulUrl=http://consul:8500
- ServiceConfig__HealthCheckEndPoint=healthcheck
- ServiceConfig__Id=ValueService.OpenApi-9300
- ServiceConfig__Name=ValueService
- ServiceConfig__Port=8080
ports :
- 9300:8080
depends_on :
- consul
services.gateway :
# Call first available service: http://localhost:9500/api/values
environment :
- ASPNETCORE_ENVIRONMENT=Development
ports :
- 9500:8080
depends_on :
- consul
- valueservice1.openapi
- valueservice2.openapi
- valueservice3.openapi
หากต้องการรันไฟล์เขียน ให้เปิด Powershell แล้วไปที่ไฟล์เขียนในโฟลเดอร์รูท จากนั้นดำเนินการคำสั่งต่อไปนี้: docker-compose up -d --build --remove-orphans ซึ่งจะเริ่มและรันบริการทั้งหมด พารามิเตอร์ -d ดำเนินการคำสั่งที่แยกออกมา ซึ่งหมายความว่าคอนเทนเนอร์จะทำงานในพื้นหลังและไม่ปิดกั้นหน้าต่าง Powershell ของคุณ หากต้องการตรวจสอบคอนเทนเนอร์ที่ทำงานอยู่ทั้งหมดให้ใช้คำสั่ง docker ps
กงสุลนำเสนออินเทอร์เฟซผู้ใช้บนเว็บที่ดีทันทีที่แกะกล่อง คุณสามารถเข้าถึงได้ที่พอร์ต 8500 : http://localhost:8500 มาดูหน้าจอกันบ้าง
หน้าแรกของบริการ Consul UI พร้อมข้อมูลที่เกี่ยวข้องทั้งหมดที่เกี่ยวข้องกับตัวแทนกงสุลและการตรวจสอบบริการทางเว็บ
มาทำการเรียกหลายครั้งผ่าน API Gateway: http://localhost:9500/api/values ตัวจัดสรรภาระงานจะวนซ้ำบริการที่มีอยู่และส่งคำขอและตอบกลับ:
ระบบบริการไมโครไม่ใช่เรื่องง่ายที่จะสร้างและบำรุงรักษา แต่บทช่วยสอนนี้แสดงให้เห็นว่าการพัฒนาและปรับใช้แอปพลิเคชันด้วยสถาปัตยกรรมบริการขนาดเล็กนั้นง่ายเพียงใด HashiCorp Consul ได้รับการสนับสนุนระดับเฟิร์สคลาสสำหรับการค้นหาบริการ การตรวจสอบสภาพ พื้นที่จัดเก็บคีย์-ค่า และศูนย์ข้อมูลหลายรายการ Ocelot เป็นเกตเวย์ที่ประสบความสำเร็จในการสื่อสารกับสำนักทะเบียนบริการกงสุล และเรียกข้อมูลการลงทะเบียนบริการ โหลดบาลานเซอร์วนซ้ำผ่านบริการที่มีอยู่ และส่งคำขอ การใช้ทั้งสองอย่างทำให้ชีวิตของนักพัฒนาที่เผชิญกับความท้าทายดังกล่าวง่ายขึ้นอย่างมาก คุณเห็นด้วยไหม?
สนุก!
ได้รับอนุญาตภายใต้ MIT ติดต่อฉันบน LinkedIn