How to know: How many requests is HttpServer-in-Vertx serving - java

I write a server with Vertx.
I have about 40k users. I can print every request but I don't know how to know how many current requests my server is serving
I used this class: http://vertx.io/docs/apidocs/io/vertx/core/http/HttpServer.html but this is no method to do it
Vertx is great toolkit but it can not show server status, really????
My server code:
public class MyFirstVerticle extends AbstractVerticle {
private HttpServer httpServer = null;
#Override
public void start() throws Exception {
httpServer = vertx.createHttpServer();
httpServer.requestHandler(new Handler<HttpServerRequest>() {
#Override
public void handle(HttpServerRequest request) {
String path = request.path();
System.out.println("incoming request: [" + request.remoteAddress() + "] at " + path);
}
});
httpServer.listen(9999);
}
}

Since vert.x 3.x.x http://vertx.io/docs/vertx-dropwizard-metrics is an official component of vert.x
I think what you search is this:
http://vertx.io/docs/vertx-dropwizard-metrics/java/#http-server-metrics
There are several possibilities to view the metrics
jmx http://vertx.io/docs/vertx-dropwizard-metrics/java/#jmx
http bridge for jmx http://vertx.io/docs/vertx-dropwizard-metrics/java/#_using_jolokia_and_hawtio
In our project we write periodically to a log file and send it in parallel to a graphite / grafana instance over udp

Vert.x requests are very short lived, so basing calculations solely on number of requests per second is not optimal. But, for the sake of discussion, you can achieve what you want in the following way:
public class MyFirstVerticle extends AbstractVerticle {
private HttpServer httpServer = null;
// Bad, done for the sake of simplicity
public final static AtomicInteger counter = new AtomicInteger(0);
#Override
public void start() throws Exception {
httpServer = vertx.createHttpServer();
httpServer.requestHandler(new Handler<HttpServerRequest>() {
public void handle(HttpServerRequest request) {
counter.incrementAndGet();
String path = request.path();
System.out.println("incoming request: [" + request.remoteAddress() + "] at " + path);
request.response().end();
request.endHandler(new Handler<Void>() {
#Override
public void handle(Void aVoid) {
counter.decrementAndGet();
}
});
}
});
httpServer.listen(9999);
}
}
Then to test yourself you can try something like that:
public class Main {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
MyFirstVerticle verticle = new MyFirstVerticle();
vertx.deployVerticle(verticle);
// Very bad, but our request are very short
vertx.setPeriodic(1, new Handler<Long>() {
#Override
public void handle(Long aLong) {
// Otherwise will overflow our console
if (MyFirstVerticle.counter.intValue() > 0) {
System.out.println(MyFirstVerticle.counter.intValue());
}
}
});
}
}

Related

Correct way of sharing singleton clients across verticles in vetx

