Implementació del patró Saga a l'arquitectura de microserveis

Bloc

Implementació del patró Saga a l'arquitectura de microserveis

Implementació del patró Saga a l'arquitectura de microserveis

En els darrers anys, els microserveis són un dels temes més actuals de la indústria, també en un context on no són necessaris. Sovint, el disseny de l’arquitectura és erroni, probablement s’assembla més a un servei de micro-monòlits. Si responeu que sí a una d’aquestes preguntes bàsiques, probablement la vostra arquitectura s’equivocarà.

  • Té una única instància del seu servei?
  • Teniu una sola base de dades (o esquema)?
  • La comunicació entre serveis és sincrònica?

Hi ha moltes preguntes per respondre, però en aquest post us mostraré una arquitectura senzilla de microserveis que compleix el patró, basat en el llibre Patrons de microserveis per Chris Richardson .

La idea principal, en el meu exemple, és construir un programari de gestió per a McPaspao, el meu hipotètic menjar ràpid :-D. A continuació, una anàlisi preliminar basada en el domini:

react-native clean-project
  • Gestions de comandes
  • Gestió de Cuines
  • Gestió del lliurament

El Gestió de comandes gestiona l'ordre d'hamburgueses, el Gestió de la cuina gestiona la feina de cuina (per exemple: cuinar hamburgueses o la gestió de la nevera), el Gestió del lliurament gestiona els lliuraments de les hamburgueses. Per tant, necessito almenys tres serveis diferents, cadascun amb la seva pròpia base de dades, i cada servei ha de comunicar-se entre ells. En aquest escenari es necessiten altres cinc components:

  • Base de dades de comandes
  • Base de dades de cuina
  • Base de dades de lliurament
  • Servei de missatgeria
  • API de passarel·la

A l’arquitectura de microserveis API Gateway, el servei de missatgeria i la base de dades per servei són patrons habituals que s’utilitzen per resoldre molts problemes, per exemple:

  • Servei de missatgeria : Els serveis sovint col·laboren per gestionar moltes sol·licituds, de manera que han d’utilitzar un protocol de comunicació entre processos. Més concretament, un sistema de missatgeria asíncrona.
  • Base de dades per servei : La base de dades del servei ha de formar part de la implementació per garantir un acoblament lliure perquè es pugui desenvolupar, desplegar i escalar independentment.
  • API de passarel·la : En una arquitectura de microserveis, hi ha molts serveis, protocols, adreces, ports, polítiques de seguretat, polítiques de redundància, etc. més.

Aquest és el títol de la imatge

Cada microserveu s'implementa seguint el hexagonal estil d'arquitectura: la lògica central està incrustada dins d'un hexàgon i les vores de l'hexàgon es consideren l'entrada i sortida. L’objectiu és capar els objectes de manera que aïlli la lògica central d’elements externs: la lògica central es troba al centre de la imatge i tots els altres elements es consideren punts d’integració (DB, API, missatgeria). En parlem adaptadors entrants que gestionen les peticions de l'exterior invocant la lògica empresarial i aproximadament adaptadors de sortida que són invocats per la lògica empresarial (per invocar aplicacions externes). A port defineix un conjunt d’operacions que interaccionen amb la lògica empresarial amb allò que n’és fora.

Aquest és el títol de la imatge

Mostraré els detalls d’un únic microservei per obtenir una explicació de l’arquitectura interna utilitzada, el Servei de lliurament . Té una única API per controlar l'estat d'un lliurament, defineix un fitxer port d'entrada IDeliveryAPI :

public interface IDeliveryApi { @ApiOperation(value = 'View delivery status', response = DeliveryDTO.class,responseContainer = 'list') @RequestMapping(value = 'status', produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) @ResponseBody List status(); }

La classe Lliurament Api és un adaptador entrant :

@RestController @RequestMapping('/delivery/') @Api(tags = 'DeliveryServices') public class DeliveryApi implements IDeliveryApi { @Autowired private DeliveryService deliveryService; @Override public List status() { return deliveryService.getAll(); } }

La classe Servei de lliurament representa la lògica empresarial :

