Vertx entry to actual combat-to achieve the penetration of the nailing robot intranet

The recent research on Vetrx is simply admirable. Can't wait to introduce it to everyone.

carbon

What is Vertx

  • Vertx is a tool set that runs on the JVM and is used to build responsive applications.
  • High-performance, asynchronous network library based on netty.
  • Netty is encapsulated to provide a more friendly API.
  • At the same time, some libraries based on asynchronous calls are implemented, including database connection, monitoring, authentication, logging, service discovery, clustering support, etc.

Why I recommend studying

In fact, with the development of technology. Asynchronous calls are becoming more and more popular.

1. Now with the popularity of RPC. Frameworks like Dubbo are based on the concept of NIO, and understanding asynchronous programming is helpful for learning to understand the framework.

2. Responsive programming gradually penetrates from the client to the front end to the back end.

3. It is easier to write high-performance asynchronous services.

Several important concepts of Vertx

Event Loop

Event Loop, as the name suggests, is the event loop. During the life cycle of Vertx, polling events are continuously polled.

In the traditional multi-threaded programming model, each request forks a new thread to process the request. Such a programming model is relatively simple to implement. One connection corresponds to one thread. If there are a large number of requests to be processed, a large number of threads need to be fork to process. For the operating system, scheduling a large number of threads causes the system load to increase.

So in order to be able to handle a large number of requests, it is necessary to transition to the Event Loop based on the Roactor model.

https://img.xilidou.com/img//event-loop.png

This picture on the official website is very vivid. Eventloop continuously trains to obtain events and arranges different Handlers to process the corresponding events.

It should be noted here that in order to ensure the normal operation of the program, the event must be non-blocking. Otherwise, eventloop will be blocked, which will affect the performance of Vertx. But in reality, programs cannot guarantee that they are non-blocking, and Vertx also provides a mechanism to deal with blocking methods. We will continue to introduce it below.

Verticle

In Vertx we can often see the Vertical component.

verticle

Verticle is a block of code deployed and run by Vert.x. By default, a Vert.x instance maintains N (by default, N = CPU cores x 2) Event Loop threads. Verticle instances can be written in any programming language supported by Vert.x, and a simple application can also contain Verticles written in multiple languages.

You can think of Verticle as an  Actor  in the Actor Model .

An application is usually composed of many Verticle instances running simultaneously in the same Vert.x instance. Different Verticle instances communicate with each other by sending and receiving messages to the Event Bus.

Event bus

The event bus in Vertx is easier to understand than the MQ commonly used in the back end. In fact, Event Bus is a bridge for transferring information between Verticles.

In other words, it is the listening mode in the Java common design pattern, or the MQ message development mode that we often say.

Event bus

Back to Vertx

We discussed the model and mechanism of vertx above, now people will see how to use vertx to develop a program.

I will explain it with the more than three nails I wrote before. Everything starts with Vertx.

    val vertx = Vertx.vertx()
复制代码

vertx is the core of the entire vert.x framework. Generally speaking, all the behavior of Vertx is generated from the vertx class.

Don’t call us, we’ll call you

Vert.x is an event-driven framework. The so-called event-driven means to do this action when something happens.

Let's return to the title, "Don't call us, we'll call you" This principle is actually when we find that you can complete this work, we will find you. You don't need to take the initiative to contact me.

We use code to understand how Vertx implements this principle:

    server.requestHandler(request -> {
      request.response().end("hello world!");
    });
复制代码

The meaning of this code block is that whenever the server request is called, one is returned hello world.

So 'you' j in Vertx is a variety of Handler. Most of the time when we write Vertx programs, we are actually writing Handler behavior. Then tell Vertx that whenever XXX event is triggered, you call XXX Handler.

Don’t block me

Vertx is based on events. We mentioned Event Loop above. In Vertx, EventLoop is a hardworking bee, constantly looking for what events are triggered. Then execute the corresponding Handler. If the thread executing Hanlder is the Event Loop thread. If the execution time of Handler is too long. Event Loop will be blocked. When other events are triggered. Event Loop also takes a long time to process Handler. Event loop does not respond to other events in a timely manner.

But in reality, it is impossible for all events to be non-blocking. For example, query the database, call the remote interface, etc., what should we do?