I have a vertx application where I deploy multiple instances of verticle A (HttpVerticle.java) and multiple instances of verticle B (AerospikeVerticle.java). The aerospike verticles need to share a single AerospikeClient. The HttpVerticle listens to port 8888 and calls AerospikeVerticle using the event bus. My questions are:
Is using sharedData the right way to share singleton client instances? Is there any other recommended / cleaner approach? I plan to create and share more such singleton objects (cosmos db clients, meterRegistry etc.) in the application. I plan to use sharedData.localMap to share them in a similar fashion.
Is it possible to use vertx's eventloop as the backing eventloop for aerospike client? Such that the aerospike client initialisation does not need to create its own new eventloop? Currently looks like the onRecord part of the aerospike get call runs on aerospike's eventloop.
public class SharedAerospikeClient implements Shareable {
public final EventLoops aerospikeEventLoops;
public final AerospikeClient client;
public SharedAerospikeClient() {
EventPolicy eventPolicy = new EventPolicy();
aerospikeEventLoops = new NioEventLoops(eventPolicy, 2 * Runtime.getRuntime().availableProcessors());
ClientPolicy clientPolicy = new ClientPolicy();
clientPolicy.eventLoops = aerospikeEventLoops;
client = new AerospikeClient(clientPolicy, "localhost", 3000);
}
}
Main.java
public class Main {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
LocalMap localMap = vertx.sharedData().getLocalMap("SHARED_OBJECTS");
localMap.put("AEROSPIKE_CLIENT", new SharedAerospikeClient());
vertx.deployVerticle("com.demo.HttpVerticle", new DeploymentOptions().setInstances(2 * 4));
vertx.deployVerticle("com.demo.AerospikeVerticle", new DeploymentOptions().setInstances(2 * 4));
}
}
HttpVerticle.java
public class HttpVerticle extends AbstractVerticle {
#Override
public void start(Promise<Void> startPromise) throws Exception {
vertx.createHttpServer().requestHandler(req -> {
vertx.eventBus().request("read.aerospike", req.getParam("id"), ar -> {
req.response()
.putHeader("content-type", "text/plain")
.end(ar.result().body().toString());
System.out.println(Thread.currentThread().getName());
});
}).listen(8888, http -> {
if (http.succeeded()) {
startPromise.complete();
System.out.println("HTTP server started on port 8888");
} else {
startPromise.fail(http.cause());
}
});
}
}
AerospikeVerticle.java
public class AerospikeVerticle extends AbstractVerticle {
private SharedAerospikeClient sharedAerospikeClient;
#Override
public void start(Promise<Void> startPromise) throws Exception {
EventBus eventBus = vertx.eventBus();
sharedAerospikeClient = (SharedAerospikeClient) vertx.sharedData().getLocalMap("SHARED_OBJECTS").get("AEROSPIKE_CLIENT");
MessageConsumer<String> consumer = eventBus.consumer("read.aerospike");
consumer.handler(this::getRecord);
System.out.println("Started aerospike verticle");
startPromise.complete();
}
public void getRecord(Message<String> message) {
sharedAerospikeClient.client.get(
sharedAerospikeClient.aerospikeEventLoops.next(),
new RecordListener() {
#Override
public void onSuccess(Key key, Record record) {
if (record != null) {
String result = record.getString("value");
message.reply(result);
} else {
message.reply("not-found");
}
}
#Override
public void onFailure(AerospikeException exception) {
message.reply("error");
}
},
sharedAerospikeClient.client.queryPolicyDefault,
new Key("myNamespace", "mySet", message.body())
);
}
}
I don't know about the Aerospike Client.
Regarding sharing objects between verticles, indeed shared data maps are designed for this purpose.
However, it is easier to:
create the shared client in your main class or custom launcher
provide the client as a parameter of the verticle constructor
The Vertx interface has a deployVerticle(Supplier<Verticle>, DeploymentOptions) method which is convenient in this case:
MySharedClient client = initSharedClient();
vertx.deploy(() -> new SomeVerticle(client), deploymentOptions);

When will event produced on server with MediaType.TEXT_EVENT_STREAM delivered to subscriber on client

