Slide 1

Slide 1 text

Stream processing avec Apache Pulsar Bruno Bonnin @_bruno_b_ https://bonnin.dev

Slide 2

Slide 2 text

Directeur Technique / Consultant @ Zenika Nantes

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Pulsar, un système de messagerie distribué Né chez Yahoo ! pour compenser certaines limites des solutions de l’époque Pour des apps critiques comme Yahoo Mail, Yahoo Finance, Yahoo Sports, etc Open sourcé en 2016 Devenu Top Level Project de la fondation Apache en sept 2018 Premier déploiement Q2 2015

Slide 5

Slide 5 text

Concepts & architecture

Slide 6

Slide 6 text

Des producteurs, des consommateurs, des topics, ... Topic 1 Producteur Consommateur Topic 2 Topic 3

Slide 7

Slide 7 text

Gestion des messages Producteurs Stockage des messages Bookie 1 Bookie 2 Bookie 3 Bookie 4 Consommateurs zkServer 1 zkServer 2 zkServer 3 Broker 1 Broker 2 Broker 3 lecture stockage meta-données coordination clusters stockage meta-données stockage des données Des brokers, des bookies, ... écriture Apache BookKeeper Apache Zookeeper

Slide 8

Slide 8 text

Bookie 1 Une architecture scalable Broker 1 Broker 2 Bookie 2 Bookie 3 Bookie 4 Gestion des messages Stockage des messages Producteurs Consommateurs Broker 3 Bookie 5 Topic 1

Slide 9

Slide 9 text

Apache BookKeeper Système de stockage scalable, tolérant aux pannes et à faible latence Conçu à l’origine comme une solution pour la haute disponibilité du NameNode de HDFS (WAL : Write-Ahead Logging) Avec Pulsar: stockage des données et des offsets (cursors)

Slide 10

Slide 10 text

Broker Topic 1 segment 2 segment 1 segment x ... Bookie 1 segment 1 Une architecture hautement disponible segment 1 segment 1 segment x segment x segment 2 segment 2 Bookie 2 Bookie 3 Un topic est constitué d’un ensemble de segments (ledgers) contenant les messages. Les segments sont stockés et répartis sur les bookies. segment x segment 2 Bookie 4

Slide 11

Slide 11 text

Broker Topic 1 segment 2 segment 1 segment x ... Bookie 1 segment 1 Une architecture hautement disponible segment 1 segment 1 segment x segment x segment 2 segment 2 Bookie 2 Bookie 3 Sur défection d’un bookie, automatiquement, récupération des segments manquants pour respecter le facteur de réplication segment x segment 2 Bookie 4 segment x segment 1

Slide 12

Slide 12 text

Modes de consommation

Slide 13

Slide 13 text

Topic Producteur Consommateur - A1 Exclusive subscription Subscription A Consommateur - A2 Les tentatives de connexions de A2 échoueront Topics et abonnements

Slide 14

Slide 14 text

Topic Producteur Consommateur - B1 Fail-over subscription Subscription B Consommateur - B2 Messages consommés par B2, uniquement en cas d’échec de B1 Topics et abonnements

Slide 15

Slide 15 text

Topic Producteur Consommateur - C1 Subscription C Consommateur - C2 Les messages sont envoyés aux deux consommateurs (round robin) -> aucune garantie d’ordre ! Topics et abonnements Shared subscription

Slide 16

Slide 16 text

Topic Producteur Consommateur - B1 Consommateur - B2 Consommateur - A1 Consommateur - C1 Consommateur - C2 Exclusive Subscription Il peut y avoir plusieurs abonnements sur un topic Fail-over Subscription Shared Subscription Topics et abonnements

Slide 17

Slide 17 text

Les messages

Slide 18

Slide 18 text

Messages Contenu d’un message: - Tableau d’octets (peut être conforme à un schéma) - Clé (optionnel) - Ensemble de propriétés (optionnel) - Nom du producteur - Id de séquence (numéro d’ordre dans le topic, attribué par le producteur) - Timestamps

Slide 19

Slide 19 text

Cycle de vie des messages Avec rétention message acquitté message acquitté Messages supprimés (hors rétention) Messages gardés car dans la période de rétention Non traités message acquitté message acquitté message non acquitté message non acquitté Messages supprimés Messages supprimés car au-delà du TTL Non traités et encore dans le TTL message non acquitté message non acquitté message non acquitté message non acquitté message acquitté message acquitté Avec TTL message acquitté message acquitté Messages supprimés Non traités (sauvegardés dans BookKeeper) message non acquitté message non acquitté message acquitté message acquitté Cas nominal

Slide 20

Slide 20 text

Démo

Slide 21

Slide 21 text

# Lancement de tous les composants # broker, bookie, zookeeper (pour des tests/dev) $ pulsar standalone $ pulsar-admin clusters list standalone $ pulsar-admin clusters get standalone { "serviceUrl":"http://hostname:8080", "brokerServiceUrl": "pulsar://hostname:6650" }

Slide 22