In the event-driven model, there are probably two kinds of routines to solve this problem. For example, in Redis, Redis will maintain a time slice very carefully. When a person executes an event for too long, the state of the current event is saved, and then the current event is suspended and scheduled again by the Event loop. Prevent Event Loop from being blocked by events.

There is also a routine to hand over blocked events to other threads for execution. Event Loop can continue the event loop to prevent being blocked. Vertx does this in fact.

    vertx.executeBlocking(promise -> {
      // Call some blocking API that takes a significant amount of time to return
      String result = someAPI.blockingMethod("hello");
      promise.complete(result);
    }, res -> {
      System.out.println("The result is: " + res.result());
    });
复制代码

If we realize that this Handler is a blocking when we develop, we need to tell vertx that this is a Blocking and needs to be handed over to other threads to process.

Coordinated asynchronous processing

As mentioned above. Vertx handles events through Handlers. However, in many cases, an operation usually requires more than one Handler to process the data. If you always use the callback method, arrow codes will be formed. There is a problem with hell callback.

As an asynchronous framework, Vertx generally uses Future to solve the problem of callback hell. Understanding Future in Vertx is the core of writing good code.

Usually we understand that Future is just a placeholder, representing the result of an operation at some time in the future. If you are not clear, you can read my previous article.

It should be pointed out that Vertx the Future and Jdk inside of CompletableFutureprinciple and philosophy is similar, but using them is very different.

Jdk inside CompletableFuturecan be used directly result()blocked waiting for the results, but Vertx the Future if used directly result(), the results will be immediately removed from the Future, rather than blocking waiting for the results, it is easy to harvest a Null.

After clarifying this difference, you won't make mistakes when you write the code.

Event Bus

If you have used the messaging system in daily development, it is easy to understand the Event bus in Vertx. The official documentation compares Event bus to Vertx's nervous system. In fact, we think that Event bus is Vertx's messaging system, just fine.

Development of Dingding Intranet Penetrating Agent

This little Demo Sparrow is small but contains several key components of Vertx. When I was writing this demo, I was learning Kotlin, so I wrote it in kotlin. If you have written Java or Typescript then you can easily understand it.

The project contains

  • Http Service is used to receive nail callback
  • The WebSocket Service is used to push the received callback to the Client to achieve the purpose of intranet penetration.
  • Vertx Config is used to configure project related parameters for easy use
  • The use of Event Bus is used to transfer messages between Http Service and WebSocket.

First come a Verticle

The Gradle configuration file is first introduced as follows:

    implementation ("io.vertx:vertx-core:3.8.5")
    implementation ("io.vertx:vertx-web:3.8.5")
    implementation ("io.vertx:vertx-lang-kotlin:3.8.5")
复制代码

Above, we have introduced what Verticle is. In order to facilitate development, Vertx provides us with an AbstractVerticle abstract class. Direct inheritance:

    class DingVerticle : AbstractVerticle() {
    }
复制代码

AbstractVerticle It contains some common methods of Vericle.

We can rewrite the start()method to initialize our Verticle behavior.

HttpService creation

    override fun start() {
        val httpServer = vertx.createHttpServer()
        val router = Router.router(vertx)
        router.post("/ding/api").handler{event ->
            val request = event.request()
            request.bodyHandler { t ->
                println(t)
            }
            event.response().end();
        }
        httpServer.requestHandler(router);
        httpServer.listen(8080);
    }
复制代码

The code is relatively simple:

  1. Create an httpService
  2. Set up a Router if you have written Spring Mvc related code. Router here is similar to RequestMapping in Controller. Used to specify a Handler corresponding to the Http request URI and Method. The Handler here is a lambda expression. It simply prints the body of the request.
  3. Add Router to httpService and listen to port 8080.

WebSocketService

The webSocket protocol is the key to this proxy, because WebSocket is different from Http and communicates in both directions. Relying on this feature, we can "push" the message to the intranet. To achieve the purpose of "penetrating" the intranet.

    httpServer.webSocketHandler { webSocket: ServerWebSocket ->
        val binaryHandlerID = webSocket.binaryHandlerID()
        webSocket.endHandler() {
            log.info("end", binaryHandlerID)
        }
        webSocket.writeTextMessage("欢迎使用 xilidou 钉钉 代理")
        webSocket.writeTextMessage("连接成功")
    }
