XML configuration file parsing process of Dubbo series notes

AYSAML 2020-11-10 12:44:24
xml configuration file parsing process


One 、 introduction

* as everyone knows

Netty It's based on NIO Customer 、 Server side Java Open source programming framework , Provide asynchronous 、 Event driven network application framework and tools , For rapid development of high performance 、 Highly reliable network server and client programs .

* Popular speaking

Netty A very useful treatment Socket Of Jar package , You can use it to develop servers and clients .

Two 、 Why study Netty

Netty As an excellent network communication framework , Many open source projects use it to build the communication layer . such as Hadoop、Cassandra、Spark、Dubbo、gRPC、RocketMQ、Zookeeper Even what we usually use Spring wait .

what's more ,Netty It's about developing high performance Java Server must learn framework .

It can be said that as a Java The engineer , To understand Java High level knowledge of servers ,Netty It's something you have to learn .

3、 ... and 、Netty Characteristics of

1、 Design
  • For different transport types ( Blocking and non-blocking ) Provide uniform API
  • Based on a flexible and extensible event model , The focus can be clearly separated
  • Highly customizable thread model : Single thread 、 One or more thread pools
  • Reliable connectionless data Socket Support (UDP)
2、 Easy to use
  • Perfect JavaDoc , User guide and sample
  • No additional dependency is required ,JDK 5 (Netty 3.x) 、JDK 6 (Netty 4.x)
3、 performance
  • Higher throughput , Lower latency
  • Save more resources
  • Reduce unnecessary memory copies
4、 Security
  • complete SSL/TLS and STARTTLS Support for
5、 Community
  • Active community and many open source contributors

Four 、 First time to know Netty

Talk is cheap, show me the code!
1、 Discard the server

Next, take a look at the code Netty, First, implement a discard( discarded ) The server , That is, the received data is not processed in any way .

  • Realization ChannelInBoundHandlerAdapter First we start with handler The realization of , Netty Use handler To deal with it I/O event .

    public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // Discard received data
    ((ByteBuf) msg).release();
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
    }
    }
    • The first 1 That's ok ,DiscardServerHandler Inherited from ChannelInboundHandlerAdapter, This class is implemented ChannelInboundHandler Interface ,ChannelInboundHandler Provides many interface methods for event handling .
    • The first 4 That's ok , When new news comes in , Will call chanelRead() Method .
    • The first 6 That's ok ,ByteBuf Is a reference count object , This object must explicitly call release() Method to release . The responsibility of the processor is to release all reference count objects passed to the processor , Here are some of the more common chanelRead() Method realization :

      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) {
      try {
      // Do something with msg
      } finally {
      ReferenceCountUtil.release(msg);
      }
      }
    • The first 10 That's ok ,exceptionCaught() Methods are methods that are called abnormally while handling events .
  • start-up Handler Realization handler after , We need a main() Method to start it .

    public class DiscardServer {
    private int port;
    public DiscardServer(int port) {
    this.port = port;
    }
    public void run() throws Exception {
    // Receiving incoming connections
    EventLoopGroup boss = new NioEventLoopGroup();
    // Handle received connections
    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 socketChannel) throws Exception {
    // Add custom handler
    socketChannel.pipeline().addLast(new DiscardServerHandler());
    }
    }).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);
    // Binding port , Start receiving incoming connections
    ChannelFuture channelFuture = bootstrap.bind(port).sync();
    // close
    channelFuture.channel().closeFuture().sync();
    } finally {
    boss.shutdownGracefully();
    worker.shutdownGracefully();
    }
    }
    public static void main(String[] args) throws Exception {
    int port = 8080;
    new DiscardServer(port).run();
    }
    }
    • The first 11 That's ok ,EventLoopGroup It's used to deal with I/O Operation of the multithreaded event circulator ,Netty Offers many different EventLoopGroup Is used to handle different transmissions . In this example, we implement a server application , So we need two EventLoopGroup . The first one to receive incoming connections , Often called boss ; The second is used to handle the received connection , Become worker. once boss Received a new incoming connection , It will register the connection information with worker above .
    • The first 15 That's ok ,ServerBootstrap It's a startup NIO The auxiliary startup class of the service .
    • The first 16 That's ok , Appoint NioServerSocketChannel Used to illustrate a new Channel How to receive incoming connections .
    • The first 20 That's ok , ChannelInitializer Used to help users create a new channel , At the same time, you can use pipline Specify some specific processors .
    • The first 22 That's ok , Through these two methods, you can specify the newly configured channel Some parameters of the configuration .
  • View the received data such , One is based on Netty The server-side program is completed , But now we can't see any interaction , So let's modify it a little bit DiscardServerHandler Class channelRead() Method , You can view the message sent by the client .

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ByteBuf byteBuf = (ByteBuf) msg;
    try {
    while (byteBuf.isReadable()) {
    System.out.print((char) byteBuf.readByte());
    System.out.flush();
    }
    } finally {
    ReferenceCountUtil.release(msg);
    }
    }
  • test So let's start DiscardServer , Use telnet Let's test it .

    image.png

    The console received a message from the command line :

    image.png

    • *
2、 Answer server