Slide 22 text

# Producteur: 100 messages, 1msg/sec $ pulsar-client produce \ -m "Hello Devoxx Morocco" \ -n 100 -r 1 \ demo-topic # Consommateur: 1msg/sec sans arrêt $ pulsar-client consume \ -n 0 -r 1 \ -s "demo-subs-exclusive" \ -t Exclusive \ demo-topic

Slide 23

Slide 23 text

Développons avec Pulsar

Slide 24

Slide 24 text

+ API WebSocket

Slide 25

Slide 25 text

// Création d’un client (idem producteur et conso) PulsarClient client = PulsarClient.builder() .serviceUrl("pulsar://myhostname:6650") .build(); .authentication(...) .connectionsPerBroker(5) .ioThreads(10) .keepAliveInterval(2, TimeUnit.MINUTES) .maxNumberOfRejectedRequestPerConnection(5) .operationTimeout(10, TimeUnit.SECONDS) ...

Slide 26

Slide 26 text

Producer producer = client.newProducer() .topic("demo-topic") .producerName("demo-producer") .... .create(); // Envoi synchrone MessageId msgId = producer.send("Hi Devoxx!".getBytes()); // Envoi asynchrone producer.sendAsync("Hi Devoxx!".getBytes()) .thenAccept(msgId -> { System.out.printf("Msg %s successfully sent", msgId); });

Slide 27

Slide 27 text

Consumer consumer = client.newConsumer() .topic("demo-topic") .subscriptionName("demo-exclusive-sub") .subscriptionType(SubscriptionType.Exclusive) .subscribe(); // Lecture à partir du premier message non acquitté Message msg = consumer.receive(1000, TimeUnit.SECONDS); System.out.printf("Message: %s, from %s with id=%s\n", new String(msg.getData()), msg.getProducerName(), msg.getMessageId()); consumer.acknowledge(msg); // A ne pas oublier

Slide 28

Slide 28 text

Schema Registry Par défaut, le type des messages est byte[] Pulsar fournit un système de contrôle des types de données: uniquement valable pour les clients Java !! Les schémas sont automatiquement uploadés sur les brokers à la création des producteurs (et bien sûr sauvegardés dans BookKeeper ) Gestion des versions

Slide 29

Slide 29 text

// Côté producteur Producer producer = client.newProducer(JSONSchema.of(Order.class)) .topic("orders") .create(); Order order = new Order(product, quantity); producer.send(order);

Slide 30

Slide 30 text

// Côté consommateur Consumer consumer = client.newConsumer(JSONSchema.of(Order.class)) .topic("orders") ... .subscribe(); Message msg = consumer.receive(1000, TimeUnit.SECONDS); Order order = msg.getValue();

Slide 31

Slide 31 text

Multi-tenancy

Slide 32

Slide 32 text

Multi-tenancy Type de topic Nom de l’entité (tenant) Nom du namespace Nom du topic persistent://public/default/demo-topic

Slide 33

Slide 33 text

Multi-tenancy Pulsar Cluster Sales Ops Tenants DC1 Online Namespaces Stores 20 To Topics Rétention: 10j Fraud Clients Orders Logs persistent://Sales/Stores/Clients

Slide 34

Slide 34 text

# Multi-tenancy # - création entité $ pulsar-admin tenants create sales # - création namespace $ pulsar-admin namespaces create sales/online # - ajout de quotas $ pulsar-admin namespaces set-retention \ --size 20T --time 2d sales/online

Slide 35

Slide 35 text

Pulsar Functions

Slide 36

Slide 36 text

Pulsar Functions Topic de sortie F But: offrir un cadre d’exécution de traitements des données sans utiliser d’environnement complémentaire Topic d’entrée 1 Topic d’entrée 2 Topic des logs State

Slide 37

Slide 37 text

def process(input): return "Hi %s!" % input

Slide 38

Slide 38 text

// Exemple le plus simple, sans framework import java.util.function.Function; public class HiFunction implements Function { @Override public String apply(String inputMsg) { return String.format("Hi %s!", inputMsg); } }

Slide 39

Slide 39 text

import org.apache.pulsar.functions.api.Context; import org.apache.pulsar.functions.api.Function; import org.slf4j.Logger; public class EnhancedHiFunction implements Function { @Override public String process(String input, Context context) throws Exception { Logger logger = context.getLogger(); String functionTenant = context.getTenant(); String functionNamespace = context.getNamespace(); String functionName = context.getFunctionName(); logger.info("Function {}/{}/{}: input={}", functionTenant, functionNamespace, functionName, input); return String.format("Hi %s!", input); } }

Slide 40

Slide 40 text

Dans les brokers Pulsar Functions - Déploiement Broker 1 Function workers Broker 2 Function workers Broker 1 Dans des containers Function worker 1 Broker 2 Function worker 2 En local Function workers

Slide 41

Slide 41 text

