Integrando Apache Storm como servidor de aplicaciones Python


Carlos Perelló Marín
@carlosperellom
Server Density

Sistema de alertas

https://blog.serverdensity.com/
Apache Storm
Sistema de computación distribuida en tiempo real gratuito y con licencia open source.
El objetivo es hacer simple el procesado de streams de datos de forma fiable y en tiempo real.

Keep calm!
Por qué Apache Storm
- escalable
- tolerancia a fallos
- garantía que todos los datos serán procesados.
- utilizado por docenas de empresas (Twitter, Yahoo, Groupon, ...)
- comunidad muy activa
- funciona con desarrollos 99,99% Python.
Conceptos
- Topology
- Stream grouping
- Bolt
- Spout
- Tuple
- Emit
- Tasks
- Workers
Ejemplo Topología
TopologyBuilder builder = new TopologyBuilder();
/*
Setup device payload processing
*/
KafkaSpout deviceSpout = new KafkaSpout(deviceKafkaConf);
builder.setSpout(deviceSpoutId, deviceSpout, 1);
builder.setBolt(
"fetchitem",
new FetchItemFromInventoryBolt(pythonPath),
10).shuffleGrouping(deviceSpoutId).shuffleGrouping("cloudpayload");
builder.setBolt(
"updateposted",
new UpdatePostedCollectionBolt(pythonPath),
2).shuffleGrouping("fetchitem");
...
StormSubmitter.submitTopology(
TOPOLOGY_NAME, conf, builder.createTopology());
Multilang
"Pegamento" entre Apache Storm y otros lenguajes de programación.
Protocolo de comunicación entre procesos mediante stdin/stdout multiservidor.
ShellBolt, ShellSpout
Ejemplo ShellBolt (Java)
public class SendToMetricsBolt extends ShellBolt implements IRichBolt {
public SendToMetricsBolt(String pythonPath) {
super(pythonPath, "send_to_metrics_bolt.py");
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("payload"));
}
@Override
public Map<String, Object> getComponentConfiguration() {
return null;
}
}Ejemplo ShellBolt (python)
"""Code sending the converted payload to metrics.
"""
__all__ = [
'SendToMetricsBolt',
]
from serverdensity.alerts_processing.storm.metrics import MetricsDispatcher
from serverdensity.alerts_processing.storm import SDBaseBolt
class SendToMetricsBolt(SDBaseBolt):
def initialize_bolt(self, stormconf, context):
self.dispatcher = MetricsDispatcher(self.statsd_conn, self.logger)
def process_payload(self, payload):
if not self.dispatcher.send_to_metrics_service(payload):
self.logger.error(
'SendToMetricsBolt unable to send to metrics',
extra={'barium': self.barium})
return False
return True
if __name__ == "__main__":
SendToMetricsBolt().run()Ejemplo ShellBolt (emits)
storm_helper.emit([json.dumps(preact_payload)], "preact")
if payload_type == 'agent':
storm_helper.emit([json.dumps(payload)])
elif payload_type == 'cloud':
storm_helper.emit([json.dumps(payload)], "cloudpayload")Ejemplo ShellBolt (helper)
def process(self, tup):
"""Process an storm tuple.
:param tup: an Storm Tuple.
"""
self.barium = 'NO_BARIUM'
timer = statsd.timer.Timer(
'alerts_processing.{0}.storm.{1}.process'.format(
self.host_name, self.name), self.statsd_conn)
timer.start()
self.statsd_counter.increment('started')
payload = tup.values[0]
if isinstance(payload, basestring):
try:
payload = json.loads(payload)
except JSONDecodeError:
self.logger.error(
"Got a JSONDecodeError decoding payload '{0}'".format(payload),
extra={'barium': self.barium})
raise
self.barium = payload['barium']
if self.process_payload(payload):
self.statsd_counter.increment('success')
timer.stop('success')
else:
self.statsd_counter.increment('failure')
timer.stop('failure')Siguientes pasos
- Usar pyleus o streamparse para no usar nada de Java
- Dejar de usar JSON para el paso de payloads
- Generar un único .jar que contenga todas las dependencias
¿Preguntas?
Carlos Perelló Marín
@carlosperellom
carlos@serverdensity.com
www.serverdensity.com
Apache Storm
By Carlos Perelló Marín
Apache Storm
- 179