Netty

Netty 自定义 RPC 协议教程 目录Netty 自定义 RPC 协议教程一、RPC 协议概述二、Netty 简介三、自定义 RPC 协议设计四、Netty 实现自定义 RPC 协议五、运行示例六、总结在分布式系统中,RPC(Remote Procedure Call,远程过程调用)是一种常见的通信方式。通过 RPC,客户端可以像调用本地函数一样调用远程服务器上的函数,而无需关心底层的网络通信细节。Netty 是一个异步的、事件驱动的网络应用框架,它提供了对 TCP、UDP 和文件传输的支持,非常适合用于实现 RPC 协议。 阅读详情
Netty概述:
1、netty是基于Java NIO的网络应用框架,client-server框架
2、Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,
作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,
通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。
3、作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,
一些业界著名的开源组件也基于Netty的NIO框架构建。

Netty创建步骤:

NIO通讯服务端步骤:
1、创建ServerSocketChannel,为它配置非阻塞模式
2、绑定监听,配置TCP参数,录入backlog大小等
3、创建一个独立的IO线程,用于轮询多路复用器Selector
4、创建Selector,将之前的ServerSocketChannel注册到Selector上,并设置监听标识位SelectionKey.ACCEPT
5、启动IO线程,在循环体中执行Selector.select()方法,轮询就绪的通道
6、当轮询到处于就绪的通道时,需要进行判断操作位,如果是ACCEPT状态,说明是新的客户端介入,则调用accept方法接受新的客户端。
7、设置新接入客户端的一些参数,并将其通道继续注册到Selector之中。设置监听标识等
8、如果轮询的通道操作位是READ,则进行读取,构造Buffer对象等
9、更细节的还有数据没发送完成继续发送的问题


Netty实现通讯的步骤:
1、创建两个NIO线程组,一个专门用来网络事件处理(接受客户端连接),另一个则进行网络通讯读写
2、创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传入数据的缓存大小等。
3、创建一个实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置传入数据的字符集,格式,实现实际处理数据的接口。
4、绑定端口,执行同步阻塞方法等待服务器启动即可。

当对于NIO模型,netty简单、健壮、性能稳定,而且这几步都是模板式开发,以后可以直接用,开发只需专注实际处理数据类的实现。


Netty最佳实践(数据通讯、心跳检测)

netty服务最好可以单独作为一个项目,当然也可以与web项目集成在一起发布到tomcat,
这样好处是可以用到web项目中的service方法,但是web项目8080关闭,netty监听的端口号也关闭了
所以netty可以打成jar包运行,当然如果要用到service层的代码,也可以将service层的代码打成jar包
给netty业务类使用。

netty通讯的方式:
①使用长连接通道不断开的形式进行通信,也就是服务器和客户端的通道一直处于开启状态,如果服务器的
性能比较好,而且客户端的数量也不多的情况下,可以考虑这种方式
②一次性批量提交数据,采用短连接的方式,也就是我们把数据保存在本地临时缓冲区或者临时表中,
当达到临界值时进行一次性批量提交,又或者根据定时任务轮询提交,这种情况下弊端是做不到
实时性传输,在实时性要求不高的程序中可以采用
③采用一种特殊的长连接,在指定某一段时间之内,服务端和某台客户端没有任何通讯,则断开连接,

下次如果客户端要向服务端发送数据时,再次建立连接。

但有两个因素要考虑:

1、如何在超时(即服务端和客户端没有任何通信)后关闭通道?关闭后如何再次连接?

2、客户端宕机,无需考虑,下次客户端重启后可以与服务端建立连接,但是服务器宕机怎么办?

服务端代码Server:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;

public class Server {

