netty

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/curiouslearnerdhh/article/details/66477423
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:


    
    
  1. import io.netty.bootstrap.ServerBootstrap;
  2. import io.netty.channel.ChannelFuture;
  3. import io.netty.channel.ChannelInitializer;
  4. import io.netty.channel.ChannelOption;
  5. import io.netty.channel.EventLoopGroup;
  6. import io.netty.channel.nio.NioEventLoopGroup;
  7. import io.netty.channel.socket.SocketChannel;
  8. import io.netty.channel.socket.nio.NioServerSocketChannel;
  9. import io.netty.handler.logging.LogLevel;
  10. import io.netty.handler.logging.LoggingHandler;
  11. import io.netty.handler.timeout.ReadTimeoutHandler;
  12. public class Server {
  13. public static void main(String[] args) throws Exception{
  14. EventLoopGroup pGroup = new NioEventLoopGroup(); //线程组:用来处理网络事件处理(接受客户端连接)
  15. EventLoopGroup cGroup = new NioEventLoopGroup(); //线程组:用来进行网络通讯读写
  16. //Bootstrap用来配置参数
  17. ServerBootstrap b = new ServerBootstrap();
  18. b.group(pGroup, cGroup)
  19. .channel(NioServerSocketChannel.class) //注册服务端channel
  20. /**
  21. * BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,
  22. * 用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1,将使用默认值50。
  23. * 服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,
  24. * 服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小
  25. */
  26. .option(ChannelOption.SO_BACKLOG, 1024)
  27. //设置日志
  28. .handler( new LoggingHandler(LogLevel.INFO))
  29. .childHandler( new ChannelInitializer<SocketChannel>() {
  30. protected void initChannel(SocketChannel sc) throws Exception {
  31. //marshaliing的编解码操作,要传输对象,必须编解码
  32. sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
  33. sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
  34. //5s没有交互,就会关闭channel
  35. sc.pipeline().addLast( new ReadTimeoutHandler( 5));
  36. sc.pipeline().addLast( new ServerHandler()); //服务端业务处理类
  37. }
  38. });
  39. ChannelFuture cf = b.bind( 8765).sync();
  40. cf.channel().closeFuture().sync();
  41. pGroup.shutdownGracefully();
  42. cGroup.shutdownGracefully();
  43. }
  44. }


