Integrando o Spring ao AWS Kinesis

Integrando o Spring ao AWS Kinesis

1. Introdução

O Kinesis é uma ferramenta para coletar, processar e analisar fluxos de dados em tempo real, desenvolvida na Amazon. Uma de suas principais vantagens é que ela ajuda no desenvolvimento de aplicativos orientados a eventos.

Neste tutorial, vamos explorar algumas bibliotecas queenable our Spring application to produce and consume records from a Kinesis Stream. Os exemplos de código mostrarão a funcionalidade básica, mas não representam o código pronto para produção.

2. Pré-requisito

Antes de prosseguirmos, precisamos fazer duas coisas.

O primeiro é paracreate a Spring project, pois o objetivo aqui é interagir com o Kinesis de um projeto Spring.

O segundo é criar um fluxo de dados Kinesis. Podemos fazer isso a partir de um navegador da web em nossa conta da AWS. Uma alternativa para os fãs do AWS CLI entre nós éuse the command line. Como vamos interagir com ele a partir do código, também devemos ter em mãos o AWSIAM Credentials, a chave de acesso e a chave secreta e a região.

Todos os nossos produtores criarão registros fictícios de endereços IP, enquanto os consumidores lerão esses valores e os listarão no console do aplicativo.

3. AWS SDK para Java

A primeira biblioteca que usaremos é o SDK da AWS para Java. Sua vantagem é que ele nos permite gerenciar muitas partes do trabalho com o Kinesis Data Streams. Podemosread data, produce data, create data streams, and reshard data streams. A desvantagem é que, para ter código pronto para produção, teremos que codificar aspectos como resharding, tratamento de erros ou um daemon para manter o consumidor ativo.

3.1. Dependência do Maven

A dependência Mavenamazon-kinesis-client trará tudo o que precisamos para ter exemplos funcionais. Agora vamos adicioná-lo ao nosso arquivopom.xml:


    com.amazonaws
    amazon-kinesis-client
    1.11.2

3.2. Configuração da Primavera

Vamos reutilizar o objetoAmazonKinesis necessário para interagir com nosso fluxo Kinesis. Vamos criá-lo como um@Bean dentro de nossa classe@SpringBootApplication:

@Bean
public AmazonKinesis buildAmazonKinesis() {
    BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
    return AmazonKinesisClientBuilder.standard()
      .withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
      .withRegion(Regions.EU_CENTRAL_1)
      .build();
}

A seguir, vamos definir osaws.access.key eaws.secret.key, necessários para a máquina local, emapplication.properties:

aws.access.key=my-aws-access-key-goes-here
aws.secret.key=my-aws-secret-key-goes-here

E vamos lê-los usando a anotação@Value:

@Value("${aws.access.key}")
private String accessKey;

@Value("${aws.secret.key}")
private String secretKey;

Por questão de simplicidade, vamos contar com os métodos@Scheduled para criar e consumir registros.

3.3. Consumidor

OAWS SDK Kinesis Consumer uses a pull model, o que significa que nosso código extrairá registros dos fragmentos do fluxo de dados do Kinesis:

GetRecordsRequest recordsRequest = new GetRecordsRequest();
recordsRequest.setShardIterator(shardIterator.getShardIterator());
recordsRequest.setLimit(25);

GetRecordsResult recordsResult = kinesis.getRecords(recordsRequest);
while (!recordsResult.getRecords().isEmpty()) {
    recordsResult.getRecords().stream()
      .map(record -> new String(record.getData().array()))
      .forEach(System.out::println);

    recordsRequest.setShardIterator(recordsResult.getNextShardIterator());
    recordsResult = kinesis.getRecords(recordsRequest);
}

The GetRecordsRequest object builds the request for stream data. Em nosso exemplo, definimos um limite de 25 registros por solicitação e continuamos lendo até que não haja mais nada para ler.

Também podemos notar que, para nossa iteração, usamos um objetoGetShardIteratorResult. Criamos esse objeto dentro de um método@PostConstruct para começarmos a rastrear os registros imediatamente:

private GetShardIteratorResult shardIterator;

@PostConstruct
private void buildShardIterator() {
    GetShardIteratorRequest readShardsRequest = new GetShardIteratorRequest();
    readShardsRequest.setStreamName(IPS_STREAM);
    readShardsRequest.setShardIteratorType(ShardIteratorType.LATEST);
    readShardsRequest.setShardId(IPS_SHARD_ID);

    this.shardIterator = kinesis.getShardIterator(readShardsRequest);
}

