@@ -5,12 +5,14 @@ use rayon::prelude::*;
5
5
use std:: {
6
6
collections:: hash_map:: HashMap ,
7
7
io:: { BufRead , BufReader , Write } ,
8
+ iter:: once,
8
9
net:: { Shutdown , TcpListener , TcpStream } ,
9
10
} ;
10
11
11
12
use crate :: {
12
13
config:: Config ,
13
14
electrum:: { Client , Rpc } ,
15
+ metrics:: { self , Metrics } ,
14
16
signals:: ExitError ,
15
17
thread:: spawn,
16
18
} ;
@@ -60,7 +62,16 @@ pub fn run() -> Result<()> {
60
62
61
63
fn serve ( ) -> Result < ( ) > {
62
64
let config = Config :: from_args ( ) ;
63
- let mut rpc = Rpc :: new ( & config) ?;
65
+ let metrics = Metrics :: new ( config. monitoring_addr ) ?;
66
+
67
+ let server_batch_size = metrics. histogram_vec (
68
+ "server_batch_size" ,
69
+ "# of server events handled in a single batch" ,
70
+ "type" ,
71
+ metrics:: default_size_buckets ( ) ,
72
+ ) ;
73
+
74
+ let mut rpc = Rpc :: new ( & config, metrics) ?;
64
75
65
76
let ( server_tx, server_rx) = unbounded ( ) ;
66
77
if !config. disable_electrum_rpc {
@@ -77,32 +88,29 @@ fn serve() -> Result<()> {
77
88
return Ok ( ( ) ) ;
78
89
}
79
90
peers = notify_peers ( & rpc, peers) ; // peers are disconnected on error.
80
- loop {
81
- select ! {
82
- // Handle signals for graceful shutdown
83
- recv( rpc. signal( ) . receiver( ) ) -> result => {
84
- result. context( "signal channel disconnected" ) ?;
85
- rpc. signal( ) . exit_flag( ) . poll( ) . context( "RPC server interrupted" ) ?;
86
- } ,
87
- // Handle new blocks' notifications
88
- recv( new_block_rx) -> result => match result {
89
- Ok ( _) => break , // sync and update
90
- Err ( _) => {
91
- info!( "disconnected from bitcoind" ) ;
92
- return Ok ( ( ) ) ;
93
- }
94
- } ,
95
- // Handle Electrum RPC requests
96
- recv( server_rx) -> event => {
97
- let event = event. context( "server disconnected" ) ?;
98
- handle_event( & rpc, & mut peers, event) ;
99
- } ,
100
- default ( config. wait_duration) => break , // sync and update
101
- } ;
102
- // continue RPC processing (if more requests are pending)
103
- if server_rx. is_empty ( ) {
104
- break ;
105
- }
91
+ select ! {
92
+ // Handle signals for graceful shutdown
93
+ recv( rpc. signal( ) . receiver( ) ) -> result => {
94
+ result. context( "signal channel disconnected" ) ?;
95
+ rpc. signal( ) . exit_flag( ) . poll( ) . context( "RPC server interrupted" ) ?;
96
+ } ,
97
+ // Handle new blocks' notifications
98
+ recv( new_block_rx) -> result => match result {
99
+ Ok ( _) => ( ) , // sync and update
100
+ Err ( _) => {
101
+ info!( "disconnected from bitcoind" ) ;
102
+ return Ok ( ( ) ) ;
103
+ }
104
+ } ,
105
+ // Handle Electrum RPC requests
106
+ recv( server_rx) -> event => {
107
+ let first = once( event. context( "server disconnected" ) ?) ;
108
+ let rest = server_rx. iter( ) . take( server_rx. len( ) ) ;
109
+ let events: Vec <Event > = first. chain( rest) . collect( ) ;
110
+ server_batch_size. observe( "recv" , events. len( ) ) ;
111
+ handle_events( & rpc, & mut peers, events) ;
112
+ } ,
113
+ default ( config. wait_duration) => ( ) , // sync and update
106
114
}
107
115
}
108
116
}
@@ -140,35 +148,52 @@ enum Message {
140
148
Done ,
141
149
}
142
150
143
- fn handle_event ( rpc : & Rpc , peers : & mut HashMap < usize , Peer > , event : Event ) {
144
- let Event { msg, peer_id } = event;
145
- match msg {
146
- Message :: New ( stream) => {
147
- debug ! ( "{}: connected" , peer_id) ;
148
- peers. insert ( peer_id, Peer :: new ( peer_id, stream) ) ;
149
- }
150
- Message :: Request ( line) => {
151
- let result = match peers. get_mut ( & peer_id) {
152
- Some ( peer) => handle_request ( rpc, peer, & line) ,
153
- None => return , // unknown peer
154
- } ;
155
- if let Err ( e) = result {
156
- error ! ( "{}: disconnecting due to {}" , peer_id, e) ;
157
- peers. remove ( & peer_id) . unwrap ( ) . disconnect ( ) ;
151
+ fn handle_events ( rpc : & Rpc , peers : & mut HashMap < usize , Peer > , events : Vec < Event > ) {
152
+ let mut events_by_peer = HashMap :: < usize , Vec < Message > > :: new ( ) ;
153
+ events
154
+ . into_iter ( )
155
+ . for_each ( |e| events_by_peer. entry ( e. peer_id ) . or_default ( ) . push ( e. msg ) ) ;
156
+ for ( peer_id, messages) in events_by_peer {
157
+ handle_peer_events ( rpc, peers, peer_id, messages) ;
158
+ }
159
+ }
160
+
161
+ fn handle_peer_events (
162
+ rpc : & Rpc ,
163
+ peers : & mut HashMap < usize , Peer > ,
164
+ peer_id : usize ,
165
+ messages : Vec < Message > ,
166
+ ) {
167
+ let mut lines = vec ! [ ] ;
168
+ let mut done = false ;
169
+ for msg in messages {
170
+ match msg {
171
+ Message :: New ( stream) => {
172
+ debug ! ( "{}: connected" , peer_id) ;
173
+ peers. insert ( peer_id, Peer :: new ( peer_id, stream) ) ;
174
+ }
175
+ Message :: Request ( line) => lines. push ( line) ,
176
+ Message :: Done => {
177
+ done = true ;
178
+ break ;
158
179
}
159
180
}
160
- Message :: Done => {
161
- // already disconnected, just remove from peers' map
162
- peers. remove ( & peer_id) ;
181
+ }
182
+ let result = match peers. get_mut ( & peer_id) {
183
+ Some ( peer) => {
184
+ let responses = rpc. handle_requests ( & mut peer. client , & lines) ;
185
+ peer. send ( responses)
163
186
}
187
+ None => return , // unknown peer
188
+ } ;
189
+ if let Err ( e) = result {
190
+ error ! ( "{}: disconnecting due to {}" , peer_id, e) ;
191
+ peers. remove ( & peer_id) . unwrap ( ) . disconnect ( ) ;
192
+ } else if done {
193
+ peers. remove ( & peer_id) ; // already disconnected, just remove from peers' map
164
194
}
165
195
}
166
196
167
- fn handle_request ( rpc : & Rpc , peer : & mut Peer , line : & str ) -> Result < ( ) > {
168
- let response = rpc. handle_request ( & mut peer. client , line) ;
169
- peer. send ( vec ! [ response] )
170
- }
171
-
172
197
fn accept_loop ( listener : TcpListener , server_tx : Sender < Event > ) -> Result < ( ) > {
173
198
for ( peer_id, conn) in listener. incoming ( ) . enumerate ( ) {
174
199
let stream = conn. context ( "failed to accept" ) ?;
0 commit comments