	public static void main(String[] args) throws Exception{
		
		EventLoopGroup pGroup = new NioEventLoopGroup(); //线程组:用来处理网络事件处理(接受客户端连接)
		EventLoopGroup cGroup = new NioEventLoopGroup(); //线程组:用来进行网络通讯读写
		
		//Bootstrap用来配置参数
		ServerBootstrap b = new ServerBootstrap();
		b.group(pGroup, cGroup)
		 .channel(NioServerSocketChannel.class) //注册服务端channel
		 /**
		  * BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,
		  * 用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1,将使用默认值50。
		  * 服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,
		  * 服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小
		  */
		 .option(ChannelOption.SO_BACKLOG, 1024)
		 //设置日志
		 .handler(new LoggingHandler(LogLevel.INFO))
		 .childHandler(new ChannelInitializer<SocketChannel>() {
			protected void initChannel(SocketChannel sc) throws Exception {
				//marshaliing的编解码操作,要传输对象,必须编解码
				sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
				sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
				//5s没有交互,就会关闭channel
				sc.pipeline().addLast(new ReadTimeoutHandler(5)); 
				sc.pipeline().addLast(new ServerHandler());   //服务端业务处理类
			}
		});
		ChannelFuture cf = b.bind(8765).sync();
		
		cf.channel().closeFuture().sync();
		pGroup.shutdownGracefully();
		cGroup.shutdownGracefully();
	}
}


客户端代码:

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.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import java.util.concurrent.TimeUnit;

public class Client {
	
	private static class SingletonHolder {
		static final Client instance = new Client();
	}
	
	public static Client getInstance(){
		return SingletonHolder.instance;
	}
	
	private EventLoopGroup group;
	private Bootstrap b;
	private ChannelFuture cf ;
	
