工业级Netty网关,京东是如何架构的?

这篇具有很好参考价值的文章主要介绍了工业级Netty网关,京东是如何架构的?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

说在前面

在40岁老架构师 尼恩的读者交流群(50+)中,很多小伙伴拿到一线互联网企业如阿里、网易、有赞、希音、百度、滴滴的面试资格。

最近,尼恩指导一个小伙伴简历,写了一个《高并发网关项目》,此项目帮这个小伙拿到 字节/阿里/微博/汽车之家 面邀, 所以说,这是一个牛逼的项目。

为了帮助大家拿到更多面试机会,拿到更多大厂offer。

尼恩决定:给大家出一章视频介绍这个项目的架构和实操,《33章:10Wqps 高并发 Netty网关架构与实操》,预计月底发布。然后,提供一对一的简历指导,这里简历金光闪闪、脱胎换骨。

《33章:10Wqps 高并发 Netty网关架构与实操》 海报如下:

工业级Netty网关,京东是如何架构的?,面试,架构,java,面试,后端,gateway,系统架构

配合《33章:10Wqps 高并发 Netty网关架构与实操》, 尼恩会梳理几个工业级、生产级网关案例,作为架构素材、设计的素材。

前面梳理了

  • 《日流量200亿,携程网关的架构设计》
  • 《千万级连接,知乎如何架构长连接网关?》
  • 《日200亿次调用,喜马拉雅网关的架构设计》
  • 《100万级连接,爱奇艺WebSocket网关如何架构》
  • 《亿级长连接,淘宝接入层网关的架构设计》
  • 《单体120万连接,小爱网关如何架构?》
  • 《100万级连接,石墨文档WebSocket网关如何架构?》
  • 《2亿用户,B站API网关如何架构?》

除了以上的8个案例,这里,尼恩又找到一个漂亮的生产级案例:《工业级Netty网关,京东是如何架构?

注意,这又一个非常 牛逼的工业级、生产级网关案例

这些案例,并不是尼恩的原创。这些案例,仅仅是尼恩在《33章:10Wqps 高并发 Netty网关架构与实操》备课的过程中,在互联网查找资料的时候,收集起来的,供大家学习和交流使用。

《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到公号【技术自由圈】获取

工业级Netty网关,京东是如何架构?

作者:张松然,京东商家研发部架构师

京麦是京东商城为其商家提供的一款后台管理工具,它能够让商家在不登录后台的情况下生成订单,快速完成订单下载和发货流程。这与淘宝的旺旺商家版(现已更名为淘宝千牛)类似。

本文主要阐述了京麦 TCP 网关的技术架构以及 Netty 的应用实践。

京东京麦商家管理平台从 2014 年开始搭建网关,从 HTTP 网关逐步升级为 TCP 网关。到了 2016 年,基于 Netty4.x+Protobuf3.x 技术,京麦构建了一个高可用、高性能和高稳定的 TCP 长连接网关,支持 PC 端和应用程序的上下行通信。

早期的京麦主要依靠 HTTP 和 TCP 长连接来发送消息通知,而没有将其应用于 API 网关。

然而,随着对 NIO 技术的深入了解和对 Netty 框架的熟练掌握,以及对系统通信稳定性要求的提高,京麦开始尝试运用 NIO 技术来实现 API 请求调用。这一设想在 2016 年终于实现,并成功支持业务运营。

得益于采用了 TCP 长连接容器、Protobuf 序列化、服务泛化调用框架等多种优化措施,京麦的 TCP 网关性能比 HTTP 网关提升了 10 倍以上,稳定性也显著超过了 HTTP 网关。

1、TCP网关的网络结构

通过 Netty 构建京麦 TCP 网关的长连接容器,作为网关接入层提供服务 API 请求调用。

客户端通过域名 + 端口访问 TCP 网关,不同域名对应不同运营商的 VIP,VIP 发布在 LVS 上,LVS 将请求转发给后端的 HAProxy,然后由 HAProxy 将请求转发给后端的 Netty 的 IP+Port。

主要,这个是高并发接入层的标准架构

LVS 将请求转发给后端的 HAProxy,经过 LVS 的请求,但响应是由 HAProxy 直接返回给客户端,这就是 LVS 的 DR 模式。

