1. Netty核心功能与线程模型详解

2023-11-15


本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。

课程内容:

01、Netty核心组件快速了解
02、Hello,Netty!
03、深入理解Channel、EventLoop(Group)
04、深入理解EventLoop和EventLoopGroup
05、ChannelHandler和它的适配器
06、ChannelPipeline辨析
07、ChannelHandlerContext辨析
08、用Netty解决TCP粘包/半包
09、Netty中的编解码器
10、实战Netty快速实现Web服务器
11、序列化框架Netty集成实战
12、Netty中的单元测试

1. 认识Netty

Netty 4.1.42.Final版本进行

<dependency>
   <groupId>io.netty</groupId>
   <artifactId>netty-all</artifactId>
   <version>4.1.42.Final</version>
   <scope>compile</scope>
</dependency>

基于Netty的知名项目

  • 数据库:Cassandra
  • 大数据处理:Spark、Hadoop
  • 消息中间件:RocketMQ
  • 检索:Elasticsearch
  • 框架:gRPC、Apache Dubbo、Spring5 WebFlux
  • 分布式协调器:ZooKeeper

Netty的优势

  1. API使用简单,开发门槛低;
  2. 功能强大,预置了多种编解码功能,支持多种主流协议;
  3. 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
  4. 性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优;
  5. 成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦恼;
  6. 社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会加入;
  7. 经历了大规模的商业应用考验,质量得到验证。

为什么不用Netty5 ?
Netty5有两个重大改变:支持AIO(异步编程,就是要回调),将线程模型改成了favk/join。通过测试发现其实对性能的提高不是很大,几乎不明显。

因为Netty5还处于Alpha1版本,几乎停止维护了

为什么Netty使用NIO而不是AIO?
Linux下的AIO是一个伪AIO,Linux下的AIO性能也不如NIO的强。

为什么不用Mina ?

其实根据诞生关系是Netty的诞生稍微比Mina早了那么一点,这个Netty与Mina是同一个作者,这个作者在完成了Mina之后又投身于Netty种了,相当于Mina处于缺少维护的状态。并且Netty之后发布的版本的性能都比Mina优秀,因此就没有使用Mina

2. 第一个Netty程序

Netty的核心组件初步认识

  • Bootstrap、EventLoop(Group)、Channel
  • 事件和ChannelHandler、ChannelPipeline
  • ChannelFuture

Bootstrap是Netty框架启动类和主入口类。Bootstrap分为服务端的与客户端的,

在 Netty 中,Channel 是网络通信的基本概念和抽象。它代表着一个开放的连接,可以进行数据的读写和事件的处理。
具体来说,Channel 可以理解为应用程序与网络套接字之间的通道,它提供了以下主要功能:

  1. 数据的读写:通过 Channel,应用程序可以从网络中读取数据或将数据写入网络。它提供了高效的读写操作,支持异步非阻塞的 I/O 操作。可以通过 Channel 的读写方法来实现数据的传输和交换。
  2. 事件的处理:Channel 是一个事件驱动的实体,它可以处理各种网络事件,例如连接建立、数据到达、写就绪等。通过注册不同类型的事件处理器(ChannelHandler),应用程序可以根据需要处理这些事件,执行相应的逻辑处理。
  3. 状态的管理:Channel 提供了连接的状态管理,包括打开、关闭、活动状态等。应用程序可以通过 Channel 的状态变化事件进行相应的处理,例如连接建立或断开时的处理操作。
  4. 通道的配置:Channel 可以进行各种配置,如设置缓冲区大小、接收和发送缓冲区的设置、超时设置等。通过配置 Channel,可以调整网络通信的性能和行为。
    总之,Channel 在 Netty 中扮演着非常重要的角色,它是应用程序与网络之间的桥梁,提供了数据读写、事件处理和状态管理等功能。通过使用 Channel,开发者可以方便地进行网络编程,实现高性能和可靠的网络通信。

