I want to test if the rate limiting of my site is working.
To do this I would like to send a controlled amount of requests. For example, exactly 100 requests per second, and probably save the responses.
From How can I perform parallel asynchronous HTTP GET requests with reqwest? and this gist I wrote the following:
# [dependencies]
# reqwest = { version = "0.11.6" }
# tokio = { version = "1.14.0", features = ["full"] }
# futures = "0.3.24"
use futures::stream::StreamExt;
use reqwest::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let paths: Vec<String> = vec![String::from("https://api.ipify.org/"); 50];
let fetches = futures::stream::iter(paths.into_iter().map(|path| {
let client = Client::new();
let send_fut = client.get(&path).send();
async move {
let response = send_fut.await;
match response {
Ok(resp) => {
println!("{}", resp.status());
}
Err(_) => println!("Error"),
}
}
}))
.buffer_unordered(100)
.collect::<Vec<()>>();
fetches.await;
Ok(())
}
The point is that I dont know how to control how many request are executed per second.
Any idea will be welcome!
You can use the tokio::time::interval
use tokio::time;
async fn request() {
println!("send request");
}
#[tokio::main]
async fn main() {
let mut interval = time::interval(time::Duration::from_millis(10));
for _i in 0..100 {
interval.tick().await;
request().await;
}
}
Related
I've spent a few hours trying to figure this out and I'm pretty done. I found the question with a similar name, but that looks like something was blocking synchronously which was messing with tokio. That very well may be the issue here, but I have absolutely no idea what is causing it.
Here is a heavily stripped down version of my project which hopefully gets the issue across.
use std::io;
use futures_util::{
SinkExt,
stream::{SplitSink, SplitStream},
StreamExt,
};
use tokio::{
net::TcpStream,
sync::mpsc::{channel, Receiver, Sender},
};
use tokio_tungstenite::{
connect_async,
MaybeTlsStream,
tungstenite::Message,
WebSocketStream,
};
#[tokio::main]
async fn main() {
connect_to_server("wss://a_valid_domain.com".to_string()).await;
}
async fn read_line() -> String {
loop {
let mut str = String::new();
io::stdin().read_line(&mut str).unwrap();
str = str.trim().to_string();
if !str.is_empty() {
return str;
}
}
}
async fn connect_to_server(url: String) {
let (ws_stream, _) = connect_async(url).await.unwrap();
let (write, read) = ws_stream.split();
let (tx, rx) = channel::<ChannelMessage>(100);
tokio::spawn(channel_thread(write, rx));
tokio::spawn(handle_std_input(tx.clone()));
read_messages(read, tx).await;
}
#[derive(Debug)]
enum ChannelMessage {
Text(String),
Close,
}
// PROBLEMATIC FUNCTION
async fn channel_thread(
mut write: SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>,
mut rx: Receiver<ChannelMessage>,
) {
while let Some(msg) = rx.recv().await {
println!("{:?}", msg); // This only fires when buffer is full
match msg {
ChannelMessage::Text(text) => write.send(Message::Text(text)).await.unwrap(),
ChannelMessage::Close => {
write.close().await.unwrap();
rx.close();
return;
}
}
}
}
async fn read_messages(
mut read: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
tx: Sender<ChannelMessage>,
) {
while let Some(msg) = read.next().await {
let msg = match msg {
Ok(m) => m,
Err(_) => continue
};
match msg {
Message::Text(m) => println!("{}", m),
Message::Close(_) => break,
_ => {}
}
}
if !tx.is_closed() {
let _ = tx.send(ChannelMessage::Close).await;
}
}
async fn handle_std_input(tx: Sender<ChannelMessage>) {
loop {
let str = read_line().await;
if tx.is_closed() {
break;
}
tx.send(ChannelMessage::Text(str)).await.unwrap();
}
}
As you can see, what I'm trying to do is:
Connect to a websocket
Print outgoing messages from the websocket
Forward any input from stdin to the websocket
Also a custom heartbeat solution which was trimmed out
The issue lies in the channel_thread() function. I move the websocket writer into this function as well as the channel receiver. The issue is, it only loops over the sent objects when the buffer is full.
I've spent a lot of time trying to solve this, any help is greatly appreciated.
Here, you make a blocking synchronous call in an async context:
async fn read_line() -> String {
loop {
let mut str = String::new();
io::stdin().read_line(&mut str).unwrap();
// ^^^^^^^^^^^^^^^^^^^
// This is sync+blocking
str = str.trim().to_string();
if !str.is_empty() {
return str;
}
}
}
You never ever make blocking synchronous calls in an async context, because that prevents the entire thread from running other async tasks. Your channel receiver task is likely also assigned to this thread, so it's having to wait until all the blocking calls are done and whatever invokes this function yields back to the async runtime.
Tokio has its own async version of stdin, which you should use instead.
I want to send Events between the game client and server and I already got it working, but I do not know how to do it with bevy.
I am dependent to use tokios async TcpStream, because I have to be able to split the stream into a OwnedWriteHalf and OwnedReadhalf using stream.into_split().
My first idea was to just spawn a thread that handles the connection and then send the received events to a queue using mpsc::channel
Then I include this queue into a bevy resource using app.insert_resource(Queue) and pull events from it in the game loop.
the Queue:
use tokio::sync::mpsc;
pub enum Instruction {
Push(GameEvent),
Pull(mpsc::Sender<Option<GameEvent>>),
}
#[derive(Clone, Debug)]
pub struct Queue {
sender: mpsc::Sender<Instruction>,
}
impl Queue {
pub fn init() -> Self {
let (tx, rx) = mpsc::channel(1024);
init(rx);
Self{sender: tx}
}
pub async fn send(&self, event: GameEvent) {
self.sender.send(Instruction::Push(event)).await.unwrap();
}
pub async fn pull(&self) -> Option<GameEvent> {
println!("new pull");
let (tx, mut rx) = mpsc::channel(1);
self.sender.send(Instruction::Pull(tx)).await.unwrap();
rx.recv().await.unwrap()
}
}
fn init(mut rx: mpsc::Receiver<Instruction>) {
tokio::spawn(async move {
let mut queue: Vec<GameEvent> = Vec::new();
loop {
match rx.recv().await.unwrap() {
Instruction::Push(ev) => {
queue.push(ev);
}
Instruction::Pull(sender) => {
sender.send(queue.pop()).await.unwrap();
}
}
}
});
}
But because all this has to be async I have block the pull() function in the sync game loop.
I do this using the futures-lite crate:
fn event_pull(
communication: Res<Communication>
) {
let ev = future::block_on(communication.event_queue.pull());
println!("got event: {:?}", ev);
}
And this works fine, BUT after around 5 seconds the whole program just halts and does not receive any more events.
It seems like that future::block_on() does block indefinitely.
Having the main function, in which bevy::prelude::App gets built and run, to be the async tokio::main function might also be a problem here.
It would probably be best to wrap the async TcpStream initialisation and tokio::sync::mpsc::Sender and thus also Queue.pull into synchronous functions, but I do not know how to do this.
Can anyone help?
How to reproduce
The repo can be found here
Just compile both server and client and then run both in the same order.
I got it to work by just replacing every tokio::sync::mpsc with crossbeam::channel, which might be a problem, as it does block
and manually initializing the tokio runtime.
so the init code looks like this:
pub struct Communicator {
pub event_bridge: bridge::Bridge,
pub event_queue: event_queue::Queue,
_runtime: Runtime,
}
impl Communicator {
pub fn init(ip: &str) -> Self {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_io()
.build()
.unwrap();
let (bridge, queue, game_rx) = rt.block_on(async move {
let socket = TcpStream::connect(ip).await.unwrap();
let (read, write) = socket.into_split();
let reader = TcpReader::new(read);
let writer = TcpWriter::new(write);
let (bridge, tcp_rx, game_rx) = bridge::Bridge::init();
reader::init(bridge.clone(), reader);
writer::init(tcp_rx, writer);
let event_queue = event_queue::Queue::init();
return (bridge, event_queue, game_rx);
});
// game of game_rx events to queue for game loop
let eq_clone = queue.clone();
rt.spawn(async move {
loop {
let event = game_rx.recv().unwrap();
eq_clone.send(event);
}
});
Self {
event_bridge: bridge,
event_queue: queue,
_runtime: rt,
}
}
}
And main.rs looks like this:
fn main() {
let communicator = communication::Communicator::init("0.0.0.0:8000");
communicator.event_bridge.push_tcp(TcpEvent::Register{name: String::from("luca")});
App::new()
.insert_resource(communicator)
.add_system(event_pull)
.add_plugins(DefaultPlugins)
.run();
}
fn event_pull(
communication: Res<communication::Communicator>
) {
let ev = communication.event_queue.pull();
if let Some(ev) = ev {
println!("ev");
}
}
Perhaps there might be a better solution.
I am attempting to make a future that continuously finds new work to do and then maintains a set of futures for those work items. I would like to make sure neither my main future that finds work to be blocked for long periods of time and to have my work being done concurrently.
Here is a rough overview of what I am trying to do. Specifically isDone does not exist and also from what I can understand from the docs isn't necessarily a valid way to use futures in Rust. What is the idomatic way of doing this kind of thing?
use std::collections::HashMap;
use tokio::runtime::Runtime;
async fn find_work() -> HashMap<i64, String> {
// Go read from the DB or something...
let mut work = HashMap::new();
work.insert(1, "test".to_string());
work.insert(2, "test".to_string());
return work;
}
async fn do_work(id: i64, value: String) -> () {
// Result<(), Error> {
println!("{}: {}", id, value);
}
async fn async_main() -> () {
let mut pending_work = HashMap::new();
loop {
for (id, value) in find_work().await {
if !pending_work.contains_key(&id) {
let fut = do_work(id, value);
pending_work.insert(id, fut);
}
}
pending_work.retain(|id, fut| {
if isDone(fut) {
// do something with the result
false
} else {
true
}
});
}
}
fn main() {
let runtime = Runtime::new().unwrap();
let exec = runtime.executor();
exec.spawn(async_main());
runtime.shutdown_on_idle();
}
I make a client request inside an Iron handler. How can I reuse Tokio's Core and Hyper's Client? I'm using hyper 0.11.0 and tokio-core 0.1.
fn get_result(req: &mut Request) -> IronResult<Response> {
let mut payload = String::new();
req.body.read_to_string(&mut payload).unwrap();
// can we re-use core and client somehow. Making then global with lazy_static!() does not work.
let mut core = tokio_core::reactor::Core::new().unwrap();
let client = Client::new(&core.handle());
let uri = "http://host:port/getResult".parse().unwrap();
let mut req: hyper::Request = hyper::Request::new(hyper::Method::Post, uri);
req.headers_mut().set(ContentType::json());
req.headers_mut().set(ContentLength(payload.len() as u64));
req.set_body(payload);
let mut results: Vec<RequestFormat> = Vec::new();
let work = client.request(req).and_then(|res| {
res.body().for_each(|chunk| {
let re: ResultFormat = serde_json::from_slice(&chunk).unwrap();
results.push(re);
Ok(())
})
});
Ok(Response::with(
(iron::status::Ok, serde_json::to_string(&results).unwrap()),
))
}
I created a Downloader class that wraps client and core. Below is snippet.
use hyper;
use tokio_core;
use std::sync::{mpsc};
use std::thread;
use futures::Future;
use futures::stream::Stream;
use std::time::Duration;
use std::io::{self, Write};
use time::precise_time_ns;
use hyper::Client;
pub struct Downloader {
sender : mpsc::Sender<(hyper::Request, mpsc::Sender<hyper::Chunk>)>,
#[allow(dead_code)]
tr : thread::JoinHandle<hyper::Request>,
}
impl Downloader {
pub fn new() -> Downloader {
let (sender, receiver) = mpsc::channel::<(hyper::Request,mpsc::Sender<hyper::Chunk>)>();
let tr = thread::spawn(move||{
let mut core = tokio_core::reactor::Core::new().unwrap();
let client = Client::new(&core.handle());
loop {
let (req , sender) = receiver.recv().unwrap();
let begin = precise_time_ns();
let work = client.request(req)
.and_then(|res| {
res.body().for_each(|chunk| {
sender.send(chunk)
.map_err(|e|{
//io::sink().write(&chunk).unwrap();
io::Error::new(io::ErrorKind::Other, e)
})?;
Ok(())
})
//sender.close();
//res.body().concat2()
});
core.run(work).map_err(|e|{println!("Error Is {:?}", e);});
//This time prints same as all request processing time.
debug!("Time taken In Download {:?} ms", (precise_time_ns() - begin) / 1000000);
}
});
Downloader{sender,
tr,
}
}
pub fn download(&self, req : hyper::Request, results: mpsc::Sender<Vec<u8>>){
self.sender.send((req, results)).unwrap();
}
}
Now client of this class can have a static variable.
lazy_static!{
static ref DOWNLOADER : Mutex<downloader::Downloader> =
Mutex::new(downloader::Downloader::new());
}
let (sender, receiver) = mpsc::channel();
DOWNLOADER.lock().unwrap().download(payload, sender);
and then read through receive channel.
One may need to close sender channel using sender.drop()
I have a small Rust application that receives some requests through a serial port, does some processing and saves the results locally. I wanted to use a browser as a remote monitor so I can see everything that is happening and as I understand SSEs are pretty good for that.
I tried using Iron for that but I can't find a way to keep the connection open. The request handlers all need to return a Response, so I can't keep sending data.
This was my (dumb) attempt:
fn monitor(req: &mut Request) -> IronResult<Response> {
let mut headers = Headers::new();
headers.set(ContentType(Mime(TopLevel::Text, SubLevel::EventStream, vec![])));
headers.set(CacheControl(vec![CacheDirective::NoCache]));
println!("{:?}", req);
let mut count = 0;
loop {
let mut response = Response::with((iron::status::Ok, format!("data: Count!:{}", count)));
response.headers = headers.clone();
return Ok(response); //obviously won't do what I want
count += 1;
std::thread::sleep_ms(1000);
}
}
I think the short answer is: you can't. The current version of Iron is built on a single request-response interaction. This can be seen in your code because the only way to send a response is to return it; terminating the handler thread.
There's an issue in Iron to utilize the new async support in Hyper, which itself was merged relatively recently. There are even other people trying to use Server-Send Events in Hyper that haven't succeeded yet.
If you are willing to use the Hyper master branch, something like this seems to work. No guarantees that this is a good solution or that it doesn't eat up all your RAM or CPU. It seems to work in Chrome though.
extern crate hyper;
use std::time::{Duration, Instant};
use std::io::prelude::*;
use hyper::{Control, Encoder, Decoder, Next };
use hyper::server::{Server, HandlerFactory, Handler, Request, Response};
use hyper::status::StatusCode;
use hyper::header::ContentType;
use hyper::net::{HttpStream};
fn main() {
let address = "0.0.0.0:7777".parse().expect("Invalid address");
let server = Server::http(&address).expect("Invalid server");
let (_listen, server_loop) = server.handle(MyFactory).expect("Failed to handle");
println!("Starting...");
server_loop.run();
}
struct MyFactory;
impl HandlerFactory<HttpStream> for MyFactory {
type Output = MyHandler;
fn create(&mut self, ctrl: Control) -> Self::Output {
MyHandler {
control: ctrl,
}
}
}
struct MyHandler {
control: Control,
}
impl Handler<HttpStream> for MyHandler {
fn on_request(&mut self, _request: Request<HttpStream>) -> Next {
println!("A request was made");
Next::write()
}
fn on_request_readable(&mut self, _request: &mut Decoder<HttpStream>) -> Next {
println!("Request has data to read");
Next::write()
}
fn on_response(&mut self, response: &mut Response) -> Next {
println!("A response is ready to be sent");
response.set_status(StatusCode::Ok);
let mime = "text/event-stream".parse().expect("Invalid MIME");
response.headers_mut().set(ContentType(mime));
every_duration(Duration:: from_secs(1), self.control.clone());
Next::wait()
}
fn on_response_writable(&mut self, response: &mut Encoder<HttpStream>) -> Next {
println!("A response can be written");
// Waited long enough, send some data
let fake_data = r#"event: userconnect
data: {"username": "bobby", "time": "02:33:48"}"#;
println!("Writing some data");
response.write_all(fake_data.as_bytes()).expect("Failed to write");
response.write_all(b"\n\n").expect("Failed to write");
Next::wait()
}
}
use std::thread;
fn every_duration(max_elapsed: Duration, control: Control) {
let mut last_sent: Option<Instant> = None;
let mut count = 0;
thread::spawn(move || {
loop {
// Terminate after a fixed number of messages
if count >= 5 {
println!("Maximum messages sent, ending");
control.ready(Next::end()).expect("Failed to trigger end");
return;
}
// Wait a little while between messages
if let Some(last) = last_sent {
let elapsed = last.elapsed();
println!("It's been {:?} since the last message", elapsed);
if elapsed < max_elapsed {
let remaining = max_elapsed - elapsed;
println!("There's {:?} remaining", remaining);
thread::sleep(remaining);
}
}
// Trigger a message
control.ready(Next::write()).expect("Failed to trigger write");
last_sent = Some(Instant::now());
count += 1;
}
});
}
And the client-side JS:
var evtSource = new EventSource("http://127.0.0.1:7777");
evtSource.addEventListener("userconnect", function(e) {
const obj = JSON.parse(e.data);
console.log(obj);
}, false);