Faust Python 패키지를 사용하여 kafka 주제를 웹 엔드 포인트와 연결하는 방법은 무엇입니까?


9

주제를 듣고 다른 하나는 웹 끝점을위한 두 가지 기능이있는 간단한 응용 프로그램이 있습니다. 클라이언트 측에서 EventSource를 사용하여들을 수 있도록 SSE (Server Side Event Streaming) 즉 텍스트 / 이벤트 스트림을 만들고 싶습니다.

각 함수가 특정 작업을 수행하는 다음 코드가 있습니다.

import faust

from faust.web import Response

app = faust.App("app1", broker="kafka://localhost:29092", value_serializer="raw")
test_topic = app.topic("test")


@app.agent(test_topic)
async def test_topic_agent(stream):
    async for value in stream:
        print(f"test_topic_agent RECEIVED -- {value!r}")
        yield value


@app.page("/")
async def index(self, request):
    return self.text("yey")

이제이 코드와 같은 색인을 원하지만 faust를 사용하고 싶습니다.

import asyncio
from aiohttp import web
from aiohttp.web import Response
from aiohttp_sse import sse_response
from datetime import datetime


async def hello(request):
    loop = request.app.loop
    async with sse_response(request) as resp:
        while True:
            data = 'Server Time : {}'.format(datetime.now())
            print(data)
            await resp.send(data)
            await asyncio.sleep(1, loop=loop)
    return resp


async def index(request):
    d = """
        <html>
        <body>
            <script>
                var evtSource = new EventSource("/hello");
                evtSource.onmessage = function(e) {
                    document.getElementById('response').innerText = e.data
                }
            </script>
            <h1>Response from server:</h1>
            <div id="response"></div>
        </body>
    </html>
    """
    return Response(text=d, content_type='text/html')


app = web.Application()
app.router.add_route('GET', '/hello', hello)
app.router.add_route('GET', '/', index)
web.run_app(app, host='127.0.0.1', port=8080)

나는 이것을 시도했다 :

import faust

from faust.web import Response

app = faust.App("app1", broker="kafka://localhost:29092", value_serializer="raw")
test_topic = app.topic("test")


# @app.agent(test_topic)
# async def test_topic_agent(stream):
#     async for value in stream:
#         print(f"test_topic_agent RECEIVED -- {value!r}")
#         yield value


@app.page("/", name="t1")
@app.agent(test_topic, name="t")
async def index(self, request):
    return self.text("yey")

그러나 다음과 같은 오류가 발생합니다.