I created an example client/server application to get familiar with Spring Webflux/Reactor Netty. Now I am a little bit confused about the behaviour on the client side when the response contains a Flux and the media type is "text/event-stream". What I could see is that each element produced on the server is sent immediately to the client but not yet delivered to the subscriber. The first delivery to the subscriber happens after the producer of the server side has completed the Flux.
This means for me that all the elements are first collected somewhere in reactor-netty on the client side until it gets a complete/error event.
Are my conclusions true or can i do something wrong there?
If it is true, will this be changed in the near future? With my currently observed behaviour most of the benefits using Spring Webflux are negated because as with Spring Mvc the consumer has to wait until the whole element collection has been created and transfered before he can start working on the elements.
My server app is:`
#SpringBootApplication
public class ServerApp {
public static void main(String[] args) {
new SpringApplicationBuilder().sources(ServerApp.class).run(args);
}
#RestController
public static class TestController {
#GetMapping(value = "/test", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> testFlux() {
class AsyncSink implements Consumer<SynchronousSink<String>> {
private List<String> allStrings = List.of(
"Hello Flux1!",
"Hello Flux2!",
"Hello Flux3!",
"Hello Flux4!",
"Hello Flux5!");
private int index = 0;
#Override
public void accept(SynchronousSink<String> sink) {
if (index == allStrings.size()) {
sink.complete();
}
else {
sink.next(allStrings.get(index++));
}
}
}
return Flux.generate(new AsyncSink());
}
}
}
and my client app is:
#SpringBootApplication
public class ClientApp {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext aContext = new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ClientApp.class).run(args);
Flux<String> aTestFlux = aContext.getBean(TestProxy.class).getFlux();
aTestFlux.subscribe(new TestSubscriber());
System.out.println("Press ENTER to exit.");
System.in.read();
}
#Bean
public WebClient webClient() {
return WebClient.builder().baseUrl("http://localhost:8080").build();
}
#Component
public static class TestProxy {
#Autowired
private WebClient webClient;
public Flux<String> getFlux() {
return webClient.get().uri("/test").accept(MediaType.TEXT_EVENT_STREAM).exchange().flatMapMany(theResponse -> theResponse.bodyToFlux(String.class));
}
}
private static class TestSubscriber extends BaseSubscriber<String> {
#Override
public void hookOnSubscribe(Subscription subscription) {
System.out.println("Subscribed");
request(Long.MAX_VALUE);
}
#Override
public void hookOnNext(String theValue) {
System.out.println(" - " + theValue);
request(1);
}
#Override
protected void hookOnComplete() {
System.out.println(" done");
}
#Override
protected void hookOnCancel() {
System.out.println(" cancelled");
}
#Override
protected void hookOnError(Throwable theThrowable) {
theThrowable.printStackTrace(System.err);
}
}
}
When i access the url http://localhost:8080/test with chrome browser i see:
data:Hello Flux1!
data:Hello Flux2!
data:Hello Flux3!
data:Hello Flux4!
data:Hello Flux5!
which for me looks like 5 http event have been sent.
Taken from the reactive documentation and rewritten to fit your need.
My guess is that in your example you have given you pass the generate function a consumer that when finished will be emitted.
By instead using the method Flux#generate(Callable<S> stateSupplier, BiFunction<S,SynchronousSink<T>,S> generator) you supply a state that will contain the items you want emitted, then in the supplied BiFunction you emit each item, one by one.
Flux<String> flux = Flux.generate(
() -> List.of("1!", "2!", "3!", "4!", "5!"),
(state, sink) -> {
if (index == allStrings.size()) {
sink.complete();
} else {
sink.next(state.get(index++));
}
});
I have not tested the code, written on mobile.

Adding new resources dynamically - CoAP

Once CoAP server is started I need to add new resources dynamically. But I have to stop and start the server again in order to access new resources. I suppose adding new resources same as adding a new HTTP servlet into already started HTTP server.
Here I added source code which is used for adding dynamic resources. If I am missing anything here let me know.
private static CoapServer server;
public CoAPEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration,
Map<String, String> globalProperties) {
this.eventAdapterConfiguration = eventAdapterConfiguration;
this.globalProperties = globalProperties;
if(server == null){
server = new CoapServer();
server.start();
}
}
#Override
public void connect() {
registerDynamicEndpoint(eventAdapterConfiguration.getName());
isConnected = true;
}
private void registerDynamicEndpoint(String adapterName) {
server.stop();
server.add(new HelloWorldResource(adapterName));
server.start();
}
class HelloWorldResource extends CoapResource {
public HelloWorldResource(String resourceName) {
// set resource identifier
super(resourceName);
// set display name
getAttributes().setTitle("Hello-World Resource");
}
#Override
public void handleGET(CoapExchange exchange) {
// respond to the request
exchange.respond("Hello World!");
}
}
I've just tried to add a CoapResource dynamically:
#Override
public void handleGET(CoapExchange exchange) {
server.getRoot().add(new CoapResource("dynstatus") {
#Override
public void handleGET(CoapExchange exchange) {
System.err.println("dynstatus!!!");
}
});
And was able to call it.

Creating SockJS server with Java

I'm very new with Vert.x so excuse my newbness.
I was able to create a very simply SockJS server with Vert.x however I can't figure out how to register events/callbacks/handlers when connections are open or closed.
With JSR-356, it's drop dead simple to handle open/close connection events:
#OnOpen
public void onOpen(Session userSession) {
// Do whatever you need
}
#OnClose
public void onClose(Session userSession) {
// Do whatever you need
}
Using the SockJS support in Spring Framework 4.0 M1+, it's almost the same as JSR-356:
public class MySockJsServer extends TextWebSocketHandlerAdapter {
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// Do whatever you need
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// Do whatever you need
}
}
For some reason I couldn't figure out how to do something so conceptually simple in Vert.x. I though Vert.x was simple ?!!
If anyone can point me in the right direction, please help.
I played around with EventBus and EventBus hooks but it didn't work. Perhaps that's the wrong approach anyhow.
I'm using Vert.x version 2.0.1
TIA
This is the answer:
HttpServer httpServer = vertx.createHttpServer();
// Create HTTP server
httpServer = httpServer.requestHandler(new Handler<HttpServerRequest>() {
#Override
public void handle(HttpServerRequest req) {
req.response().sendFile("web/" + req.path());
}
});
// Create SockJS Server
SockJSServer sockJSServer = vertx.createSockJSServer(httpServer);
sockJSServer = sockJSServer.installApp(new JsonObject().putString("prefix", "/test"), new Handler<SockJSSocket>() {
public void handle(final SockJSSocket sock) {
System.out.println("New session detected!");
// Message handler
sock.dataHandler(new Handler<Buffer>() {
public void handle(Buffer buffer) {
System.out.println("In dataHandler");
}
});
// Session end handler
sock.endHandler(new Handler<Void>() {
#Override
public void handle(Void arg) {
System.out.println("In endHandler");
}
});
}
});
httpServer.listen(8080);

tomcat websocket servlet listening port

I'm trying to write a servlet that uses org.apache.catalina.websocket.WebSocketServlet. I've found an example of websocket chat, but I can't figure out how can I specify the listening port for a websocket server (which is implemented in this servlet)? For example I need to listen for clients connection on port 11337. But how I can express this?
OK, here is the (simplified) code:
public class TestServlet extends WebSocketServlet {
private static final Logger logger = LoggerFactory.getLogger(TestServlet.class);
public TestServlet() {
logger.error("Initializing TestServlet");
}
#Override
protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {
logger.error("New WS connection, subProtocol=" + subProtocol + ", request=" + request.getRequestURL());
return new TestConnection();
}
private class TestConnection extends MessageInbound {
#Override
protected void onBinaryMessage(ByteBuffer byteBuffer) throws IOException {
logger.error("onBinaryMessage");
}
#Override
protected void onTextMessage(CharBuffer charBuffer) throws IOException {
logger.error("onBinaryMessage: " + charBuffer);
sendMessage("Test message");
}
public void sendMessage(String message) {
WsOutbound outbound = this.getWsOutbound();
CharBuffer cb = CharBuffer.wrap(message);
try {
outbound.writeTextMessage(cb);
} catch (IOException e) {
logger.error("failed to write outbound");
}
}
}
}
I can't find where and how I can set listening port. Official websocket documentation also doesn't help much.
So I guess it can be set somewhere in servlet settings, but can't find where.
Does anyone have any ideas?
WebSocket is designed to work over HTTP protocol and so it won't have different listening port like normal TCP Socket but it use the same server port that it deployed on.
so here in your case, if you are using tomcat server than websocket may use port 8080 (if you haven't modified explicitly) for communication. You may need to override onOpen and onClose methods of MessageInbound class to get notified on Connection established and connection close. Refer below sample code for more details.
public class IncomingMessageHandler extends MessageInbound {
private WsOutbound myoutbound;
public IncomingMessageHandler() {
}
#Override
public void onOpen(WsOutbound outbound) {
logger.info("Open Client.");
this.myoutbound = outbound;
}
#Override
public void onClose(int status) {
logger.info("Close Client.");
}
/**
* Called when received plain Text Message
*/
#Override
public void onTextMessage(CharBuffer cb) throws IOException {
}
/**
* We can use this method to pass image binary data, eventually !
*/
#Override
public void onBinaryMessage(ByteBuffer bb) throws IOException {
}
public synchronized void sendTextMessage(String message) {
try {
CharBuffer buffer = CharBuffer.wrap(message);
this.getMyoutbound().writeTextMessage(buffer);
this.getMyoutbound().flush();
} catch (IOException e) {
}
}
/**
* Set timeout in milliseconds, -1 means never
*/
#Override
public int getReadTimeout() {
return -1;
}
public WsOutbound getMyoutbound() {
return myoutbound;
}
public void setMyoutbound(WsOutbound myoutbound) {
this.myoutbound = myoutbound;
}
}

Categories

Resources