	private Client(){
			group = new NioEventLoopGroup();
			b = new Bootstrap();
			b.group(group)
			 .channel(NioSocketChannel.class)
			 .handler(new LoggingHandler(LogLevel.INFO))
			 .handler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel sc) throws Exception {
						sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
						sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
						//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
						sc.pipeline().addLast(new ReadTimeoutHandler(5)); 
						sc.pipeline().addLast(new ClientHandler());  //客户端业务处理类
					}
		    });
	}
	
	public void connect(){
		try {
			this.cf = b.connect("127.0.0.1", 8765).sync();
			System.out.println("远程服务器已经连接, 可以进行数据交换..");				
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public ChannelFuture getChannelFuture(){
		//如果管道没有被开启或者被关闭了,那么重连
		if(this.cf == null){
			this.connect();
		}
		if(!this.cf.channel().isActive()){
			this.connect();
		}
		return this.cf;
	}
	
	public static void main(String[] args) throws Exception{
		final Client c = Client.getInstance();
		
		ChannelFuture cf = c.getChannelFuture();
		for(int i = 1; i <= 3; i++ ){
			//客户端发送的数据
			UserParam request = new UserParam();
			request.setId("" + i);
			request.setName("pro" + i);
			request.setRequestMessage("数据信息" + i);
			
			cf.channel().writeAndFlush(request);
			TimeUnit.SECONDS.sleep(4);
		}
		//当5s没有交互,就会异步关闭channel
		cf.channel().closeFuture().sync();
		
		//再模拟一次传输
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ChannelFuture cf = c.getChannelFuture();
					//System.out.println(cf.channel().isActive());
					//System.out.println(cf.channel().isOpen());
					
					//再次发送数据
					UserParam request = new UserParam();
					request.setId("" + 4);
					request.setName("pro" + 4);
					request.setRequestMessage("数据信息" + 4);
					
					cf.channel().writeAndFlush(request);					
					cf.channel().closeFuture().sync();
					System.out.println("子线程结束.");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();
		
		System.out.println("断开连接,主线程结束..");
	}
}

服务端处理类:
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class ServerHandler extends ChannelHandlerAdapter{

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {

	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//接受客户端对象
		UserParam user = (UserParam)msg;
		System.out.println("客户端发来的消息 : " + user.getId() + ", " + user.getName() + ", " + user.getRequestMessage());
		//给客户端返回对象
		UserData response = new UserData();
		response.setId(user.getId());
		response.setName("response" + user.getId());
		response.setResponseMessage("响应内容" + user.getId());
		ctx.writeAndFlush(response);
		//处理完毕,关闭服务端
		//ctx.addListener(ChannelFutureListener.CLOSE);
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
}

客户端处理类:

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;

public class ClientHandler extends ChannelHandlerAdapter{
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		try {
			UserData user = (UserData)msg;
			System.out.println("服务器返回的消息  : " + user.getId() + ", " + user.getName() + ", " + user.getResponseMessage());			
		} finally {
			ReferenceCountUtil.release(msg);
		}
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
}

客户端传输的参数对象UserParam -- > id  name  requestMessage

服务端传输的参数对象UserData   -- >  id  name responseMessage


心跳检测:

Server代码,Client代码是模板代码,基本都一样,不同是业务处理的方法。

Server业务处理类ServerHeartBeatHandler:

import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

import java.util.HashMap;

public class ServerHeartBeatHandler extends ChannelHandlerAdapter {
    
	/**
	 * key:ip value:auth **
	 * 拥有的客户端列表,从数据库中或者配置文件中读取
	 */
	private static HashMap<String, String> AUTH_IP_MAP = new HashMap<String, String>();
	//模拟授权的key
	private static final String SUCCESS_KEY = "auth_success_key";
	
	static {
		AUTH_IP_MAP.put("192.168.1.200", "1234");
	}
	
	private boolean auth(ChannelHandlerContext ctx, Object msg){
			//System.out.println(msg);
			String [] ret = ((String) msg).split(",");
			String auth = AUTH_IP_MAP.get(ret[0]);
			if(auth != null && auth.equals(ret[1])){
				ctx.writeAndFlush(SUCCESS_KEY);
				return true;
			} else {
				ctx.writeAndFlush("auth failure !").addListener(ChannelFutureListener.CLOSE);
				return false;
			}
	}
	
	@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if(msg instanceof String){
			auth(ctx, msg);
		} else if (msg instanceof RequestInfo) {
			//接受客户端发来的他机器的性能参数
			RequestInfo info = (RequestInfo) msg;
			System.out.println("--------------------------------------------");
			System.out.println("当前主机ip为: " + info.getIp());
			System.out.println("当前主机cpu情况: ");
			HashMap<String, Object> cpu = info.getCpuPercMap();
			System.out.println("总使用率: " + cpu.get("combined"));
			System.out.println("用户使用率: " + cpu.get("user"));
			System.out.println("系统使用率: " + cpu.get("sys"));
			System.out.println("等待率: " + cpu.get("wait"));
			System.out.println("空闲率: " + cpu.get("idle"));
			
			System.out.println("当前主机memory情况: ");
			HashMap<String, Object> memory = info.getMemoryMap();
			System.out.println("内存总量: " + memory.get("total"));
			System.out.println("当前内存使用量: " + memory.get("used"));
			System.out.println("当前内存剩余量: " + memory.get("free"));
			System.out.println("--------------------------------------------");
			
			//通知客户端消息已收到
			ctx.writeAndFlush("info received!");
		}else {
			ctx.writeAndFlush("connect failure!").addListener(ChannelFutureListener.CLOSE);
		}
    }


}

Client业务处理类ClienHeartBeattHandler:

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.Sigar;

public class ClienHeartBeattHandler extends ChannelHandlerAdapter {

    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
    private ScheduledFuture<?> heartBeat;
	//主动向服务器发送认证信息
    private InetAddress addr ;
    
    private static final String SUCCESS_KEY = "auth_success_key";

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		addr = InetAddress.getLocalHost();
        String ip = addr.getHostAddress();
		String key = "1234";
		//证书
		String auth = ip + "," + key;
		ctx.writeAndFlush(auth);
	}
	
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    	try {
        	if(msg instanceof String){
        		String ret = (String)msg;
        		if(SUCCESS_KEY.equals(ret)){
        	    	// 握手成功,主动发送心跳消息
        	    	this.heartBeat = this.scheduler.scheduleWithFixedDelay(new HeartBeatTask(ctx), 0, 2, TimeUnit.SECONDS);
        		    System.out.println(msg);    			
        		}
        		else {
        			System.out.println(msg);
        		}
        	}
		} finally {
			ReferenceCountUtil.release(msg);
		}
    }

    private class HeartBeatTask implements Runnable {
    	private final ChannelHandlerContext ctx;

		public HeartBeatTask(final ChannelHandlerContext ctx) {
		    this.ctx = ctx;
		}
	
		@Override
		public void run() {
			try {
			    RequestInfo info = new RequestInfo();
			    //ip
			    info.setIp(addr.getHostAddress());
		        Sigar sigar = new Sigar();
		        //cpu prec
		        CpuPerc cpuPerc = sigar.getCpuPerc();
		        HashMap<String, Object> cpuPercMap = new HashMap<String, Object>();
		        cpuPercMap.put("combined", cpuPerc.getCombined());
		        cpuPercMap.put("user", cpuPerc.getUser());
		        cpuPercMap.put("sys", cpuPerc.getSys());
		        cpuPercMap.put("wait", cpuPerc.getWait());
		        cpuPercMap.put("idle", cpuPerc.getIdle());
		        // memory
		        Mem mem = sigar.getMem();
				HashMap<String, Object> memoryMap = new HashMap<String, Object>();
				memoryMap.put("total", mem.getTotal() / 1024L);
				memoryMap.put("used", mem.getUsed() / 1024L);
				memoryMap.put("free", mem.getFree() / 1024L);
				info.setCpuPercMap(cpuPercMap);
			    info.setMemoryMap(memoryMap);
			    ctx.writeAndFlush(info);
			    
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
	    	cause.printStackTrace();
			if (heartBeat != null) {
			    heartBeat.cancel(true);
			    heartBeat = null;
			}
			ctx.fireExceptionCaught(cause);
	    }
	    
	}
}

