Spring Cloud Gateway | Zuul වෙනුවට නව යුගය | Microservices SC Guide

Spring Cloud Gateway | Zuul වෙනුවට නව යුගය | Microservices SC Guide

ඉතින් කොහොමද යාලුවනේ? ඔන්න ආයෙත් අපි හම්බවෙන්නේ නියම ටෙක්නිකල් මාතෘකාවක් අරගෙන. මේ දවස්වල හැමතැනම කතාබහ වෙන මයික්‍රෝසර්විසස් (Microservices) ආකෘතියත් එක්ක, අපිට අනිවාර්යයෙන්ම අවශ්‍ය වෙන දෙයක් තමයි API Gateway එකක් කියන්නේ. කාලයක් තිස්සේ ජනප්‍රියව තිබ්බ Gateway එකක් තමයි Netflix Zuul. හැබැයි, අලුත් ටෙක්නොලොජිත් එක්ක, Spring Cloud Gateway කියන අලුත් ක්‍රමවේදය දැන් ගොඩක් දෙනෙක් අතර ප්‍රචලිත වෙලා තියෙනවා. ඉතින්, අද අපි බලමු කොහොමද මේ Zuul පරණ එකෙන් අලුත් Spring Cloud Gateway එකට අපේ Project එක මාරු කරගන්නේ කියලා, ඒ වගේම Spring Cloud Gateway වලින් Request Router කරන්නේ කොහොමද කියලත් අපි අද කතා කරමු. මේක හරියට පරණ බස් එකෙන් අලුත් ලක්සරි කෝච්චියකට මාරු වෙනවා වගේ වැඩක්!

Zuul ගේ කතාව: ඇයි අපිට Gateway එකක් ඕනේ?

මුලින්ම බලමු මේ API Gateway එකක් කියන්නේ මොකක්ද කියලා. සරලව කිව්වොත්, ඒක තමයි අපේ Client ලා (web browser, mobile app, etc.) Microservices එක්ක කතා කරන ප්‍රධාන දොරටුව. Microservices Architecture එකේදී, අපිට පොඩි පොඩි Services ගොඩක් තියෙනවා. Client එකක් මේ හැම Service එකක් එක්කම කෙලින්ම කතා කරන්න ගියොත්, ඒක හරිම අවුල් වැඩක්. ගොඩක් Network calls කරන්න වෙනවා, Security Manage කරන්න අමාරුයි, Load Balancing කරන්නත් බැහැ. මේ හැම ප්‍රශ්නයකටම උත්තරේ තමයි API Gateway එකක් කියන්නේ.

Gateway එකක ප්‍රධාන වැඩ ටිකක් තියෙනවා:

  • Request Routing: Client Request එක අදාළ Service එකට යොමු කරනවා.
  • Security: Authentication, Authorization වගේ දේවල් කරලා, අනවසර පිවිසීම් වළක්වනවා.
  • Load Balancing: Client Requests Services අතරේ බෙදා හරිනවා.
  • API Composition: සමහර වෙලාවට එක Client Request එකකට Services කීපයකින් Data ඕනේ වෙනවා, ඒ හැම Service එකකින්ම Data අරගෙන එක Response එකක් විදියට Client එකට දෙනවා.
  • Monitoring: Requests ගැන විස්තර (Logs) තියාගන්නවා.

Netflix Zuul කියන්නේ කාලයක් තිස්සේ මේ හැමදේම කරපු ජනප්‍රිය Gateway එකක්. ඒක Spring Cloud Ecosystem එකේ කොටසක් විදියට ගොඩක් දෙනෙක් පාවිච්චි කළා. හැබැයි, Zuul හදලා තිබ්බේ Blocking I/O Model එකකට. ඒ කියන්නේ, එක Request එකක් process කරන අතරතුර, අනිත් Requests ටිකට ඉවසගෙන ඉන්න වෙනවා. Microservices වලදී Requests ගොඩක් එකපාර එන්න පුළුවන් නිසා, මේක Performance එකට ලොකු බලපෑමක් කළා. ඒ වගේම, Netflix ආයතනය Zuul 1 maintenance mode එකට දාලා, Zuul 2 එකක් ගැන කතා කළත්, ඒක ඒ තරම් ජනප්‍රිය වුණේ නැහැ. ඉතින්, Spring Framework එක පැත්තෙන් අලුත් විසඳුමක් අවශ්‍ය වුණා.