3.4. Produtor

Agora vamos ver comohandle the creation of records for our Kinesis data stream.

We insert data using a PutRecordsRequest object. Para este novo objeto, adicionamos uma lista que compreende vários objetosPutRecordsRequestEntry:

List entries = IntStream.range(1, 200).mapToObj(ipSuffix -> {
    PutRecordsRequestEntry entry = new PutRecordsRequestEntry();
    entry.setData(ByteBuffer.wrap(("192.168.0." + ipSuffix).getBytes()));
    entry.setPartitionKey(IPS_PARTITION_KEY);
    return entry;
}).collect(Collectors.toList());

PutRecordsRequest createRecordsRequest = new PutRecordsRequest();
createRecordsRequest.setStreamName(IPS_STREAM);
createRecordsRequest.setRecords(entries);

kinesis.putRecords(createRecordsRequest);

Criamos um consumidor básico e um produtor de registros IP simulados. Tudo o que falta fazer agora é executar nosso projeto Spring e ver os IPs listados em nosso console de aplicativo.

4. KCL e KPL

Kinesis Client Library (KCL) is a library that simplifies the consuming of records. É também uma camada de abstração sobre as APIs Java do AWS SDK para Kinesis Data Streams. Nos bastidores, a biblioteca lida com o balanceamento de carga em várias instâncias, respondendo a falhas da instância, verificando os registros processados ​​e reagindo ao re-compartilhamento.

Kinesis Producer Library (KPL) is a library useful for writing to a Kinesis data stream. Ele também fornece uma camada de abstração que fica sobre as APIs Java do AWS SDK para Kinesis Data Streams. Para um melhor desempenho, a biblioteca lida automaticamente com a lógica de lotes, multiencadeamento e repetição.

O KCL e o KPL têm a principal vantagem de serem fáceis de usar, para que possamos nos concentrar na produção e no consumo de registros.

4.1. Dependências do Maven

As duas bibliotecas podem ser trazidas separadamente em nosso projeto, se necessário. Para incluirKPLeKCL em nosso projeto Maven, precisamos atualizar nosso arquivo pom.xml:


    com.amazonaws
    amazon-kinesis-producer
    0.13.1


    com.amazonaws
    amazon-kinesis-client
    1.11.2

4.2. Configuração da Primavera

A única preparação para a primavera de que precisamos é garantir que tenhamos as credenciais do IAM em mãos. Os valores paraaws.access.keyeaws.secret.key são definidos em nosso arquivoapplication.properties para que possamos lê-los com@Value quando necessário.

4.3. Consumidor

Primeiro, vamoscreate a class that implements the IRecordProcessor interface and defines our logic for how to handle Kinesis data stream records, que é para imprimi-los no console:

public class IpProcessor implements IRecordProcessor {
    @Override
    public void initialize(InitializationInput initializationInput) { }

    @Override
    public void processRecords(ProcessRecordsInput processRecordsInput) {
        processRecordsInput.getRecords()
          .forEach(record -> System.out.println(new String(record.getData().array())));
    }

    @Override
    public void shutdown(ShutdownInput shutdownInput) { }
}

A próxima etapa édefine a factory class that implements the IRecordProcessorFactory interfacee retorna um objetoIpProcessor criado anteriormente:

public class IpProcessorFactory implements IRecordProcessorFactory {
    @Override
    public IRecordProcessor createProcessor() {
        return new IpProcessor();
    }
}

E agora, para a etapa final,we’ll use a Worker object to define our consumer pipeline. Precisamos de um objetoKinesisClientLibConfiguration que definirá, se necessário, as credenciais IAM e a região AWS.

Vamos passar oKinesisClientLibConfiguration, e nosso objetoIpProcessorFactory, para nossoWorker e, em seguida, iniciá-lo em um thread separado. Mantemos essa lógica de consumo de registros sempre ativa com o uso da classeWorker, portanto, estamos continuamente lendo novos registros agora:

BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
KinesisClientLibConfiguration consumerConfig = new KinesisClientLibConfiguration(
  APP_NAME,
  IPS_STREAM,
  new AWSStaticCredentialsProvider(awsCredentials),
  IPS_WORKER)
    .withRegionName(Regions.EU_CENTRAL_1.getName());

final Worker worker = new Worker.Builder()
  .recordProcessorFactory(new IpProcessorFactory())
  .config(consumerConfig)
  .build();
