03-Netty编解码,粘包拆包及零拷贝详解

Day18_Netty Channel代表了一个网络连接,可以是客户端到服务器的连接,也可以是服务器之间的连接。每个Channel都由一个EventLoop负责处理,而一个EventLoop可以管理多个Channel。Channel中可以执行I/O操作,如读取、写入、连接和绑定等。是一个由一系列组成的处理链。如进行数据编解码、业务逻辑处理等。将这些串联起来,形成一个处理链。每个Channel都有一个关联的,用于处理入站和出站的数据。编解码器(Codec)是负责将数据在不同表示形式之间进行转换的组件。 阅读详情

Netty编解码

Netty涉及到编解码的组件有Channel、ChannelHandler、ChannelPipe等,先大概了解下这几个组件的作用。

ChannelHandler

ChannelHandler充当了处理入站和出站数据的应用程序逻辑容器。例如,实现ChannelInboundHandler接口(或ChannelInboundHandlerAdapter),你就可以接收入站事件和数据,这些数据随后会被你的应用程序的业务逻辑处理。当你要给连接的客户端发送响应时,也可以从ChannelInboundHandler冲刷数据。你的业务逻辑通常写在一个或者多个ChannelInboundHandler中。ChannelOutboundHandler原理一样,只不过它是用来处理出站数据的。

ChannelPipeline

ChannelPipeline提供了ChannelHandler链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过pipeline中的一系列ChannelOutboundHandler(ChannelOutboundHandler调用是从tail到head方向逐个调用每个handler的逻辑),并被这些Handler处理,反之则称为入站的,入站只调用pipeline里的ChannelInboundHandler逻辑(ChannelInboundHandler调用是从head到tail方向逐个调用每个handler的逻辑)。

编码解码器

当你通过Netty发送或者接受一个消息的时候,就将会发生一次数据转换。入站消息会被解码:从字节转换为另一种格式(比如java对象);如果是出站消息,它会被编码成字节。

Netty提供了一系列实用的编码解码器,他们都实现了ChannelInboundHadnler或者ChannelOutboundHandler接口。在这些类中,channelRead方法已经被重写了。以入站为例,对于每个从入站Channel读取的消息,这个方法会被调用。随后,它将调用由已知解码器所提供的decode()方法进行解码,并将已经解码的字节转发给ChannelPipeline中的下一个ChannelInboundHandler。

Netty提供了很多编解码器,比如编解码字符串的StringEncoder和StringDecoder,编解码对象的ObjectEncoder和ObjectDecoder等。

如果要实现高效的编解码可以用protobuf,但是protobuf需要维护大量的proto文件比较麻烦,现在一般可以使用protostuff。

protostuff是一个基于protobuf实现的序列化方法,它较于protobuf最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写.proto文件来实现序列化。使用它也非常简单,代码如下:

引入依赖:

<dependency>
            <groupId>com.dyuproject.protostuff</groupId>
            <artifactId>protostuff-api</artifactId>
            <version>1.0.10</version>
        </dependency>
        <dependency>
            <groupId>com.dyuproject.protostuff</groupId>
            <artifactId>protostuff-core</artifactId>
            <version>1.0.10</version>
        </dependency>
        <dependency>
            <groupId>com.dyuproject.protostuff</groupId>
            <artifactId>protostuff-runtime</artifactId>
            <version>1.0.10</version>
        </dependency>

protostuff使用示例:

package com.tuling.netty;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import com.tuling.netty.codec.User;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * protostuff 序列化工具类,基于protobuf封装
 */
public class ProtostuffUtil {
    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();

    private static <T> Schema<T> getSchema(Class<T> clazz) {
        @SuppressWarnings("unchecked") Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
        if (schema == null) {
            schema = RuntimeSchema.getSchema(clazz);
            if (schema != null) {
                cachedSchema.put(clazz, schema);
            }
        }
        return schema;
    }