netty编解码技术:
java序列化技术,序列化目的:
①网络传输(网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式)
②对象持久化(对象必须在JVM中存活,不可能超过JVM的生命周期)


虽然我们可以使用java进行对象序列化,netty去传输,但是java序列化的硬伤太多:
1.无法跨语言。这应该是java序列化最致命的问题了。
由于java序列化是java内部私有的协议,其他语言不支持,导致别的语言无法反序列化,这严重阻碍了它的应用。
关于跨语言问题,也就是对象传输,一般都采用json字符串。
2.序列后的码流太大。java序列化的大小是二进制编码的5倍多!
3.序列化性能太低。java序列化的性能只有二进制编码的6.17倍,可见java序列化性能实在太差了。

我们判断一个编码框架的优劣主要从以下几个方面:
1.是否支持跨语言,支持语种是否丰富
2.编码后的码流
3.编解码的性能
4.类库是否小巧,API使用是否方便

5.使用者开发的工作量和难度。

java序列化前3条变现太差,导致在远程服务调用中很少用它

主流的编解码框架:
①JBoss的Marshalling包:
对jdk默认的序列化进行了优化,又保持跟java.io.Serializable接口的兼容,同时增加了一些可调的参数和附加特性,
并且这些参数和特性可通过工厂类的配置
1.可拔插的类解析器,提供更加便捷的类加载定制策略,通过一个接口即可实现定制。
2.可拔插的对象替换技术,不需要通过继承的方式。
3.可拔插的预定义类缓存表,可以减少序列化的字节数组长度,提升常用类型的对象序列化性能。
4.无须实现java.io.Serializable接口
5.通过缓存技术提升对象的序列化性能。
6.使用非常简单
②google的Protobuf
③基于Protobuf的Kyro
④MessagePack框架