工业级Netty网关,京东是如何架构的?,面试,架构,java,面试,后端,gateway,系统架构

注意:请点击图像以查看清晰的视图!

LVS+Keepalived(DR模式)的深入学习,尼恩给大家写了一篇非常详细的、全面的文章, 建议大家收藏起,好好掌握:

《10Wqps网关接入层,LVS+Keepalived(DR模式)如何搭建?》

2、TCP网关长连接容器架构

TCP网关的核心组件是Netty,而Netty的NIO模型是Reactor反应堆模型(Reactor相当于有分发功能的多路复用器Selector)。

每一个连接对应一个Channel(多路指多个Channel,复用指多个连接复用了一个线程或少量线程,在Netty指EventLoop),一个Channel对应唯一的ChannelPipeline,多个Handler串行的加入到Pipeline中,每个Handler关联唯一的ChannelHandlerContext。TCP网关长连接容器的Handler就是放在Pipeline的中。

我们知道TCP属于OSI的传输层,所以建立Session管理机制构建会话层来提供应用层服务,可以极大的降低系统复杂度。所以,每一个Channel对应一个Connection,一个Connection又对应一个Session,Session由Session Manager管理,Session与Connection是一一对应,Connection保存着ChannelHandlerContext (ChannelHanderContext可以找到Channel), Session通过心跳机制来保持Channel的Active状态。

每一次Session的会话请求(ChannelRead)都是通过Proxy代理机制调用Service层,数据请求完毕后通过写入ChannelHandlerConext再传送到Channel中。

数据下行主动推送也是如此,通过Session Manager找到Active的Session,轮询写入Session中的ChannelHandlerContext,就可以实现广播或点对点的数据推送逻辑。如下图所示。

工业级Netty网关,京东是如何架构的?,面试,架构,java,面试,后端,gateway,系统架构

注意:请点击图像以查看清晰的视图!

京麦TCP网关使用Netty Channel进行数据通信,使用Protobuf进行序列化和反序列化,每个请求都将被封装成Byte二进制字节流,在整个生命周期中,Channel保持长连接,而不是每次调用都重新创建Channel,达到链接的复用。

我们接下来来看看基于Netty的具体技术实践。

3、TCP网关Netty Server的IO模型

具体的实现过程如下

  • 1)创建ServerBootstrap,设定BossGroup与WorkerGroup线程池;
  • 2)bind指定的port,开始侦听和接受客户端链接(如果系统只有一个服务端port需要监听,则BossGroup线程组线程数设置为1);
  • 3)在ChannelPipeline注册childHandler,用来处理客户端链接中的请求帧。

4、TCP网关的线程模型

TCP网关使用Netty的线程池,共三组线程池,分别为BossGroup、WorkerGroup和ExecutorGroup。

其中,BossGroup用于接收客户端的TCP连接,WorkerGroup用于处理I/O、执行系统Task和定时任务,ExecutorGroup用于处理网关业务加解密、限流、路由,及将请求转发给后端的抓取服务等业务操作。

工业级Netty网关,京东是如何架构的?,面试,架构,java,面试,后端,gateway,系统架构

注意:请点击图像以查看清晰的视图!

NioEventLoop是Netty的Reactor线程,其角色

  • 1)Boss Group:作为服务端Acceptor线程,用于accept客户端链接,并转发给WorkerGroup中的线程;
  • 2)Worker Group:作为IO线程,负责IO的读写,从SocketChannel中读取报文或向SocketChannel写入报文;
  • 3)Task Queue/Delay Task Queu:作为定时任务线程,执行定时任务,例如链路空闲检测和发送心跳消息等。

5、TCP网关执行时序图

工业级Netty网关,京东是如何架构的?,面试,架构,java,面试,后端,gateway,系统架构

注意:请点击图像以查看清晰的视图!

如上图所示,其中步骤一至步骤九是Netty服务端的创建时序,步骤十至步骤十三是TCP网关容器创建的时序。

步骤一:创建ServerBootstrap实例,ServerBootstrap是Netty服务端的启动辅助类。

步骤二:设置并绑定Reactor线程池,EventLoopGroup是Netty的Reactor线程池,EventLoop负责所有注册到本线程的Channel。