    /**
     * 序列化 * * @param obj * @return
     */
    public static <T> byte[] serializer(T obj) {
        @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(clazz);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    /**
     * 反序列化 * * @param data * @param clazz * @return
     */
    public static <T> T deserializer(byte[] data, Class<T> clazz) {
        try {
            T obj = clazz.newInstance();
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(data, obj, schema);
            return obj;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public static void main(String[] args) {
        byte[] userBytes = ProtostuffUtil.serializer(new User(1, "zhuge"));
        User user = ProtostuffUtil.deserializer(userBytes, User.class);
        System.out.println(user);
    }
}

参见项目示例com.tuling.netty.codec包下代码

Netty粘包拆包

TCP是一个流协议,就是没有界限的一长串二进制数据。TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。面向流的通信是无消息保护边界的。

如下图所示,client发了两个数据包D1和D2,但是server端可能会收到如下几种情况的数据。

解决方案

1)消息定长度,传输的数据大小固定长度,例如每段的长度固定为100字节,如果不够空位补空格

2)在数据包尾部添加特殊分隔符,比如下划线,中划线等,这种方法简单易行,但选择分隔符的时候一定要注意每条数据的内部一定不能出现分隔符。

3)发送长度:发送每条数据的时候,将数据的长度一并发送,比如可以选择每条数据的前4位是数据的长度,应用层处理时可以根据长度来判断每条数据的开始和结束。

Netty提供了多个解码器,可以进行分包的操作,如下:

  • LineBasedFrameDecoder (回车换行分包)
  • DelimiterBasedFrameDecoder(特殊分隔符分包)
  • FixedLengthFrameDecoder(固定长度报文来分包)

自定义长度分包编解码器,参见项目示例com.tuling.netty.split包下代码

Netty心跳检测机制

所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性.

在 Netty 中, 实现心跳机制的关键是 IdleStateHandler, 看下它的构造器:

public IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) { this((long)readerIdleTimeSeconds, (long)writerIdleTimeSeconds, (long)allIdleTimeSeconds, TimeUnit.SECONDS); }

这里解释下三个参数的含义:

  • readerIdleTimeSeconds: 读超时. 即当在指定的时间间隔内没有从 Channel 读取到数据时, 会触发一个 READER_IDLE 的 IdleStateEvent 事件.
  • writerIdleTimeSeconds: 写超时. 即当在指定的时间间隔内没有数据写入到 Channel 时, 会触发一个 WRITER_IDLE 的 IdleStateEvent 事件.
  • allIdleTimeSeconds: 读/写超时. 即当在指定的时间间隔内没有读或写操作时, 会触发一个 ALL_IDLE 的 IdleStateEvent 事件.

注:这三个参数默认的时间单位是秒。若需要指定其他时间单位,可以使用另一个构造方法:

IdleStateHandler(boolean observeOutput, long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit)

要实现Netty服务端心跳检测机制需要在服务器端的ChannelInitializer中加入如下的代码:

pipeline.addLast(new IdleStateHandler(3, 0, 0, TimeUnit.SECONDS));

初步地看下IdleStateHandler源码,先看下IdleStateHandler中的channelRead方法:

红框代码其实表示该方法只是进行了透传,不做任何业务逻辑处理,让channelPipe中的下一个handler处理channelRead方法

我们再看看channelActive方法:

这里有个initialize的方法,这是IdleStateHandler的精髓,接着探究:

这边会触发一个Task,ReaderIdleTimeoutTask,这个task里的run方法源码是这样的:

第一个红框代码是用当前时间减去最后一次channelRead方法调用的时间,假如这个结果是6s,说明最后一次调用channelRead已经是6s之前的事情了,你设置的是5s,那么nextDelay则为-1,说明超时了,那么第二个红框代码则会触发下一个handler的userEventTriggered方法:

如果没有超时则不触发userEventTriggered方法。

Netty心跳检测代码示例:

package com.tuling.netty.heartbeat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

public class HeartBeatServer {