Marshalling工具类:

import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallingDecoder;
import io.netty.handler.codec.marshalling.MarshallingEncoder;
import io.netty.handler.codec.marshalling.UnmarshallerProvider;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

public final class MarshallingCodeCFactory {
    /**
     * 创建Jboss Marshalling解码器MarshallingDecoder
     * @return MarshallingDecoder
     */
    public static MarshallingDecoder buildMarshallingDecoder() {
    	//首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
		final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
		//创建了MarshallingConfiguration对象,配置了版本号为5 
		final MarshallingConfiguration configuration = new MarshallingConfiguration();
		configuration.setVersion(5);
		//根据marshallerFactory和configuration创建provider
		UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
		//构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
		MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024);
		return decoder;
    }

    /**
     * 创建Jboss Marshalling编码器MarshallingEncoder
     * @return MarshallingEncoder
     */
    public static MarshallingEncoder buildMarshallingEncoder() {
		final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
		final MarshallingConfiguration configuration = new MarshallingConfiguration();
		configuration.setVersion(5);
		MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
		//构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
		MarshallingEncoder encoder = new MarshallingEncoder(provider);
		return encoder;
    }
}

RPC(Remote Procedure Call):
RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,
由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

比如远程调用方法:Employee getEmployeeByName(String fullName)
1、要解决通讯的问题,主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。
连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。

2、要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,
如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么,这样才能完成调用。
比如基于Web服务协议栈的RPC,就要提供一个endpoint URI,或者是从UDDI服务上查找。
如果是RMI调用的话,还需要一个RMI Registry来注册服务的地址。

3、要解决编码的问题,当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,
由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),
通过寻址和传输将序列化的二进制发送给B服务器。

4、要解决解码的问题,B服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,
然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。

5、返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,
恢复为内存中的表达方式,交给A服务器上的应用

为什么RPC呢?
就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,
比如不同的系统间的通讯,甚至不同的组织间的通讯。由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用。


而Netty框架不局限于RPC,更多的是作为一种网络协议的实现框架,
由于RPC需要高效的网络通信,就可能选择以Netty作为基础

Netty实现RPC服务器之出、入站处理器 netty使用了一种事件驱动的应用范式,因此数据处理的管道是一个经过处理器的事件链。事件和处理器可以关联到xxInbound入站 与xxOutbound出站 数据流。消息类型处理特定类型的消息,使用泛型指定。自动释放:Netty 会在方法完成后自动释放消息,避免内存泄漏。线程安全中的代码通常在 I/O 线程中执行,确保线程安全。错误处理:在方法中处理异常,避免未捕获的异常导致应用程序崩溃。通过使用,你可以简化处理入站消息的代码,提高代码的可读性和可维护性。 阅读详情

相关推荐

【Netty入门】基于Netty的Server / Client

回顾NIO我在之前介绍了NIO的server与client,虽然说编程相对于BIO比较复杂,但是性能高啊,而且Netty的server/client也是基于NIO的。所以,在介绍Netty的server/client 之前,先回顾一下NIO 的server / Client的步骤还是很有你要的.1.NIO 服务端Server开发步骤:(1)创建ServerSocketChannel,配置它为非阻塞模

白夜行 1万+

IO、Netty与RPC设计笔记

目录第一节 Socket 回顾与I/0模型1.1 Socket 网络编程回顾1.1.1 Socket 概述1.1.2 Socket 整体流程1.1.3 代码实现1.2 I/O 模型1.2.1 I/O 模型说明1.2.2 BIO (同步并阻塞)1.2.3 NIO(同步非阻塞)1.2.4 AIO(异步非阻塞)1.2.5 BIO、NIO、AIO 适用场景分析第二节 NIO编程2.1 NIO介绍2.2 NIO和 BIO的比较2.3 NIO 三大核心原理示意图2.4 缓冲区(Buffer)2.4.1 基本介绍2.4.