客户端代码:


    
    
  1. import io.netty.bootstrap.Bootstrap;
  2. import io.netty.channel.ChannelFuture;
  3. import io.netty.channel.ChannelInitializer;
  4. import io.netty.channel.EventLoopGroup;
  5. import io.netty.channel.nio.NioEventLoopGroup;
  6. import io.netty.channel.socket.SocketChannel;
  7. import io.netty.channel.socket.nio.NioSocketChannel;
  8. import io.netty.handler.logging.LogLevel;
  9. import io.netty.handler.logging.LoggingHandler;
  10. import io.netty.handler.timeout.ReadTimeoutHandler;
  11. import java.util.concurrent.TimeUnit;
  12. public class Client {
  13. private static class SingletonHolder {
  14. static final Client instance = new Client();
  15. }
  16. public static Client getInstance(){
  17. return SingletonHolder.instance;
  18. }
  19. private EventLoopGroup group;
  20. private Bootstrap b;
  21. private ChannelFuture cf ;
  22. private Client(){
  23. group = new NioEventLoopGroup();
  24. b = new Bootstrap();
  25. b.group(group)
  26. .channel(NioSocketChannel.class)
  27. .handler( new LoggingHandler(LogLevel.INFO))
  28. .handler( new ChannelInitializer<SocketChannel>() {
  29. @Override
  30. protected void initChannel(SocketChannel sc) throws Exception {
  31. sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
  32. sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
  33. //超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
  34. sc.pipeline().addLast( new ReadTimeoutHandler( 5));
  35. sc.pipeline().addLast( new ClientHandler()); //客户端业务处理类
  36. }
  37. });
  38. }
  39. public void connect(){
  40. try {
  41. this.cf = b.connect( "127.0.0.1", 8765).sync();
  42. System.out.println( "远程服务器已经连接, 可以进行数据交换..");
  43. } catch (Exception e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. public ChannelFuture getChannelFuture(){
  48. //如果管道没有被开启或者被关闭了,那么重连
  49. if( this.cf == null){
  50. this.connect();
  51. }
  52. if(! this.cf.channel().isActive()){
  53. this.connect();
  54. }
  55. return this.cf;
  56. }
  57. public static void main(String[] args) throws Exception{
  58. final Client c = Client.getInstance();
  59. ChannelFuture cf = c.getChannelFuture();
  60. for( int i = 1; i <= 3; i++ ){
  61. //客户端发送的数据
  62. UserParam request = new UserParam();
  63. request.setId( "" + i);
  64. request.setName( "pro" + i);
  65. request.setRequestMessage( "数据信息" + i);
  66. cf.channel().writeAndFlush(request);
  67. TimeUnit.SECONDS.sleep( 4);
  68. }
  69. //当5s没有交互,就会异步关闭channel
  70. cf.channel().closeFuture().sync();
  71. //再模拟一次传输
  72. new Thread( new Runnable() {
  73. @Override
  74. public void run() {
  75. try {
  76. ChannelFuture cf = c.getChannelFuture();
  77. //System.out.println(cf.channel().isActive());
  78. //System.out.println(cf.channel().isOpen());
  79. //再次发送数据
  80. UserParam request = new UserParam();
  81. request.setId( "" + 4);
  82. request.setName( "pro" + 4);
  83. request.setRequestMessage( "数据信息" + 4);
  84. cf.channel().writeAndFlush(request);
  85. cf.channel().closeFuture().sync();
  86. System.out.println( "子线程结束.");
  87. } catch (InterruptedException e) {
  88. e.printStackTrace();
  89. }
  90. }
  91. }).start();
  92. System.out.println( "断开连接,主线程结束..");
  93. }
  94. }

服务端处理类:

    
    
  1. import io.netty.channel.ChannelHandlerAdapter;
  2. import io.netty.channel.ChannelHandlerContext;
  3. public class ServerHandler extends ChannelHandlerAdapter{
  4. @Override
  5. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  6. }
  7. @Override
  8. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  9. //接受客户端对象
  10. UserParam user = (UserParam)msg;
  11. System.out.println( "客户端发来的消息 : " + user.getId() + ", " + user.getName() + ", " + user.getRequestMessage());
  12. //给客户端返回对象
  13. UserData response = new UserData();
  14. response.setId(user.getId());
  15. response.setName( "response" + user.getId());
  16. response.setResponseMessage( "响应内容" + user.getId());
  17. ctx.writeAndFlush(response);
  18. //处理完毕,关闭服务端
  19. //ctx.addListener(ChannelFutureListener.CLOSE);
  20. }
  21. @Override
  22. public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
  23. }
  24. @Override
  25. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  26. ctx.close();
  27. }
  28. }

客户端处理类:


    
    
  1. import io.netty.channel.ChannelHandlerAdapter;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.util.ReferenceCountUtil;
  4. public class ClientHandler extends ChannelHandlerAdapter{
  5. @Override
  6. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  7. }
  8. @Override
  9. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  10. try {
  11. UserData user = (UserData)msg;
  12. System.out.println( "服务器返回的消息 : " + user.getId() + ", " + user.getName() + ", " + user.getResponseMessage());
  13. } finally {
  14. ReferenceCountUtil.release(msg);
  15. }
  16. }
  17. @Override
  18. public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
  19. }
  20. @Override
  21. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  22. ctx.close();
  23. }
  24. }

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

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


心跳检测:

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