    public static void main(String[] args) throws Exception {
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("decoder", new StringDecoder());
                            pipeline.addLast("encoder", new StringEncoder());
                            //IdleStateHandler的readerIdleTime参数指定超过3秒还没收到客户端的连接,
                            //会触发IdleStateEvent事件并且交给下一个handler处理,下一个handler必须
                            //实现userEventTriggered方法处理对应事件
                            pipeline.addLast(new IdleStateHandler(3, 0, 0, TimeUnit.SECONDS));
                            pipeline.addLast(new HeartBeatServerHandler());
                        }
                    });
            System.out.println("netty server start。。");
            ChannelFuture future = bootstrap.bind(9000).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            worker.shutdownGracefully();
            boss.shutdownGracefully();
        }
    }
}
package com.tuling.netty.heartbeat;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;

public class HeartBeatServerHandler extends SimpleChannelInboundHandler<String> {

    int readIdleTimes = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        System.out.println(" ====== > [server] message received : " + s);
        if ("Heartbeat Packet".equals(s)) {
            ctx.channel().writeAndFlush("ok");
        } else {
            System.out.println(" 其他信息处理 ... ");
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent event = (IdleStateEvent) evt;

        String eventType = null;
        switch (event.state()) {
            case READER_IDLE:
                eventType = "读空闲";
                readIdleTimes++; // 读空闲的计数加1
                break;
            case WRITER_IDLE:
                eventType = "写空闲";
                // 不处理
                break;
            case ALL_IDLE:
                eventType = "读写空闲";
                // 不处理
                break;
        }



        System.out.println(ctx.channel().remoteAddress() + "超时事件:" + eventType);
        if (readIdleTimes > 3) {
            System.out.println(" [server]读空闲超过3次,关闭连接,释放更多资源");
            ctx.channel().writeAndFlush("idle close");
            ctx.channel().close();
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.err.println("=== " + ctx.channel().remoteAddress() + " is active ===");
    }
}
package com.tuling.netty.heartbeat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Random;

public class HeartBeatClient {
    public static void main(String[] args) throws Exception {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("decoder", new StringDecoder());
                            pipeline.addLast("encoder", new StringEncoder());
                            pipeline.addLast(new HeartBeatClientHandler());
                        }
                    });

            System.out.println("netty client start。。");
            Channel channel = bootstrap.connect("127.0.0.1", 9000).sync().channel();
            String text = "Heartbeat Packet";
            Random random = new Random();
            while (channel.isActive()) {
                int num = random.nextInt(10);
                Thread.sleep(2 * 1000);
                channel.writeAndFlush(text);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            eventLoopGroup.shutdownGracefully();
        }
    }

    static class HeartBeatClientHandler extends SimpleChannelInboundHandler<String> {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(" client received :" + msg);
            if (msg != null && msg.equals("idle close")) {
                System.out.println(" 服务端关闭连接,客户端也关闭");
                ctx.channel().closeFuture();
            }
        }
    }
}

Netty断线自动重连实现

1、客户端启动连接服务端时,如果网络或服务端有问题,客户端连接失败,可以重连,重连的逻辑加在客户端。

参见代码com.tuling.netty.reconnect.NettyClient

2、系统运行过程中网络故障或服务端故障,导致客户端与服务端断开连接了也需要重连,可以在客户端处理数据的Handler的channelInactive方法中进行重连。

参见代码com.tuling.netty.reconnect.NettyClientHandler

Netty零拷贝

Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。
如果使用传统的JVM堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才能写入Socket中。JVM堆内存的数据是不能直接写入Socket中的。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
可以看下netty的读写源码,比如read源码NioByteUnsafe.read()

直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,某些情况下这部分内存也会被频繁地使用,而且也可能导致OutOfMemoryError异常出现。Java里用DirectByteBuffer可以分配一块直接内存(堆外内存),元空间对应的内存也叫作直接内存,它们对应的都是机器的物理内存