# Déploiement d’une fonction $ pulsar-admin functions create \ --jar my_pulsar_fct.jar \ --className MyPulsarFunction \ --fqfn demo/test/hello \ --inputs persistent://demo/test/input \ --output persistent://demo/test/output \ --log-topic persistent://demo/test/logs \ --cpu 8 \ --ram 8589934592 \ --disk 10737418240

Slide 42

Slide 42 text

Pulsar Functions Exemples de cas d’utilisation des Functions: - Routage des messages - Filtrage if ("US".equals(order.country()) context.publish("sales/online/orders-us", order); else context.publish("sales/online/orders-not-us", order); public Order process(Order order) { if (order.total() > 10000) return order; return null; }

Slide 43

Slide 43 text

Pulsar Functions Exemples de cas d’utilisation des Functions: - Modification des messages - Alertes public Order process(Order order) { order.setEmail(anonymize(order.getEmail())); return order; } public String process(Sensor sensor) { if (sensor.getTemp() > 50) // Envoi d’un mail ... return null; }

Slide 44

Slide 44 text

Pulsar Functions Et quoi d’autres ? - stockage de métriques, de compteurs (stockés dans Apache BookKeeper, bien sûr) - utilisation de paramètres de config, - fenêtrage (sliding window, tumbling window) public Void process(Order order, Context context) { Double threshold = context.getUserConfigValue("threshold"); Float previous = context.getState(order.getId() + "-metric"); context.incrCounter(order.getId() + "-metric", 1); }

Slide 45

Slide 45 text

Pulsar IO

Slide 46

Slide 46 text

Pulsar IO Pulsar Topic Connecteur Source Connecteur Sink

Slide 47

Slide 47 text

Pulsar IO CDC basé sur

Slide 48

Slide 48 text

SQL

Slide 49

Slide 49 text

Pulsar SQL Basé sur Lecture directe sur Apache BookKeeper

Slide 50

Slide 50 text

$ pulsar sql-worker run

Slide 51

Slide 51 text

$ pulsar sql presto> show catalogs; presto> show schemas in pulsar; presto> show tables in pulsar."demo/ecommerce"; Catalog --------- pulsar system Schema ----------------------- information_schema public/default public/functions demo/ecommerce Table --------------- orders-all orders-not-us orders-us TOPICS NAMESPACES

Slide 52

Slide 52 text

presto> select id, orderdate, ipaddress, amount, email from pulsar."demo/ecommerce"."orders-all"; id | orderdate | ipaddress | amount | email -------------+---------------+-----------------+--------+---------------------------------- 307-02-9402 | 1561409774204 | 94.244.112.41 | 596.59 | [email protected] 470-87-6280 | 1561409774501 | 82.244.158.201 | 135.39 | [email protected] 842-89-5722 | 1561409774704 | 60.73.130.208 | 112.89 | [email protected] 795-45-3565 | 1561409775408 | 231.96.223.0 | 60.75 | [email protected] 876-36-9065 | 1561409775924 | 29.189.193.82 | 768.13 | [email protected] 840-97-8273 | 1561409776044 | 93.240.237.245 | 308.92 | [email protected] 043-41-7461 | 1561409776348 | 186.116.245.21 | 206.03 | [email protected] 069-72-8645 | 1561409776591 | 106.113.25.240 | 144.54 | [email protected]

Slide 53

Slide 53 text

Geo-replication

Slide 54

Slide 54 text

Réplication géographique Cluster A Topic 1 Producteur Cluster B Topic 1 Cluster C Topic 1 Consommateur Les topics doivent appartenir à un namespace global Réplication asynchrone ou synchrone (persistence local d’abord, puis envoi aux autres clusters) Possibilité pour un producteur de restreindre la réplication à des clusters donnés Producteur

Slide 55

Slide 55 text

Démo https://github.com/bbonnin/talk-intro-apache-pulsar

Slide 56

Slide 56 text

Autres frameworks Ma supérette en ligne !!! Broker Topic orders-all Producteur Java “Order Generator” Consommateur CLI “pulsar-client” Functions FilterOrder Topic orders-us Connectors (IO) Elasticsearch Sink SQL Stockage Bookie Flink

Slide 57

Slide 57 text

Gestion des messages Brokers Stockage Bookies En résumé... Traitements Pulsar Functions Connecteurs Pulsar IO Analytics Pulsar SQL (Presto) Stockage tiers AWS Google Azure HDFS Clients Producteurs / Consommateurs Flink Spark

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

Conclusion Il existe beaucoup de fonctions intéressantes : - découplage broker et stockage - tier-storage, - geo-replication, - Pulsar IO - Pulsar Functions - Schema registry, - SQL, - … Environnement complet pour couvrir vos besoins en termes de stream processing Aujourd’hui chez Yahoo ! - > 2 millions de topics - > 100 milliards de messages / jour - > 150 brokers Plus d’infos: https://streaml.io/blog

Slide 60

Slide 60 text

Auto-promo : https://github.com/bbonnin/pulsar-express

Slide 61

Slide 61 text

MERCI ! Illustrations: https://undraw.co/illustrations