1.Vertx重要概念
vertx demo项目 方便了解vertx框架使用
- 概念1:Vertx
实现类VertxImpl,它的重要线程
eventLoopGroup
acceptorEventLoopGroup - 概念2: Verticle
1) Verticle起作用的方式,主要是通过Vertx进行部署,也就是deployVerticle
2) Verticle部署,主要是Vertx框架把vertx 和context 传给Verticle,并且通过context启动 - 概念3: Context上下文
1) 子类:ContextInternal EventLoopContext
2) 上下文是通过Vertx创建,如ContextInternal callingContext = vertx.getOrCreateContext();
3) context 带有线程,可以跑task
2.Vertx创建
以io.vertx.example.core.http.sharing.HttpServerVerticle为例子,该例子来源于上述的demo工程
Vertx创建很简单,传入VertxOptions 通过VertxBuilder进行创建VertxImpl实例,两类重要的网络I/O线程就是在此时创建的,见VertxImpl的构造函数
eventLoopGroup = transport.eventLoopGroup(Transport.IO_EVENT_LOOP_GROUP, options.getEventLoopPoolSize(), eventLoopThreadFactory, NETTY_IO_RATIO);
acceptorEventLoopGroup = transport.eventLoopGroup(Transport.ACCEPTOR_EVENT_LOOP_GROUP, 1, acceptorEventLoopThreadFactory, 100);
线程和线程上下文:Vertx线程VertxThread,vertx框架创建出来的线程都是VertxThread。
VertxBuilder 初始化线程工厂
private void initThreadFactory() {
if (threadFactory != null) {
return;
}
threadFactory = VertxThreadFactory.INSTANCE;
}
VertxThreadFactory 匿名内部类创建INSTANCE,类似的用法还有ExecutorServiceFactory
public interface VertxThreadFactory extends VertxServiceProvider {
VertxThreadFactory INSTANCE = new VertxThreadFactory() {
};
@Override
default void init(VertxBuilder builder) {
if (builder.threadFactory() == null) {
builder.threadFactory(this);
}
}
default VertxThread newVertxThread(Runnable target, String name, boolean worker, long maxExecTime, TimeUnit maxExecTimeUnit) {
return new VertxThread(target, name, worker, maxExecTime, maxExecTimeUnit);
}
}
vertx常用获取上下文方法getOrCreateContext
public ContextInternal getOrCreateContext() {
AbstractContext ctx = getContext();
if (ctx == null) {
// We are running embedded - Create a context
ctx = createEventLoopContext();
stickyContext.set(new WeakReference<>(ctx));
}
return ctx;
}
// 判断当前线程是不是VertxThread,是的话取线程上下文
static ContextInternal current() {
Thread current = Thread.currentThread();
if (current instanceof VertxThread,是的话取线程上下文) {
return ((VertxThread) current).context();
}
return null;
}
3.Vertx部署Verticle
vertx实例调用deployVerticle进行并部署,deployVerticle等待参数有Verticle,如例子中的HttpServerVerticle,另外可以定制部署参数,通过DeploymentOptions配置。
如下是部署过程,关键的流程
// 开始部署
verticleManager.deployVerticle(name, options).map(Deployment::deploymentID);
// 创建context
ContextInternal callingContext = vertx.getOrCreateContext();
//最后进入DeploymentManager.doDeploy,循环部署多个verticle实例
for (Verticle verticle: verticles) {
...
// 创建新的context
ContextImpl context = (options.isWorker() ? vertx.createWorkerContext(deployment, closeFuture, workerPool, tccl) :
vertx.createEventLoopContext(deployment, closeFuture, workerPool, tccl));
...
// 通过context的线程驱动verticle运行
context.runOnContext(v -> {
try {
// 把vertx和context传给verticle
verticle.init(vertx, context);
Promise<Void> startPromise = context.promise();
Future<Void> startFuture = startPromise.future();
// 运行start方法,用户的业务逻辑是在start方法实现,驱动用户的业务逻辑
verticle.start(startPromise);
...
} catch (Throwable t) {
if (failureReported.compareAndSet(false, true))
deployment.rollback(callingContext, promise, context, holder, t);
}
});
}
3.1 Server sharing 分析
vertx官方文档 里提到Server sharing,比如,部署一个HttpServerVerticle,启动多个实例,多verticle实例如何共享一个listen port呢?
3.1.1 demo演示
HttpServerVerticle启动httpServer,监听8080端口,
public class HttpServerVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
vertx.createHttpServer().requestHandler(req -> {
req.response()
.putHeader("content-type", "text/html")
.end("<html><body><h1>Hello from " + this + "</h1></body></html>");
}).listen(8080);
}
}
Server verticle部署HttpServerVerticle,配置启动2个HttpServerVerticle instance
@Override
public void start() throws Exception {
vertx.deployVerticle(
"io.vertx.example.core.http.sharing.HttpServerVerticle",
new DeploymentOptions().setInstances(2));
}
启动Client运行结果如下,说明两个HttpServerVerticle实例成功部署,并且负载均衡对client服务
3.1.2 分析
分析1:多个verticle instance共享端口和轮询处理
入口是listen(),HttpServerVerticle的第一个instance负责启动Netty 监听端口,后面的instance,全部复用这个监听端口,只不过把自己的eventloop线程加入到网络I/O线程池。
// HttpServerImpld的listen,开始获取上下文
ContextInternal listenContext = vertx.getOrCreateContext();
// TCPServerBase,所有密码藏在这里
public synchronized io.netty.util.concurrent.Future<Channel> listen(SocketAddress localAddress, ContextInternal context, Handler<Channel> worker) {
...
// 获取共享服务
Map<ServerID, TCPServerBase> sharedNetServers = vertx.sharedTCPServers((Class<TCPServerBase>) getClass());
synchronized (sharedNetServers) {
...
if (actualPort > 0 || localAddress.isDomainSocket()) {
id = new ServerID(actualPort, hostOrPath);
// 第一个instance进来 main是null,剩下的instance尽量main都不为null
main = sharedNetServers.get(id);
shared = true;
bindAddress = localAddress;
} else {
...
}
// 第一个instance进来 main是null
if (main == null) {
servers = new HashSet<>();
servers.add(this);
// 所谓的负载均衡器,就是这里
channelBalancer = new ServerChannelLoadBalancer(vertx.getAcceptorEventLoopGroup().next());
channelBalancer.addWorker(eventLoop, worker);
// netty的ServerBootstrap终于出现了!!!!!
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(vertx.getAcceptorEventLoopGroup(), channelBalancer.workers());
....
// netty的childHandler,请求处理一切秘密都在里面
bootstrap.childHandler(channelBalancer);
...
if (shared) {
sharedNetServers.put(id, this);
}
actualServer = this;
} else { // 从第2个instance开始,都走这里,共享listen port,只不过,把自己的eventloop加入到netty的I/O线程池罢了。
// Server already exists with that host/port - we will use that
actualServer = main;
actualServer.servers.add(this);
actualServer.channelBalancer.addWorker(eventLoop, worker);
metrics = main.metrics;
listenContext.addCloseHook(this);
}
}
return actualServer.bindFuture;
}
轮询: 从bootstrap启动可知,I/O线程轮询,I/O线程是channelBalancer.workers(),也就是VertxEventLoopGroup。Netty网络框架的acceptor线程接收连接,把网络连接I/O处理,分配给I/O线程池,如何获取线程池中的线程,应该是通过next方法获取,因此是轮询。
// VertxEventLoopGroup 取线程方法就是轮询
@Override
public synchronized EventLoop next() {
if (workers.isEmpty()) {
throw new IllegalStateException();
} else {
EventLoop worker = workers.get(pos).worker;
pos++;
checkPos();
return worker;
}
}
分配verticle实例,每部署一个HttpServerVerticle,就创建一个HttpServerImpl实例,每个HttpServerImpl创建HttpServerWorker作为Hanlder。因此可以把verticle实例看作是ServerChannelLoadBalancer 某个I/O线程的一个Hanlder,
ServerChannelLoadBalancer类:
1) worker集中在workers,worker的Hanlder放在workerMap中;
2) 由于每个worker可能承载多个Handler,所以workerMap的value是个WorkerList,是个Handler列表。
3) Hanlder就是承载一个verticle实例,worker就是vertx的一个eventloop线程
// HttpServerImpl的listen 创建HttpServerWorker
Handler<Channel> channelHandler = childHandler(connContext, streamContextSupplier, hello, exceptionHandler, address, serverOrigin);
io.netty.util.concurrent.Future<Channel> bindFuture = listen(address, listenContext, channelHandler);
// TCPServerBase中的listen,worker就是上述的channelHandler
actualServer.channelBalancer.addWorker(eventLoop, worker);
以某个新建的连接为例,从workers I/O线程池取一个woker处理,在channel初始化的时候,在workerMap 轮询一个Hanler(也就是verticle某个instance)处理,分配给这个channel,以worker做为驱动处理这个channel请求。
//ServerChannelLoadBalancer 中
@Override
protected void initChannel(Channel ch) {
// 取某个Hanlder,Handler也就是上诉述的某个verticle实例
Handler<Channel> handler = chooseInitializer(ch.eventLoop());
if (handler == null) {
ch.close();
} else {
channelGroup.add(ch);
handler.handle(ch);
}
}
分析2:当vertx eventloop个数小于verticle instance个数的情况
我们知道HttpServerVerticle部署的时候可以设置实例数量,demo例子是2个,我们可以设置大一点,比如20个。另外,我们考察一下vertx默认启动的eventloop线程个数,我们发现默认是DEFAULT_EVENT_LOOP_POOL_SIZE,即机器核数乘以2。
当instance个数大于eventloop个数,必将会出现1个eventloop绑定到多个instance的情况。最终体现到上述ServerChannelLoadBalancer中,一个worker对应多个Handler。
如下图,2个eventloop线程,HttpServerVerticle部署5个 instance,可以看出每个eventloop管理多个instance。客户端请求,先按eventloop轮询,找到eventloop后,轮询instance
TODO:于是,自然而然引出一个问题,一个eventloop对应多个verticle instance,
如何保证线程安全?
每个instance对应的业务有一个response,如何利用eventloop发送响应,而不会导致错乱?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。