public class MemoryTest {
5
6 public static void heapAccess() {
7 long startTime = System.currentTimeMillis();
8 //分配堆内存
9 ByteBuffer buffer = ByteBuffer.allocate(1000);
10 for (int i = 0; i < 100000; i++) {
11 for (int j = 0; j < 200; j++) {
12 buffer.putInt(j);
13 }
14 buffer.flip();
15 for (int j = 0; j < 200; j++) {
16 buffer.getInt();
17 }
18 buffer.clear();
19 }
20 long endTime = System.currentTimeMillis();
21 System.out.println("堆内存访问:" + (endTime ‐ startTime));
22 }
23
24 public static void directAccess() {
25 long startTime = System.currentTimeMillis();
26 //分配直接内存
27 ByteBuffer buffer = ByteBuffer.allocateDirect(1000);
28 for (int i = 0; i < 100000; i++) {
29 for (int j = 0; j < 200; j++) {
30 buffer.putInt(j);
31 }
32 buffer.flip();
33 for (int j = 0; j < 200; j++) {
34 buffer.getInt();
35 }
36 buffer.clear();
37 }
38 long endTime = System.currentTimeMillis();
39 System.out.println("直接内存访问:" + (endTime ‐ startTime));
40 }
41
42 public static void heapAllocate() {
43 long startTime = System.currentTimeMillis();
44 for (int i = 0; i < 100000; i++) {
45 ByteBuffer.allocate(100);
46 }
47 long endTime = System.currentTimeMillis();
48 System.out.println("堆内存申请:" + (endTime ‐ startTime));
49 }
50
51 public static void directAllocate() {
52 long startTime = System.currentTimeMillis();
53 for (int i = 0; i < 100000; i++) {
54 ByteBuffer.allocateDirect(100);
55 }
56 long endTime = System.currentTimeMillis();
57 System.out.println("直接内存申请:" + (endTime ‐ startTime));
58 }
59
60 public static void main(String args[]) {
61 for (int i = 0; i < 10; i ++) {
62 heapAccess();
63 directAccess();
64 }
65
66 System.out.println();
67
68 for (int i = 0; i < 10; i ++) {
69 heapAllocate();
70 directAllocate();
71 }
72 }
73 }
74
75 运行结果:
76 堆内存访问:53
77 直接内存访问:43
78 堆内存访问:32
79 直接内存访问:21
80 堆内存访问:55
81 直接内存访问:32
82 堆内存访问:63
83 直接内存访问:48
84 堆内存访问:35
85 直接内存访问:19
86 堆内存访问:35
87 直接内存访问:19
88 堆内存访问:34
89 直接内存访问:20
90 堆内存访问:52
91 直接内存访问:28
92 堆内存访问:41
93 直接内存访问:34
94 堆内存访问:64
95 直接内存访问:23
96
97 堆内存申请:14
98 直接内存申请:37
99 堆内存申请:9
100 直接内存申请:33
101 堆内存申请:60
102 直接内存申请:44
103 堆内存申请:1
104 直接内存申请:36
105 堆内存申请:1
106 直接内存申请:69
107 堆内存申请:1
108 直接内存申请:32
109 堆内存申请:2
110 直接内存申请:25
111 堆内存申请:1
112 直接内存申请:29
113 堆内存申请:6
114 直接内存申请:27
115 堆内存申请:6
116 直接内存申请:158

从程序运行结果看出直接内存申请较慢,但访问效率高。在java虚拟机实现上,本地IO会直接操作直接内存(直接内存=>系统调用=>硬盘/网卡),而非直接内存则需要二次拷贝(堆内存=>直接内存=>系统调用=>硬盘/网卡)。

直接内存分配源码分析:

package‐private
7 super(‐1, 0, cap, cap);
8 boolean pa = VM.isDirectMemoryPageAligned();
9 int ps = Bits.pageSize();
10 long size = Math.max(1L, (long)cap + (pa ? ps : 0));
11 //判断是否有足够的直接内存空间分配,可通过‐XX:MaxDirectMemorySize=<size>参数指定直接内存最大可分配空间,如果不指定默认为最
大堆内存大小,
12 //在分配直接内存时如果发现空间不够会显示调用System.gc()触发一次full gc回收掉一部分无用的直接内存的引用对象,同时直接内存也会
被释放掉
13 //如果释放完分配空间还是不够会抛出异常java.lang.OutOfMemoryError
14 Bits.reserveMemory(size, cap);
15
16 long base = 0;
17 try {
18 // 调用unsafe本地方法分配直接内存
19 base = unsafe.allocateMemory(size);
20 } catch (OutOfMemoryError x) {
21 // 分配失败,释放内存
22 Bits.unreserveMemory(size, cap);
23 throw x;
24 }
25 unsafe.setMemory(base, size, (byte) 0);
26 if (pa && (base % ps != 0)) {
27 // Round up to page boundary
28 address = base + ps ‐ (base & (ps ‐ 1));
29 } else {
30 address = base;
31 }
32
33 // 使用Cleaner机制注册内存回收处理函数,当直接内存引用对象被GC清理掉时,
34 // 会提前调用这里注册的释放直接内存的Deallocator线程对象的run方法
35 cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
36 att = null;
37 }
38
39
40 // 申请一块本地内存。内存空间是未初始化的,其内容是无法预期的。
41 // 使用freeMemory释放内存,使用reallocateMemory修改内存大小
42 public native long allocateMemory(long bytes);
43
44 // openjdk8/hotspot/src/share/vm/prims/unsafe.cpp
45 UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size))
46 UnsafeWrapper("Unsafe_AllocateMemory");
47 size_t sz = (size_t)size;
48 if (sz != (julong)size || size < 0) {
49 THROW_0(vmSymbols::java_lang_IllegalArgumentException());
50 }
51 if (sz == 0) {
52 return 0;
53 }
54 sz = round_to(sz, HeapWordSize);
55 // 调用os::malloc申请内存,内部使用malloc这个C标准库的函数申请内存
56 void* x = os::malloc(sz, mtInternal);
57 if (x == NULL) {
58 THROW_0(vmSymbols::java_lang_OutOfMemoryError());
59 }
60 //Copy::fill_to_words((HeapWord*)x, sz / HeapWordSize);
61 return addr_to_java(x);
62 UNSAFE_END

使用直接内存的优缺点:
优点:
不占用堆内存空间,减少了发生GC的可能
java虚拟机实现上,本地IO会直接操作直接内存(直接内存=>系统调用=>硬盘/网卡),而非直接内存则需要二次拷贝(堆内
存=>直接内存=>系统调用=>硬盘/网卡)
缺点:
初始分配较慢
没有JVM直接帮助管理内存,容易发生内存溢出。为了避免一直没有FULL GC,最终导致直接内存把物理内存被耗完。我们可以指定直接内存的最大值,通过-XX:MaxDirectMemorySize来指定,当达到阈值的时候,调用system.gc来进行一次FULL GC,
间接把那些没有被使用的直接内存回收掉。
有道云笔记:文档:03-VIP-Netty编解码,粘包拆包及零拷贝详解
链接:http://note.youdao.com/noteshare?
id=b8970e44473486a48178193d68929008&sub=2FBCDAF79D794F8DBBD027A5F2C29249

Netty实现高并发多Agent系统状态同步实战 分布式系统中的Agent模式通过自主运行的软件实体实现环境感知与决策执行,其核心挑战在于大规模Agent的实时状态同步。传统HTTP轮询面临高延迟与性能瓶颈,而基于TCP长连接的技术方案能显著降低网络开销,实现服务端主动推送。Netty作为Java生态的高性能网络框架,凭借其事件驱动架构、零拷贝技术和灵活编解码器,成为实现10万级并发连接的理想选择。在物联网、智能家居等场景中,结合Redis集群的分布式状态管理方案,可构建延迟低于100ms、可用性99.99%的实时同步系统。本文通过连接管理、心跳机制等核心 阅读详情

相关推荐

第三章 Netty 网络编程深度解析:从 HTTP 协议处理到自定义协议设计实战