步骤三:设置并绑定服务器Channel,Netty Server需要创建NioServerSocketChannel对象。

步骤四:TCP链接建立时创建ChannelPipeline,ChannelPipeline本质上是一个负责和执行ChannelHandler的职责链。

步骤五:添加并设置ChannelHandler,ChannelHandler串行的加入ChannelPipeline中。

步骤六:绑定监听端口并启动服务端,将NioServerSocketChannel注册到Selector上。

步骤七:Selector轮训,由EventLoop负责调度和执行Selector轮询操作。

步骤八:执行网络请求事件通知,轮询准备就绪的Channel,由EventLoop执行ChannelPipeline。

步骤九:执行Netty系统和业务ChannelHandler,依次调度并执行ChannelPipeline的ChannelHandler。

步骤十:通过Proxy代理调用后端服务,ChannelRead事件后,通过发射调度后端Service。

步骤十一:创建Session,Session与Connection是相互依赖关系。

步骤十二:创建Connection,Connection保存ChannelHandlerContext。

步骤十三:添加SessionListener,SessionListener监听SessionCreate和SessionDestory等事件。

6、TCP网关源码分析

6.1 Session管理

Session是客户端与服务端建立的一次会话链接,会话信息中保存着SessionId、连接创建时间、上次访问事件,以及Connection和SessionListener,在Connection中保存了Netty的ChannelHandlerContext上下文信息。Session会话信息会保存在SessionManager内存管理器中。

创建Session的源码

@Override
public synchronized Session createSession(String sessionId, ChannelHandlerContext ctx){
    Session session = sessions.get(sessionId);
    if (session != null){
        session.close();
    }
    session = new ExchangeSession();
    session.setSessionId(sessionId);
    session.setValid(true);
    session.setMaxInactiveInterval(this.getMaxInactiveInterval());
    session.setCreationTime(System.currentTimeMillis());
    session.setLastAccessedTime(System.currentTimeMillis());
    session.setSessionManager(this);
    session.setConnection(createTcpConnection(session, ctx));
    for (SessionListener listener : essionListeners){
        session.addSessionListener(listener);
    }
    return session;
}

通过源码分析,如果Session已经存在销毁Session,但是这个需要特别注意,创建Session一定不要创建那些断线重连的Channel,否则会出现Channel被误销毁的问题。因为如果在已经建立Connection(1)的Channel上,再建立Connection(2),进入session.close方法会将cxt关闭,Connection(1)和Connection(2)的Channel都将会被关闭。在断线之后再建立连接Connection(3),由于Session是有一定延迟,Connection(3)和Connection(1/2)不是同一个,但Channel可能是同一个。

所以,如何处理是否是断线重练的Channel,具体的方法是在Channel中存入SessionId,每次事件请求判断Channel中是否存在SessionId,如果Channel中存在SessionId则判断为断线重连的Channel,代码如下图所示。

private String getChannelSessionHook(ChannelHandlerContext ctx){
    return ctx.channel().attr (Constants.SERVER_SESSION_HOOK).get();
}

private void setChannelSessionHook(ChannelHandlerContext ctx, String sessionId){
    ctx.channel().attr(Constants.SERVER_SESSION_HOOK).set(sessionId);
}

6.2 心跳

心跳用于检测保持连接的客户端是否仍然活跃,客户端每隔一段时间发送一次心跳包到服务端,服务端收到心跳后更新 Session 的最后访问时间。

在服务端,长连接会话检测通过轮询 Session 集合来判断最后访问时间是否过期,如果过期,则关闭 Session 和 Connection,包括从内存中删除,同时注销 Channel 等。如下面代码所示。

Session session = tcpSessionManager.createSession(wrapper.getSessionId(), ctx);
session.addSessionListener(tcpHeartbeatListener);
session.connect();

tcpSessionManager.addSession(session);

通过源码分析,在每个Session创建成功之后,都会在Session中添加TcpHeartbeatListener这个心跳检测的监听,TcpHeartbeatListener是一个实现了SessionListener接口的守护线程,通过定时休眠轮询Sessions检查是否存在过期的Session,如果轮训出过期的Session,则关闭Session。如下面代码所示。