Server业务处理类ServerHeartBeatHandler:


    
    
  1. import io.netty.channel.ChannelFutureListener;
  2. import io.netty.channel.ChannelHandlerAdapter;
  3. import io.netty.channel.ChannelHandlerContext;
  4. import java.util.HashMap;
  5. public class ServerHeartBeatHandler extends ChannelHandlerAdapter {
  6. /**
  7. * key:ip value:auth **
  8. * 拥有的客户端列表,从数据库中或者配置文件中读取
  9. */
  10. private static HashMap<String, String> AUTH_IP_MAP = new HashMap<String, String>();
  11. //模拟授权的key
  12. private static final String SUCCESS_KEY = "auth_success_key";
  13. static {
  14. AUTH_IP_MAP.put( "192.168.1.200", "1234");
  15. }
  16. private boolean auth(ChannelHandlerContext ctx, Object msg){
  17. //System.out.println(msg);
  18. String [] ret = ((String) msg).split( ",");
  19. String auth = AUTH_IP_MAP.get(ret[ 0]);
  20. if(auth != null && auth.equals(ret[ 1])){
  21. ctx.writeAndFlush(SUCCESS_KEY);
  22. return true;
  23. } else {
  24. ctx.writeAndFlush( "auth failure !").addListener(ChannelFutureListener.CLOSE);
  25. return false;
  26. }
  27. }
  28. @Override
  29. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  30. if(msg instanceof String){
  31. auth(ctx, msg);
  32. } else if (msg instanceof RequestInfo) {
  33. //接受客户端发来的他机器的性能参数
  34. RequestInfo info = (RequestInfo) msg;
  35. System.out.println( "--------------------------------------------");
  36. System.out.println( "当前主机ip为: " + info.getIp());
  37. System.out.println( "当前主机cpu情况: ");
  38. HashMap<String, Object> cpu = info.getCpuPercMap();
  39. System.out.println( "总使用率: " + cpu.get( "combined"));
  40. System.out.println( "用户使用率: " + cpu.get( "user"));
  41. System.out.println( "系统使用率: " + cpu.get( "sys"));
  42. System.out.println( "等待率: " + cpu.get( "wait"));
  43. System.out.println( "空闲率: " + cpu.get( "idle"));
  44. System.out.println( "当前主机memory情况: ");
  45. HashMap<String, Object> memory = info.getMemoryMap();
  46. System.out.println( "内存总量: " + memory.get( "total"));
  47. System.out.println( "当前内存使用量: " + memory.get( "used"));
  48. System.out.println( "当前内存剩余量: " + memory.get( "free"));
  49. System.out.println( "--------------------------------------------");
  50. //通知客户端消息已收到
  51. ctx.writeAndFlush( "info received!");
  52. } else {
  53. ctx.writeAndFlush( "connect failure!").addListener(ChannelFutureListener.CLOSE);
  54. }
  55. }
  56. }