本文系统介绍了基于Netty构建高性能HTTP服务器的完整技术体系。首先分析了Netty的核心架构,包括Reactor线程模型和ChannelPipeline工作机制;然后详细解析HTTP协议基础与Netty实现方式,展示编解码器配置;重点探讨了自定义HTTP协议的设计与实现方法,包括二进制协议头设计;最后涵盖高级特性(连接池、零拷贝等)、API网关实战、测试调试技巧等内容。全文从理论到实践,循序渐进地讲解如何利用Netty开发高性能网络应用,适合中高级开发者学习网络编程和协议设计。

ywl470812087的博客 224

浅谈Protobuf

最近在学习Netty,通信层框架就讲到了序列化和反序列化的框架,其中就谈到了Protobuf,结合官网上的讲解(官网URL:https://developers.google.com/protocol-buffers/docs/reference/java-generated#invocation),总结一些自己的理解,protobuf的源码在github上的地址为:URL:https://git

空谷幽兰草堂 3045

protobuf-zero-copy-network-stream-sample:ZeroCopyInputStreamZeroCopyOutputStream 使用预分配缓冲区处理流式套接字的示例

protobuf 零拷贝网络流样本 这是使用预分配缓冲区处理流式套接字的 ZeroCopyInputStream 和 ZeroCopyOutputStream 的示例。 如果您有缓冲池或希望使用堆栈缓冲区,这将很有用。 笔记 记录代码被注释掉。 您最好取消注释并使用您自己的日志记录框架来捕获错误。 此代码用于生产服务器,但目前没有任何相关的测试。 它按原样提供,没有任何保证。

Netty

简介 Netty是一个异步事件驱动的网络应用程序框架,一个基于NIO客户端服务器框架,支持多种协议(自定义的传输协议),可以灵活快速的搭建与开发,支持自定义扩展时间模型和通用的通信API并且Byte Buffer实现了零拷贝;用于快速开发可维护的高性能协议服务器和客户端。学netty之前可以先了解一下NIO模型。 特性 使用各种传输类型的统一API--阻塞和非阻塞嵌套字。 基于灵活且扩展的事件模型,可将关注点明确分离。 高度可定制的线程模型-单线程,一个或多个线程池。 从3.1版本后真正...

YaYicho的博客 311

踩坑记录:服务商模式下微信小程序支付,为什么必须用子商户的AppID获取openid?

本文深入解析了服务商模式下微信小程序支付必须使用子商户AppID获取openid的技术原因,详细对比了服务商模式与直连模式的关键差异,并提供了正确的配置指南和常见问题解决方案,帮助开发者避免微信支付集成中的常见陷阱。

weixin_28318011的博客 550

SpringCloudGateway网关高频20个面试题整理

Spring Cloud Gateway 是基于 Spring 5、Project Reactor 和 Spring Boot 2 构建的 API 网关,提供动态路由、监控、弹性、安全等核心功能。Spring Cloud Gateway 是 Spring Cloud 生态中的 API 网关,用于微服务架构中的请求路由、负载均衡、限流、鉴权等。实现@Component@Override// 逻辑处理。

u012024132的专栏 1114

网络编程-编码与解码(Protobuf)

Netty 本身自带的 ObjectDecoder 和 ObjectEncoder 可以用来实现 POJO 对象或各种业务对象的编码和解码,但其内部使用的仍是 Java 序列化技术,所以在某些场景下不适用。使用 protobuf 编译器能自动生成代码,Protobuf 是将类的定义使用.proto 文件进行描述,然后通过 protoc.exe 编译器根据.proto 自动成.java 文件在使用 Netty 开发时,经常会结合 Protobuf 作为 codec (编解码器)去使用,具体用法如下所示。

qq_43259860的博客 1741

解决mamba_ssm库安装难题:两种高效方法详解

本文详细解析了mamba_ssm库安装失败的常见原因,并提供了两种高效的解决方案。针对其依赖C++/CUDA扩展编译的特性,文章手把手指导用户通过GitHub克隆源码编译安装,或寻找匹配的预编译Wheel文件直接安装,帮助开发者彻底解决环境配置难题,顺利部署Mamba模型。

weixin_29056865的博客 554

10.netty客户端与服务器使用protobuf传输报文

1)ProtoBuf定义:是 Protocol Buffer 的缩写,即协议缓冲区;它是一种开源跨平台的序列化资料结构的协议。① 语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台;② 高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单;③ 扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序;说的直接点,protobuf序列化性能 优于 java序列化;