public void checkHeartBeat(){
    Session[] sessions = tcpSessionManager.getSessions();
    for (Session session : sessions){
        if (session.expire()){
            session.close();
            logger.info("heart is expire, clear sessionId:"+ session.getSessionId());
        }
    }
}

同时,注意到session.connect方法,在connect方法中会对Session添加的Listeners进行添加时间,它会循环调用所有Listner的sessionCreated事件,其中TcpHeartbeatListener也是在这个过程中被唤起。如下面代码所示。

private void addSessionEvent(){
    SessionEvent event = new SessionEvent(this);
    for (SessionListener listener : listeners){
        try{
            listener.sessionCreated(event);
            logger.info("SessionListener" + listener + ".sessionCreated() is invoked successfully!");
        } catch (Exception e){
            logger.error("addSessionEvent error.", e);
        }
    }
}

6.3 数据上行

数据上行特指从客户端发送数据到服务端,数据从ChannelHander的channelRead方法获取数据。数据包括创建会话、发送心跳、数据请求等。这里注意的是,channelRead的数据包括客户端主动请求服务端的数据,以及服务端下行通知客户端的返回数据,所以在处理object数据时,通过数据标识区分是请求-应答,还是通知-回复。如下面代码所示。

public void channelRead(ChannelHandlerContext ctx, Object o) throws Exception{
    try{
        if (o instanceof MessageBuf.JMTransfer) {
            SystemMessage sMsg = generateSystemMessage(ctx);
            MessageBuf.JMTransfer message = (MessageBuf.JMTransfer) o;
            //inbound
            if(message.getFormat() == SEND) {
                MessageWrapper wrapper = proxy.invoke(sMsg, message);
                if (wrapper != null)
                    this.receive(ctx, wrapper);
            }
            // outbound
            if (message.getFormat() == REPLY) {
                notify.reply(message);
            }
        }else{
            logger. warn("TcpServerHandler channelRead message is not proto.");
        }
    }catch (Exception e) {
        logger.error("TcpServerHandler TcpServerHandler handler error.", e);
        throw e;
    }
}

6.4 数据下行

数据下行通过MQ广播机制到所有服务器,所有服务器收到消息后,获取当前服务器所持有的所有Session会话,进行数据广播下行通知。

如果是点对点的数据推送下行,数据也是先广播到所有服务器,每个服务器判断推送的端是否是当前服务器持有的会话,如果判断消息数据中的信息是在当前服务,则进行推送,否则抛弃。如下面代码所示。

private Notifyfuture doSendAsync(long seq, Messagelrapper wrapper, int timeout) throws Exception {
    if (wrapper == null) {
        throw new Exception("wrapper cannot be null.");
    }
    String sessionId = wrapper.getSessionId();
    if (StringUtils.isBlank(sessionId)) {
        throw new Exception("sessionId cannot be null.")
    }
    if (tcpConnector.exist sessionId)) {
        //start.
        final NotifyFuture future = new NotifyFuture(timeout);
        this.futureMap.put(seq, future);
        tcpConnector.send(sessionId, wrapper.getBody());
        future.setSentTime(System.currentTimeMillis()); // 置为已发送return future.
    } else {
        // tcpConnector not exist sessionId
        return null;
    }
}

通过源码分析,数据下行则通过NotifyProxy的方式发送数据,需要注意的是Netty是NIO,如果下行通知需要获取返回值,则要将异步转同步,所以NotifyFuture是实现java.util.concurrent.Future的方法,通过设置超时时间,在channelRead获取到上行数据之后,通过seq来关联NotifyFuture的方法。如下面代码所示。

public void reply (MessageBuf.JMTransfer message) throws Exception {
    try {
        long seg = message.getSeq();
        final NotifyFuture future = this.futureMap.get(seg);
        if (future != null){
            future.setSuccess(true);
            futureMap.remove(seg);
        }
    } catch (Exception e) {
        throw e;
    }
}

下行的数据通过TcpConnector的send方法发送,send方式则是通过ChannelHandlerContext的writeAndFlush方法写入Channel,并实现数据下行,这里需要注意的是,之前有另一种写法就是cf.await,通过阻塞的方式来判断写入是否成功,这种写法偶发出现BlockingOperationException的异常。如下面代码所示。

