{"id":304681,"date":"2020-06-02T03:00:12","date_gmt":"2020-06-02T03:00:12","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=304681"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=304681","title":{"rendered":"\u042d\u0432\u043e\u043b\u044e\u0446\u0438\u044f \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/504740\/\">\n<p>\u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e-\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0434\u0451\u0448\u0435\u0432\u043e, \u043c\u044b \u043d\u0435 \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u0435\u043c\u0441\u044f \u043d\u0430\u0434 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c\u044e \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c\u044e \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0431\u043e\u043b\u0438. \u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.<br \/>  \u041d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u043e\u0442 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c, \u044f \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u044e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043b\u044e\u0441\u044b \u0438 \u043c\u0438\u043d\u0443\u0441\u044b \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430. \u0421\u0442\u0430\u0442\u044c\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0430 \u043d\u0430 \u0442\u0440\u0438 \u0447\u0430\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0412 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043e \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u0432\u0435\u0431-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Dash, \u0442.\u0435. \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0438\u0445 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435.<\/li>\n<li>\u0412\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u0440\u0430\u0437\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0442.\u0435. \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u0438\u043d \u0441\u0435\u0440\u0432\u0438\u0441, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c \u0434\u0440\u0443\u0433\u043e\u0439, \u0430 \u0441\u0432\u044f\u0437\u044c \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043b\u0430\u0436\u0435\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u043e\u043a\u0435\u0440 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 Kafka.<\/li>\n<li>\u0412 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0431\u0443\u0434\u0443\u0442 &quot;\u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u044b&quot; \u0432 Docker \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c, \u043a\u0430\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0430 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0435 \u0441\u043d\u0438\u0437\u0443.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/6l\/ly\/j_\/6llyj_lypfj8dct_9amkrpofuye.png\"><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h3 id=\"vvedenie\">\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h3>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043b\u0443\u0447\u0448\u0435 \u043f\u043e\u043d\u044f\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440, \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u0445\u043e\u0442\u044f \u0431\u044b \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f \u0432 Kafka \u0438 Docker, \u043f\u0440\u0438\u0432\u0435\u0434\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043a\u0443\u0440\u0441\u043e\u0432 \u0438 \u0441\u0442\u0430\u0442\u0435\u0439:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041f\u043e Kafka, \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043d\u043e \u043d\u0430 youtube \u043a\u0430\u043d\u0430\u043b\u0435 <a href=\"https:\/\/www.youtube.com\/user\/Nephaste20\" rel=\"nofollow\">Stephane Maarek<\/a>, \u043a\u0430\u043d\u0430\u043b \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435.<\/li>\n<li>\u041f\u0440\u043e Docker \u043c\u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0441\u044f <a href=\"https:\/\/www.youtube.com\/playlist?list=PLU2ftbIeotGoheVSAKOYuKmOBiWAMFv5v\" rel=\"nofollow\">\u043f\u043b\u0435\u0439\u043b\u0438\u0441\u0442<\/a> \u0441 youtube \u043a\u0430\u043d\u0430\u043b\u0430 <a href=\"https:\/\/www.youtube.com\/channel\/UC1g3kT0ZcSXt4_ZyJOshKJQ\" rel=\"nofollow\">letsCode<\/a> \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435.<\/li>\n<li>\u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0430 \u0445\u0430\u0431\u0440\u0435 <a href=\"https:\/\/habr.com\/ru\/company\/raiffeisenbank\/blog\/346380\/\">\u041f\u0440\u043e\u0441\u0442\u043e \u043e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445<\/a> \u043e\u0442 <a href=\"https:\/\/habr.com\/ru\/users\/yuryka\/\" class=\"user_link\">YuryKa<\/a>.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0437\u0430\u043b\u0438\u0442 \u043d\u0430 github, \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0447\u0430\u0442\u044c <a href=\"https:\/\/github.com\/cdies\/kafka\" rel=\"nofollow\">\u043e\u0442\u0441\u044e\u0434\u0430<\/a>.<\/p>\n<p>  <\/p>\n<h3 id=\"chast-1-monolitnoe-prilozhenie\">\u0427\u0430\u0441\u0442\u044c 1. \u041c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/h3>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u044f \u0432\u0437\u044f\u043b \u043a\u043e\u0434 \u0438\u0437 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043f\u043e Dash (Plotly), \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e <a href=\"https:\/\/dash.plotly.com\/live-updates\" rel=\"nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>, \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0435 <a href=\"https:\/\/github.com\/cdies\/kafka\/tree\/master\/local_app\" rel=\"nofollow\"><code>local_app<\/code><\/a>. \u042d\u0442\u043e \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. <\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 monolith.py<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">import datetime  import dash import dash_core_components as dcc import dash_html_components as html import plotly from dash.dependencies import Input, Output  # pip install pyorbital from pyorbital.orbital import Orbital satellite = Orbital('TERRA')  external_stylesheets = ['https:\/\/codepen.io\/chriddyp\/pen\/bWLwgP.css']  app = dash.Dash(__name__, external_stylesheets=external_stylesheets) app.layout = html.Div(     html.Div([         html.H4('TERRA Satellite Live Feed'),         html.Div(id='live-update-text'),         dcc.Graph(id='live-update-graph'),         dcc.Interval(             id='interval-component',             interval=1*1000, # in milliseconds             n_intervals=0         )     ]) )  @app.callback(Output('live-update-text', 'children'),               [Input('interval-component', 'n_intervals')]) def update_metrics(n):     lon, lat, alt = satellite.get_lonlatalt(datetime.datetime.now())     style = {'padding': '5px', 'fontSize': '16px'}     return [         html.Span('Longitude: {0:.2f}'.format(lon), style=style),         html.Span('Latitude: {0:.2f}'.format(lat), style=style),         html.Span('Altitude: {0:0.2f}'.format(alt), style=style)     ]  # Multiple components can update everytime interval gets fired. @app.callback(Output('live-update-graph', 'figure'),               [Input('interval-component', 'n_intervals')]) def update_graph_live(n):     satellite = Orbital('TERRA')     data = {         'time': [],         'Latitude': [],         'Longitude': [],         'Altitude': []     }      # Collect some data     for i in range(180):         time = datetime.datetime.now() - datetime.timedelta(seconds=i*20)         lon, lat, alt = satellite.get_lonlatalt(             time         )         data['Longitude'].append(lon)         data['Latitude'].append(lat)         data['Altitude'].append(alt)         data['time'].append(time)      # Create the graph with subplots     fig = plotly.tools.make_subplots(rows=2, cols=1, vertical_spacing=0.2)     fig['layout']['margin'] = {         'l': 30, 'r': 10, 'b': 30, 't': 10     }     fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}      fig.append_trace({         'x': data['time'],         'y': data['Altitude'],         'name': 'Altitude',         'mode': 'lines+markers',         'type': 'scatter'     }, 1, 1)     fig.append_trace({         'x': data['Longitude'],         'y': data['Latitude'],         'text': data['time'],         'name': 'Longitude vs Latitude',         'mode': 'lines+markers',         'type': 'scatter'     }, 2, 1)      return fig  if __name__ == '__main__':     app.run_server(debug=True)<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0433\u0440\u0430\u0444\u0438\u043a\u0438 \u0438 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u044d\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0440\u0435\u0430\u043b-\u0442\u0430\u0439\u043c \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f python \u043f\u0430\u043a\u0435\u0442 <a href=\"https:\/\/pypi.org\/project\/pyorbital\/\" rel=\"nofollow\">pyorbital<\/a>, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0430\u0441\u0442\u0440\u043e\u043d\u043e\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f (\u0432 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430\u0443\u0447\u043d\u043e-\u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0441\u043f\u0443\u0442\u043d\u0438\u043a\u0430 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A2%D0%B5%D1%80%D1%80%D0%B0_(%D1%81%D0%BF%D1%83%D1%82%D0%BD%D0%B8%D0%BA)\" rel=\"nofollow\">Terra (EOS AM-1)<\/a>). \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0433\u0440\u0430\u0444\u0438\u043a\u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0447\u0435\u0440\u0435\u0437 Dash (Plotly) \u043d\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u043c \u0445\u043e\u0441\u0442\u0435: <code>127.0.0.1:8050<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u2014 \u044d\u0442\u043e altitude, longitude \u0438 latitude (\u0432\u044b\u0441\u043e\u0442\u0430, \u0434\u043e\u043b\u0433\u043e\u0442\u0430 \u0438 \u0448\u0438\u0440\u043e\u0442\u0430), \u0442.\u0435. \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0441\u043f\u0443\u0442\u043d\u0438\u043a\u0430 \u0432 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u0435\u0440\u0432\u044b\u0439 \u0433\u0440\u0430\u0444\u0438\u043a \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u2014 \u0434\u043e\u043b\u0433\u043e\u0442\u044b \u0438 \u0448\u0438\u0440\u043e\u0442\u044b \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/3q\/yl\/ks\/3qylks32vc4mcuk64quut6uwz3y.png\"><br \/>  (\u041d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0433\u043e (\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e) \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f)<\/p>\n<p>  <\/p>\n<p>\u041a \u043f\u043b\u044e\u0441\u0430\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043d\u0435\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0412\u044b\u0441\u043e\u043a\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b, \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u044e\u0442\u0441\u044f \u0438 \u0441\u0440\u0430\u0437\u0443 \u0436\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041a \u043c\u0438\u043d\u0443\u0441\u0430\u043c:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0412\u044b\u0441\u043e\u043a\u0430\u044f \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u043f\u0440\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0442\u043e \u0438 \u0438\u0445 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0442\u043e\u0436\u0435 \u043f\u043e\u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442, \u0442.\u043a. \u044d\u0442\u043e \u043f\u043e\u0432\u043b\u0435\u0447\u0435\u0442 \u043f\u0430\u0434\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u0445\u043e\u0442\u044f \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0448\u0438\u0431\u043e\u043a, \u043d\u043e \u044d\u0442\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u0438\u0442 \u043a\u043e\u0434).<\/li>\n<li>\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c\/\u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u043b\u0435\u0442\u0443. \u0415\u0441\u043b\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0451, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435.<\/li>\n<\/ul>\n<p>  <\/p>\n<h3 id=\"chast-2-prilozhenie-na-osnove-mikroservisov\">\u0427\u0430\u0441\u0442\u044c 2. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432<\/h3>\n<p>  <\/p>\n<p>\u0412 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0435 <a href=\"https:\/\/github.com\/cdies\/kafka\/tree\/master\/local_microservices_app\" rel=\"nofollow\"><code>local_microservices_app<\/code><\/a>, \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0442\u0443\u0434\u0430 \u0436\u0435 \u044f \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0441\u0435\u0440\u0432\u0438\u0441 Kafka \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432 Docker, \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/simplesteph\/kafka-stack-docker-compose\/blob\/master\/zk-single-kafka-single.yml\" rel=\"nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a> (\u0432\u0437\u044f\u0442\u043e \u0441 github \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f <a href=\"https:\/\/github.com\/simplesteph\" rel=\"nofollow\">Stephane Maarek<\/a>)<\/p>\n<p>  <\/p>\n<p>\u041c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043b \u043d\u0430 \u0434\u0432\u0435 \u0447\u0430\u0441\u0442\u0438, \u043f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u2014 backend (<code>producer.py<\/code>), \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0445 \u0432 Kafka, \u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u2014 frontend (<code>consumer.py<\/code>, <code>graph_display.py<\/code>) \u0447\u0438\u0442\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438\u0437 Kafka \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u0433\u0440\u0430\u0444\u0438\u043a\u0438 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.<\/p>\n<p>  <\/p>\n<p><em>backend:<\/em><br \/>  Producer (\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445) \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0445 \u0432 Kafka \u0440\u0430\u0437 \u0432 \u043e\u0434\u043d\u0443 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 (\u0432 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u043b\u0438\u0441\u044c \u0437\u0430 \u043a\u0430\u0436\u0434\u044b\u0435 20 \u0441\u0435\u043a\u0443\u043d\u0434)<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 producer.py<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">from time import sleep import datetime from confluent_kafka import Producer import json from pyorbital.orbital import Orbital  satellite = Orbital('TERRA')  topic = 'test_topic'  producer = Producer({'bootstrap.servers': 'localhost:9092'})  def acked(err, msg):     if err is not None:         print(&quot;Failed to deliver message: {}&quot;.format(err))     else:         print(&quot;Produced record to topic {} partition [{}] @ offset {}&quot;               .format(msg.topic(), msg.partition(), msg.offset()))  # send data every one second while True:     time = datetime.datetime.now()     lon, lat, alt = satellite.get_lonlatalt(time)     record_value = json.dumps({'lon':lon, 'lat': lat, 'alt': alt, 'time': str(time)})      producer.produce(topic, key=None, value=record_value, on_delivery=acked)      producer.poll()     sleep(1) <\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p><em>frontend:<\/em><br \/>  Consumer (\u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445) \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u0432 \u0432\u0438\u0434\u0435 \u043a\u043b\u0430\u0441\u0441\u0430 <code>MyKafkaConnect<\/code> \u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0444\u0430\u0439\u043b\u0435 <code>consumer.py<\/code>, \u043f\u0440\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0438\u0437 Kafka \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 180 (\u0438\u043b\u0438 \u043c\u0435\u043d\u044c\u0448\u0435, \u0435\u0441\u043b\u0438 \u0438\u0445 \u043d\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e) \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439. \u041f\u0440\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f\u0445 \u0434\u043e\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u043d\u043e\u0432\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438\u0437 Kafka.<\/p>\n<p>  <\/p>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (<code>monolith.py<\/code>) \u043d\u0435 \u0441\u0438\u043b\u044c\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c, \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0435 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435, \u0430 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043a\u043b\u0430\u0441\u0441 <code>MyKafkaConnect<\/code>, \u0432\u0441\u0435 \u043f\u0440\u0435\u0436\u043d\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0442\u0430\u043a\u0436\u0435.<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 consumer.py<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">import datetime from confluent_kafka import Consumer, TopicPartition import json from collections import deque from time import sleep  class MyKafkaConnect:     def __init__(self, topic, group, que_len=180):         self.topic = topic          self.conf = {             'bootstrap.servers': 'localhost:9092',             'group.id': group,             'enable.auto.commit': True,         }          # the application needs a maximum of 180 data units         self.data = {             'time': deque(maxlen=que_len),             'Latitude': deque(maxlen=que_len),             'Longitude': deque(maxlen=que_len),             'Altitude': deque(maxlen=que_len)         }          consumer = Consumer(self.conf)         consumer.subscribe([self.topic])          # download first 180 messges         self.partition = TopicPartition(topic=self.topic, partition=0)         low_offset, high_offset = consumer.get_watermark_offsets(self.partition)          # move offset back on 180 messages         if high_offset &gt; que_len:             self.partition.offset = high_offset - que_len         else:             self.partition.offset = low_offset          # set the moved offset to consumer         consumer.assign([self.partition])          self.__update_que(consumer)      # https:\/\/docs.confluent.io\/current\/clients\/python.html#delivery-guarantees     def __update_que(self, consumer):         try:             while True:                 msg = consumer.poll(timeout=0.1)                 if msg is None:                     break                 elif msg.error():                     print('error: {}'.format(msg.error()))                     break                 else:                     record_value = msg.value()                     json_data = json.loads(record_value.decode('utf-8'))                      self.data['Longitude'].append(json_data['lon'])                     self.data['Latitude'].append(json_data['lat'])                     self.data['Altitude'].append(json_data['alt'])                     self.data['time'].append(datetime.datetime.strptime(json_data['time'], '%Y-%m-%d %H:%M:%S.%f'))                      # save local offset                     self.partition.offset += 1                   finally:             # Close down consumer to commit final offsets.             # It may take some time, that why I save offset locally             consumer.close()      def get_graph_data(self):         consumer = Consumer(self.conf)         consumer.subscribe([self.topic])            # update low and high offsets (don't work without it)         consumer.get_watermark_offsets(self.partition)          # set local offset         consumer.assign([self.partition])          self.__update_que(consumer)          # convert data to compatible format         o = {key: list(value) for key, value in self.data.items()}         return o              def get_last(self):         lon = self.data['Longitude'][-1]         lat = self.data['Latitude'][-1]         alt = self.data['Altitude'][-1]          return lon, lat, alt  # for test if __name__ == '__main__':     connect = MyKafkaConnect(topic='test_topic', group='test_group')      while True:                 test = connect.get_graph_data()          print('number of messages:', len(test['time']),              'unique:', len(set(test['time'])),              'time:', test['time'][-1].second)          sleep(0.1) <\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 graph_display.py<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">import datetime  import dash import dash_core_components as dcc import dash_html_components as html import plotly from dash.dependencies import Input, Output  from consumer import MyKafkaConnect  connect = MyKafkaConnect(topic='test_topic', group='test_group')  external_stylesheets = ['https:\/\/codepen.io\/chriddyp\/pen\/bWLwgP.css']  app = dash.Dash(__name__, external_stylesheets=external_stylesheets) app.layout = html.Div(     html.Div([         html.H4('TERRA Satellite Live Feed'),         html.Div(id='live-update-text'),         dcc.Graph(id='live-update-graph'),         dcc.Interval(             id='interval-component',             interval=1*1000, # in milliseconds             n_intervals=0         )     ]) )  @app.callback(Output('live-update-text', 'children'),               [Input('interval-component', 'n_intervals')]) def update_metrics(n):     lon, lat, alt = connect.get_last()      print('update metrics')      style = {'padding': '5px', 'fontSize': '16px'}     return [         html.Span('Longitude: {0:.2f}'.format(lon), style=style),         html.Span('Latitude: {0:.2f}'.format(lat), style=style),         html.Span('Altitude: {0:0.2f}'.format(alt), style=style)     ]  # Multiple components can update everytime interval gets fired. @app.callback(Output('live-update-graph', 'figure'),               [Input('interval-component', 'n_intervals')]) def update_graph_live(n):     # Collect some data     data = connect.get_graph_data()     print('Update graph, data units:', len(data['time']))      # Create the graph with subplots     fig = plotly.tools.make_subplots(rows=2, cols=1, vertical_spacing=0.2)     fig['layout']['margin'] = {         'l': 30, 'r': 10, 'b': 30, 't': 10     }     fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}      fig.append_trace({         'x': data['time'],         'y': data['Altitude'],         'name': 'Altitude',         'mode': 'lines+markers',         'type': 'scatter'     }, 1, 1)     fig.append_trace({         'x': data['Longitude'],         'y': data['Latitude'],         'text': data['time'],         'name': 'Longitude vs Latitude',         'mode': 'lines+markers',         'type': 'scatter'     }, 2, 1)      return fig  if __name__ == '__main__':     app.run_server(debug=True) <\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u041a \u043f\u043b\u044e\u0441\u0430\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043d\u0435\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041c\u0435\u043d\u044c\u0448\u0435 \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u0439 \u043e\u0442 \u043e\u0448\u0438\u0431\u043e\u043a, \u0435\u0441\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u043f\u0440\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 backend \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0435, \u0442\u043e \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0441\u044f, \u0445\u043e\u0442\u044f \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043e\u043d\u0438 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 (\u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0433\u0440\u0430\u0444\u0438\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043b\u0438\u0448\u044c \u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442 \u0440\u0430\u0431\u043e\u0442\u0443).<\/li>\n<li>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f\/\u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043e\u043a \u043d\u0430 \u043b\u0435\u0442\u0443, \u0442.\u043a. \u043c\u043e\u0434\u0443\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e, \u0442\u043e \u043a\u0440\u0430\u0442\u043a\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 backend \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435 \u0432\u044b\u0437\u043e\u0432\u0435\u0442 \u043f\u0430\u0434\u0435\u043d\u0438\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432, \u0445\u043e\u0442\u044f \u0438 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043b\u0430\u0433.<\/li>\n<li>\u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041a \u043c\u0438\u043d\u0443\u0441\u0430\u043c:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041c\u0435\u043d\u044c\u0448\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u044b\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c, \u043c\u0435\u0436\u0434\u0443 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0438\u0445 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u0432 \u0432\u0438\u0434\u0435 Kafka, \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0445\u043e\u0442\u044c \u0438 \u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u043e\u0435, \u043d\u043e \u0432\u0440\u0435\u043c\u044f.<\/li>\n<\/ul>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ko\/dc\/uy\/kodcuy7iegggn32dma1ow6rb08i.png\"><br \/>  (\u041d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435 \u0441\u0432\u0435\u0440\u0445\u0443 backend \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0434\u0432\u043e\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u0447\u0442\u043e \u0432\u0438\u0434\u043d\u043e \u043d\u0430 \u0433\u0440\u0430\u0444\u0438\u043a\u0435)<\/p>\n<p>  <\/p>\n<h3 id=\"chast-3-prilozhenie-na-osnove-mikroservisov-upakovannyh-v-docker\">\u0427\u0430\u0441\u0442\u044c 3. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0432 Docker<\/h3>\n<p>  <\/p>\n<p>\u0412 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0435 <a href=\"https:\/\/github.com\/cdies\/kafka\/tree\/master\/docker_microservices_app\" rel=\"nofollow\"><code>docker_microservices_app<\/code><\/a>. \u041e\u0442 \u0432\u0442\u043e\u0440\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u043c, \u0447\u0442\u043e backend \u0438 frontend \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u044b \u0432 Docker. \u0422\u0430\u043a\u0436\u0435 \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0435\u0449\u0451 \u0434\u0432\u0430 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0432 backend (\u0435\u0449\u0451 \u0434\u0432\u0430 \u043d\u0430\u0443\u0447\u043d\u043e-\u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0441\u043f\u0443\u0442\u043d\u0438\u043a\u0430 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/Aura_(%D1%81%D0%BF%D1%83%D1%82%D0%BD%D0%B8%D0%BA)\" rel=\"nofollow\">Aura (EOS CH-1)<\/a> \u0438 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/Aqua_(%D1%81%D0%BF%D1%83%D1%82%D0%BD%D0%B8%D0%BA)\" rel=\"nofollow\">Aqua (EOS PM-1)<\/a>). <\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Docker \u0444\u0430\u0439\u043b\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 backend\/Dockerfile (\u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d producer.py)<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"plaintext\">FROM python:3.7  RUN python -m pip install confluent-kafka  RUN python -m pip install pyorbital  WORKDIR \/app  COPY producer.py .\/  CMD [&quot;python&quot;, &quot;producer.py&quot;] <\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 frontend\/Dockerfile (\u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u044b consumer.py \u0438 graph_display.py)<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"plaintext\">FROM python:3.7  RUN python -m pip install confluent-kafka  RUN python -m pip install dash plotly  WORKDIR \/app  COPY consumer.py graph_display.py .\/  CMD [&quot;python&quot;, &quot;graph_display.py&quot;] <\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 docker-compose.yml (\u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0432 \u0441\u0435\u0431\u0435 backend, frontend \u0438 Kafka)<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"plaintext\">version: '2.1'  # Stephane Maarek's kafka-docker # https:\/\/github.com\/simplesteph\/kafka-stack-docker-compose\/blob\/master\/zk-single-kafka-single.yml services:   zoo1:     image: zookeeper:3.4.9     hostname: zoo1     ports:       - &quot;2181:2181&quot;     restart: unless-stopped     environment:         ZOO_MY_ID: 1         ZOO_PORT: 2181         ZOO_SERVERS: server.1=zoo1:2888:3888     volumes:       - .\/zk-single-kafka-single\/zoo1\/data:\/data       - .\/zk-single-kafka-single\/zoo1\/datalog:\/datalog    kafka1:     image: confluentinc\/cp-kafka:5.5.0     hostname: kafka1     ports:       - &quot;9092:9092&quot;     restart: unless-stopped     environment:       KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL:\/\/kafka1:19092,LISTENER_DOCKER_EXTERNAL:\/\/${DOCKER_HOST_IP:-127.0.0.1}:9092       KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LISTENER_DOCKER_INTERNAL:PLAINTEXT,LISTENER_DOCKER_EXTERNAL:PLAINTEXT       KAFKA_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER_INTERNAL       KAFKA_ZOOKEEPER_CONNECT: &quot;zoo1:2181&quot;       KAFKA_BROKER_ID: 1       KAFKA_LOG4J_LOGGERS: &quot;kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO&quot;       KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1     volumes:       - .\/zk-single-kafka-single\/kafka1\/data:\/var\/lib\/kafka\/data     depends_on:       - zoo1    backend_terra:     build:       context: .\/backend     restart: unless-stopped     environment:       BOOTSTRAP_SERVERS: &quot;kafka1:19092&quot;       TOPIC: &quot;terra_topic&quot;       SATELLITE: &quot;TERRA&quot;     depends_on:       - kafka1    backend_aqua:     build:       context: .\/backend     restart: unless-stopped     environment:       BOOTSTRAP_SERVERS: &quot;kafka1:19092&quot;       TOPIC: &quot;aqua_topic&quot;       SATELLITE: &quot;AQUA&quot;     depends_on:       - kafka1    backend_aura:     build:       context: .\/backend     restart: unless-stopped     environment:       BOOTSTRAP_SERVERS: &quot;kafka1:19092&quot;       TOPIC: &quot;aura_topic&quot;       SATELLITE: &quot;AURA&quot;     depends_on:       - kafka1    frontend:     build:       context: .\/frontend     ports:       - &quot;8050:8050&quot;     restart: unless-stopped     environment:       BOOTSTRAP_SERVERS: &quot;kafka1:19092&quot;     depends_on:       - backend_terra       - backend_aqua       - backend_aura <\/code><\/pre>\n<\/div><\/div>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Python \u0444\u0430\u0439\u043b\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 backend\/producer.py<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">from time import sleep import datetime from confluent_kafka import Producer import json from pyorbital.orbital import Orbital import os  topic = os.environ['TOPIC']  bootstrap_servers = os.environ['BOOTSTRAP_SERVERS']  s_name = os.environ['SATELLITE']  satellite = Orbital(s_name)  producer = Producer({'bootstrap.servers': bootstrap_servers})  def acked(err, msg):     if err is not None:         print(&quot;Failed to deliver message: {}&quot;.format(err))     else:         print(&quot;Produced record to topic {} partition [{}] @ offset {}&quot;               .format(msg.topic(), msg.partition(), msg.offset()))  # send data every one second while True:     time = datetime.datetime.now()     lon, lat, alt = satellite.get_lonlatalt(time)     record_value = json.dumps({'lon':lon, 'lat': lat, 'alt': alt, 'time': str(time)})      producer.produce(topic, key=None, value=record_value, on_delivery=acked)      producer.poll()     sleep(1)<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 frontend\/consumer.py (\u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0441\u044f)<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">import datetime from confluent_kafka import Consumer, TopicPartition import json from collections import deque from time import sleep  class MyKafkaConnect:     def __init__(self, topic, group, que_len=180):         self.topic = topic          self.conf = {             'bootstrap.servers': 'localhost:9092',             'group.id': group,             'enable.auto.commit': True,         }          # the application needs a maximum of 180 data units         self.data = {             'time': deque(maxlen=que_len),             'Latitude': deque(maxlen=que_len),             'Longitude': deque(maxlen=que_len),             'Altitude': deque(maxlen=que_len)         }          consumer = Consumer(self.conf)         consumer.subscribe([self.topic])          # download first 180 messges         self.partition = TopicPartition(topic=self.topic, partition=0)         low_offset, high_offset = consumer.get_watermark_offsets(self.partition)          # move offset back on 180 messages         if high_offset &gt; que_len:             self.partition.offset = high_offset - que_len         else:             self.partition.offset = low_offset          # set the moved offset to consumer         consumer.assign([self.partition])          self.__update_que(consumer)      # https:\/\/docs.confluent.io\/current\/clients\/python.html#delivery-guarantees     def __update_que(self, consumer):         try:             while True:                 msg = consumer.poll(timeout=0.1)                 if msg is None:                     break                 elif msg.error():                     print('error: {}'.format(msg.error()))                     break                 else:                     record_value = msg.value()                     json_data = json.loads(record_value.decode('utf-8'))                      self.data['Longitude'].append(json_data['lon'])                     self.data['Latitude'].append(json_data['lat'])                     self.data['Altitude'].append(json_data['alt'])                     self.data['time'].append(datetime.datetime.strptime(json_data['time'], '%Y-%m-%d %H:%M:%S.%f'))                      # save local offset                     self.partition.offset += 1                   finally:             # Close down consumer to commit final offsets.             # It may take some time, that why I save offset locally             consumer.close()      def get_graph_data(self):         consumer = Consumer(self.conf)         consumer.subscribe([self.topic])            # update low and high offsets (don't work without it)         consumer.get_watermark_offsets(self.partition)          # set local offset         consumer.assign([self.partition])          self.__update_que(consumer)          # convert data to compatible format         o = {key: list(value) for key, value in self.data.items()}         return o              def get_last(self):         lon = self.data['Longitude'][-1]         lat = self.data['Latitude'][-1]         alt = self.data['Altitude'][-1]          return lon, lat, alt  # for test if __name__ == '__main__':     connect = MyKafkaConnect(topic='test_topic', group='test_group')      while True:                 test = connect.get_graph_data()          print('number of messages:', len(test['time']),              'unique:', len(set(test['time'])),              'time:', test['time'][-1].second)          sleep(0.1)<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 frontend\/graph_display.py<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"python\">import datetime  import dash import dash_core_components as dcc import dash_html_components as html import plotly from dash.dependencies import Input, Output  from consumer import MyKafkaConnect  external_stylesheets = ['https:\/\/codepen.io\/chriddyp\/pen\/bWLwgP.css']  app = dash.Dash(__name__, external_stylesheets=external_stylesheets) app.layout = html.Div(     html.Div([         html.Div([             html.H4('TERRA Satellite Live Feed'),             html.Div(id='terra-text'),             dcc.Graph(id='terra-graph')             ], className=&quot;four columns&quot;),         html.Div([             html.H4('AQUA Satellite Live Feed'),             html.Div(id='aqua-text'),             dcc.Graph(id='aqua-graph')             ], className=&quot;four columns&quot;),         html.Div([             html.H4('AURA Satellite Live Feed'),             html.Div(id='aura-text'),             dcc.Graph(id='aura-graph')             ], className=&quot;four columns&quot;),         dcc.Interval(             id='interval-component',             interval=1*1000, # in milliseconds             n_intervals=0         )     ], className=&quot;row&quot;) )  def create_graphs(topic, live_update_text, live_update_graph):      connect = MyKafkaConnect(topic=topic, group='test_group')      @app.callback(Output(live_update_text, 'children'),                   [Input('interval-component', 'n_intervals')])     def update_metrics_terra(n):         lon, lat, alt = connect.get_last()          print('update metrics')          style = {'padding': '5px', 'fontSize': '15px'}         return [             html.Span('Longitude: {0:.2f}'.format(lon), style=style),             html.Span('Latitude: {0:.2f}'.format(lat), style=style),             html.Span('Altitude: {0:0.2f}'.format(alt), style=style)         ]      # Multiple components can update everytime interval gets fired.     @app.callback(Output(live_update_graph, 'figure'),                   [Input('interval-component', 'n_intervals')])     def update_graph_live_terra(n):         # Collect some data         data = connect.get_graph_data()         print('Update graph, data units:', len(data['time']))          # Create the graph with subplots         fig = plotly.tools.make_subplots(rows=2, cols=1, vertical_spacing=0.2)         fig['layout']['margin'] = {             'l': 30, 'r': 10, 'b': 30, 't': 10         }         fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}          fig.append_trace({             'x': data['time'],             'y': data['Altitude'],             'name': 'Altitude',             'mode': 'lines+markers',             'type': 'scatter'         }, 1, 1)         fig.append_trace({             'x': data['Longitude'],             'y': data['Latitude'],             'text': data['time'],             'name': 'Longitude vs Latitude',             'mode': 'lines+markers',             'type': 'scatter'         }, 2, 1)          return fig  create_graphs('terra_topic', 'terra-text', 'terra-graph') create_graphs('aqua_topic', 'aqua-text', 'aqua-graph') create_graphs('aura_topic', 'aura-text', 'aura-graph')  if __name__ == '__main__':     app.run_server(         host='0.0.0.0',         port=8050,         debug=True)<\/code><\/pre>\n<\/div><\/div>\n<\/div><\/div>\n<p>  <\/p>\n<p>\u041a \u043f\u043b\u044e\u0441\u0430\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043d\u0435\u0441\u0442\u0438 (\u043a\u0440\u043e\u043c\u0435 \u0442\u0435\u0445, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u044b \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u0447\u0430\u0441\u0442\u0438):<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0418\u0437\u043e\u043b\u044f\u0446\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432, \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u0439 \u0438 \u0442.\u0434. (\u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 Docker-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441\u043e \u0432\u0441\u0435\u043c \u0441\u0432\u043e\u0438\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435\u043c \u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438).<\/li>\n<li>\u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u0445\u043e\u0441\u0442\u0435.<\/li>\n<li>\u041b\u0451\u0433\u043a\u0430\u044f \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043b\u0435\u0433\u043a\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041a \u043c\u0438\u043d\u0443\u0441\u0430\u043c:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0422.\u043a. Docker \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u043e\u043c \u043c\u0435\u0436\u0434\u0443 \u041e\u0421 \u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c, \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0438 \u0440\u0430\u0441\u0445\u043e\u0434\u0443 \u0431\u043e\u043b\u044c\u0448\u0435\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435 \u043d\u0438\u0436\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0434\u0438\u043d backend \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0442\u0430\u043a \u043a\u0430\u043a \u0432\u0441\u0451 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, \u0442\u043e \u043e\u043d\u043e \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0439 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438), \u0431\u043e\u043b\u0435\u0435 \u0442\u043e\u0433\u043e, \u043a\u043e\u0433\u0434\u0430 \u044d\u0442\u043e\u0442 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u0438\u043b \u0440\u0430\u0431\u043e\u0442\u0443, \u043e\u043d \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0441\u044f \u043d\u0430 \u043b\u0435\u0442\u0443, \u0438 \u043d\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0432\u0441\u0451 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ja\/4_\/c_\/ja4_c_atss8lkvim34vxhlqdf6i.png\"><br \/>  (\u041d\u0435 \u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043e\u0434\u0438\u043d \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u0437\u0430\u0432\u0438\u0441, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c)<\/p>\n<p>  <\/p>\n<h3 id=\"vyvody\">\u0412\u044b\u0432\u043e\u0434\u044b<\/h3>\n<p>  <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b, \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 (\u0434\u0430\u043b\u0435\u043a\u043e \u043d\u0435 \u0432\u0441\u0435) \u0434\u043e\u0441\u0442\u043e\u0438\u043d\u0441\u0442\u0432\u0430 \u0438 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0445\u043e\u0442\u044c \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 Kafka \u0438 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043d\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0448\u0438\u0442\u044c \u043e\u0431\u044b\u0447\u043d\u043e \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0442\u0438\u043f\u043e\u0432\u044b\u043c\u0438, \u0442\u0430\u043a\u0438\u043c\u0438 \u043a\u0430\u043a \u043d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u043e\u0435 \u0447\u0442\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0431\u0434 \u0438\u043b\u0438 \u0438\u0445 \u0437\u0430\u043f\u0438\u0441\u044c, \u0441\u043b\u0438\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0431\u0434 \u0432 \u043e\u0434\u043d\u0443 \u0438 \u0442.\u0434. \u0414\u043b\u044f \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0445 \u0446\u0435\u043b\u0435\u0439 \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0430\u0442\u044c \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438\u0437 <a href=\"https:\/\/www.confluent.io\/hub\/\" rel=\"nofollow\">Kafka connectors<\/a>, \u0442\u0430\u043c \u0435\u0441\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0434\u043b\u044f \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0441\u0435\u0445 \u0445\u043e\u0442\u044c \u0441\u043a\u043e\u043b\u044c\u043a\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u0431\u0434.<\/p>\n<p>  <\/p>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043f\u043e \u0442\u0435\u043c\u0435:<br \/>  <a href=\"https:\/\/www.youtube.com\/watch?v=4HFAM9u1wKk\" rel=\"nofollow\">Python + Kafka =? \/ \u041d\u0438\u043a\u043e\u043b\u0430\u0439 \u0421\u0430\u0441\u043a\u043e\u0432\u0435\u0446\/ bitnet [Python Meetup 14.09.2019]<\/a><br \/>  <a href=\"https:\/\/www.youtube.com\/watch?v=Q6rAuZ2W404\" rel=\"nofollow\">\u041d\u0438\u043a\u043e\u043b\u0430\u0439 \u0421\u0430\u0441\u043a\u043e\u0432\u0435\u0446, \u041f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Kafka<\/a><\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/504740\/\"> https:\/\/habr.com\/ru\/post\/504740\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/504740\/\">\n<p>\u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e-\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0434\u0451\u0448\u0435\u0432\u043e, \u043c\u044b \u043d\u0435 \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u0435\u043c\u0441\u044f \u043d\u0430\u0434 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c\u044e \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c\u044e \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0431\u043e\u043b\u0438. \u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.<br \/>  \u041d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u043e\u0442 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c, \u044f \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u044e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043b\u044e\u0441\u044b \u0438 \u043c\u0438\u043d\u0443\u0441\u044b \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430. \u0421\u0442\u0430\u0442\u044c\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0430 \u043d\u0430 \u0442\u0440\u0438 \u0447\u0430\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0412 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043e \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u0432\u0435\u0431-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Dash, \u0442.\u0435. \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0438\u0445 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435.<\/li>\n<li>\u0412\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u0440\u0430\u0437\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0442.\u0435. \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u0438\u043d \u0441\u0435\u0440\u0432\u0438\u0441, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c \u0434\u0440\u0443\u0433\u043e\u0439, \u0430 \u0441\u0432\u044f\u0437\u044c \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043b\u0430\u0436\u0435\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u043e\u043a\u0435\u0440 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 Kafka.<\/li>\n<li>\u0412 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0431\u0443\u0434\u0443\u0442 &quot;\u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u044b&quot; \u0432 Docker \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c, \u043a\u0430\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0430 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0435 \u0441\u043d\u0438\u0437\u0443.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/6l\/ly\/j_\/6llyj_lypfj8dct_9amkrpofuye.png\"><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-304681","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/304681","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=304681"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/304681\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=304681"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=304681"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=304681"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}