复制代码

The code is relatively simple, which is to register a Handler to handle WebSocket with Vertx.

Use of Event Bus

As the core function of the agent is to forward the spiked callback message. As I said earlier, Event Bus plays a "nerve role" in Vertx. In fact, in other words, when the HTTP service receives the callback, it can pass Event Bus sends a message. When WebSocket receives the message from Event Bus, it pushes it to the client. See the picture below:

For ease of understanding, we use the usual concept producers and consumers in MQ.

So we use to register a producer in HttpService and forward the message after receiving the callback of Dingding.

For ease of writing, we can write a HttpHandler separately

    //1
    class HttpHandler(private val eventBus: EventBus) : Handler<RoutingContext> {

        private val log = LoggerFactory.getLogger(this.javaClass);

        override fun handle(event: RoutingContext) {
            val request = event.request()
            request.bodyHandler { t->
                    val jsonObject = JsonObject(t)
                    val toString = jsonObject.toString()
                    log.info("request is {}",toString);
                    // 2
                    eventBus.publish("callback", toString)
            }
            event.response().end("ok")
        }
    }
复制代码

There are a few issues to note here:

  1. We need to use Event Bus to send messages, so we need to pass in an Event Bus in the constructor
  2. After we receive the message, you can first convert the data to Json string, and then send a message, pay attention to here are using the publish()broadcast means, so that all clients can subscribe to receive new messages.

With the producer and the data sent out, we can consume this message in WebSocket and push it to the client

Let's write a WebSocket Handler

    //1
    class WebSocketHandler(private val eventBus: EventBus) : Handler<ServerWebSocket> {

        private val log = LoggerFactory.getLogger(this.javaClass)

        override fun handle(webSocket: ServerWebSocket) {
            val binaryHandlerID = webSocket.binaryHandlerID()

            //2
            val consumer = eventBus.consumer<String>("callback") { message ->
                val body = message.body()
                log.info("send message {}", body)
                //3
                webSocket.writeTextMessage(body)
            }
            webSocket.endHandler() {
                log.info("end", binaryHandlerID)
                //4
                consumer.unregister();
            }
            webSocket.writeTextMessage("欢迎使用 xilidou 钉钉 代理")
            webSocket.writeTextMessage("连接成功")
        }
    }
复制代码

There are a few issues to note here:

  1. EventBus needs to be injected during initialization
  2. Write a consumer()consumer HttpHandler message sent
  3. Write message to webSocket and send to Client
  4. Consumer needs to be recycled after WebSocket is disconnected

Initialize Vertx

With so much preparation, we can finally initialize our Vertx

    class DingVerticleV2: AbstractVerticle(){
        override fun start() {

            //2
            val eventBus = vertx.eventBus()
            val httpServer = vertx.createHttpServer()

            val router = Router.router(vertx);
            //3
            router.post("/api/ding").handler(HttpHandler(eventBus));
            httpServer.requestHandler(router);

            //4
            httpServer.webSocketHandler(WebSocketHandler(eventBus));
            httpServer.listen(8080);
        }
    }

    //1
    fun main() {
        val vertx = Vertx.vertx()
        vertx.deployVerticle(DingVerticleV2())
    }
复制代码

There are a few issues to note here:

  1. Initialize Vertx and deploy it
  2. Initialize eventBus
  3. Register HttpHandler
  4. Register WebSocketHandler

to sum up

  • Vertx is a tool, not a framework, so it can be easily combined with other frameworks.
  • Vertx is an asynchronous framework based on Netty. We can write asynchronous code just as we write synchronous code.
  • vertx has two main functions in the code, one is to initialize the component, such as:
    val eventBus = vertx.eventBus()
    val httpServer = vertx.createHttpServer()
复制代码

Another one is to register Handler:

     httpServer.webSocketHandler(WebSocketHandler(eventBus));
复制代码
  • Event Bus is a message system. Used to transfer data directly to different Handlers, simplifying development.

Related connections

  1. Tutorial nails robot callback within the network through a proxy - use articles
  2. Github 地址: Github
  3. 官网教程:A gentle guide to asynchronous programming with Eclipse Vert.x for Java developers;
  4. Vert.x Core Manual

Guess you like

Origin juejin.im/post/5e932d76f265da47b554f34c