ChannelFuture cf = cxt.writeAndFlush(message);
cf.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws PushException{
        if (future.isSuccess()) {
            logger.debug("send success.");
        } else {
            throw new PushException("Failed to send message.");
        }
        Throwable cause = future.cause() ;
        if (cause != null) {
            throw new PushException(cause);
        }
    }
});

使用阻塞获取返回值的写法

boolean success = true;
boolean sent = true;
int timeout = 60;
try {
    ChannelFuture cf = cxt.write(message);
    cxt.flush();
    if (sent){
        success = cf.await(timeout);
    }
    if (cf.isSuccess()) {
        logger.debug("send success.");
    }
    Throwable cause = cf.cause();
    if (cause != null) {
        this.fireError(new PushException(cause));
    }
} catch (Throwable e) {
    this.fireError(new PushException("Failed to send message, cause:" + e. getMessage(),e));
}

关于BlockingOperationException的问题我在StackOverflow进行提问,非常幸运的得到了Norman Maurer(Netty的核心贡献者之一)的解答

工业级Netty网关,京东是如何架构的?,面试,架构,java,面试,后端,gateway,系统架构

最终结论大致分析出,在执行write方法时,Netty会判断current thread是否就是分给该Channe的EventLoop,如果是则行线程执行IO操作,否则提交executor等待分配。

当执行await方法时,会从executor里fetch出执行线程,这里就需要checkDeadLock,判断执行线程和current threads是否时同一个线程,如果是就检测为死锁抛出异常BlockingOperationException。

说在最后:有问题可以找老架构取经

架构之路,充满了坎坷

架构和高级开发不一样 , 架构问题是open/开放式的,架构问题是没有标准答案的

正由于这样,很多小伙伴,尽管耗费很多精力,耗费很多金钱,但是,遗憾的是,一生都没有完成架构升级

所以,在架构升级/转型过程中,确实找不到有效的方案,可以来找40岁老架构尼恩求助.

前段时间一个小伙伴,他是跨专业来做Java,现在面临转架构的难题,但是经过尼恩几轮指导,顺利拿到了Java架构师+大数据架构师offer 。所以,如果遇到职业不顺,找老架构师帮忙一下,就顺利多了。

推荐阅读

《百亿级访问量,如何做缓存架构设计》

《多级缓存 架构设计》

《消息推送 架构设计》

《阿里2面:你们部署多少节点?1000W并发,当如何部署?》

《美团2面:5个9高可用99.999%,如何实现?》

《网易一面:单节点2000Wtps,Kafka怎么做的?》

《字节一面:事务补偿和事务重试,关系是什么?》

《网易一面:25Wqps高吞吐写Mysql,100W数据4秒写完,如何实现?》

《亿级短视频,如何架构?》

《炸裂,靠“吹牛”过京东一面,月薪40K》

《太猛了,靠“吹牛”过顺丰一面,月薪30K》

《炸裂了…京东一面索命40问,过了就50W+》

《问麻了…阿里一面索命27问,过了就60W+》

《百度狂问3小时,大厂offer到手,小伙真狠!》

《饿了么太狠:面个高级Java,抖这多硬活、狠活》

《字节狂问一小时,小伙offer到手,太狠了!》

《收个滴滴Offer:从小伙三面经历,看看需要学点啥?》

《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF,请到下面公号【技术自由圈】取↓↓↓文章来源地址https://www.toymoban.com/news/detail-725065.html

