12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
- # Companion code to the blog post "Integrating Kafka With Python Asyncio Web Applications"
15
+ # Companion code to the blog post "Integrating Kafka With Python
16
+ # Asyncio Web Applications"
16
17
# https://www.confluent.io/blog/[path-to-blog-post]
17
18
19
+ # Example Siege [https://github.com/JoeDog/siege] test:
20
+ # siege -c 400 -r 200 'http://localhost:8000/items1 POST {"name":"testuser"}'
21
+
18
22
import asyncio
19
- from confluent_kafka import Producer
23
+ import confluent_kafka
20
24
from confluent_kafka import KafkaException
21
25
from fastapi import FastAPI , HTTPException
22
26
from pydantic import BaseModel
26
30
27
31
28
32
class AIOProducer :
29
- def __init__ (self , configs , loop ):
33
+ def __init__ (self , configs , loop = None ):
30
34
self ._loop = loop or asyncio .get_event_loop ()
31
- self ._producer = Producer (configs )
35
+ self ._producer = confluent_kafka . Producer (configs )
32
36
self ._cancelled = False
33
37
self ._poll_thread = Thread (target = self ._poll_loop )
34
38
self ._poll_thread .start ()
@@ -41,8 +45,9 @@ def close(self):
41
45
self ._cancelled = True
42
46
self ._poll_thread .join ()
43
47
44
- def async_produce (self , topic , value ):
48
+ def produce (self , topic , value ):
45
49
result = self ._loop .create_future ()
50
+
46
51
def ack (err , msg ):
47
52
if err :
48
53
self ._loop .call_soon_threadsafe (result .set_exception , KafkaException (err ))
@@ -51,60 +56,114 @@ def ack(err, msg):
51
56
self ._producer .produce (topic , value , on_delivery = ack )
52
57
return result
53
58
54
- def produce (self , topic , value , ack = None ):
59
+ def produce2 (self , topic , value , on_delivery ):
60
+ result = self ._loop .create_future ()
61
+
62
+ def ack (err , msg ):
63
+ if err :
64
+ self ._loop .call_soon_threadsafe (
65
+ result .set_exception , KafkaException (err ))
66
+ else :
67
+ self ._loop .call_soon_threadsafe (
68
+ result .set_result , msg )
69
+ if on_delivery :
70
+ self ._loop .call_soon_threadsafe (
71
+ on_delivery , err , msg )
55
72
self ._producer .produce (topic , value , on_delivery = ack )
73
+ return result
74
+
75
+
76
+ class Producer :
77
+ def __init__ (self , configs ):
78
+ self ._producer = confluent_kafka .Producer (configs )
79
+ self ._cancelled = False
80
+ self ._poll_thread = Thread (target = self ._poll_loop )
81
+ self ._poll_thread .start ()
82
+
83
+ def _poll_loop (self ):
84
+ while not self ._cancelled :
85
+ self ._producer .poll (0.1 )
86
+
87
+ def close (self ):
88
+ self ._cancelled = True
89
+ self ._poll_thread .join ()
90
+
91
+ def produce (self , topic , value , on_delivery = None ):
92
+ self ._producer .produce (topic , value , on_delivery = on_delivery )
56
93
57
94
95
+ config = {"bootstrap.servers" : "localhost:9092" }
96
+
58
97
app = FastAPI ()
59
98
99
+
60
100
class Item (BaseModel ):
61
101
name : str
62
102
103
+
104
+ aio_producer = None
63
105
producer = None
64
106
107
+
65
108
@app .on_event ("startup" )
66
109
async def startup_event ():
67
- global producer
68
- producer = AIOProducer (
69
- { "bootstrap.servers" : "localhost:9092" },
70
- asyncio . get_event_loop ())
110
+ global producer , aio_producer
111
+ aio_producer = AIOProducer (config , asyncio . get_event_loop ())
112
+ producer = Producer ( config )
113
+
71
114
72
115
@app .on_event ("shutdown" )
73
116
def shutdown_event ():
117
+ aio_producer .close ()
74
118
producer .close ()
75
119
120
+
76
121
@app .post ("/items1" )
77
122
async def create_item1 (item : Item ):
78
123
try :
79
- result = await producer . async_produce ("items" , item .name )
80
- return { "timestamp" : result .timestamp () }
124
+ result = await aio_producer . produce ("items" , item .name )
125
+ return {"timestamp" : result .timestamp ()}
81
126
except KafkaException as ex :
82
127
raise HTTPException (status_code = 500 , detail = ex .args [0 ].str ())
83
128
84
129
cnt = 0
130
+
131
+
85
132
def ack (err , msg ):
86
133
global cnt
87
134
cnt = cnt + 1
88
135
136
+
89
137
@app .post ("/items2" )
90
138
async def create_item2 (item : Item ):
91
139
try :
92
- result = producer . produce ("items" , item .name , ack = ack )
93
- return { "timestamp" : time () }
140
+ aio_producer . produce2 ("items" , item .name , on_delivery = ack )
141
+ return {"timestamp" : time ()}
94
142
except KafkaException as ex :
95
143
raise HTTPException (status_code = 500 , detail = ex .args [0 ].str ())
96
144
145
+
97
146
@app .post ("/items3" )
98
147
async def create_item3 (item : Item ):
99
148
try :
100
- result = producer .produce ("items" , item .name )
101
- return { "timestamp" : time () }
149
+ producer .produce ("items" , item .name , on_delivery = ack )
150
+ return {"timestamp" : time ()}
102
151
except KafkaException as ex :
103
152
raise HTTPException (status_code = 500 , detail = ex .args [0 ].str ())
104
153
154
+
105
155
@app .post ("/items4" )
106
156
async def create_item4 (item : Item ):
107
- return { "timestamp" : time () }
157
+ try :
158
+ producer .produce ("items" , item .name )
159
+ return {"timestamp" : time ()}
160
+ except KafkaException as ex :
161
+ raise HTTPException (status_code = 500 , detail = ex .args [0 ].str ())
162
+
163
+
164
+ @app .post ("/items5" )
165
+ async def create_item5 (item : Item ):
166
+ return {"timestamp" : time ()}
108
167
109
168
110
169
if __name__ == '__main__' :
0 commit comments