@Service public class DeliveryService { @Autowired private DeliveryRepository deliveryRepository; @Autowired private DozerBeanMapper dozerBeanMapper; public List getAll() { List deliveryList =deliveryRepository.findAll(); List res=null; if(deliveryList!=null) { res=new ArrayList(); for(Delivery delivery:deliveryList) { DeliveryDTO deliveryDTO=dozerBeanMapper.map(delivery,DeliveryDTO.class); res.add(deliveryDTO); } } return res; } }

La interfície IDeliveryPublisher és un port de sortida :

redis 5 contra 6
public interface IDeliveryPublisher { void sendToOrderCallback(OrderDTO orderDTO) throws JsonProcessingException; }

La classe DeliveryPublisher és un adaptador de sortida :

@Service public class DeliveryPublisher implements IDeliveryPublisher { @Autowired private ObjectMapper objectMapper; @Autowired private KafkaTemplate kafkaTemplate; @Override public void sendToOrderCallback(OrderDTO orderDTO) throws JsonProcessingException { kafkaTemplate.send(TOPIC_ORDER_CALLBACK,objectMapper.writeValueAsString(orderDTO)); } }

Cada microservei (en el meu exemple) té aquest estil d’arquitectura internament per assegurar un alt acoblament fluix entre capes de programari. Però això només és l’arquitectura interna d’un únic microservei, és possible que altres microserveis utilitzin un Capes estil arquitectònic, per exemple.

Bé, un cas d'ús senzill que implica tots els microserveis és el gestió de comandes , és a dir: un navegador sol·licita una hamburguesa, el Servei de comandes rep la comanda i escriu-la a la base de dades, el treball de gestió de la comanda ha finalitzat, però per completar la comanda ha de contactar amb Servei de cuina , de manera que envia un missatge sobre un tema (per assegurar una interconnexió asíncrona-comunicació), el Servei de cuina escolta sobre aquest tema, consumeix el missatge i processa l 'ordre, donant un feedback al Servei de comandes a través d’un altre tema. Quan el Servei de cuina ha cuinat l'hamburguesa a la qual envia un missatge Servei de lliurament , el Servei de lliurament processa el missatge, lliura l'hamburguesa i envia un comentari. Totes les comunicacions entre els microserveis passen per l'intermediari de missatges, en el meu exemple Kafka , He aplicat un Patró de saga coreogràfica , això és:

reacciona arrossega-redimensiona

Per veure tota l'arquitectura, faig servir el docker-compose.yml (aplicació docker) que es mostra a continuació:

version: '3.2' services: order-service: image: paspaola/order-service:0.0.1 ports: - 8090:8090 depends_on: - mongodb-order - kafkabroker networks: - mcpaspao kitchen-service: image: paspaola/kitchen-service:0.0.1 ports: - 8080:8080 depends_on: - mongodb-kitchen - kafkabroker networks: - mcpaspao delivery-service: image: paspaola/delivery-service:0.0.1 ports: - 8070:8070 depends_on: - mongodb-delivery - kafkabroker networks: - mcpaspao mongodb-delivery: image: mongo:3.4.22-xenial ports: - 27017:27017 networks: - mcpaspao mongodb-order: image: mongo:3.4.22-xenial ports: - 27018:27017 networks: - mcpaspao mongodb-kitchen: image: mongo:3.4.22-xenial ports: - 27019:27017 networks: - mcpaspao kafkabroker: image: paspaola/kafka-mcpaspao ports: - 2181:2181 - 9092:9092 environment: - KAFKA_ADVERTISED_LISTNERS=${advertised.addr} networks: - mcpaspao kong-mcpaspao: image: paspaola/kong-mcpaspao:0.0.1 ports: - 8000:8000 - 8443:8443 - 8001:8001 - 8444:8444 networks: - mcpaspao depends_on: - delivery-service - kitchen-service - order-service networks: mcpaspao:

Com a la imatge general de dalt, hi ha tres serveis i tres bases de dades, després hi ha el corredor Kafka, una imatge personalitzada que ja inclou tots els temes necessaris:

  • servei d’ordres
  • Ordreservicecallback
  • servei de cuina
  • servei de lliurament

Al contenidor de Kafka també hi ha una instància de Zookeeper, necessària per iniciar Kafka, podeu llegir com fer-lo aquí .