到了这里,关于工业级Netty网关,京东是如何架构的?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 68元工业级双核A7,全新T113核心板震撼上市!

    万象奥科全新T113核心板震撼上市,仅68元、工业级品质、双核A7处理器、外设丰富,诠释极致性价比! 参数速递 1.全志T113工业级处理器,主频1.2GHz 2.双核Cortex-A7+玄铁C906 RISC-V + HiFi4 DSP异构多核 3.内存128MB/256MB/512MB,存储256MB/8GB 4.支持Video Decoding H.265/H.264,Video Encoding JPEG/MJPEG

    2024年02月06日
    浏览(46)
  • 工业级开源facechain人物写真sd-webui插件使用方式

    facechain人物写真应用自8月11日开源了第一版证件照生成后。目前在github(https://github.com/modelscope/facechain)上已有近6K的star,论文链接:FaceChain: A Playground for Identity-Preserving Portrait Generation:https://arxiv.org/abs/2308.14256。facechain社区非常活跃,fork、commit数都很大,相应截图如下

    2024年01月20日
    浏览(92)
  • 善睐物联:工业级机器人物联网卡为数据采集提供便利

    工业4.0的推进 , 智能制造已经逐渐成为制造业的核心竞争力。在这个变革的时代 , 工业级机器人作为智能制造的重要一环 , 其地位和作用愈发突出。而物联网技术的快速发展 , 使得工业级机器人与互联网的连接成为可能 , 进一步推动了机器人的智能化和网络化。其中 , 物联网

    2024年02月19日
    浏览(49)
  • ToB还是ToC?工业级与消费级AR眼镜都能干什么?

    随着科技的飞速发展,增强现实(AR)技术逐渐融入我们的日常生活。我国AR眼镜消费市场分为消费级和工业级应用。其中消费级主要分为游戏、影视、直播以及社交购物与旅游;工业级主要应用于医疗、汽车、工业、船舶、电力和仓储等专业领域。我们从以下几个方面来介绍

    2024年02月04日
    浏览(46)
  • 工业级高性能3D模型渲染引擎,专注于3D Web轻量化!

    一、技术概览 HOOPS Communicator 是一个SDK,用于在Web浏览器中开发3D工程应用程序,重点在于: 完全访问工程数据 使用方便 快速发展 高性能可视化 快速模型流 灵活使用和部署 点击此处获取3D模型轻量化及格式转换解决方案 它的主要组件是 HOOPS Web查看器,这是一个功能强大

    2024年02月07日
    浏览(32)
  • 【论文阅读】Paraformer工业级非自回归端到端语音识别模型

    论文下载 目前ASR常用的Transformer模型虽然效果比较好,但是因为引入了自回归的解码器,所以计算相对效率低一些。为了加速推理,设计了非自回归模型并行生成识别结果,比如单步自回归模型。然而由于输出标签之间的独立性假设,单步自回归模型的效果相比自回归模型会

    2024年02月09日
    浏览(49)
  • ARM/X86工业级数据采集 (DAQ) 与控制产品解决方案

    I/O设备,包括信号调理模块、嵌入式PCI/PCIE卡、便携式USB模块、DAQ嵌入式计算机、模块化DAQ系统,以及DAQNavi/SDK软件开发包和DAQNavi/MCM设备状态监测软件。 工业I/O产品适用于各种工业自动化应用,从机器自动化控制、测试测量到设备状态监测。 工业级数据采集 (DAQ) 与控制产品

    2024年02月09日
    浏览(54)
  • 3D模型格式转换工具HOOPS Exchange对工业级3D产品HOOPS的支持与应用

    一、概述 HOOPS Exchange是一套高性能模型转换软件库,可以给软件提供强大的模型的导入和导出功能,我们可以将其单独作为转换工具使用,也可以将其集成到自己的软件中。 同样,HOOPS 的其它产品,也离不开HOOPS Exchange的支持,它们也需要HOOPS Exchange为其提供模型的导入和导

    2024年02月07日
    浏览(44)
  • 工业级成熟航运港口人工智能产品全球前三船公司及港口码头落地,中国上海人工智能独角兽中集飞瞳全球应用最广规模最大最先进港航AI企业

    中国上海人工智能独角兽CIMCAI中集飞瞳,是全球应用落地最广,规模最大的港口航运人工智能高科技企业。中国人工智能独角兽CIMCAI中集飞瞳 巅峰产品行业第一,产品在全球港区及集装箱枢纽规模投产包括:全球港口码头智能闸口;全球港口岸边卸/装船;全球航运船公司;

    2024年02月05日
    浏览(45)
  • 如何优化工业5G网关的网络信号

    工业5G网关,通常是指支持5G网络,具有高速率、低时延、广接入等特点的高性能工业物联网智能网关,这类网关具有强大的设备接入能力、通信协议转换、运算处理能力、联动控制能力,有助于提升工业物联网整体通信效率,实现生产管理能力和水平的飞跃。 要发挥工业5

    2024年02月08日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包