-
Notifications
You must be signed in to change notification settings - Fork 327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[API] increase api listener limit #4374
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
) | ||
|
||
type ( | ||
streamContextKey struct{} | ||
|
||
StreamContext struct { | ||
listenerIDs map[string]struct{} | ||
mutex sync.Mutex | ||
} | ||
) | ||
|
||
func (sc *StreamContext) AddListener(id string) { | ||
sc.mutex.Lock() | ||
defer sc.mutex.Unlock() | ||
sc.listenerIDs[id] = struct{}{} | ||
} | ||
|
||
func (sc *StreamContext) RemoveListener(id string) { | ||
sc.mutex.Lock() | ||
defer sc.mutex.Unlock() | ||
delete(sc.listenerIDs, id) | ||
} | ||
|
||
func (sc *StreamContext) ListenerIDs() []string { | ||
sc.mutex.Lock() | ||
defer sc.mutex.Unlock() | ||
ids := make([]string, 0, len(sc.listenerIDs)) | ||
for id := range sc.listenerIDs { | ||
ids = append(ids, id) | ||
} | ||
return ids | ||
} | ||
|
||
func WithStreamContext(ctx context.Context) context.Context { | ||
return context.WithValue(ctx, streamContextKey{}, &StreamContext{ | ||
listenerIDs: make(map[string]struct{}), | ||
}) | ||
} | ||
|
||
func StreamFromContext(ctx context.Context) (*StreamContext, bool) { | ||
sc, ok := ctx.Value(streamContextKey{}).(*StreamContext) | ||
return sc, ok | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package api | ||
|
||
import "github.com/prometheus/client_golang/prometheus" | ||
|
||
var ( | ||
apiLimitMtcs = prometheus.NewGaugeVec(prometheus.GaugeOpts{ | ||
Name: "iotex_api_limit_metrics", | ||
Help: "api limit metrics.", | ||
}, []string{"limit"}) | ||
) | ||
|
||
func init() { | ||
prometheus.MustRegister(apiLimitMtcs) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,6 +82,7 @@ var ( | |
errInvalidBlock = errors.New("invalid block") | ||
errUnsupportedAction = errors.New("the type of action is not supported") | ||
errMsgBatchTooLarge = errors.New("batch too large") | ||
errHTTPNotSupported = errors.New("http not supported") | ||
|
||
_pendingBlockNumber = "pending" | ||
_latestBlockNumber = "latest" | ||
|
@@ -224,7 +225,11 @@ func (svr *web3Handler) handleWeb3Req(ctx context.Context, web3Req *gjson.Result | |
case "eth_newBlockFilter": | ||
res, err = svr.newBlockFilter() | ||
case "eth_subscribe": | ||
res, err = svr.subscribe(web3Req, writer) | ||
sc, ok := StreamFromContext(ctx) | ||
if !ok { | ||
return errHTTPNotSupported | ||
} | ||
res, err = svr.subscribe(sc, web3Req, writer) | ||
case "eth_unsubscribe": | ||
res, err = svr.unsubscribe(web3Req) | ||
//TODO: enable debug api after archive mode is supported | ||
|
@@ -924,35 +929,36 @@ func (svr *web3Handler) getFilterLogs(in *gjson.Result) (interface{}, error) { | |
return svr.getLogsWithFilter(from, to, filterObj.Address, filterObj.Topics) | ||
} | ||
|
||
func (svr *web3Handler) subscribe(in *gjson.Result, writer apitypes.Web3ResponseWriter) (interface{}, error) { | ||
func (svr *web3Handler) subscribe(ctx *StreamContext, in *gjson.Result, writer apitypes.Web3ResponseWriter) (interface{}, error) { | ||
subscription := in.Get("params.0") | ||
if !subscription.Exists() { | ||
return nil, errInvalidFormat | ||
} | ||
switch subscription.String() { | ||
case "newHeads": | ||
return svr.streamBlocks(writer) | ||
return svr.streamBlocks(ctx, writer) | ||
case "logs": | ||
filter, err := parseLogRequest(in.Get("params.1")) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return svr.streamLogs(filter, writer) | ||
return svr.streamLogs(ctx, filter, writer) | ||
default: | ||
return nil, errInvalidFormat | ||
} | ||
} | ||
|
||
func (svr *web3Handler) streamBlocks(writer apitypes.Web3ResponseWriter) (interface{}, error) { | ||
func (svr *web3Handler) streamBlocks(ctx *StreamContext, writer apitypes.Web3ResponseWriter) (interface{}, error) { | ||
chainListener := svr.coreService.ChainListener() | ||
streamID, err := chainListener.AddResponder(NewWeb3BlockListener(writer.Write)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
ctx.AddListener(streamID) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and add here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the removing should after wss disconnected, not here |
||
return streamID, nil | ||
} | ||
|
||
func (svr *web3Handler) streamLogs(filterObj *filterObject, writer apitypes.Web3ResponseWriter) (interface{}, error) { | ||
func (svr *web3Handler) streamLogs(ctx *StreamContext, filterObj *filterObject, writer apitypes.Web3ResponseWriter) (interface{}, error) { | ||
filter, err := newLogFilterFrom(filterObj.Address, filterObj.Topics) | ||
if err != nil { | ||
return nil, err | ||
|
@@ -962,6 +968,7 @@ func (svr *web3Handler) streamLogs(filterObj *filterObject, writer apitypes.Web3 | |
if err != nil { | ||
return nil, err | ||
} | ||
ctx.AddListener(streamID) | ||
return streamID, nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,8 +31,9 @@ const ( | |
|
||
// WebsocketHandler handles requests from websocket protocol | ||
type WebsocketHandler struct { | ||
msgHandler Web3Handler | ||
limiter *rate.Limiter | ||
coreService CoreService | ||
msgHandler Web3Handler | ||
limiter *rate.Limiter | ||
} | ||
|
||
var upgrader = websocket.Upgrader{ | ||
|
@@ -75,14 +76,15 @@ func (c *safeWebsocketConn) SetWriteDeadline(t time.Time) error { | |
} | ||
|
||
// NewWebsocketHandler creates a new websocket handler | ||
func NewWebsocketHandler(web3Handler Web3Handler, limiter *rate.Limiter) *WebsocketHandler { | ||
func NewWebsocketHandler(coreService CoreService, web3Handler Web3Handler, limiter *rate.Limiter) *WebsocketHandler { | ||
if limiter == nil { | ||
// set the limiter to the maximum possible rate | ||
limiter = rate.NewLimiter(rate.Limit(math.MaxFloat64), 1) | ||
} | ||
return &WebsocketHandler{ | ||
msgHandler: web3Handler, | ||
limiter: limiter, | ||
msgHandler: web3Handler, | ||
limiter: limiter, | ||
coreService: coreService, | ||
} | ||
} | ||
|
||
|
@@ -112,10 +114,18 @@ func (wsSvr *WebsocketHandler) handleConnection(ctx context.Context, ws *websock | |
return nil | ||
}) | ||
|
||
ctx, cancel := context.WithCancel(ctx) | ||
ctx, cancel := context.WithCancel(WithStreamContext(ctx)) | ||
safeWs := &safeWebsocketConn{ws: ws} | ||
go ping(ctx, safeWs, cancel) | ||
|
||
defer func() { | ||
// clean up the stream context | ||
sc, _ := StreamFromContext(ctx) | ||
for _, id := range sc.ListenerIDs() { | ||
wsSvr.coreService.ChainListener().RemoveResponder(id) | ||
} | ||
}() | ||
|
||
for { | ||
select { | ||
case <-ctx.Done(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here should also call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why remove here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
receiving the errChan means the end of the
StreamBlocks