@@ -3,8 +3,192 @@ package kafka
3
3
import (
4
4
"bufio"
5
5
"bytes"
6
+ "context"
7
+ "fmt"
8
+ "net"
9
+ "time"
10
+
11
+ "github.com/segmentio/kafka-go/protocol"
12
+ "github.com/segmentio/kafka-go/protocol/consumer"
13
+ "github.com/segmentio/kafka-go/protocol/joingroup"
6
14
)
7
15
16
+ // JoinGroupRequest is the request structure for the JoinGroup function.
17
+ type JoinGroupRequest struct {
18
+ // Address of the kafka broker to send the request to.
19
+ Addr net.Addr
20
+
21
+ // GroupID of the group to join.
22
+ GroupID string
23
+
24
+ // The duration after which the coordinator considers the consumer dead
25
+ // if it has not received a heartbeat.
26
+ SessionTimeout time.Duration
27
+
28
+ // The duration the coordination will wait for each member to rejoin when rebalancing the group.
29
+ RebalanceTimeout time.Duration
30
+
31
+ // The ID assigned by the group coordinator.
32
+ MemberID string
33
+
34
+ // The unique identifier for the consumer instance.
35
+ GroupInstanceID string
36
+
37
+ // The name for the class of protocols implemented by the group being joined.
38
+ ProtocolType string
39
+
40
+ // The list of protocols the member supports.
41
+ Protocols []GroupProtocol
42
+ }
43
+
44
+ // GroupProtocol represents a consumer group protocol.
45
+ type GroupProtocol struct {
46
+ // The protocol name.
47
+ Name string
48
+
49
+ // The protocol metadata.
50
+ Metadata GroupProtocolSubscription
51
+ }
52
+
53
+ type GroupProtocolSubscription struct {
54
+ // The Topics to subscribe to.
55
+ Topics []string
56
+
57
+ // UserData assosiated with the subscription for the given protocol
58
+ UserData []byte
59
+
60
+ // Partitions owned by this consumer.
61
+ OwnedPartitions map [string ][]int
62
+ }
63
+
64
+ // JoinGroupResponse is the response structure for the JoinGroup function.
65
+ type JoinGroupResponse struct {
66
+ // An error that may have occurred when attempting to join the group.
67
+ //
68
+ // The errors contain the kafka error code. Programs may use the standard
69
+ // errors.Is function to test the error against kafka error codes.
70
+ Error error
71
+
72
+ // The amount of time that the broker throttled the request.
73
+ Throttle time.Duration
74
+
75
+ // The generation ID of the group.
76
+ GenerationID int
77
+
78
+ // The group protocol selected by the coordinatior.
79
+ ProtocolName string
80
+
81
+ // The group protocol name.
82
+ ProtocolType string
83
+
84
+ // The leader of the group.
85
+ LeaderID string
86
+
87
+ // The group member ID.
88
+ MemberID string
89
+
90
+ // The members of the group.
91
+ Members []JoinGroupResponseMember
92
+ }
93
+
94
+ // JoinGroupResponseMember represents a group memmber in a reponse to a JoinGroup request.
95
+ type JoinGroupResponseMember struct {
96
+ // The group memmber ID.
97
+ ID string
98
+
99
+ // The unique identifier of the consumer instance.
100
+ GroupInstanceID string
101
+
102
+ // The group member metadata.
103
+ Metadata GroupProtocolSubscription
104
+ }
105
+
106
+ // JoinGroup sends a join group request to the coordinator and returns the response.
107
+ func (c * Client ) JoinGroup (ctx context.Context , req * JoinGroupRequest ) (* JoinGroupResponse , error ) {
108
+ joinGroup := joingroup.Request {
109
+ GroupID : req .GroupID ,
110
+ SessionTimeoutMS : int32 (req .SessionTimeout .Milliseconds ()),
111
+ RebalanceTimeoutMS : int32 (req .RebalanceTimeout .Milliseconds ()),
112
+ MemberID : req .MemberID ,
113
+ GroupInstanceID : req .GroupInstanceID ,
114
+ ProtocolType : req .ProtocolType ,
115
+ Protocols : make ([]joingroup.RequestProtocol , 0 , len (req .Protocols )),
116
+ }
117
+
118
+ for _ , proto := range req .Protocols {
119
+ protoMeta := consumer.Subscription {
120
+ Version : consumer .MaxVersionSupported ,
121
+ Topics : proto .Metadata .Topics ,
122
+ UserData : proto .Metadata .UserData ,
123
+ OwnedPartitions : make ([]consumer.TopicPartition , 0 , len (proto .Metadata .OwnedPartitions )),
124
+ }
125
+ for topic , partitions := range proto .Metadata .OwnedPartitions {
126
+ tp := consumer.TopicPartition {
127
+ Topic : topic ,
128
+ Partitions : make ([]int32 , 0 , len (partitions )),
129
+ }
130
+ for _ , partition := range partitions {
131
+ tp .Partitions = append (tp .Partitions , int32 (partition ))
132
+ }
133
+ protoMeta .OwnedPartitions = append (protoMeta .OwnedPartitions , tp )
134
+ }
135
+
136
+ metaBytes , err := protocol .Marshal (consumer .MaxVersionSupported , protoMeta )
137
+ if err != nil {
138
+ return nil , fmt .Errorf ("kafka.(*Client).JoinGroup: %w" , err )
139
+ }
140
+
141
+ joinGroup .Protocols = append (joinGroup .Protocols , joingroup.RequestProtocol {
142
+ Name : proto .Name ,
143
+ Metadata : metaBytes ,
144
+ })
145
+ }
146
+
147
+ m , err := c .roundTrip (ctx , req .Addr , & joinGroup )
148
+ if err != nil {
149
+ return nil , fmt .Errorf ("kafka.(*Client).JoinGroup: %w" , err )
150
+ }
151
+
152
+ r := m .(* joingroup.Response )
153
+
154
+ res := & JoinGroupResponse {
155
+ Error : makeError (r .ErrorCode , "" ),
156
+ Throttle : makeDuration (r .ThrottleTimeMS ),
157
+ GenerationID : int (r .GenerationID ),
158
+ ProtocolName : r .ProtocolName ,
159
+ ProtocolType : r .ProtocolType ,
160
+ LeaderID : r .LeaderID ,
161
+ MemberID : r .MemberID ,
162
+ Members : make ([]JoinGroupResponseMember , 0 , len (r .Members )),
163
+ }
164
+
165
+ for _ , member := range r .Members {
166
+ var meta consumer.Subscription
167
+ err = protocol .Unmarshal (member .Metadata , consumer .MaxVersionSupported , & meta )
168
+ if err != nil {
169
+ return nil , fmt .Errorf ("kafka.(*Client).JoinGroup: %w" , err )
170
+ }
171
+ subscription := GroupProtocolSubscription {
172
+ Topics : meta .Topics ,
173
+ UserData : meta .UserData ,
174
+ OwnedPartitions : make (map [string ][]int , len (meta .OwnedPartitions )),
175
+ }
176
+ for _ , owned := range meta .OwnedPartitions {
177
+ subscription .OwnedPartitions [owned .Topic ] = make ([]int , 0 , len (owned .Partitions ))
178
+ for _ , partition := range owned .Partitions {
179
+ subscription .OwnedPartitions [owned .Topic ] = append (subscription .OwnedPartitions [owned .Topic ], int (partition ))
180
+ }
181
+ }
182
+ res .Members = append (res .Members , JoinGroupResponseMember {
183
+ ID : member .MemberID ,
184
+ GroupInstanceID : member .GroupInstanceID ,
185
+ Metadata : subscription ,
186
+ })
187
+ }
188
+
189
+ return res , nil
190
+ }
191
+
8
192
type groupMetadata struct {
9
193
Version int16
10
194
Topics []string
0 commit comments