EventLoop(Group)
在 Netty 中,EventLoop 是用于处理 I/O 事件和任务的线程。它是 Netty 异步事件驱动的核心组件,负责管理 Channel 的生命周期、处理事件和执行任务。
EventLoop 在 EventLoopGroup 中扮演着重要角色,而 EventLoopGroup 则是一组维护和管理 EventLoop 的容器。一般情况下,一个应用程序只需要一个 EventLoopGroup,而其中的多个 EventLoop 可以同时处理多个 Channel 的 I/O 操作。
EventLoop 的主要功能包括:

  1. I/O 事件的处理:EventLoop 负责处理 Channel 的 I/O 事件,如数据的读写、连接的建立和关闭等。当一个 I/O 事件发生时,EventLoop 会调用相应的 ChannelHandler 处理事件,并触发相应的回调方法。通过事件驱动的方式,实现了高效的异步非阻塞 I/O。
  2. 任务的执行:除了处理 I/O 事件外,EventLoop 还可以执行用户自定义的任务。这些任务可以是一些耗时的计算、定时任务、事件调度等。通过将任务提交给 EventLoop,可以在事件循环中异步执行,避免了阻塞主线程的情况。
  3. 定时任务的处理:EventLoop 提供了定时任务的调度和执行,支持在一定的时间间隔或固定的时间点执行任务。这对于需要定时执行一些操作的应用场景非常有用,例如心跳检测、定时数据刷新等。

需要注意的是,EventLoop 在内部维护一个事件循环(Event Loop),它会不断地轮询 I/O 事件和任务,以便及时处理。每个 EventLoop 都有一个独立的线程来执行这个循环,确保了并发的安全性。

总之,EventLoop 是 Netty 中负责处理 I/O 事件和任务的线程,通过事件驱动的方式实现了高效的异步非阻塞 I/O。它在 EventLoopGroup 中起着重要的作用,为应用程序提供了高性能和可扩展的网络编程能力。

在 Netty 中,事件(Event)、ChannelHandler 和 ChannelPipeline 是实现高效网络通信的重要组件。

  1. 事件(Event):事件是 Netty 中的核心概念,它代表了网络通信过程中发生的各种状态和操作,如连接建立、数据到达、数据写就绪等。Netty 使用事件来驱动整个网络通信过程,通过触发和处理事件来实现异步的、非阻塞的网络通信。
  2. ChannelHandler:ChannelHandler 是用于处理事件和执行业务逻辑的组件。它定义了一组回调方法,如 channelRead()、channelActive()、channelInactive() 等,用于处理不同类型的事件。开发者可以实现自己的 ChannelHandler,并将其注册到 ChannelPipeline 中,以便在特定事件发生时执行相应的逻辑处理。
  3. ChannelPipeline:ChannelPipeline 是 ChannelHandler 的容器,它负责管理和执行 ChannelHandler 的调用顺序。每个 Channel 都会有一个对应的 ChannelPipeline,用于处理该 Channel 上的事件和数据。当一个事件被触发时,ChannelPipeline 会按照预定的顺序依次调用注册的 ChannelHandler 来处理事件。通过配置不同的 ChannelHandler,可以实现数据的编解码、数据处理、安全认证等功能。

ChannelPipeline 的设计遵循了责任链模式,每个 ChannelHandler 只负责自己关心的事件,通过调用下一个 ChannelHandler 来传递事件和数据。这样可以实现组件的解耦和灵活的功能扩展。

通过将不同的 ChannelHandler 注册到 ChannelPipeline 中,可以构建一个处理器链,每个处理器负责特定的功能。数据在经过 ChannelPipeline 时,会按照处理器链的顺序依次经过每个处理器,最终被发送到合适的目标。

总结来说,事件、ChannelHandler 和 ChannelPipeline 是 Netty 中实现高效网络通信的核心组件。事件用于驱动整个网络通信过程,ChannelHandler 用于处理事件和执行业务逻辑,而 ChannelPipeline 则是管理和执行 ChannelHandler 的调用顺序,实现了组件的解耦和灵活的功能扩展。通过合理配置和使用这些组件,可以实现高性能、可扩展的网络应用。