L'últim component és l'API Gateway, Kong : la instal·lació clàssica utilitza una base de dades com Postgresql , però també és possible (per a ús de desenvolupament) iniciar Kong de manera declarativa, seguint la configuració senzilla de Kong kong.yml :

_format_version: '1.1' services: - name: order-service url: http://order-service:8090 routes: - name: order-service paths: - /order-service - name: kitchen-service url: http://kitchen-service:8080 routes: - name: kitchen-service paths: - /kitchen-service - name: delivery-service url: http://delivery-service:8070 routes: - name: delivery-service paths: - /delivery-service plugins: - name: request-transformer service: kitchen-service config: add: headers: - x-forwarded-prefix:/kitchen-service - name: request-transformer service: order-service config: add: headers: - x-forwarded-prefix:/order-service - name: request-transformer service: delivery-service config: add: headers: - x-forwarded-prefix:/delivery-service

En aquest exemple, faig servir la passarel·la d’API de la manera més senzilla, sense cap servei d’autenticació i autorització, ni rèplica de serveis, ni detecció de serveis, etc., per evitar confusions sobre l’aspecte principal: la implementació de Patró de saga coreogràfica .

Per construir el projecte, podeu utilitzar-lo maven i després comenceu manualment tots els serveis, o podeu crear-ho tot amb el fitxer Dockerfase de diverses etapes (heu d'activar el fitxer característiques experimentals a Docker 19.x):

docker buildx build --target=order-service -t paspaola/order-service:0.0.1 --load . && docker buildx build --target=kitchen-service -t paspaola/kitchen-service:0.0.1 --load . && docker buildx build --target=delivery-service -t paspaola/delivery-service:0.0.1 --load . && docker buildx build --target=kong-mcpaspao -t paspaola/kong-mcpaspao:0.0.1 --load .

i després comenceu amb l'ordre:

docker app render -s advertised.addr='your docker host ip' mcpaspao.dockerapp| docker-compose -f - up

És hora de provar!

Podeu verificar que tots els microserveis s’executen mitjançant la interfície d’usuari de Swagger:

Ara vull una hamburguesa !!! La cuina necessita unes hamburgueses, la nevera està buida, per tant (cal instal·lar-la) jq ):

curl -X POST 'http://localhost:8000/kitchen-service/kitchen/add?hamburgerType=KOBE&quantity=2' -H 'accept: application/json'|jq -C && curl -X GET 'http://localhost:8000/kitchen-service/kitchen/status' -H 'accept: application/json'|jq -C

He afegit dues hamburgueses, ara faig una sol·licitud de comanda amb dues hamburgueses:

no puc iniciar la sessió a aol
printf ' --START-- ' && curl -X POST 'http://localhost:8000/order-service/order/create' -H 'accept: application/json' -H 'Content-Type: application/json' -d '{ 'addressDTO': { 'number': 'string', 'street': 'string' }, 'cookingType': 'BLOOD', 'hamburgerList': [ { 'hamburgerType': 'KOBE', 'quantity': 2 } ], 'price': 10}' |jq -C && printf ' --------- ' && curl -X GET 'http://localhost:8000/order-service/order/view' -H 'accept: application/json'|jq -C && sleep 5 && printf ' --------- ' && curl -X GET 'http://localhost:8000/order-service/order/view' -H 'accept: application/json'|jq -C && sleep 5 && printf ' --------- ' && curl -X GET 'http://localhost:8000/order-service/order/view' -H 'accept: application/json'|jq -C && sleep 5 && printf ' --------- ' && curl -X GET 'http://localhost:8000/order-service/order/view' -H 'accept: application/json'|jq -C && printf ' --------- ' && curl -X GET 'http://localhost:8000/delivery-service/delivery/status' -H 'accept: application/json'|jq -C && printf ' --END-- '

Al primer pas, l'ordre entra ESPERA estat, doncs CUINA , EMBALATGE i LLIURAT estat. Si torneu a executar l'script, el sistema no té prou hamburgueses i el següent ordre estarà en estat ESPERA i llavors ABORTAT .

Espero que aquesta guia us ajudi a aclarir la potència i la complexitat d’una arquitectura de microserveis, aquest és només un exemple pràctic implementat mitjançant components senzills i bàsics, però podeu endevinar quan l’utilitzeu o no. Gràcies per llegir.

#microservices # spring-boot #docker #devops