杜莱的博客 991

Netty札记-为什么要学习Netty

在学习Netty之前,有必要了解下我们为什么要学习Netty。 什么是Netty Netty User guide for 4.x中这样介绍 Netty是一个提供异步事件驱动的网络应用框架,是一个用以快速开发高性能、可扩展协议的服务器和客户端的工具。 换句话说,Netty 是一个 NIO 客户端服务器框架,使用它可以快速简单地开发网络应用程序,比如服务器和客户端的协议。Net

潘威威的博客 5465

使用Netty实现RPC

Netty作为一个异步事件驱动的网络应用框架,可以用于快速开发可维护的高性能服务器和客户端。国内著名的RPC框架Dubbo底层使用的是Netty作为网络通信的。本篇文章我们来探索一下RPC框架的本质以及使用Netty来实现一个简单地RPC框架。 1. RPC是什么 RPC(Remote Procedure Call),翻译成中文就是远程过程调用。远程过程就是相对于本地方法而言的,是运行在某个远程的地方而不是本地。通过RPC可以实现像本地函数调用一样调用远程服务,是一种进程间的通信方式。RPC调用的本质可

卓立的博客 3404

Netty-Netty实现自己的通信框架

客户首先与根服务器之一联系,它将返回顶级域名 com 的 TLD 服务器的 IP 地址。最后,该 客户与 baidu.com 权威服务器之一联系,它为主机名 www.baidu.com 返回其 IP 地址。有另一类重要的 DNS 服务器,称为本地 DNS 服务器( local DNS server)。根 DNS 服务器。DNS 一个由分层的服务系统,大致说来,有 3 种类型的 DNS 服务器:根 DNS 服务器、顶。当主机发出 DNS 请求时,该请求被发往本地 DNS 服务器,它起着代理的作用,并将该请。

weixin_43874650的博客 1353

基于netty实现的支持长连接的rpc

1、下载后导入eclipse(maven工程) 2、先运行ServerTest 3、再运行ClientTest,即可看到输出结果 4、保证可用,亲手编写,欢迎大家指正不足

Netty原理与源码分析(4) - Netty源码解读之底层原理

假设客户端分别发送了两个数据包ABC和DEF给服务端,由于服务端一次读取到的字节数是不确定的,故可能存在以下4种情况1、服务端分两次读取到了两个独立的数据包,分别是ABC和DDEF,没有粘包和拆包;2、服务端一次接收到了两个数据包,ABC和DEF粘合在一起,被称为TCP粘包;3、服务端分两次读取到了两个数据包,第一次读取到了完整的ABC包和DEF包的部分内容(ABCD),第二次读取到了DEF包的剩余内容(EF),这被称为TCP拆包。

How - Liked | Blog 2038

NettyRpc:一个基于Netty,ZooKeeper和Spring的简单RPC框架

NettyRpc 基于Netty,ZooKeeper和Spring的RPC框架中文详情: 特征: 简单的代码和框架 ZooKeeper的服务注册表/发现支持 高可用性,负载平衡和故障转移 支持不同的负载均衡策略 支持异步/同步调用 支持不同版本的服务 支持不同的串行器/解串器 使用心跳检测无效的TCP连接 设计: 使用方法(netty-rpc-test) 定义一个接口: public interface HelloService { String hello(String name); String hello(Person person); } 用注解@NettyRpcService实现接口: @NettyRpcService(HelloService.class, version = "1.0") public class HelloServiceImpl

Netty 是什么,我们为什么使用 Netty

为什么要学习Netty: 在java的网络编程方面,最开始的BIO 到现在被广泛使用的NIO,它们都能完成网络编程的工作,但是使用原生的java API去完成网络编程,往往非常困难。连接管理、消息分割、数据编解码,还有对应应用层协议的实现,这些对开发人员的要求非常高。所以使用和学习Netty这样的网络框架是非常有必要的。 Netty是什么: Netty是对 Java NIO 的进一步封装,实现了众多的协议,并且提供了更加易用的API来帮助我们进行网络编程。本篇博客对Netty的概念进行了详细说名,并且介绍