Spring Cloud Gateway: අලුත් ආරම්භයක්!

ඔන්න ඒ ප්‍රශ්නෙට උත්තරේ විදියට ආපු වීරයා තමයි Spring Cloud Gateway කියන්නේ! මේක හදලා තියෙන්නේ Spring Framework එකේම අලුත් Reactive Programming Model එක පාවිච්චි කරලා. ඒ කියන්නේ, Zuul වගේ Blocking I/O වෙනුවට, මේක Non-blocking, Asynchronous I/O වලින් වැඩ කරනවා. මේ නිසා, එකම Hardware Resources ප්‍රමාණයකින්, Zuul වලට වඩා වැඩි Requests ප්‍රමාණයක් Spring Cloud Gateway එකට හසුරුවන්න පුළුවන්. හිතන්නකෝ, Zuul කියන්නේ එකපාරට එක්කෙනෙක්ට විතරක් කතා කරන්න පුළුවන් Phone Call එකක් වගේ නම්, Spring Cloud Gateway කියන්නේ එකපාරට ගොඩක් දෙනෙක්ට කතා කරන්න පුළුවන් Conference Call එකක් වගේ!

Spring Cloud Gateway වල ප්‍රධානම විශේෂාංග ටිකක් බලමු:

  • Built on Spring WebFlux: මේක Netty කියන Highly Performant, Non-blocking HTTP client එක පාවිච්චි කරලා හදලා තියෙන්නේ. ඒක නිසා ඉතාම වේගවත්.
  • Predicates: මේවා තමයි Requests Filter කරන්නේ. Request එකක Path, Host, Headers, Methods වගේ දේවල් බලලා, මේ Request එක මේ Service එකට යවන්නද නැද්ද කියලා තීරණය කරන්නේ Predicates වලින්.
  • Filters: මේවා පාවිච්චි කරන්නේ Requests හෝ Responses Modify කරන්න. උදාහරණයක් විදියට, Request එකකට අලුත් Header එකක් එකතු කරන්න, Response එකක Body එක වෙනස් කරන්න, වගේ දේවල් කරන්න පුළුවන්.
  • Dynamic Routing: Service Discovery (Eureka, Consul) වගේ දේවල් එක්ක එකතු වෙලා, Services වල IP Addresses වෙනස් වුණත් Gateway එකට ඒවා Auto Discover කරලා Requests Route කරන්න පුළුවන්.

මේ විශේෂාංග නිසා Spring Cloud Gateway කියන්නේ Microservices Architecture එකකදී API Gateway එකක් විදියට ඉතාම බලවත් සහ කාර්‍යක්ෂම විසඳුමක්. විශේෂයෙන්ම, High-traffic applications වලට මේක ඉතාම හොඳයි.

Project එකට Spring Cloud Gateway එකතු කරගමු

හරි, දැන් අපි බලමු කොහොමද අපේ Spring Boot Project එකකට Spring Cloud Gateway එකතු කරගෙන Requests Route කරන්නේ කියලා. මේක කිසිම අමාරුවක් නැහැ, හරියට කැඳ හැඳි ගානවා වගේ!

පළවෙනි පියවර: Dependencies එකතු කිරීම

මුලින්ම, ඔබේ pom.xml (Maven) හෝ build.gradle (Gradle) File එකට අවශ්‍ය Dependencies එකතු කරගන්න ඕනේ. අපි මෙතනදී Maven පාවිච්චි කරමු.


<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <!-- If using Eureka for Service Discovery -->
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2023.0.1</version> <!-- Replace with your Spring Cloud version -->
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

මතක තියාගන්න, spring-cloud-dependencies version එක ඔබ පාවිච්චි කරන Spring Boot version එකට ගැළපෙන්න ඕනේ. (උදා: Spring Boot 3.x -> Spring Cloud 2022.0.x or 2023.0.x)

දෙවෙනි පියවර: Configuration Setup කිරීම