We have realized that the server can receive messages from clients , Usually the server will respond to requests from the client , Let's go through ECHO Protocol to implement the message response to the client .

ECHO The protocol will return the data sent by the client as it is , So it's also called “ Table Tennis ” agreement .

On the basis of the above code , We just need to be right about DiscardServerHandler Class channelRead() The method is slightly modified :

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.write(msg);
ctx.flush();
}
  • ChannelHandlerContext Objects provide many operations , Enables you to trigger all kinds of I/O Events and operations . Here we call write(Object) Method to write the received message verbatim to . Please note that it is different from DISCARD We didn't release the received message , This is because when writing Netty Has helped us release .
  • ctx.write(Object) Method does not cause messages to be written to the channel , He was buffered inside , You need to call ctx.flush() Method to force the output of data in the buffer . Or you can use more concise cxt.writeAndFlush(msg) To achieve the same purpose .

Run again telnet command , You will receive the message you sent .


3、 Time server

Next we are based on TIME agreement , To build and send a message , Then close the connection when it's done . Unlike the previous example, a message containing 32 Bit integer message , And once the message is sent, the connection will be closed immediately .

TIME The protocol can provide machine-readable date and time information .

We will send a time message when the connection is created , So we need to cover channelActive() Method :

public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// Allocate space
final ByteBuf time = ctx.alloc().buffer(4);
// obtain 32 Bit timestamp and write
time.writeInt((int) (System.currentTimeMillis() / 1000L));
final ChannelFuture future = ctx.writeAndFlush(time);
// Add listener
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
assert future == channelFuture;
// Close the connection
ctx.close();
}
});
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
  • The first 4 That's ok ,channelActive() Method will be called when the connection is established and ready for communication .
  • The first 6 That's ok , Same as Java Of NIO similar , To build a message , You need to allocate space for the buffer . Because I want to send a 32 Bit time stamp , So at least 4 byte .
  • The first 8 That's ok , After the message is built , Write at . Recall using Java NIO Of Buffer when , Between read and write operations , Need to call buffer.flip( ) Method to set the pointer position . But in Netty You don't need to do this in , as a result of Netty Two pointers are provided , A read pointer and a write pointer , There is no interaction between the two in reading and writing . Don't worry about forgetting to call again flip( ) Method when the data is empty or the data is wrong .
  • The first 11 That's ok , In the 9 The line is finished ctx.writeAndFlush(time) It will return to a ChannelFuture object , Represents a time that hasn't happened yet I/O operation . This means that any request operation will not be executed immediately , Because in Netty All operations are asynchronous . Like to see , We want to close the connection after the message has been sent , Call directly after ctx.close( ) You may not be able to close the connection immediately . Back to ChannelFuture Object notifies its listener when the operation is complete , Continue to perform the action after the operation is completed .
    • *
4、 Time client

For the time server, you can't use it directly telnet The way to test , Because you can't make one by hand 32 Bit binary data translated into time , So the next step is to implement a time client .

The only difference with the server-side implementation is the use of different Bootstrap and Channel Realization :

public class TimeClient {
private String host;
private int port;
public TimeClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() throws Exception{
EventLoopGroup worker = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(worker).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
}).option(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);
// start-up
ChannelFuture future = bootstrap.connect(host, port).sync();
// Wait for the connection to close
future.channel().closeFuture().sync();
} finally {
worker.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
TimeClient timeClient = new TimeClient("localhost", 8080);
timeClient.run();
}
}
  • The first 13 That's ok , contrast server The end specifies only one EventLoopGroup , It will act as boss group It will also serve as worker group, Although the client does not need to use boss group.
  • The first 15 That's ok ,Bootstrap and ServerBootstrap similar ,Bootstrap Server oriented channel , Such as client and connectionless transmission mode channel.

Just a little bit more handler :

public class TimeClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// stay TCP/IP in ,Netty Will put the data read into ByteBuf in
ByteBuf byteBuf = (ByteBuf) msg;
try {
long time = byteBuf.readUnsignedInt() * 1000L;
System.out.println(new Date(time));
ctx.close();
}finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

To start, respectively, TimeServer and TimeClient , The console prints out the current time :

image.png

However , After running many times, the processor sometimes throws IndexOutOfBoundsException And refuse to work . With this question , Keep looking down .

5、 Handling stream based transport

More typical based on Streaming TCP/IP agreement , in other words , Application layer two different packets , stay TCP/IP When the protocol is transmitted , Data from application layer protocols may be combined or split . because There is no boundary between two packets , It may cause the message to be read incorrectly .

Many materials also call this phenomenon as TCP Sticky package , And it's worth noting that :

1、TCP The protocol itself is designed to be flow oriented , Provide reliable transmission . 2、 Because it's stream oriented , For application layer packets , There is no boundary . This requires the application layer to actively handle the assembly of different packets . 3、 Sticking is not the case TCP The defects of , It's just that the application layer doesn't take the initiative to handle data packets .

Back to the above program , This is the reason why the above anomalies occur . One 32 Bit integers are very small data , It doesn't always have to be split into different segments . However , The problem is that it can actually be split into different data segments .