Traceback (most recent call last):
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/cli/base.py", line 299, in find_app
    val = symbol_by_name(app, imp=imp)
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/utils/imports.py", line 262, in symbol_by_name
    module = imp(  # type: ignore
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/utils/imports.py", line 376, in import_from_cwd
    return imp(module, package=package)
  File "/Users/maverick/.pyenv/versions/3.8.1/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/maverick/company/demo1/baiohttp-demo/app1.py", line 18, in <module>
    async def index(self, request):
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/app/base.py", line 1231, in _decorator
    view = view_base.from_handler(cast(ViewHandlerFun, fun))
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/web/views.py", line 50, in from_handler
    return type(fun.__name__, (cls,), {
AttributeError: 'Agent' object has no attribute '__name__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/maverick/.pyenv/versions/faust_demo/bin/faust", line 8, in <module>
    sys.exit(cli())
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/click/core.py", line 781, in main
    with self.make_context(prog_name, args, **extra) as ctx:
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/cli/base.py", line 407, in make_context
    self._maybe_import_app()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/cli/base.py", line 372, in _maybe_import_app
    find_app(appstr)
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/cli/base.py", line 303, in find_app
    val = imp(app)
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/utils/imports.py", line 376, in import_from_cwd
    return imp(module, package=package)
  File "/Users/maverick/.pyenv/versions/3.8.1/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/maverick/company/demo1/baiohttp-demo/app1.py", line 18, in <module>
    async def index(self, request):
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/app/base.py", line 1231, in _decorator
    view = view_base.from_handler(cast(ViewHandlerFun, fun))
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/web/views.py", line 50, in from_handler
    return type(fun.__name__, (cls,), {
AttributeError: 'Agent' object has no attribute '__name__'

나는 이것을 시도했다 :

import faust

from faust.web import Response

app = faust.App("app1", broker="kafka://localhost:29092", value_serializer="raw")
test_topic = app.topic("test")


# @app.agent(test_topic)
# async def test_topic_agent(stream):
#     async for value in stream:
#         print(f"test_topic_agent RECEIVED -- {value!r}")
#         yield value


@app.agent(test_topic, name="t")
@app.page("/", name="t1")
async def index(self, request):
    return self.text("yey")

그러나 다음과 같은 오류가 발생합니다.

[2020-03-28 10:32:50,676] [29976] [INFO] [^--Producer]: Creating topic 'app1-__assignor-__leader'
[2020-03-28 10:32:50,695] [29976] [INFO] [^--ReplyConsumer]: Starting...
[2020-03-28 10:32:50,695] [29976] [INFO] [^--AgentManager]: Starting...
[2020-03-28 10:32:50,695] [29976] [INFO] [^---Agent: app1.index]: Starting...
[2020-03-28 10:32:50,696] [29976] [ERROR] [^Worker]: Error: TypeError("__init__() missing 1 required positional argument: 'web'")
Traceback (most recent call last):
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/worker.py", line 273, in execute_from_commandline
    self.loop.run_until_complete(self._starting_fut)
  File "/Users/maverick/.pyenv/versions/3.8.1/lib/python3.8/asyncio/base_events.py", line 612, in run_until_complete
    return future.result()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 736, in start
    await self._default_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 743, in _default_start
    await self._actually_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 767, in _actually_start
    await child.maybe_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 795, in maybe_start
    await self.start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 736, in start
    await self._default_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 743, in _default_start
    await self._actually_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 767, in _actually_start
    await child.maybe_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 795, in maybe_start
    await self.start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 736, in start
    await self._default_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 743, in _default_start
    await self._actually_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 760, in _actually_start
    await self.on_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/agents/manager.py", line 58, in on_start
    await agent.maybe_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 795, in maybe_start
    await self.start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 736, in start
    await self._default_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 743, in _default_start
    await self._actually_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/mode/services.py", line 760, in _actually_start
    await self.on_start()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/agents/agent.py", line 282, in on_start
    await self._on_start_supervisor()
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/agents/agent.py", line 312, in _on_start_supervisor
    res = await self._start_one(
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/agents/agent.py", line 251, in _start_one
    return await self._start_task(
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/agents/agent.py", line 617, in _start_task
    actor = self(
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/agents/agent.py", line 525, in __call__
    return self.actor_from_stream(stream,
  File "/Users/maverick/.pyenv/versions/3.8.1/envs/faust_demo/lib/python3.8/site-packages/faust/agents/agent.py", line 552, in actor_from_stream
    res = self.fun(actual_stream)
TypeError: __init__() missing 1 required positional argument: 'web'
[2020-03-28 10:32:50,703] [29976] [INFO] [^Worker]: Stopping...
[2020-03-28 10:32:50,703] [29976] [INFO] [^-App]: Stopping...
[2020-03-28 10:32:50,703] [29976] [INFO] [^-App]: Flush producer buffer...
[2020-03-28 10:32:50,703] [29976] [INFO] [^--TableManager]: Stopping...

이것에 대한 방법이있을 수 있습니까? 많은 감사드립니다!


AIOHttp에 대한 경험은 없지만 SocketIO
cricket_007

'Agent' object has no attribute '__name__'... 제거해 보셨습니까 name="t"?
cricket_007

예, 나는 name="t"도움이되지 않는 동일한 오류를 제거하려고 시도했습니다 . @ cricket_007
Maverick

Faust가 터미널로 메시지를 인쇄하도록 할 수 있었습니까? 오류는 SSE와 관련이 없습니다
cricket_007

2
디버깅을 돕기 위해 노력하고 있습니다. : / 1) 문제를 모르겠습니다. 2) 최소한의 재현 가능한 예
cricket_007

답변:


4

Faust 작업자는 모든 인스턴스에서 기본적으로 포트 6066에서 실행되는 웹 서버를 공개합니다.

서버는 aiohttp HTTP 서버 라이브러리를 사용하며이 기능을 활용하여 예제 코드와 같이 서버 측 이벤트 스트리밍 (SSE)을 만들 수 있습니다.

Kafka 주제에서 읽을 에이전트를 생성하고 주제 의 마지막 메시지 test로 변수 last_message_from_topic를 업데이트 할 수 있습니다.이 변수는 웹 페이지에서도 볼 수 있습니다.

색인 페이지 ( @app.page('/'))에서 EventSource 인터페이스는 서버가 보낸 이벤트를 수신하는 데 사용됩니다. HTTP를 통해 서버에 /hello연결하고 연결을 닫지 않고 페이지에서 텍스트 / 이벤트 스트림 형식의 이벤트를 수신합니다 .

/hello매초마다 웹 페이지 는 Kafka 주제의 마지막 메시지 test와 서버의 현재 시간과 함께 메시지 텍스트를 전송 합니다.

내 파일 my_worker.py코드는 다음과 같습니다.

import asyncio
from datetime import datetime

import faust
from aiohttp.web import Response
from aiohttp_sse import sse_response

app = faust.App(
    "app1",
    broker='kafka://localhost:9092',
    value_serializer='json',
)
test_topic = app.topic("test")

last_message_from_topic = ['No messages yet']


@app.agent(test_topic)
async def greet(greetings):
    async for greeting in greetings:
        last_message_from_topic[0] = greeting


@app.page('/hello')
async def hello(self, request):
    loop = request.app.loop
    async with sse_response(request) as resp:
        while True:
            data = f'last message from topic_test: {last_message_from_topic[0]} | '
            data += f'Server Time : {datetime.now()}'

            print(data)
            await resp.send(data)
            await asyncio.sleep(1, loop=loop)
    return resp


@app.page('/')
async def index(self, request):
    d = """
        <html>
        <body>
            <script>
                var evtSource = new EventSource("/hello");
                evtSource.onmessage = function(e) {
                    document.getElementById('response').innerText = e.data
                }
            </script>
            <h1>Response from server:</h1>
            <div id="response"></div>
        </body>
    </html>
    """
    return Response(text=d, content_type='text/html')

이제 다음 명령으로 Faust 워커를 시작해야합니다.

faust -A my_worker worker -l info

웹 브라우저에서 다음에 액세스 할 수 있습니다 http://localhost:6066/.

여기에 이미지 설명을 입력하십시오


다음은 test(다른 파이썬 파일에서) 주제에 대해 Kafka에 메시지를 보내는 코드입니다 .

import time
import json

from kafka import  KafkaProducer

producer = KafkaProducer(bootstrap_servers=['localhost:9092'],value_serializer=lambda x: json.dumps(x).encode('utf-8'))


for i in range(220):
    time.sleep(1)
    producer.send('test', value=f'Some message from kafka id {i}')

1

파우스트 웹 문서를 읽으면 SSE를 처리하지 못하는 것 같습니다.

@app.agentkafka 메시지가 소비되고 @app.pagehttp 요청이 처리 될 때 콜백됩니다 . 그것들을 결합하는 것은 불가능할 것입니다.

사용하는 다른 방법 faust.web은 자바 스크립트에서 폴링하는 것입니다.
예를 들어

import faust
from faust.web import Response

app = faust.App("myapp", broker="kafka://kafka:9092", value_serializer="raw")
test_topic = app.topic("test")
app.lastmsg = ""

@app.agent(test_topic)
async def test_topic_agent(stream):
    async for value in stream:
        app.lastmsg = str(value)
        yield value

@app.page("/msg")
async def msg(self, request):
    return self.text(app.lastmsg)

@app.page("/")
async def index(self, request):
    body = """
        <html>
        <body>
            <script>
                setInterval(()=>{
                    let xhr = new XMLHttpRequest();
                    xhr.open('GET', '/msg');
                    xhr.send();
                    xhr.onload = function() {
                        if (xhr.status == 200) {
                            document.getElementById('response').innerText = xhr.response
                        }
                    }
                },1000);
            </script>
            <h1>Response from server:</h1>
            <div id="response"></div>
        </body>
    </html>
    """
    return self.html(body)

이 순진한 구현 app.lastmsg은 API에 의해 얻을 수있는 kafka 메시지를 저장합니다 /msg.

SSE를 사용 asyncio하기 위해 웹 파트 및 faustkafka 소비자에 사용할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.