在 Netty 中,ChannelFuture 是用于异步操作结果的表示和处理的对象。它表示一个尚未完成的 I/O 操作,可以通过 ChannelFuture 来获取操作的结果、添加监听器以及执行后续的操作。
ChannelFuture 提供了以下主要功能:

  1. 异步操作的结果:ChannelFuture 可以获取异步操作的结果,如数据的发送、连接的建立等。它提供了方法来判断操作是否完成,获取操作的成功或失败状态,以及获取操作返回的结果对象。
  2. 添加操作完成的监听器:可以通过 ChannelFuture 添加一个或多个监听器,以便在操作完成后执行相应的操作。例如,可以添加一个监听器来处理操作完成时的逻辑,或者添加一个监听器来处理操作失败时的错误处理。通过监听器,可以对异步操作的结果进行处理。
  3. 等待和同步操作:可以通过 ChannelFuture 进行等待和同步操作,即阻塞当前线程,直到操作完成。这对于需要等待操作结果并进行后续处理的情况非常有用。可以使用 ChannelFuture 的一些方法,如 await()、sync() 等来实现等待和同步操作。
  4. 链式操作:ChannelFuture 支持链式操作,可以在一个操作完成后继续执行下一个操作。通过调用 ChannelFuture 的一些方法,如 addListener()、addListener(ChannelFutureListener) 等,可以实现链式操作的编写,简化了代码的编写。

总之,ChannelFuture 在 Netty 中用于表示和处理异步操作的结果。通过它,可以获取操作的结果、添加监听器来处理操作完成后的逻辑,进行等待和同步操作,以及实现链式操作。它提供了灵活的方式来处理和管理异步操作,使得网络编程更加简洁和高效。

在这里插入图片描述

Hello,Netty! 我们的第一个Netty程序

首先导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ding</groupId>
    <artifactId>netty</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>netty</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.6.2</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

只要使用Netty,下面这个代码是必写的,唯一不同的是每个应用的handler不太一样。

package com.ding.nettybasic;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

public class EchoServer {
    private static final Logger LOG = LoggerFactory.getLogger(EchoServer.class);

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        int port = 9999;
        EchoServer echoServer = new EchoServer(port);
        LOG.info("服务器即将启动");
        echoServer.start();
        LOG.info("服务器关闭");
    }

    private void start() throws InterruptedException {
        /*线程组*/
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            /*服务端启动必备*/
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                    .channel(NioServerSocketChannel.class)/*指定使用NIO的通信模式*/
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            /*异步绑定到服务器,sync()会阻塞到完成*/
            ChannelFuture f = b.bind().sync();
            /*阻塞当前线程,知道服务器的ServerChannel被关闭*/
            f.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully().sync();
        }
    }
}

SocketChannel在与业务流转之间还有一个Buffer,因此这个里面也要有一个Buffer

在这里插入图片描述

package com.ding.nettybasic;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        ByteBuf in = (ByteBuf)msg;
        System.out.println("server accept:"+in.toString(CharsetUtil.UTF_8));
        ctx.writeAndFlush(in);
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("连接已建立");
        super.channelActive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
package com.ding.nettybasic;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

public class EchoClient {
    private static final Logger LOG = LoggerFactory.getLogger(EchoClient.class);

    private final int port;
    private final String host;

