Closed pakipaki2000 closed 5 years ago
You should use the actix app state to store the read/write handles to an evmap
Example:
use std::sync::{Arc, Mutex};
use actix::prelude::*;
use actix_web::{server, ws, App, Path, Responder, State};
pub struct AppState {
read: evmap::ReadHandle<i32, i32>,
write: Arc<Mutex<evmap::WriteHandle<i32, i32>>>,
}
/// Define http actor
struct Ws;
impl Actor for Ws {
type Context = ws::WebsocketContext<Self, AppState>; // Note the extra generic parameter
}
/// Handler for ws::Message message
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
// Here is where you would access the state
let some_value = ctx.state().read.get_and(&0, |vs| vs[0]).unwrap();
match msg {
ws::Message::Ping(msg) => ctx.pong(&msg),
ws::Message::Text(text) => ctx.text(text),
ws::Message::Binary(bin) => ctx.binary(bin),
_ => (),
}
}
}
fn index(info: Path<(String, u32)>, state: State<AppState>) -> impl Responder {
format!(
"Hello {}! id:{} value: {}",
info.0,
info.1,
// get entry
state.read.get_and(&0, |vs| vs[0]).unwrap()
)
}
fn main() {
server::new(|| {
let (read, write) = evmap::new();
App::with_state(AppState {
read,
write: Arc::new(Mutex::new(write)),
})
.resource("/{name}/{id}/index.html", |r| r.with(index))
.resource("/ws/", |r| r.f(|req| ws::start(req, Ws)))
})
.bind("127.0.0.1:8080")
.unwrap()
.run();
}
Thank you so much!
Sorry I'm a little confused about one thing, can you help me integrating it into:
server::new(
|| App::new()
.middleware(middleware::Logger::default())
.resource(SERVER_WEBSOCKET_ROUTE_PATH, |r| r.method(http::Method::GET).f(ws_index))
.handler("/", fs::StaticFiles::new(SERVER_HTTP_ROUTE_PATH)
.unwrap()
.index_file(SERVER_HTTP_ROUTE_DEFAULT_HTML_FILE)))
.bind(format!("{}:{}", SERVER_IP, SERVER_PORT)).unwrap()
.start();
I need to have access in ws_index (in the websocket)
I've updated the example code to include the standard actix websocket setup and how you'd access the app state there.
I haven't used a regular "with" style function for websockets before, so I'm unsure what the function signature of that is.
Man thank you so much, you saved my day :)
No problem. Feel free to close the issue if there are no further questions.
I comment here again because apparently in your function, the evmap::new() is executed multiple times.
server::new(|| {
let (read, mut write) = evmap::new();
println!("test");
At runtime, the "test" will show 16 times.
Thanks :)
Interesting. It seems actix spawns server threads for the number of logical CPUs on your system, evaluating the setup on each. Makes sense, I suppose.
In that case, you'll want to create the evmap
and app state outside of the server creation, like so:
#[derive(Clone)]
pub struct AppState {
read: evmap::ReadHandle<MyHash, MyHash>,
write: Arc<Mutex<evmap::WriteHandle<MyHash, MyHash>>>,
}
fn main() {
let (read, write) = evmap::new();
let app_state = AppState {
read,
write: Arc::new(Mutex::new(write)),
};
server::new(move || {
App::with_state(app_state.clone())
.resource("/{name}/{id}/index.html", |r| r.with(index))
.resource("/ws/", |r| r.f(|req| ws::start(req, Ws)))
})
.bind("127.0.0.1:8080")
.unwrap()
.run();
}
Note that any additional state you add to AppState
which is shared among all threads should use Arc
for keeping a single shared instance.
Hmm. It may actually be better to create a whole Actor
for the evmap
at this point. I'll have to experiment with that.
Hmm. It may actually be better to create a whole
Actor
for theevmap
at this point. I'll have to experiment with that.
Thanks so much, i've integrated your code up, it works. What make you say that you need a whole Actor? What is the flaw in the current way?
Given #22, you may want to take advantage of the whole Actor lifecycle and event handling stuff, but if you just want to use the evmap
as a regular cache in your app, feel free to ignore that comment.
So is this particular issue solved for your use case? @pakipaki2000
It's solved, you've been so helpful! Thank you.
Hi!
i'd like to use a map in actixweb. What's the best and current method to use an evmap as a global?
Thanks!