PacosonSWJTU的博客 1901

网工实验笔记:SNMP的配置

简单网络管理协议SNMP(Simple Network Management Protocol)是广泛应用于TCP/IP网络的网络管理标准协议。SNMP提供了一种通过运行网络管理软件的中心计算机(即网络管理工作站)来管理设备的方法。

2301_76170756的博客 2116

ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: NO)解决办法

问题:ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’ (using password: NO) 很久没用这台电脑的mysql了,今天需要连接数据库,启动数据库报错: ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’ (using password: NO) 解决方法: 查阅资料后知道了,应该使用已知的用户名和密码登陆 mysql -u root -p 然而密

一个超会写Bug的程序猿的博客 43万+

从NIO到netty(13)Netty的架构设计和开发中的几个注意点

之前把netty启动时的几大模块都分析完了,下面就Netty的架构设计做一个总结 1、一个EventLoopGroup当中会包含一个或多个EventLoop。 2、一个EventLoop在它的整个生命周期当中都只会与唯一一个Thread进行绑定。 3、所有由EventLoop所处理的各种I/O事件都将在它所关联的那个Thread上进行处理。 4、一个Channel在它的整个生命周期中只会注册在一...

m0_37139189的博客 268

【系统架构设计师】二十六、论文写作要点

论文写作概述:时间所需120分钟;题目可四选一,必有一题考察软件架构,总有一款适合你;考察形式是机试(开心,再也不用担心字丑了),约2500字。

帅次的博客 4599

Netty从0到1系列之拆包、粘包【4】

本文介绍了Netty自定义通信协议的设计要点。一个完整的通信协议需要包含魔数(安全验证)、协议版本号(兼容性)、报文类型(区分业务场景)、长度域字段(确定报文边界)、序列化算法(数据转换方法)、请求数据(业务内容)、状态字段(请求状态)和保留字段(未来扩展)等关键要素。通过合理设计这些要素,可以构建出高性能、可扩展且安全的私有协议,避免使用通用协议可能带来的性能损耗和安全隐患。文中给出了一个典型协议结构示例,展示了各字段的排列方式。

编程技术圈 831

用Python绘制DCA曲线详解及示例代码

在这篇文章中,我们将详细解释如何使用Python绘制DCA曲线,并提供示例代码。通过使用上述的示例代码和解释,你可以自己尝试不同的投资金额、期数和增长率来绘制自定义的DCA曲线。通过运行上述代码,我们将得到一个绘制了DCA曲线的图表。该图表将显示出DCA投资策略在给定投资金额、投资期数和增长率下的投资价值随时间的变化情况。在上述代码中,我们使用一个循环来计算每个投资期的DCA曲线值。现在,我们可以使用上述函数来计算DCA曲线的值,并将其绘制成图表。最后,我们返回计算得到的DCA曲线值列表。

TechPr的博客 1410

Ubuntu-22.04安装VMware虚拟机并安装Windows10

最近在研究虚拟机,主要集中在KVM、Virtualbox和VMware这三种虚拟机上。前面我已经研究了前两种虚拟机的安装和使用方式,这篇文章介绍3兄弟中最后一个虚拟机安装和使用方式。Virtualbox虚拟机安装和使用KVM虚拟机安装和使用相比于前两种,VMware的安装方式最麻烦,但是综合体验下来我感觉流畅度最好。如果你确实有在Linux上使用VMware虚拟机的需求,那不妨跟着我看下去吧。1、gcc版本和内核版本一定要匹配上,否则会出各种错误。

jiexijihe945的博客 4118

Netty从0到1系列之内置Handler【上】

Netty框架提供了丰富的内置Handler组件,用于处理网络通信中的各类事件。这些开箱即用的Handler可分为编解码、日志、心跳、SSL加密、压缩等类型,构成Netty Pipeline的核心处理链。通过内置Handler的组合使用,开发者可以快速实现网络协议解析、安全通信、流量控制等常见功能,而无需重复造轮子。文章通过架构图展示了Netty Handler的组织结构,为深入理解其工作原理奠定了基础。

