第四章 shardingsphere源码-如何启动的?

本文详细描述了项目启动过程,使用Netty作为服务器,涉及BootstrapArguments、YamlProxyConfiguration、ServerBootstrap初始化、ChannelFuture等关键组件,展示了从配置到接收客户端请求的核心逻辑。
摘要由CSDN通过智能技术生成


一、项目启动核心源码

上一篇文章说了项目真正启动之前,所做的准备工作,大体就是 将配置信息进行解析成对象,然后按照注册中心 进行持久化,
本篇介绍一下,这个项目到底是使用什么启动的?答案借助于netty

1.new ShardingSphereProxy().start(port, addresses); 使用了netty作为服务器

    public static void main(final String[] args) throws IOException, SQLException {
        BootstrapArguments bootstrapArgs = new BootstrapArguments(args);
        YamlProxyConfiguration yamlConfig = ProxyConfigurationLoader.load(bootstrapArgs.getConfigurationPath());
        int port = bootstrapArgs.getPort().orElseGet(() -> new ConfigurationProperties(yamlConfig.getServerConfiguration().getProps()).getValue(ConfigurationPropertyKey.PROXY_DEFAULT_PORT));
        List<String> addresses = bootstrapArgs.getAddresses();
        new BootstrapInitializer().init(yamlConfig, port, bootstrapArgs.getForce());
        boolean cdcEnabled = null != yamlConfig.getServerConfiguration().getCdc() && yamlConfig.getServerConfiguration().getCdc().isEnabled();
        if (cdcEnabled) {
            new CDCServer(addresses, yamlConfig.getServerConfiguration().getCdc().getPort()).start();
        }
        // 类似netty启动,里面注册了很多消息处理器,规定了客户端发送消息服务端到底如何接收
        new ShardingSphereProxy().start(port, addresses);
    }

1.核心方法 start

其中核心方法startInternal(),表示开启内部,也就是netty的配置
accept(),主要是阻塞主线程,也就是main线程暂时不会执行到finally里面,nettyserver也持续运行,如果监听到关闭事件,可以优雅的关闭通道和nettyserver。

       public void start(final int port, final List<String> addresses) {
        try {
            List<ChannelFuture> futures = startInternal(port, addresses);
            accept(futures);
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            BackendExecutorContext.getInstance().getExecutorEngine().close();
        }
    }

=》startInternal(port, addresses)
再使用netty当服务器时,会有一下几步,几乎是一个公式化

  1. createEventLoopGroup() : 创建boss线程组和worker线程组.
    boss线程组指的是用于监听客户端的连接请求,将连接请求发送给 workerGroup 进行处理;
    worker线程组指的是用于处理客户端连接的数据读写
  2. 创建 ServerBootstrap 对象,用于启动 Netty 服务器
  3. initServerBootstrap来初始化netty服务器的配置
  4. bind 绑定端口,开始接收客户端请求
        private List<ChannelFuture> startInternal(final int port, final List<String> addresses) throws InterruptedException {
        createEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        initServerBootstrap(bootstrap);
        List<ChannelFuture> futures = new ArrayList<>();
        for (String address : addresses) {
            futures.add(bootstrap.bind(address, port).sync());
        }
        return futures;
    }
    }

=》initServerBootstrap(port, addresses)
接下来,使用ServerBootstrap 配置一下之前设置的线程组,option 设置写缓冲区水位线,防止待发送数据内存占用无限制的增长从而导致 OOM;childOption 设置子通道的内存分配器,handler 注册了一个日志处理器,按照info级别,来打印netty日志;childHandler 通道初始化回调函数,在启动的时候可以自动调用,这个是最核心的,项目启动之后,发过来请求包,到底先经过什么,就在里面定义。

    private void initServerBootstrap(final ServerBootstrap bootstrap) {
        Integer backLog = ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().getProps().<Integer>getValue(ConfigurationPropertyKey.PROXY_NETTY_BACKLOG);
        bootstrap.group(bossGroup, workerGroup)
                .channel(Epoll.isAvailable() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
                .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(8 * 1024 * 1024, 16 * 1024 * 1024))
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .option(ChannelOption.SO_REUSEADDR, true)
                .option(ChannelOption.SO_BACKLOG, backLog)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childOption(ChannelOption.TCP_NODELAY, true)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ServerHandlerInitializer(FrontDatabaseProtocolTypeFactory.getDatabaseType()));
    }

=》ServerHandlerInitializer()
在这里又使用了spi机制DatabaseProtocolFrontendEngine,目前来说我们实现了兼容达梦、人大金仓、oceanbase、tidb,
下面的消息处理器依次会执行,总的来说就是
channelActive,当客户端上线的时候会触发这个方法;
channelRead,当 Channel 中有来自客户端的数据时就会触发这个方法
exceptionCaught,当有异常时触发这个方法
其中PacketCodec编解码引擎、FrontendChannelInboundHandler核心处理逻辑,这两个是比较重要的,使用SPI机制时已经获取到了关于这个数据库编解码引擎,具体核心的处理包在下图所示。FrontendChannelInboundHandler下面介绍

    protected void initChannel(final SocketChannel socketChannel) {
        DatabaseProtocolFrontendEngine databaseProtocolFrontendEngine = TypedSPIRegistry.getRegisteredService(DatabaseProtocolFrontendEngine.class, databaseType.getType(), new Properties());
        databaseProtocolFrontendEngine.initChannel(socketChannel);
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new ChannelAttrInitializer());
        pipeline.addLast(new PacketCodec(databaseProtocolFrontendEngine.getCodecEngine()));
        pipeline.addLast(new FrontendChannelLimitationInboundHandler(databaseProtocolFrontendEngine));
        pipeline.addLast(new FrontendChannelInboundHandler(databaseProtocolFrontendEngine, socketChannel));
    }

在这里插入图片描述
在这里插入图片描述
=》FrontendChannelInboundHandler()
当 Channel 中有来自客户端的数据时就会触发这个方法,从而首先做的是认证,验证用户名密码是否正确,后面会详细展开介绍,而ProxyStateContext.execute() 的下一步回到核心处理流程中 也就是 CommandExecutorTask.run()方法

    public void channelRead(final ChannelHandlerContext context, final Object message) {
        if (!authenticated) { 
            authenticated = authenticate(context, (ByteBuf) message);
            return;
        }
        ProxyStateContext.execute(context, message, databaseProtocolFrontendEngine, connectionSession);
    }

二、总结

本篇主要介绍了,该项目是如何启动的,借助于netty当做服务器,是如何配置的,以及当启动完成后,进行连接的时候到底从那个地方开始接收报文,方便后续debug。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值