Two common solutions are length based or terminator based solutions , Continue with the above TIME Based on protocol procedures , Start to solve this problem . Because only one 32 Bit shaping timestamp , We use a data length based approach :

* Solution 1

The simplest solution is to construct an internal cumulative buffer , until 4 All bytes received the internal buffer . Revise it TimeClientHandler Code for :

public class TimeClientHandler extends ChannelInboundHandlerAdapter {
private ByteBuf buf;
private static final int CAPACITY = 4;
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
buf = ctx.alloc().buffer(CAPACITY);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
buf.release();
buf = null;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
buf.writeBytes(byteBuf);
byteBuf.release();
// Data is greater than or equal to 4 byte
if (buf.readableBytes() >= CAPACITY) {
long time = buf.readUnsignedInt() * 1000L;
System.out.println(new Date(time));
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

It covers handler Two approaches to life cycle :

  • The first 8 That's ok ,handlerAdded(): When a new connection is detected , call ch.pipeline().addLast(new LifeCycleTestHandler()) After the callback , Represents the current channel A logical processor has been successfully added to the
  • The first 13 That's ok ,handlerRemoved(): After the connection is closed, remove all logical processors from the connection .
* Solution 2

Although the above solution has been solved TIME There's something wrong with the client , But added logic to the processor , We can extract the part that processes the message , Become a single processor , And you can add more than one ChannelHandler To ChannelPipline , Each processor does its own job , Reduce the complexity of the module .

thus , Break it down into one TimeDecoder Used to process messages :

public class TimeDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() >= 4) {
out.add(in.readBytes(4));
}
}
}
  • ByteToMessageDecoder Inherited from ChannelInboundHandlerAdapter , Whenever there's new data coming in ,ByteToMessageDecoder Will be called decode() Method to handle the internal cumulative buffer .
  • If in decode() Method to add an object to out In the object , This means that the decoder decodes the message successfully .ByteToMessageDecoder Data that has been read in the accumulation buffer will be discarded .

Last , modify TimeClient Code for , take TimeDecoder Join in ChannelPipline :

bootstrap.group(worker).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeDecoder(), new TimeClientHandler());
}
}).option(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);

besides ,Netty It also provides more out of the box decoders, making it easier for you to implement more protocols , Help you avoid developing a processor implementation that is difficult to maintain , Interested partners can learn about .

6、 Decode the message into a custom object

We have been using the above examples ByteBuf As the main data structure of protocol messages , But in practice , The messages that need to be transmitted are more complex , It is more convenient to abstract objects . Continue with TIME Client and server based , Use custom objects instead ByteBuf .

  • Object that defines the save time OurTime :

    public class OurTime {
    private final long value;
    public OurTime() {
    this(System.currentTimeMillis() / 1000L);
    }
    public OurTime(long value) {
    this.value = value;
    }
    public long value() {
    return value;
    }
    @Override
    public String toString() {
    return new Date(value() * 1000L).toString();
    }
    }
  • modify TimeDecoder class , return OurTime class :

    public class TimeDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    if (in.readableBytes() >= 4) {
    out.add(new OurTime(in.readUnsignedInt()));
    }
    }
    }
  • The modified TimeClientHandler class , It's easier to handle new news :

    public class TimeClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    OurTime ourTime = (OurTime) msg;
    System.out.println(ourTime);
    ctx.close();
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
    }
    }
    • *

And for the server , Be the same in essentials while differing in minor points .

modify TimeServerHandler Code for :

@Override
public void channelActive(ChannelHandlerContext ctx) {
ChannelFuture f = ctx.writeAndFlush(new UnixTime());
f.addListener(ChannelFutureListener.CLOSE);
}

Now? , The only missing feature is an encoder , yes ChannelOutboundHandler The implementation of the , Used to put OurTime Object is converted back to a ByteBuf. It's much easier than writing a decoder , Because there are no packets to process when encoding messages, split and assemble .

public class TimeEncoder extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
UnixTime m = (OurTime) msg;
ByteBuf encoded = ctx.alloc().buffer(4);
encoded.writeInt((int)m.value());
ctx.write(encoded, promise); // (1)
}
}

There are several important things in these lines of code . First of all , adopt ChannelPromise, When the encoded data is written to the channel Netty You can mark success or failure with this object . second , We don't need to call cxt.flush(). Because the processor has isolated a method void flush(ChannelHandlerContext cxt), If it's like self realization flush() The method content can override the method itself .

Further simplify the operation , You can use MessageToByteEncode:

public class TimeEncoder extends MessageToByteEncoder<UnixTime> {
@Override
protected void encode(ChannelHandlerContext ctx, UnixTime msg, ByteBuf out) {
out.writeInt((int)msg.value());
}
}

Last in TimeServerHandler Before you leave TimeEncoder Insert into ChannelPipeline.

5、 ... and 、 summary

I believe that after reading this article from beginning to end , My friends use Netty Write a client and server have a general understanding of . We will continue to explore Netty Source code implementation , And combined with the basic knowledge involved to understand 、 thorough .

Please indicate the address or source of this article , Thank You for Your Cooperation


wx.png

版权声明
本文为[AYSAML]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云