ඊළඟට, application.yml File එකේ අපේ Gateway එකේ Routes Define කරමු. මේක තමයි Gateway එකේ හදවත වගේ වැඩේ. අපි හිතමු අපිට User Service එකක් http://localhost:8081 එකේ දුවනවා කියලා, ඒ වගේම Product Service එකක් http://localhost:8082 එකේ දුවනවා කියලා. Gateway එක http://localhost:8080 එකේ දුවනවා.


server:
  port: 8080

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: http://localhost:8081
          predicates:
            - Path=/users/**
          filters:
            - StripPrefix=1 # Remove the /users segment from the path before forwarding

        - id: product-service-route
          uri: http://localhost:8082
          predicates:
            - Path=/products/**
          filters:
            - StripPrefix=1 # Remove the /products segment from the path before forwarding

මේ Configuration එකෙන් කියන්නේ මොකක්ද කියලා අපි පොඩ්ඩක් විස්තර කරමු.

  • id: user-service-route: මේක Route එකට දෙන නමක්. ඕනෑම දෙයක් දෙන්න පුළුවන්.
  • uri: http://localhost:8081: මේ තමයි Client Request එක යොමු කරන්න ඕන Service එකේ Address එක. Service Discovery පාවිච්චි කරනවා නම් lb://user-service-name වගේ දෙයක් දෙන්න පුළුවන්.
  • predicates:: මේවා තමයි Request එක අහුවෙන්න ඕන නීති රීති.
    • - Path=/users/**: මේකෙන් කියන්නේ, Request එකේ Path එක /users/ කියලා පටන් ගන්නවා නම්, මේ Route එකට අදාළයි කියලා. (උදා: /users/1, /users/all).
  • filters:: මේවා Request එක Service එකට යවන්න කලින් හෝ Service එකෙන් Response එක එලියට දෙන්න කලින් කරන වෙනස්කම්.
    • - StripPrefix=1: මේක වැදගත් Filter එකක්. /users/1 වගේ Request එකක් ආවම, Gateway එකට යවන්නේ http://localhost:8081/users/1 විදියට. හැබැයි, අපේ User Service එකේ Controller එකේ Path එක බොහෝවිට / හෝ /1 වගේ වෙන්න පුළුවන්. මේ StripPrefix=1 කියන එකෙන් කරන්නේ, Original Path එකේ මුල් කොටස (/users) අයින් කරලා, Service එකට /1 වගේ Request එකක් යවන එක. එතකොට Service එකට එන්නේ හරියටම අවශ්‍ය Path එක විතරයි.

තුන්වෙනි පියවර: Gateway Application එක Run කිරීම

දැන් ඔයාගේ Spring Boot Application එක @SpringBootApplication Annotation එක එක්ක Run කරන්න පුළුවන්. ඒක සාමාන්‍ය Spring Boot Application එකක් වගේමයි.


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApiGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
}

මේ විදියට Run කලාම, ඔයාට http://localhost:8080/users/123 වගේ URL එකකට Request එකක් යවලා, ඒක User Service එකට (http://localhost:8081/123) හරියට Redirect වෙනවද කියලා බලන්න පුළුවන්.

Predicates සහ Filters: Gateway එක Powerful කරමු!

Spring Cloud Gateway වල තියෙන ලොකුම වාසියක් තමයි මේ Predicates සහ Filters පාවිච්චි කරලා Gateway එකට අතිරේක Functionality එකතු කරන්න පුළුවන් එක. මේවා තමයි අපේ Gateway එක Smart කරන්නේ!

Predicates

Predicates කියන්නේ java.util.function.Predicate එකක්. ඒ කියන්නේ, මේවා Input එකක් (ServerWebExchange එකක්) අරගෙන Boolean Result එකක් (true/false) Return කරනවා. Request එකක් යම්කිසි Route එකකට අයිතිද නැද්ද කියලා තීරණය කරන්නේ මේ Predicates වලින්.

Commonly භාවිතා වෙන Predicates කිහිපයක්:

  • Path: Path=/users/** - Request Path එක Match කරනවා.
  • Host: Host=my.example.com - Request Host Header එක Match කරනවා.
  • Method: Method=GET,POST - HTTP Method එක Match කරනවා.
  • Header: Header=X-Request-Id, \d+ - Request Header එකක් සහ ඒකේ Value එක Regular Expression එකකින් Match කරනවා.
  • Query: Query=name, myname - Request URL එකේ Query Parameter එකක් Match කරනවා.
  • After/Before/Between: After=2023-01-20T17:42:17.335+07:00[Asia/Colombo] - Request එක නිශ්චිත වෙලාවකට පස්සේ ආවොත් පමණක් Route කරනවා.
  • RemoteAddr: RemoteAddr=192.168.1.1/24 - Client IP Address එක Match කරනවා.

උදාහරණයක් විදියට, අපිට පුළුවන් /admin/** කියන Path එකට එන Requests යවන්න පුළුවන් localhost:8083 කියන Admin Service එකට, හැබැයි ඒ Requests එන්න ඕනේ 192.168.1.x Network එකෙන් විතරක් කියලා Define කරන්න:


        - id: admin-service-route
          uri: http://localhost:8083
          predicates:
            - Path=/admin/**
            - RemoteAddr=192.168.1.1/24

මේ වගේ එක Route එකකට Predicates කීපයක් දාන්න පුළුවන්. හැම Predicate එකක්ම true වුණොත් විතරයි ඒ Route එක Activate වෙන්නේ.

Filters

Filters කියන්නේ Gateway එක හරහා යන Requests හෝ Responses වෙනස් කරන්න පාවිච්චි කරන ක්‍රමවේදයක්. මේවා Global Filter හෝ Gateway Filter Definitions විදියට භාවිතා කරන්න පුළුවන්.

Commonly භාවිතා වෙන Filters කිහිපයක්:

  • AddRequestHeader: AddRequestHeader=X-Programmer-Name, Kasun - Request එකට අලුත් Header එකක් එකතු කරනවා.
  • AddResponseHeader: AddResponseHeader=X-API-Version, 1.0 - Response එකට අලුත් Header එකක් එකතු කරනවා.
  • RewritePath: RewritePath=/foo/(?<segment>.*), /${segment} - Request Path එක Regular Expression පාවිච්චි කරලා Rewrite කරනවා. (අපේ StripPrefix Filter එකත් මේ වගේම වැඩක් කරනවා)
  • PrefixPath: PrefixPath=/v2 - Request Path එකට Prefix එකක් එකතු කරනවා.
  • Retry: Retry=3 - Service එක Fail වුණොත්, Request එක නැවත වතාවක් යවන්න උත්සාහ කරනවා.

උදාහරණයක් විදියට, අපිට පුළුවන් සෑම Request එකකටම X-Request-Id කියන Header එකක් එකතු කරන්න, ඒක Unique ID එකක් වෙන්න:


spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=X-Request-Id, #{T(java.util.UUID).randomUUID().toString()} # Global filter
      routes:
        - id: user-service-route
          uri: http://localhost:8081
          predicates:
            - Path=/users/**
          filters:
            - StripPrefix=1
            - AddRequestHeader=X-User-Agent, Gateway-Service # Route specific filter

මේ default-filters කියන එකෙන් කරන්නේ හැම Route එකකටම Apply වෙන Filters Specify කරන එක. routes ඇතුලේ දාන Filters අදාළ Route එකට විතරයි Apply වෙන්නේ.

ඔබට අවශ්‍ය නම් Custom Predicates සහ Filters හදාගන්නත් පුළුවන්. ඒක තරමක් Advanced මාතෘකාවක්, අපි ඒ ගැන වෙනම දවසක කතා කරමු.

Zuul migration Tips: මාරු වෙද්දී මතක තියාගන්න දේවල්

Zuul වලින් Spring Cloud Gateway වලට මාරු වෙද්දී පොඩි පොඩි දේවල් මතක තියාගන්න ඕනේ. හරියට පරණ Bike එකකින් අලුත් Car එකකට මාරු වෙද්දී Gears Shift කරන විදිය වෙනස් වෙනවා වගේ!

  1. Blocking vs. Non-blocking: Zuul Blocking නිසා, ඔබගේ Downstream Services ගොඩක් වෙලාවට Blocking APIs පාවිච්චි කරනවා වෙන්න පුළුවන්. Spring Cloud Gateway කියන්නේ Reactive, Non-blocking. ඒක නිසා Performance වාසි ගන්න නම්, පුළුවන් නම් ඔබේ Microservices වලත් Reactive Programming (Spring WebFlux) පාවිච්චි කරන්න පුළුවන් නම් හොඳයි. හැබැයි, නොකලත් Gateway එක වැඩ කරනවා, නමුත් සම්පූර්ණ වාසි ගන්න අමාරුයි.
  2. Configuration වෙනස්කම්: Zuul වල Routes Config කළ විදියට වඩා Spring Cloud Gateway වල Config කරන විදිය වෙනස්. application.yml එකේ Syntax එක හොඳින් අවබෝධ කරගන්න. විශේෂයෙන්ම Predicates සහ Filters වල Names සහ Syntax වෙනස්. Zuul Filters වල තිබ්බ Pre-filters, Route-filters, Post-filters කියන Concept එක Spring Cloud Gateway වලට එනකොට Pre- and Post-Filters විදියටම තියෙනවා, හැබැයි Implementation එක වෙනස්.
  3. Custom Filters: Zuul වල Custom Filters පාවිච්චි කරලා තියෙනවා නම්, ඒවා Spring Cloud Gateway වලට ගැලපෙන විදියට නැවත ලියන්න වෙනවා. මේවා GlobalFilter Interface එක Implement කරලා හෝ GatewayFilter Factories විදියට හදන්න පුළුවන්.
  4. Error Handling: Gateway එකේ Error Handling Mechanisms ගැන අවධානය යොමු කරන්න. Spring Cloud Gateway වල GlobalErrorWebExceptionHandler වගේ දේවල් පාවිච්චි කරලා Customised Error Responses දෙන්න පුළුවන්.
  5. Gradual Migration: එකපාරටම හැමදේම මාරු කරන්න හදන්නේ නැතුව, ටිකෙන් ටික මාරු කරන්න උත්සාහ කරන්න. මුලින්ම සරලම Route එකක් මාරු කරලා, ඒක හරියට වැඩ කරනවද කියලා බලලා, ඊටපස්සේ සංකීර්ණ Routes ටික මාරු කරන්න. Blue/Green Deployment හෝ Canary Deployment වගේ Strategies පාවිච්චි කරන්නත් පුළුවන්.

අවසන් වශයෙන් කියන්න තියෙන්නේ, Zuul වලින් Spring Cloud Gateway වලට මාරු වීම කියන්නේ ඔබේ Microservices Architecture එකේ Performance සහ Scalability සැලකිය යුතු ලෙස වැඩි දියුණු කරගන්න ලැබෙන හොඳ අවස්ථාවක් කියන එකයි. අලුත් තාක්ෂණයට බය නැතුව යන්න!

නිගමනය: අනාගතයට පියවරක්!

ඉතින් යාලුවනේ, අද අපි Spring Cloud Gateway ගැන හොඳටම කතා කළා. Zuul කියන්නේ පරණ හිතවතෙක් වගේ වුණත්, අලුත් දියුණුවත් එක්ක Spring Cloud Gateway කියන්නේ අනිවාර්යයෙන්ම බලන්න ඕනේ දෙයක්. ඒකේ Non-blocking, Reactive Architecture එක නිසා, අපේ Microservices Applications වලට අතිවිශාල Performance වාසි ලබාගන්න පුළුවන්. Predicates සහ Filters පාවිච්චි කරලා අපිට අපේ API Gateway එක ඕන විදියට Customise කරන්නත් පුළුවන්.

මේක කියවලා විතරක් මදි. පුළුවන් නම් අදම පොඩි Project එකක් හදලා මේ Gateway එක Deploy කරලා බලන්න. ඔයාලට ගැටළු ආවොත්, නැත්නම් මේ ගැන තව මොනවා හරි දැනගන්න ඕනේ නම්, අනිවාර්යයෙන්ම පහලින් comment එකක් දාගෙන යන්න. ඒ වගේම, මේ Article එක තව කෙනෙක්ට ප්‍රයෝජනවත් වෙයි කියලා හිතනවා නම් Share කරන්නත් අමතක කරන්න එපා. තවත් මේ වගේම වැදගත් Tech Article එකකින් හම්බවෙමු! ඔබට ජය!