
API Gateway to wzorzec integracyjny. W wzorcu tym, tylko jeden serwis (może być oczywiście replikowany) udostępniony jest publicznie. Klientami naszego API mogą być aplikacje mobilne, strony web lub inne urządzenia. Musimy zapewnić im możliwość korzystania z naszego API w sposób zunifikowany. Aby to osiągnąć udostępniamy jeden publiczny serwis zwany API Gateway, który odpytywany przez różnych klientów zajmuje się kierowaniem ruchu na podstawie filtrów i określonych routingów.
Zastosowanie wzorca API Gateway ma wiele zalet, które mogą rozwiązać sporo problemów w środowisku rozproszonym:
- security - decyduje, czy zapytanie może być “wpuszczone” do systemu
- zarządzanie ruchem - routing na podstawie URI
- testowanie - możemy kierować ruchem na nowe usługi w celu przetestowania (na przykład canary release)
- eksperymenty - daje nam możliwość eskperymetowania z przekierowywaniem ruchu
- wstrzykiwanie danych - możemy dodawać dane do żądań (na przykład do nagłówków)
- monitoring - na podstawie tego serwisu mamy wgląd do tego jak ruch jest rozdzielany oraz co dzieję się w systemie
- wstrzykiwanie błędów - możemy na przykład wstrzykiwać złe nagłówki i obserwować jak odpowie na to system
- odporność - jeśli któryś z serwisów będzie niedostępny, gateway może przekierować ruch na działające instancje.
First service
Stwórzmy prosty serwis z jednym endpointem /codecouple:
@RestController
public class FirstController {
@GetMapping("/codecouple")
public String showCodeCouple() {
return "CodeCouple first service!";
}
}
Natomiast w pliku application.properties ustawiamy:
#Random server port
server.port=0
#Application name
spring.application.name=first-service
#Default zone
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
Więcej o wzorcu Service Discovery z wykorzystaniem biblioteki Eurek’a można przeczytać tutaj.
Second service
Drugi serwis wygląda jak tak samo jak pierwszy, ale zwraca napis CodeCouple second service!. Musimy pamiętać także o zmianie nazwy spring.application.name w application.properties.
Gateway
Dodajemy serwis odpowiedzialny za routing. Aby zrealizować wzorzec API Gateway wykorzystamy bibliotekę Zuul ze stajni Netflixa:
Uruchomienie biblioteki odbywa się poprzez adnotację @EnableZuulProxy:
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServiceApplication.class, args);
}
}
Dodatkowo musimy skonfigurować nasz routing, czyli wskazanie adresów do przekierowania:
#Server port
server.port=8080
#Application name
spring.application.name=zuul-service
#Default zone
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
#Zuul prefix
zuul.prefix=/api
#First service
zuul.routes.first-service.path=/first/**
zuul.routes.first-service.serviceId=first-service
#Second service
zuul.routes.second-service.path=/second/**
zuul.routes.second-service.serviceId=second-service
Property zuul.prefix określa prefix pod jakim będziemy “odpytywać” zuul-service. Następnie w zuul.routes podajemy nazwę serwisu, w naszym przypadku first-service oraz second-service. Kolejno dla każdego serwisu określamy path, czyli ścieżkę pod jaką ma być dostępny nasz serwis poprzez proxy oraz określamy serviceId, czyli nazwę serwisu zarejestrowanego w Eurece. Jeśli nie korzystamy z service discovery możemy ustawić property url, w którym wskazujemy URL do konkretnej usługi. Jeśli nie ustawimy żadnych zuul.routes to domyślnym adresem usługi jest nazwa ustawiona w spring.application.name.
Testujemy
Aby przetestować API Gateway w działaniu należy udać się pod adres http://localhost:8080/api/first/codecouple. Powinien pojawić się napis CodeCouple first service! teraz zmieniamy adres na http://localhost:8080/api/second/codecouple i dostajemy CodeCouple second service!. Udało nam się zrealizować routing!
Filtry
Biblioteka Zuul operuje na filtrach, które pisane są w Groovym oraz Javie. W Groovym, ponieważ mogą być podmieniane w runtime. Filtry są dostępne w czterech stanach:
- Pre - wywoływany w momencie przyjścia żądania - można wykorzystać do sprawdzania, czy żądanie jest poprawne
- Route - zaraz po
prefiltrze wywoływany jestroutefilter - zawiera logikę odpowiedzialną za przekierowanie - Post - służy do operowania na odpowiedzi
- Error - typ służący do obsługi niepoprawnych zdarzeń
Dla przykładu możemy dodać nowy filtr dla typu PRE, który będzie dodawał nagłówek Code-Couple-Header z wartością CodeCouple.pl! :
@Component
class CustomFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE\_TYPE;
}
@Override
public int filterOrder() {
return PRE\_DECORATION\_FILTER\_ORDER - 1 ;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
context.addZuulRequestHeader("Code-Couple-Header", "CodeCouple!");
return null;
}
}
GitHub
Całość jak zawsze na GitHubie.