不会骑摩托的程序员不是好的摄影师 4610

Wireshark、Fiddler、Charles 三款抓包工具的本质差异与选型指南

抓包工具是网络调试与接口分析的基础能力,其核心在于理解OSI模型分层原理——不同工具工作在不同协议层级,解决不同维度问题。Wireshark面向网络层(L2/L3),可捕获原始数据帧与TCP握手细节,适用于DNS异常、丢包重传等底层问题定位;Fiddler和Charles则工作在应用层(L7),作为HTTP(S)代理实现HTTPS解密、请求改写与弱网模拟,是API调试与移动端测试的首选。技术价值体现在:精准匹配问题域(如证书固定需Fiddler脚本绕过,iOS调试依赖Charles证书信任链),避免‘用显微

weixin_28831341的博客 253

CorelDRAW 2019安装指南:从下载到激活的完整步骤

本文提供了一份详细的CorelDRAW 2019安装指南,涵盖从下载安装包、检查系统环境到完成激活的完整步骤。文章重点解析了安装过程中的关键选项设置,并分享了首次启动后的优化配置与常见问题解决方案,旨在帮助用户高效、顺利地完成这款经典矢量设计软件的安装与配置。

weixin_29213827的博客 829

Netty入门 -- 什么是Netty?

通俗易懂,一文让你搞懂Netty~

wanghuichen的博客 2329

c++11 无锁队列