编程技术圈 1123

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Netty从0到1系列之基于Netty的开发流程

系统介绍了Netty的核心概念与实践。内容涵盖I/O模型、NIO、Selector、Channel、Buffer、零拷贝技术等基础组件,深入解析EventLoop、Future/Promise、Pipeline、Handler等核心机制,并详细讨论线程模型、内存管理、拆包粘包等关键问题。该系列从入门到进阶,逐步剖析Netty的整体架构、启动流程和底层实现原理,适合开发者系统学习Netty网络编程框架。

编程技术圈 1245

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Dubbo教程-03-netty框架

什么是netty? 1)本质:JBoss做的一个Jar包 2)目的:快速开发高性能、高可靠性的网络服务器和客户端程序 3)优点:提供异步的、事件驱动的网络应用程序框架和工具 通俗的说:一个好使的处理Socket的东东 如果没有Netty? 远古:java.net + java.io 近代:java.nio 其他:Mina,Grizzly netty为什么性能高? 高性能的三...

laomumu1992的博客 309

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

2021-03-23

JDK从1.4版本引入 NIO 的类库,但是其设计有点反人类,使用较为麻烦,需要熟练掌握Selector、 ServerSocketChannel、 SocketChannel、 ByteBuffer等很多类库的使用。 在用NIO类库开发网络程序时还面临大量复杂问题需要解决,例如客户端断线重连、 网络闪断、心跳处理、半包读写、 网络拥塞和异常流的处理等等。Netty作为现在最流行的网络通信框架,对 JDK 自带的 NIO 的类库进行了良好的封装,解决了上述NIO问题。且Netty拥有高性能、 吞吐量更高、延

weixin_55765992的博客 230

深入高性能NIO通信框架,Netty权威详解,大数据时代构建高可用系统利器

本篇适合架构师、设计师、软件开发工程师、测试人员和其他对Java NIO 框架、Java 通信感兴趣的相关人士阅读,相信通过学习本篇,能够熟悉和掌握Netty这一优秀的异步通信框架,实现高可用分布式系统的构建。作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于Netty构建,比如RPC框架、Zookeeper等。如果你觉得你对Netty比较了解,那么试试你是否能通过下方这Netty追魂十一问。

kenzsoft的博客 679

Netty 知识汇总03

1.Netty 是什么? Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。Netty是基于nio的,它封装了jdk的nio,让我们使用起来更加方法灵活。 2.Netty 的特点是什么? 高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发...

郭传志的博客 142

深入理解Netty:高性能网络编程框架详解

Netty 是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。它提供了多种传输协议(如TCP和UDP)的实现,并且对不同协议(如HTTP、WebSocket)的支持良好。Netty广泛应用于高性能网络应用程序中,是Java开发者不可或缺的工具之一。 本文将详细介绍Netty,包括其基本概念、架构设计、核心组件、常用功能及实际应用示例。

fudaihb的博客 1850

netty03-netty基础

what   netty架构   Extensible Event Model-可扩展的事件模型Universal Communication API-统一的通讯APIZero-Copy-Capable Rich Byte Buffer-零拷贝的BufferTransport Services-传输层服务Protocol Support-应用层协议支持 Netty模块/组件

沐宇熙IT成长之路 631

【分享】精通并发与Netty教程

01_学习的要义02_Netty宏观理解03_Netty课程大纲深度解读04_项目环境搭建与Gradle配置05_Netty执行流程分析与重要组件介绍06_Netty回调与Channel执行流程分析07_Netty的Socket编程详解08_Netty多客户端连接与通信09_Netty读写检测机制与长连接要素10_Netty对WebSocket的支援11_Netty实现服务器端与客户端的长连接通信...

buling1122的博客 238
上一篇: 01-IO模型精讲
下一篇: skywalking trace跟踪原理
liuhehe321
博客等级 码龄10年 105粉丝 261原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值