当前位置: 首页 >  资讯  > 正文

世界要闻:【Netty源码分析】03 客户端接入流程

  • 2023-03-28 22:17:11 来源:腾讯云

Netty服务端启动完成,这时候客户端连接就可以接入进来了,下面我们就来分析下客户端连接接入的流程。

之前分析过NioEventLoop线程启动方法是startThread(),由于这个方法里面的逻辑比较复杂,并没有展开,这一节就是从这个方法开始分析。


(资料图片)

startThread

private void startThread() {    if (state == ST_NOT_STARTED) {        if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {            try {                doStartThread();            } catch (Throwable cause) {                STATE_UPDATER.set(this, ST_NOT_STARTED);                PlatformDependent.throwException(cause);            }        }    }}

这个方法主要主要完成2件事:

利用casNioEventLoop的状态由ST_NOT_STARTED修改成ST_STARTED,即表示NioEventLoop线程启动;执行doStartThread()方法;

doStartThread()方法看着比较复杂,核心逻辑如下,向线程池执行器executor提交一个任务,而这个线程池执行器类型是ThreadPerTaskExecutor,即每次执行任务都会创建一个新线程,而且这个任务是无限循环的:事件轮询selector.select()、事件处理processSelectedKeys()和任务队列处理runAllTasks(),这样NioEventLoop就和具体的Thread线程进行了关联:

private void doStartThread() {    assert thread == null;    //executor线程执行器,类型是:ThreadPerTaskExecutor,即每次执行任务都会创建一个新线程    executor.execute(new Runnable() {        @Override        public void run() {            //将executor线程执行器创建的线程:FastThreadLocalThread保存到EventLoop的全局变量中,相当于thread和EventLoop的绑定            thread = Thread.currentThread();            if (interrupted) {                thread.interrupt();            }            boolean success = false;            updateLastExecutionTime();            try {                //然后调用EventLoop中的run方法进行启动                SingleThreadEventExecutor.this.run();                success = true;            } catch (Throwable t) {                logger.warn("Unexpected exception from an event executor: ", t);            }        }    });}

该方法大致完成2件事:

thread = Thread.currentThread();:将executor线程池分配的线程保存起来,这样就完成了NioEventLoopThread线程的关联;SingleThreadEventExecutor.this.run():具体实现在NioEventLoop.run()方法,所以,startThread()核心就是分配一个线程运行NioEventLoop.run()方法。
protected void run() {    for (;;) {        try {            try {                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {                case SelectStrategy.CONTINUE:// 默认实现下,不存在这个情况                    continue;                case SelectStrategy.BUSY_WAIT:                case SelectStrategy.SELECT:                    //selector.select轮询io事件                    select(wakenUp.getAndSet(false));                    if (wakenUp.get()) {                        selector.wakeup();                    }                default:                }            } catch (IOException e) {                rebuildSelector0();                handleLoopException(e);                continue;            }            cancelledKeys = 0;            needsToSelectAgain = false;            final int ioRatio = this.ioRatio;            if (ioRatio == 100) {               try {                    // 处理 Channel 感兴趣的就绪 IO 事件                    processSelectedKeys();                } finally {                    // 运行所有普通任务和定时任务,不限制时间                    runAllTasks();                }            } else {                final long ioStartTime = System.nanoTime();                try {                    // 处理IO事件                    processSelectedKeys();                } finally {                    // 运行所有普通任务和定时任务,限制时间                    final long ioTime = System.nanoTime() - ioStartTime;                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);                }            }        } catch (Throwable t) {            handleLoopException(t);        }        //  EventLoop 优雅关闭        try {            if (isShuttingDown()) {                closeAll();                if (confirmShutdown()) {                    return;                }            }        } catch (Throwable t) {            handleLoopException(t);        }    }}

该方法主要完成三件事:

select(wakenUp.getAndSet(false)):主要执行selector.select()方法进行事件轮询processSelectedKeys():如果轮询到事件,会在这里进行处理runAllTasks():处理任务队列和定时任务队列中的任务

下面我们就分别来分析下这三个方法。

select

private void select(boolean oldWakenUp) throws IOException {    Selector selector = this.selector;    try {        int selectCnt = 0;//计数器置0        long currentTimeNanos = System.nanoTime();        /**         * selectDeadLineNanos是select()方法运行的截止时间         *         * currentTimeNanos:可以看成当前时间         * delayNanos(currentTimeNanos):获取间隔时间,这里分为两种情况:         * 1、netty里面定时任务队列scheduledTaskQueue是按照延迟时间从小到大进行排序,如果定时任务队列中有任务,         * 则只需要获取到第一个任务的启动时间 - 当前时间 = select()方法可以运行的时间间隔,即:select()方法要在第一个定时任务执行之前退出,这样才能去执行定时任务         * 2、如果定时任务队列没有任务,则delayNanos(currentTimeNanos)返回1秒对应的时间间隔         *         */        long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);        for (;;) {            //计算超时时间            long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;            /**             * timeoutMillis <= 0表示当前已经超时了,不能继续向下执行select()方法了,需要立即退出select方法,在退出前还有个判断:selectCnt == 0             * selectCnt == 0表示第一次进入循环,则执行下Selector.selectNow()检出准备好的网络IO事件,该方法不会阻塞,             */            if (timeoutMillis <= 0) {                if (selectCnt == 0) {                    selector.selectNow();                    selectCnt = 1;                }                break;            }            /**             * 如果没有超时,但是通过hasTasks()判断到taskQueue任务队列中有需要执行的任务,这时也需要退出select()方法             * 1、利用cas将wakeUp值由false变成true,wakeUp=true表示线程处于唤醒状态,可以执行任务,进入select()方法前会把wakeUp设置成false             * 表示线程处于select()方法阻塞中,不能处理任务队列中的任务,这时只要处理Selector.select()             * 2、退出前执行一次:selector.selectNow()             */            if (hasTasks() && wakenUp.compareAndSet(false, true)) {                //有任务,进行一次非阻塞式的select                selector.selectNow();                selectCnt = 1;                break;            }            //调用select方法,阻塞时间为上面算出的最近一个将要超时的定时任务时间            /**             * 未超时,任务队列中也没有需要执行的任务,这时就可以放心的执行Selector.select()方法了,这里带上之前计算出的超时时间             * 如果之前计算时存在定时任务,则保证在第一个定时任务启动前唤醒即可,没有定时任务则默认超时1秒             */            int selectedKeys = selector.select(timeoutMillis);            //轮询次数+1            selectCnt ++;            /**             * 发生如下几种情况,select()方法都需要退出:             * 1、selectedKeys != 0:表示轮询到IO事件             * 2、oldWakenUp:这个是入参,值为false,是在select()方法中控制是否需要退出,默认是没有使用到的,没有意义             * 3、wakenUp.get():进入select()方法之前,wakeUp被设置成false,如果这里为true,表示已有外部线程对线程进行唤醒操作,             *      一般就是addTask()添加新任务时会触发唤醒,然后及时去执行taskQueue中的任务             * 4、hasTasks() || hasScheduledTasks():判断任务队列和定时任务队列是否有任务需要执行             */            if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {                break;            }            //线程中断响应:如果线程被中断,计数器置1,break退出for循环,则退出select()检测            if (Thread.interrupted()) {                if (logger.isDebugEnabled()) {                    logger.debug("Selector.select() returned prematurely because " +                            "Thread.currentThread().interrupt() was called. Use " +                            "NioEventLoop.shutdownGracefully() to shutdown the NioEventLoop.");                }                selectCnt = 1;                break;            }            /**             * 正常情况下:time >= currentTimeNanos + TimeUnit.MILLISECONDS.toNanos(timeoutMillis)             * 但是,jdk nio中存在一个bug,selector.select(timeoutMillis)在没有IO事件触发时并不会等待超时而是立即返回,造成空轮询             *             * 下面就是Netty解决空轮询问题             * 1、if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos)             *      表示selector.select(timeoutMillis)经过超时后才被唤醒,属于正常情况,把selectCnt重置成1             * 2、如果不是,表示可能发生空轮询selectCnt不会被重置成1,for循环一次selectCnt就会被累加1次;             * 3、等到 selectCnt > 门限值,默认是512,可以通过io.netty.selectorAutoRebuildThreshold参数设置,             *      则判断真正发生了nio空循环bug,则重建Selector替换掉当前这个出问题的Selector             */            long time = System.nanoTime();            //判断执行了一次阻塞式select后,当前时间和开始时间是否大于超时时间。(大于是很正常的,小于的话,说明没有执行发生了空轮询)            if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {                selectCnt = 1;             } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&                    selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {                selector = selectRebuildSelector(selectCnt);                selectCnt = 1;                break;            }            currentTimeNanos = time;        }    } catch (CancelledKeyException e) {        if (logger.isDebugEnabled()) {            logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",                        selector, e);        }    }}

select()方法代码看着很复杂,其核心思想理解再来分析就比较简单的。select()主要是用来执行selector.select()IO事件进行轮询,作为server,这里就是轮询OP_ACCEPT事件,看是否有客户端接入进来。但是NioEventLoop是单线程处理模式,不可能让线程一直处理selector.select(),还有轮询到的事件以及任务队列中任务等等都需要使用这个线程进行处理,所以,上面一大堆代码都是用来判断什么时候退出select()方法的,总结下退出逻辑主要分为如下几种情况:

在执行selector.select()方法之前,计算出一个超时时间,超时时间默认是1秒,如果定时任务队列有任务,则取出第一个任务(按顺序存放),保证在该定时任务执行之前退出select()方法即可;如果超时就退出,退出前判断是否是第一次进入for循环,如果是在退出之前调用一次无阻塞的selector.selectNow()轮询下判断任务队列taskQueue中是否有任务,如果有则将wakenUp利用cas设置成true,执行下无阻塞的selector.selectNow()轮询后退出select()方法如果上面情况都不存在,开始执行阻塞selector.select(timeoutMillis)轮询,并将之前计算的超时时间带上;selector.select(timeoutMillis)执行完成后,继续判断是否需要退出select()方法,发生如下任一情况则要退出:轮询到IO事件,则需要退出select()方法去处理事件外部线程对对线程执行过唤醒操作,比如addTask()等操作需要唤醒线程执行队列任务,才能及时去执行taskQueue中的任务:进入select()方法之前,wakeUp被设置成false,如果这里为true,表示已有外部线程对线程进行唤醒操作任务队列taskQueue或定时任务队列scheduledTaskQueue中有需要处理的任务,这时需要退出select()方法,转去执行任务

processSelectedKeys

private void processSelectedKeys() {    //selectedKeys != null表示已对Selector进行优化过,替换掉Selector内部的selectedKeys,正常情况下进入这个流程    if (selectedKeys != null) {        processSelectedKeysOptimized();    } else {        processSelectedKeysPlain(selector.selectedKeys());    } }

processSelectedKeys()主要是对selector.select()方法轮询到的事件进行处理,作为server,如果轮询到OP_ACCEPT,就表示有客户端接入进来了,那我们就跟踪下这个方法,看接入进来的客户端处理流程。

Netty是对Selector进行了优化,将selectedKeysSet实现替换成了数组实现,提升性能,所以,这里一般走的是processSelectedKeysOptimized()这个流程:

private void processSelectedKeysOptimized() {    for (int i = 0; i < selectedKeys.size; ++i) {        final SelectionKey k = selectedKeys.keys[i];        selectedKeys.keys[i] = null;        //k.attachment()获取到的就是NioServerSocketChannel        final Object a = k.attachment();        if (a instanceof AbstractNioChannel) {//一般是走这个分支流程            processSelectedKey(k, (AbstractNioChannel) a);        } else {            @SuppressWarnings("unchecked")            NioTask task = (NioTask) a;            processSelectedKey(k, task);        }        if (needsToSelectAgain) {            selectedKeys.reset(i + 1);            selectAgain();            i = -1;        }    }}

这里关键一点是Object a = k.attachment();,之前分析过向selector注册时把NioServerSocketChannel作为attachment添加进去,所以,这里取出来的就是NioServerSocketChannel对象。processSelectedKey()方法通过if判断事件类型进行处理,server端这里肯定是OP_ACCEPT

if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) { unsafe.read();}

具体的处理逻辑交由Unsafe对象进行处理:

public void read() {    assert eventLoop().inEventLoop();    final ChannelConfig config = config();    final ChannelPipeline pipeline = pipeline();    final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();    allocHandle.reset(config);    boolean closed = false;    Throwable exception = null;    try {        try {            do {                //doReadMessages()读出来一个客户端连接的Channel                int localRead = doReadMessages(readBuf);                if (localRead == 0) {                    break;                }                if (localRead < 0) {                    closed = true;                    break;                }                allocHandle.incMessagesRead(localRead);            } while (allocHandle.continueReading());        } catch (Throwable t) {            exception = t;        }        int size = readBuf.size();        for (int i = 0; i < size; i ++) {            readPending = false;            pipeline.fireChannelRead(readBuf.get(i));        }        readBuf.clear();        allocHandle.readComplete();        pipeline.fireChannelReadComplete();        if (exception != null) {            closed = closeOnReadError(exception);            pipeline.fireExceptionCaught(exception);        }        if (closed) {            inputShutdown = true;            if (isOpen()) {                close(voidPromise());            }        }    } finally {        if (!readPending && !config.isAutoRead()) {            removeReadOp();        }    }}

这个类主要完成2件事:

doReadMessages(readBuf):调用serverSocketChannel.accept()接收到客户端连接socketChannel,并封装成Netty中类型:NioSocketChannel,然后放入到readBuf集合中;pipeline.fireChannelRead(readBuf.get(i));:将读入的客户端连接作为参数,即NioSocketChannel对象,通过pipeline触发channelRead事件进行handler间传播,注意这里的pipelineNioServerSocketChannel中的,即server端的。最终会进入到ServerBootstrapAcceptor#channelRead方法中进行处理。
public void channelRead(ChannelHandlerContext ctx, Object msg) {    final Channel child = (Channel) msg;    child.pipeline().addLast(childHandler);    setChannelOptions(child, childOptions, logger);    for (Entry, Object> e: childAttrs) {        child.attr((AttributeKey) e.getKey()).set(e.getValue());    }    try {        //childGroup.register(child):会给客户端连接进来的Channel从线程池中获取一个EventLoop绑定给Channel        childGroup.register(child).addListener(new ChannelFutureListener() {            @Override            public void operationComplete(ChannelFuture future) throws Exception {                if (!future.isSuccess()) {                    forceClose(child, future.cause());                }            }        });    } catch (Throwable t) {        forceClose(child, t);    }}

这个方法主要完成3件事:

child.pipeline().addLast(childHandler):向NioSocketChannel中添加ServerBootstrap.childHandler(new TestServerInitializer()),后面通过触发handlerAdded()时回调initChannel()实现向pipeline添加handler;设置optionattr信息;childGroup.register(child):将客户端连接NioSocketChannel注册到NioEventLoop实例上,基本和之前分析NioServerSocketChannel注册逻辑一致,这个过程中会触发三个事件:handlerAddedchannelRegisteredchannelActive,之前NioServerSocketChannel注册时只能触发前两个,绑定端口后才能触发第三个事件,客户端连接不存在端口绑定问题,所以这里会直接触发channelActive。和NioServerSocketChannel一样,真正向selector注册感兴趣事件就是在channelActive触发这里:
public void channelActive(ChannelHandlerContext ctx) {    //触发channelActive事件传播    ctx.fireChannelActive();    //向selector注册真正关注的事件    readIfIsAutoRead();}

channelActive和之前分析NioServerSocketChannel的处理逻辑一致,就不再分析。

总结

分析到这里,基本搞清楚了客户端接入的处理流程,现在再次总结下:

NioServerSocketChannel绑定的NioEventLoop不停轮询OP_ACCEPT,触发后通过调用java api获取到ServerSocket,然后包装成NioSocketChannel;然后触发channelRead事件传播,然后会进入server pipeline中非常重要的一个handlerServerBootstrapAcceptor,连接处理器专门处理客户端连接;在ServerBootstrapAcceptor#channelRead()方法中,完成NioSocketChannel的设置:optionattrhandler添加等;最重要的是将channel注册到NioEventLoop上,注册过程中会触发三种事件:handlerAddedchannelRegisteredchannelActive,和之前分析server channel注册过程一样,最终在channelActive这里向selector注册真正感兴趣IO事件,整个流程全部完成。

关键词:

最新推荐

每日资讯:安民巷古民居

1、安民巷古民居位于福州市安民巷。文章到此就分享结束,希望对大家有

当前关注:selfiecity是什么牌子_selfiecity是什么软件简介介绍

对于selfiecity是什么软件这个问题感兴趣的朋友应该很多,这个也是目前

全球消息!夫妻离婚一方出轨财产怎样分割

夫妻离婚一方出轨财产的分割可以由双方协商处理,协商的时候还可以在自

RNG老板辟谣:没有强制Uzi退役,也没有卡合同!惨遭2位大佬打脸-全球视点

谢帆是WE老板的好友,他也是第一个爆料EDG签了Uzi的人,并且也爆料过WE

李诗沣2-战胜新加坡选手骆建佑,晋级印尼羽毛球公开赛男单八强-世界报资讯

在比赛中,李诗沣表现出了极强的竞技实力和严谨的比赛态度,让人叹为观

天天日报丨上周ATP网坛 ,属于穆雷,属于德约,更属于80后选手(下)

2023年法网首轮,德约科维奇三盘直落战胜了首次出战大满贯正赛的美国选

环球信息:王楠亿万家产老公曾因故意开日本酒店水管被骂,他真是个坏人吗?

从奥运会期间接济被媳妇马蓉戴了绿帽子的王宝强开始,郭斌就被外界认为

CSGO最划算交易网站是哪个?csgodemo怎么切换玩家?

CSGO最划算交易网站是哪个CSGO饰品交易网站最划算大全还是值得大家去体验一把,如果你担心亏钱,可以先...

手机软件:天猫新风尚红包如何用 手机天猫新风尚红包使用限制-天天滚动

手机这种移动通讯设备闲置已经相当的普及了,大多数的手机用户,对手机

产假工资如何支付?宝宝还没有满周岁可以离婚吗?

产假工资如何支付正常进行支付。会由生育保险账户进行支出。法律依据:《社会保险法》第五十四条 用人...

夫妻没有结婚证财产怎么分?月子里离婚孩子归谁呢能离婚吗?

夫妻没有结婚证财产怎么分没有领取结婚证的夫妻不是合法的夫妻,不是法律上的夫妻,自然也不用走法律上...

有抚养权能带孩子在身边吗?已婚夫妻是否也可办理婚前财产公证?

有抚养权能带孩子在身边吗可以的,抚养权就是指直接抚养照顾的权利。法律依据:《婚姻法》第三十六条 ...

自愿离婚需要带什么资料?找不到人分居一年的起诉离婚吗?

自愿离婚需要带什么资料离婚需要出具双方的户口簿、身份证、结婚证、双方当事人共同签署的离婚协议书等...

抚养费该谁负担?小孩才两个月怎样离婚?

抚养费该谁负担离婚后,一方抚养的子女,另一方应负担必要的生活费和教育费的一部或全部,就是说,抚养...

女方有理由不出抚养费吗?因出轨离婚无过错方能否索要赔偿?

女方有理由不出抚养费吗没有。因为根据婚姻法,一方抚养的子女,另一方应负担必要的生活费和教育费的一...

离婚案件房产的分割分哪两种?怎么证明父母是赠与方?

离婚案件房产的分割分哪两种可以选择房产或货币补偿。法律依据:《物权法》第一百条 共有人可以协商确...

夫妻共同财产的特别规定是什么?父母的宅基地其子女可以继承并分配吗?

夫妻共同财产的特别规定是什么法律分析:夫妻对共同财产,有平等的处理权。离婚时,夫妻的共同财产由双...

世界热门:逆水寒神秘箱子怎么过_逆水寒追命支线任务攻略 神秘的箱子任务怎么完成

1、逆水寒追命支线神秘的箱子任务攻略介绍:破庙场景,正中间马车中三

沪深两市成交额突破8000亿元 天天快播

沪深两市成交额突破8000亿元

国足最新球员号码,武磊7号艾克森9号,泰山队5人号码确定

赛前,国足球员的号码确定,武磊将继续身穿7号球衣,艾克森9号,10号归

环球热文:记者:巴萨签基米希的可能性基本排除 还未报价求购罗克

直播吧6月15日讯RAC1记者RogerSaperas报道了关于巴萨引进基米希和维克

2023法网:红土传奇,恒久留存|焦点滚动

凭借这一冠,她不但成为2007年的海宁以来首位在罗兰·加洛斯实现卫冕的

国际篮联盛赞中国2米16小将 U19男篮世界杯重点关注

北京时间6月15日消息,2023年U19男篮世界杯将于6月24日-7月2日在匈牙利

布阵大漠,开火!

文|朱志强、宋照康、孙泽志图|翟一龙、周杰、冉思远、李进平大漠砺精兵

遥控直升机原理图解_遥控直升机原理 全球消息

1、无人直升机的构造和旋翼飞行器差不多,其工作原理和旋翼式直升机也

天天时讯:后梅罗时代开启!“新双骄”诞生统治足坛OR群雄逐鹿?

显然,“梅罗时代”的诞生离不开当年西甲的繁荣,皇马与巴萨两大豪门的

时讯:英超夏转窗口开启 切尔西拒绝曼联对芒特报价 姆巴佩牵一发动全身

6月15日消息,利物浦3500万英镑搞定麦卡利斯特,这给今夏转会打了一个

星芒在燃,雷柏游戏携手武汉eSarPro征战2023KPL夏季赛

并肩作战是热爱的【无限】可能,在桀然闪耀的星途之行中,操控【无线】

国足23人球衣号码:艾克森重披9号战袍 武磊7号

搜狐体育消息,北京时间6月15日,据媒体人丁旭消息,国足23名球员的球

天天微头条丨安源消防为高考送清凉

为传承弘扬“奉献、友爱、互助”的志愿精神,全力为考生以及陪考的家长

全球新动态:纷玩岛:对五月天演唱会异常订单进行退款退票处理

6月12日,五月天演唱会官方售票平台纷玩岛APP发布公告称,纷玩岛APP工

短讯!船舶电子电气技术可以考哪些大学 船舶电子电气技术学校排名

大连航运职业技术学院船舶电子电气技术(专科类)专业介绍各位同学大家好

广东连南:开展大数据应用工具实操测试|微速讯

正义网清远6月14日电(通讯员唐艳)为检验近期开展的大数据应用工具实操

广东英德:立足检察职能全面开展家庭教育指导 世界新消息

正义网清远6月14日电(通讯员高海兰)近年来,广东省英德市检察院在开展未

6月5日有6只新债招标_世界快资讯

6月5日有6只新债招标。

我家的女人

1、百度文库直接下载·······················...

机械助力小麦抢收 科技显著提升作业效率 当前观点

(点击图片查看视频)日前,山西临汾尧都区的38万亩小麦进入夏收高峰期

环球速读:哪里可以处理违章扣分

处理违章扣分应当要到车辆辖区车管所或交警队处理,具体需要携带的资料

汽车违章罚款扣分网上怎么交罚款的-环球热议

汽车违章罚款扣分网上交罚款的的方式是用车主身份证在交管12123官方应

世界最新:拼好货商城app下载(拼好货商城)

来为大家解答以上问题,拼好货商城app下载,拼好货商城很多人还不知道

“热血英雄”为“健康天津”献红色保障线

6月14日是第20个世界献血者日,从天津市血液中心获悉,天津市已连续12

2023年义务教育阶段招生政策公布

  6月13日,市教育局发布《关于做好2023年普通中小学招生入学工作的

环球观点:华夏鼎利债券每10份派发0.65元 单日累计申购不超过100万元

华夏鼎利债券6月14日每10份分红0 65元。本次分红为2023年度的第1次分红

全国农技中心开展农作物病虫害绿色防控技术培训

为加快农作物病虫害绿色防控关键技术的推广应用,促进农药减量增效,保

皇马疯狂1夜,弃姆总选33岁老将,新帅将就位,蓝军巴黎自食苦果_每日短讯

除了放弃姆巴佩之外,皇家马德里还拒绝了来自切尔西的“敲竹杠”,老佛

记者:AC米兰和一些西班牙球队均有意免签卢卡-罗梅罗

直播吧6月15日讯西班牙媒体Relevo的记者MatteoMoretto报道,AC米兰和一

膏药代加工发展迎来新机遇 仙佑集团质量严把关受关注

作为国内知名的膏药代加工企业之一,仙佑集团已经拥有了多年的历史,一直致力于膏药代加工、膏药加工、...

ct是什么单位外贸 ct是什么单位 消息

1、ct是宝石重量单位,克拉。2、1CT=200mg,多用于称量宝石,克拉(Ct)

大玻璃需要安装防护栏吗 快资讯

很多家庭为了安全起见,会安装大玻璃,不过对于楼层较高的业主来说,最

壮观!南宁上万株铁树开花了|每日短讯

苏铁植物起源于苏铁植物还保存得下来所以它被称为植物界的活化石最近,

精彩放送

全球看点:《广西“最美撑伞女孩”找到了》后续:她用奖金购买慰问品回报乡邻

每日精选:用法治力量推动湿地保护高质量发展 2023年广西环保世纪行宣传活动启动

重实效 强实干 抓落实丨广州市国资国企入桂开展产业合作 全球讯息

前5月广西外贸增速快于全国48.5个百分点 进出口2875.1亿元,同比增长53.2%|全球短讯

湖南九条举措排查整治重大事故隐患

环球观速讯丨面试季,太狠了!!!

形容烟花的词语有哪些?古人形容烟花漂亮诗词合集来了

房地产工作总结怎么写?房地产新入职早上自我介绍来了

浪漫情书范本来了!一段简短而深情的告白看这里

激励人心的电影金句有哪些?人生必看的十大励志电影一览

表达父爱的古诗有哪些?《我的父亲》艾青原文看这里

卖电视机的广告文案怎么写?品质宣传经典广告词都在这儿

最新快讯!拒续约后,姆巴佩爆猛料,梅西平反,巴黎演砸,皇马卷入,太黑暗

当前视点!记者:AC米兰和一些西班牙球队均有意免签卢卡-罗梅罗

晚上8点,国际足联重磅官宣!24天后实行2大变革,K联赛先做榜样 热议

曝赵泰隆孙喆加盟广西威壮 扣篮+三分双料冠军离开CBA

群众一句“有曹睿在,我心里就踏实”,是对他最好的褒奖

中新网丨张家川:派出所打造矛盾纠纷解决新机制专解群众烦心事 每日消息

《暗黑破坏神4》6月14日热修内容汇总

盛夏游太行,暑中觅清凉!这才是打开八泉峡的正确方式

电脑怎么添加打印机 电脑怎么添加打印机扫描功能 世界关注

瑞星安全浏览器怎么卸载删除 瑞星安全网址导航卸载-天天观点

全球简讯:怎样手机开店 金元宝微店手机开店软件使用图文教程

百度地图App中怎么查看卫星图和热力图 百度地图怎么看卫星地图实景 快看

世界快资讯:手机淘宝中商品怎么免费试用

父亲三周年祭奠对联有哪些?祭祀对联大全来了

高一英语语法知识点总结来了!英语语法基础知识大全笔记一览

6月14日上海期货交易所锌锭仓单统计

离休和退休的区别有哪些?离退休人员死亡一次性抚恤金发放办法看这里

关于篮球的论文题目有哪些?篮球技术论文3000字看这里

新消息丨黑科技护肤让你更“好看”

罗体:国米关注马马达什维利,但维卡里奥仍是奥纳纳替代者首选

快播:2023印尼羽毛球公开赛:刘雨辰、欧烜屹逆转晋级

世界讯息:拒离队!巴萨乱了!亏5600万,哈维沉默,下最后通牒,拿他没办法

聚焦:温网奖金再创新高,德约科维奇若冠军将拿2000万,冲击第24冠!

世界今头条!合同盖合同专用章有效吗

世界动态:检车过期多久要处罚新规定了

2023年西安市中考政策发布 2所初中学业水平考试被新纳入城六区管理-焦点精选

重点聚焦!稳经济 促发展 强信心|简化行政审批 提升服务效率

当前要闻:测血糖用的稀有GBA卡带,被收录进了游戏历史基金会

罗体:罗马近日将官宣恩迪卡 米兰巴黎试图截胡但穆帅说服了球员 环球最资讯

今头条!意甲媒体透露:楚克乌泽已同意加盟米兰,双方就合同问题正在沟通

【独家焦点】记者:拜仁将和曼联竞争金玟哉,也在关注凯塞多

天天实时:马琳下课!孔令辉回归?李隼发言透露态度,国乒球迷争议不断

全球微速讯:省级名单!宜昌4个乡镇35个村入选

美联储暂停加息 美股收盘涨跌不一 科技股多数上涨英伟达大涨超4%|今日观点

最新战报!印尼公开赛第二比赛日,中国队7胜3负,陈雨菲继续抗日

克罗斯不点名批评阿扎尔:他花了皇马很多钱 但职业生涯已经完蛋了

凯恩又走不成了?皇马兴趣降低,热刺也不想卖给英超球队

当前速递!何卓佳与周恺险胜韩国组合,孙颖莎好友打入正赛,郭雨涵横扫对手

联系我们:55 16 53 8@qq.com
关于我们| 联系方式| 版权声明| 供稿服务| 友情链接

塞北网 版权所有,未经书面授权禁止使用

京ICP备2021034106号-10 营业执照公示信息

Copyright©2008-2020 By www.saibeinews.com All Rights Reserved