/************************************************************************* &gt; File Name: lockfreequeue.cpp &gt; Author: &gt; Mail: &gt; Created Time: Wed 08 Aug 2018 11:38:08 AM CST ********...

qq_35728402的博客 5021

Netty学习——Netty入门

二. Netty 入门 1. 概述 1.1 Netty 是什么? Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty 是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端。 这里的异步是指Netty采用了

maruofan666的博客 1984

RS485设备联网避坑指南:塔石串口服务器在MODBUS平台中的3个关键配置项

本文针对塔石串口服务器在MODBUS物联网平台接入中的常见问题,详细解析了三个关键配置项:工作模式选择、远程端口与保活机制、寄存器映射与轮询策略。通过对比错误与正确配置案例,帮助工程师避开配置陷阱,实现RS-485设备的稳定、高效联网,提升批量部署的成功率与系统可靠性。

weixin_29281941的博客 405

机器人动力学仿真软件:Webots_(14).机器人声音仿真

Webots 中的声音仿真主要通过Sound和两种节点来实现。Sound节点用于播放预定义的音频文件,而节点则允许在仿真过程中动态生成和播放声音。这些节点可以用于模拟机器人的语音输出、报警声、环境噪音等。以下是一个示例,展示如何在控制器代码中播放预定义的声音。// 控制器代码(.c) # include <webots/robot.h> # include <webots/sound.h> # define TIME_STEP 64 const char * sound_name = "sound";

kkchenjj的博客 1311

Netty入门(一)环境搭建及使用

本文介绍了使用Netty框架实现简单服务端程序的步骤:1. 在Eclipse中创建Maven项目并添加Netty依赖;2. 编写自定义消息处理器类HelloServerHandler,实现接收客户端消息并输出到控制台;3. 创建HelloServer类配置Netty服务器参数,包括线程组、通道初始化等;4. 使用telnet作为客户端进行测试,验证服务端能正确接收并显示客户端发送的消息。整个过程展示了Netty服务端的基本实现流程,从项目配置到代码编写再到功能测试。

ywl470812087的博客 16万+

芒果YOLOv10改进31:特征融合Neck篇:改进特征融合网络 BiFPN 结构,融合更多有效特征

YOLOv10改进:特征融合Neck篇:改进特征融合网络 BiFPN 结构,融合更多有效特征

包括YOLOv5、YOLOv7、YOLOv8等模型改进 684

【Netty】手写Netty

文章目录1. 目的2 手写netty架构的nio代码2.1 单线程的nio架构2.2 改写为多线程架构3. 原理分析4. 总结 1. 目的 我们知道Netty优点很多以及netty原理的底层机制,但是是否真正理解呢?如果真的理解了那么自然回答出一下几个问题: 1.你能看的懂netty源码吗 2.你能讲出netty源码的架构吗 3.netty中的NioEventLoop对应一个线程,并绑定了一个selector,为何主Reactor中的NioEventLoop可以负责接收请求,而从Reactor中的NioE

m0_45406092的博客 1266

深入剖析 YOLOv11 中的 C2PSA 注意力机制

YOLOv11中的C2PSA注意力机制在目标检测领域取得突破性进展。该机制通过跨通道分组交互和轻量级位置编码,在仅增加0.6%计算量的情况下提升mAP 1.5-2.3个百分点。C2PSA创新性地结合局部跨通道交互、位置编码和双分支并行调制,采用纯卷积实现自注意力,避免传统注意力机制的高计算复杂度。实验表明,C2PSA在COCO数据集上以微小计算代价显著提升检测性能,特别是对小目标和遮挡目标效果突出。该模块可直接嵌入YOLO架构,为轻量级目标检测提供新的优化方向。

shrgegrb的博客 3397

【零基础入门unity游戏开发——2D篇】SpriteEditor图片编辑器

【零基础入门unity游戏开发——2D篇】SpriteEditor图片编辑器

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 2329

Netty入门教程——认识Netty

我们知道,Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。NIO和BIO的区别主要是在第一步。多路复用IO,他的两个步骤处理是分开的,也就是说,一个连接可能他的数据接收是线程a完成的,数据处理是线程b完成的,他比BIO能处理更多请求。原因就是图二中出现的。

lonelykkk的博客 1439

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Netty原理 & 手写简化版Netty

Netty原理 & 手写简化版Netty背景&目的手写简化版NettyNetty 原理Reactor 线程模型简化为两个线程组Selector 事件注册事件响应与处理Channel 通道NettyChannel 通道Pipeline 职责链ByteBuf 复用机制总结 背景&目的 本文提出netty有这么4个核心概念: Reactor线程模型:一种高性能的多线程程序设计思...

香菇鸡仔的博客 1924

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

netty原理梳理及netty相关实例

netty之原理梳理及相关实例 前言:netty之初学总结,主要是对netty原理的梳理及netty相关实例的整合。总结如下:一张图,一个项目。手动biu一下自己,好好学习,天天向上 原理图 这张原理图旨在提供一个netty的全局学习思路,需自行深入学习每个模块,作为底层框架的netty,想要很好的理解它,每个模块都值得深入研究哦。 相关实例整合项目 项目git地址 本项目仅用于学习交流,其中如有不当之处欢迎指正。 整个项目包括以下几个实例: BIO->NIO->NETTY过程演化对比实例(

swhuan007的博客 335

Netty 5用户指南

本文介绍了Netty网络框架的核心功能和使用方法,重点展示了如何利用Netty快速开发自定义协议的网络应用。文章首先指出通用协议(如HTTP)无法满足特定场景需求(如大文件传输、实时消息等)的问题,提出使用Netty可以轻松实现定制化协议。然后通过三个渐进式实例演示Netty的核心功能:1. DISCARD服务(丢弃所有数据的简单协议);2. ECHO服务(请求-响应协议);3. TIME服务(发送时间戳后关闭连接),并详细讲解了客户端实现。文章特别强调了Netty处理TCP流式数据的解决方案

ywl470812087的博客 10万+
上一篇: tomcat并发峰值,JVM参数查看
下一篇: 网络通讯概念
wive
博客等级 码龄14年 33粉丝 119原创
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值