Client业务处理类ClienHeartBeattHandler:


    
    
  1. import io.netty.channel.ChannelHandlerAdapter;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.util.ReferenceCountUtil;
  4. import java.net.InetAddress;
  5. import java.util.HashMap;
  6. import java.util.concurrent.Executors;
  7. import java.util.concurrent.ScheduledExecutorService;
  8. import java.util.concurrent.ScheduledFuture;
  9. import java.util.concurrent.TimeUnit;
  10. import org.hyperic.sigar.CpuPerc;
  11. import org.hyperic.sigar.Mem;
  12. import org.hyperic.sigar.Sigar;
  13. public class ClienHeartBeattHandler extends ChannelHandlerAdapter {
  14. private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 1);
  15. private ScheduledFuture<?> heartBeat;
  16. //主动向服务器发送认证信息
  17. private InetAddress addr ;
  18. private static final String SUCCESS_KEY = "auth_success_key";
  19. @Override
  20. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  21. addr = InetAddress.getLocalHost();
  22. String ip = addr.getHostAddress();
  23. String key = "1234";
  24. //证书
  25. String auth = ip + "," + key;
  26. ctx.writeAndFlush(auth);
  27. }
  28. @Override
  29. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  30. try {
  31. if(msg instanceof String){
  32. String ret = (String)msg;
  33. if(SUCCESS_KEY.equals(ret)){
  34. // 握手成功,主动发送心跳消息
  35. this.heartBeat = this.scheduler.scheduleWithFixedDelay( new HeartBeatTask(ctx), 0, 2, TimeUnit.SECONDS);
  36. System.out.println(msg);
  37. }
  38. else {
  39. System.out.println(msg);
  40. }
  41. }
  42. } finally {
  43. ReferenceCountUtil.release(msg);
  44. }
  45. }
  46. private class HeartBeatTask implements Runnable {
  47. private final ChannelHandlerContext ctx;
  48. public HeartBeatTask(final ChannelHandlerContext ctx) {
  49. this.ctx = ctx;
  50. }
  51. @Override
  52. public void run() {
  53. try {
  54. RequestInfo info = new RequestInfo();
  55. //ip
  56. info.setIp(addr.getHostAddress());
  57. Sigar sigar = new Sigar();
  58. //cpu prec
  59. CpuPerc cpuPerc = sigar.getCpuPerc();
  60. HashMap<String, Object> cpuPercMap = new HashMap<String, Object>();
  61. cpuPercMap.put( "combined", cpuPerc.getCombined());
  62. cpuPercMap.put( "user", cpuPerc.getUser());
  63. cpuPercMap.put( "sys", cpuPerc.getSys());
  64. cpuPercMap.put( "wait", cpuPerc.getWait());
  65. cpuPercMap.put( "idle", cpuPerc.getIdle());
  66. // memory
  67. Mem mem = sigar.getMem();
  68. HashMap<String, Object> memoryMap = new HashMap<String, Object>();
  69. memoryMap.put( "total", mem.getTotal() / 1024L);
  70. memoryMap.put( "used", mem.getUsed() / 1024L);
  71. memoryMap.put( "free", mem.getFree() / 1024L);
  72. info.setCpuPercMap(cpuPercMap);
  73. info.setMemoryMap(memoryMap);
  74. ctx.writeAndFlush(info);
  75. } catch (Exception e) {
  76. e.printStackTrace();
  77. }
  78. }
  79. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  80. cause.printStackTrace();
  81. if (heartBeat != null) {
  82. heartBeat.cancel( true);
  83. heartBeat = null;
  84. }
  85. ctx.fireExceptionCaught(cause);
  86. }
  87. }
  88. }

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工具类:


    
    
  1. import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
  2. import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
  3. import io.netty.handler.codec.marshalling.MarshallerProvider;
  4. import io.netty.handler.codec.marshalling.MarshallingDecoder;
  5. import io.netty.handler.codec.marshalling.MarshallingEncoder;
  6. import io.netty.handler.codec.marshalling.UnmarshallerProvider;
  7. import org.jboss.marshalling.MarshallerFactory;
  8. import org.jboss.marshalling.Marshalling;
  9. import org.jboss.marshalling.MarshallingConfiguration;
  10. public final class MarshallingCodeCFactory {
  11. /**
  12. * 创建Jboss Marshalling解码器MarshallingDecoder
  13. * @return MarshallingDecoder
  14. */
  15. public static MarshallingDecoder buildMarshallingDecoder() {
  16. //首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
  17. final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory( "serial");
  18. //创建了MarshallingConfiguration对象,配置了版本号为5
  19. final MarshallingConfiguration configuration = new MarshallingConfiguration();
  20. configuration.setVersion( 5);
  21. //根据marshallerFactory和configuration创建provider
  22. UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
  23. //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
  24. MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024);
  25. return decoder;
  26. }
  27. /**
  28. * 创建Jboss Marshalling编码器MarshallingEncoder
  29. * @return MarshallingEncoder
  30. */
  31. public static MarshallingEncoder buildMarshallingEncoder() {
  32. final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory( "serial");
  33. final MarshallingConfiguration configuration = new MarshallingConfiguration();
  34. configuration.setVersion( 5);
  35. MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
  36. //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
  37. MarshallingEncoder encoder = new MarshallingEncoder(provider);
  38. return encoder;
  39. }
  40. }

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作为基础

				<script>
					(function(){
						function setArticleH(btnReadmore,posi){
							var winH = $(window).height();
							var articleBox = $("div.article_content");
							var artH = articleBox.height();
							if(artH > winH*posi){
								articleBox.css({
									'height':winH*posi+'px',
									'overflow':'hidden'
								})
								btnReadmore.click(function(){
									articleBox.removeAttr("style");
									$(this).parent().remove();
								})
							}else{
								btnReadmore.parent().remove();
							}
						}
						var btnReadmore = $("#btn-readmore");
						if(btnReadmore.length>0){
							if(currentUserName){
								setArticleH(btnReadmore,3);
							}else{
								setArticleH(btnReadmore,1.2);
							}
						}
					})()
				</script>
				</article>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值