    public EchoClient(int port, String host) {
        this.port = port;
        this.host = host;
    }
    public void start() throws InterruptedException {
        /*线程组*/
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            /*服务端启动必备*/
            Bootstrap b = new Bootstrap();
            b.group(group)
                .channel(NioSocketChannel.class)/*指定使用NIO的通信模式*/
                .remoteAddress(new InetSocketAddress(host,port))
                .handler(new ChannelInitializer<SocketChannel>() {
                     @Override
                     protected void initChannel(SocketChannel ch) throws Exception {
                         ch.pipeline().addLast(new EchoClientHandler());
                     }
                 });
            /*异步绑定到服务器,sync()会阻塞到完成*/
            ChannelFuture f = b.connect().sync();
            /*阻塞当前线程,知道服务器的ServerChannel被关闭*/
            f.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient(9999,"127.0.0.1").start();
    }
}
package com.ding.nettybasic;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        /*读取网络数据后进行业务处理,并关闭连接*/
        System.out.println("client Accept" + msg.toString(CharsetUtil.UTF_8));
        /*关闭连接*/
        ctx.close();
    }

    /*channel活跃后,做业务处理*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Netty",CharsetUtil.UTF_8));
    }
}

在这里插入图片描述
在这里插入图片描述

3. Netty组件

3.1 EventLoop和EventLoopGroup

关系说明

  • 一个EventLoopGroup包含一个或者多个EventLoop;
  • 一个EventLoop在它的生命周期内只和一个Thread 绑定;
  • 所有由EventLoop 处理的I/O事件都将在它专有的Thread上被处理;

在这里插入图片描述

在这里插入图片描述

Channel

  • Channel-Socket;
  • EventLoop—控制、多线程处理、并发;

在这里插入图片描述

ChannelPipeline和ChannelHandlerContext

ChannelPipeline 提供了ChannelHandler链的容器,并定义了用于在该链上传播入站和出站事件流的APl。

ChannelHandler 的生命周期

在这里插入图片描述
ChannelHandler 的生命周期

在ChannelHandler被添加到ChannelPipeline 中或者被从ChannelPipeline中移除时会调用下面这些方法。这些方法中的每一个都接受一个ChannelHandlerContext参数。

handlerAdded当把 ChannelHandler 添加到ChannelPipeline 中时被调用
handlerRemoved当从ChannelPipeline中移除ChannelHandler时被调用
exceptionCaught当处理过程中在 ChannelPipeline中有错误产生时被调用

ChannelPipeline中ChannelHandler

Netty会把出站Handler和入站Handler放到一个Pipeline中,数据结构上是一个双向链表

在这里插入图片描述

ChannelPipeline

在这里插入图片描述

那么分属出站和入站不同的Handler,在业务没要求的情况下是可以不考虑顺序的。
而同属一个方向的Handler则是有顺序的,因为上一个Handler处理的结果往往是下一个Handler的要求的输入。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

1. Netty核心功能与线程模型详解 的相关文章

随机推荐

  • 如何帮服务器设置虚拟内存,服务器里面怎么设置虚拟内存

    服务器里面怎么设置虚拟内存 内容精选 换一换 对象存储调优主要分为 冷存储配置调优所有数据盘都是机械硬盘 HDD 的场景 即DB WAL分区 元数据存储池都使用机械硬盘所有数据盘都是机械硬盘 HDD 的场景 即DB WAL分区 元数据存储池
  • binlog_do_db 与 binlog_ignore_db

    前言 经过前面文章学习 我们知道 binlog 会记录数据库所有执行的 DDL 和 DML 语句 除了数据查询语句select show等 注意默认情况下会记录所有库的操作 那么如果我们有另类需求 比如说只让某个库记录 binglog 或排
  • [羊城杯 2020]A Piece Of Java

    羊城杯 2020 A Piece Of Java 文章目录 羊城杯 2020 A Piece Of Java 源码分析 从后往前测试 逐步写exp 构造DatabaseInfo类对象 InfoInvocationHandler 动态代理 序
  • 树莓派配置motion获取实时视频流

    一 串口连接CSI摄像头模块 二 升级安装程序apt get 输入以下命令 sudo apt get update sudo apt get upgrade 三 激活树莓派摄像头模块 输入sudo raspi config 选择Interf
  • Android透明状态栏和导航栏方案最终版

    前言 仔细留意常用App 就会发现有些 App 的状态栏和导航栏有透明效果 或者是沉浸式效果 比如QQ音乐客户端 是像这个样子的 我们看到整个页面顶部与导航栏浑然一体 在看导航栏 虽然我们打开了手机导航栏 但是整个页面 还是延伸到了导航栏底
  • 避免 PageHelper 使用中的一些坑

    多年不用PageHelper了 最近新入职的公司 采用了此工具集成的框架 作为一个独立紧急项目开发的基础 项目开发起来 还是手到擒来的 但是没想到 最终测试的时候 深深的给我上了一课 我的项目发生了哪些奇葩现象 一切的问题都要从我接受的项目
  • C++ PRIMER PLUS 第六版编程答案(二)

    2 7编程练习 1 编写一个小程序 要求用户使用一个整数指出自己的身高 单位为英寸 然后将身高转换为英尺和英寸 该程序使用下划线字符来指示输入位置 另外 使用一个const符号常量来表示转换因子 include
  • 解决eclipse中启动Tomcat成功但是访问不了Tomcat问题

    自己搭建了一个springMVC项目 中间出了一些问题 在排查问题的过程中发现eclipse成功启动了Tomcat 但是在浏览器中输入localhost 8080却给我一个冷冷的404 我以为是Tomcat出问题了 心情大好 以为自己搭建的
  • Github copilot几个使用技巧,自动补全代码

    上一篇文章介绍了如何在vscode 中引入 Github Copilot 这一张我们介绍一下Github Copilot 的使用技巧 一 常用快捷键 快捷键 含义 tab 应用提示代码 esc 拒绝提示代码 ctrl enter 打开提示面
  • Caused by: java.lang.UnsupportedOperationException 解决方案

    b 背景 b 今天在跑一个UnitTest 跑的过程中想在list的最后多加一个Element 即 List add Element e 多测试一条数据 可是在run的过程中 却一直在抛 Caused by java lang Unsupp
  • V-REP安装

    小知识 是当前目录 是父级目录 是根目录 1 下载V REP 官网地址 http www v rep eu downloads html 我用ubuntu16 04下载V REP PRO EDU V3 5 0 Linux tar 2 解压安
  • STM32通用定时器输出PWM控制舵机 —— 重装载值、比较值、当前值

    参考 stm32 定时器输出PWM原理及工作原理 控制舵机 作者 点灯小哥 发布时间 2021 03 09 23 17 52 网址 https blog csdn net weixin 46016743 article details 11
  • 【数理统计】双因素方差分析

    下面用SPSS搞一下 这一步选择模型 要不要考虑交叉因素 根据实际情况 我先不选交叉因素 选主效应 在这里可以看到随机误差项的自由度为0 不满足方差齐性 这是为什么呢 这是因为SPSS的自由度和上述经典算法是不一致的 SPSS中是怎么算的呢
  • python自动化课程笔记(十二)闭包、装饰器

    闭包 闭包就是能够读取其他函数内部变量的函数 例如在javascript中 只有函数内部的子函数才能读取局部变量 所以闭包可以理解成 定义在一个函数内部的函数 在本质上 闭包是将函数内部和函数外部连接起来的桥梁 闭包 def test nu
  • 【专题5: 硬件设计】之 【66.开关电源 之 buck电路和引入电感】

    嵌入式工程师成长之路 系列文章 总目录 系列文章总目录 希望本是无所谓有 无所谓无的 这正如脚下的路 其实地上本没有路 走的人多了 也便成了路 原创不易 文章会持续更新 欢迎微信扫码关注公众号 承接 小程序 嵌入式 PC端项目开发 联系作者
  • Kubernetes tutorial - K8S 官方入门教程

    tutorials 教程 kubectl 的命令手册 1 Creating a Cluster 1 1 Using Minikube to Create a Cluster Kubernetes Clusters Kubernetes co
  • 51单片机总结【引脚、时钟电路、复位电路、I/O端口、内部结构】

    1 功能简述 STC89C52 是一种低功耗 高性能CMOS8位微控制器 具有8K在系统可编程Flash存储器 ROM STC89C52具有以下标准功能 8k字节Flash 程序存储器ROM 512字节RAM 256字节内部和256字节外部
  • 解决Linux系统字符集不匹配安装软件失败问题

    使用SSHSecureShellClient客户端连接Linux服务器 把字符集设置为 export LC CTYPE zh CN GB18030 export LC ALL zh CN GB18030 export LANG zh CN
  • 面试官:熔断和降级有什么区别?

    熔断和降级都是系统自我保护的一种机制 但二者又有所不同 它们的区别主要体现在以下几点 概念不同 触发条件不同 归属关系不同 1 概念不同 1 1 熔断概念 熔断 一词早期来自股票市场 熔断 Circuit Breaker 也叫自动停盘机制
  • 1. Netty核心功能与线程模型详解

    Netty 1 认识Netty 2 第一个Netty程序 3 Netty组件 3 1 EventLoop和EventLoopGroup Channel ChannelPipeline和ChannelHandlerContext Channe