CompletableFuture.runAsync(worker.run());

4.4. Produtor

Vamos agora definir o objetoKinesisProducerConfiguration, adicionando as credenciais IAM e a região AWS:

BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
KinesisProducerConfiguration producerConfig = new KinesisProducerConfiguration()
  .setCredentialsProvider(new AWSStaticCredentialsProvider(awsCredentials))
  .setVerifyCertificate(false)
  .setRegion(Regions.EU_CENTRAL_1.getName());

this.kinesisProducer = new KinesisProducer(producerConfig);

Incluiremos o objetokinesisProducer criado anteriormente em um trabalho@Scheduled e produziremos registros para nosso fluxo de dados Kinesis continuamente:

IntStream.range(1, 200).mapToObj(ipSuffix -> ByteBuffer.wrap(("192.168.0." + ipSuffix).getBytes()))
  .forEach(entry -> kinesisProducer.addUserRecord(IPS_STREAM, IPS_PARTITION_KEY, entry));

5. Fichário de fluxo de nuvem de primavera Kinesis

Já vimos duas bibliotecas, ambas criadas fora do ecossistema Spring. Agora vamossee how the Spring Cloud Stream Binder Kinesis can simplify our life further enquanto construímos sobreSpring Cloud Stream.

5.1. Dependência do Maven

A dependência Maven que precisamos definir em nosso aplicativo paraSpring Cloud Stream Binder Kinesis é:


    org.springframework.cloud
    spring-cloud-stream-binder-kinesis
    1.2.1.RELEASE

5.2. Configuração da Primavera

Ao executar no EC2, as propriedades necessárias da AWS são descobertas automaticamente, portanto, não há necessidade de defini-las. Como estamos executando nossos exemplos em uma máquina local, precisamos definir nossa chave de acesso IAM, chave secreta e região para nossa conta AWS. Também desativamos a detecção automática do nome da pilha do CloudFormation para o aplicativo:

cloud.aws.credentials.access-key=my-aws-access-key
cloud.aws.credentials.secret-key=my-aws-secret-key
cloud.aws.region.static=eu-central-1
cloud.aws.stack.auto=false

Spring Cloud Stream é fornecido com três interfaces que podemos usar em nossa ligação de fluxo:

  • OSink é para ingestão de dados

  • OSource é usado para publicar registros

  • OProcessor é uma combinação de ambos

Também podemos definir nossas próprias interfaces, se precisarmos.

5.3. Consumidor

Definir um consumidor é um trabalho de duas partes. Primeiro, vamos definir, emapplication.properties, o fluxo de dados do qual consumiremos:

spring.cloud.stream.bindings.input.destination=live-ips
spring.cloud.stream.bindings.input.group=live-ips-group
spring.cloud.stream.bindings.input.content-type=text/plain

E a seguir, vamos definir uma classe Spring@Component. A anotação@EnableBinding(Sink.class) will allow us to read from the Kinesis stream using the method annotated with @StreamListener(Sink.INPUT):

@EnableBinding(Sink.class)
public class IpConsumer {

    @StreamListener(Sink.INPUT)
    public void consume(String ip) {
        System.out.println(ip);
    }
}

5.4. Produtor

O produtor também pode ser dividido em dois. Primeiro, temos que definir nossas propriedades de fluxo dentro deapplication.properties:

spring.cloud.stream.bindings.output.destination=live-ips
spring.cloud.stream.bindings.output.content-type=text/plain

E entãowe add @EnableBinding(Source.class) on a Spring @Component and create new test messages a cada poucos segundos:

@Component
@EnableBinding(Source.class)
public class IpProducer {

    @Autowired
    private Source source;

    @Scheduled(fixedDelay = 3000L)
    private void produce() {
        IntStream.range(1, 200).mapToObj(ipSuffix -> "192.168.0." + ipSuffix)
          .forEach(entry -> source.output().send(MessageBuilder.withPayload(entry).build()));
    }
}

Isso é tudo de que precisamos para o Spring Cloud Stream Binder Kinesis funcionar. Podemos simplesmente iniciar o aplicativo agora.

6. Conclusão

Neste artigo, vimos como integrar nosso projeto Spring com duas bibliotecas AWS para interagir com um fluxo de dados Kinesis. Também vimos como usar a biblioteca Spring Cloud Stream Binder Kinesis para tornar a implementação ainda mais fácil.

O código-fonte deste artigo pode ser encontradoover on Github.