编程知识 cdmana.com

Teach you to use netty to implement a simple RPC!

author : Monaru
https://www.cnblogs.com/stateis0/p/8960791.html

as everyone knows ,dubbo The bottom layer uses Netty As a network communication framework , and Netty We have also analyzed the source code before , I know him better .

Today we'll use it ourselves Netty Implement a simple one RPC frame .

1、 demand

imitation dubbo, Consumers and providers agree on interfaces and protocols , Consumer remote call provider , The provider returns a string , The consumer prints the data returned by the provider . The underlying network communication uses Netty 4.1.16.

2、 Design

  1. Create an interface , Define abstract methods . Used in a contract between a consumer and a provider .

  2. Create a provider , This class needs to listen to the consumer's request , And return the data according to the agreement .

  3. Create a consumer , This class needs to transparently call methods that do not exist , Internal use required Netty Request the provider to return data .

3、  Realization

1. establish maven project , Import Netty 4.1.16.

<groupId>cn.thinkinjava</groupId>
<artifactId>rpc-demo</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
   <dependency>
     <groupId>io.netty</groupId>
     <artifactId>netty-all</artifactId>
     <version>4.1.16.Final</version>
   </dependency>
</dependencies>

2. The project directory structure is as follows :

3. Design interfaces

===============

A simple hello world:

public interface HelloService {
	String hello(String msg);
}

4. Provider related implementation

==================

4.1. First, implement the contract interface , Used to return client data :

/**
 *  Implementation class 
 */
public class HelloServiceImpl implements HelloService {
  public String hello(String msg) {
    return msg != null ? msg + " -----> I am fine." : "I am fine.";
  }
}

4.2. Realization Netty Server and custom handler

start-up Netty Server Code :

private static void startServer0(String hostName, int port) {    try {
     ServerBootstrap bootstrap = new ServerBootstrap();
     NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
     bootstrap.group(eventLoopGroup)
         .channel(NioServerSocketChannel.class)
         .childHandler(new ChannelInitializer<SocketChannel>() {            @Override
           protected void initChannel(SocketChannel ch) throws Exception {
             ChannelPipeline p = ch.pipeline();
             p.addLast(new StringDecoder());
             p.addLast(new StringEncoder());
             p.addLast(new HelloServerHandler());
           }
         });
     bootstrap.bind(hostName, port).sync();
   } catch (InterruptedException e) {
     e.printStackTrace();
   }
 }

The code above adds String Type encoding and decoding handler, Added a custom handler.

Customize handler The logic is as follows :

/**
*  Used to process request data 
*/public class HelloServerHandler extends ChannelInboundHandlerAdapter {  @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) {    //  How to comply with the agreement , Call the local method , Return the data 
   if (msg.toString().startsWith(ClientBootstrap.providerName)) {
     String result = new HelloServiceImpl()
         .hello(msg.toString().substring(msg.toString().lastIndexOf("#") + 1));
     ctx.writeAndFlush(result);
   }
 }
}

This shows whether the agreement is met ( There's no complicated protocol , It's just a string judgment ), Then create a concrete implementation class , And call the method to write back to the client . Why? Netty So fire ? Why? ?

You also need a startup class :

public class ServerBootstrap {  public static void main(String[] args) {
   NettyServer.startServer("localhost", 8088);
 }
}

good , The code for the provider is finished , The main thing is to create a netty Server side , Implement a custom handler, Customize handler Judge whether it is in accordance with the agreement between ( It's an agreement ), If meet , Just create an interface implementation class , And call his method to return the string .

5. Consumer related implementation

There is one thing consumers need to pay attention to , That is, the call needs to be transparent , in other words , Framework users don't care about the underlying network implementation . Here we can use JDK To achieve this goal .

Ideas : The client calls the proxy method , Back to an implementation HelloService The proxy object of the interface , Call the method of the proxy object , Return results .

We need to fiddle with agents , When a proxy method is called , We need to initialize Netty client , You also need to request data from the server , And return the data .

5.1. First create a proxy related class

public class RpcConsumer {  private static ExecutorService executor = Executors
     .newFixedThreadPool(Runtime.getRuntime().availableProcessors());  private static HelloClientHandler client;  /**
  *  Create a proxy object 
  */
 public Object createProxy(final Class<?> serviceClass,      final String providerName) {    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),        new Class<?>[]{serviceClass}, (proxy, method, args) -> {          if (client == null) {
           initClient();
         }          //  Set parameters 
         client.setPara(providerName + args[0]);          return executor.submit(client).get();
       });
 }  /**
  *  Initialize client 
  */
 private static void initClient() {
   client = new HelloClientHandler();
   EventLoopGroup group = new NioEventLoopGroup();
   Bootstrap b = new Bootstrap();
   b.group(group)
       .channel(NioSocketChannel.class)
       .option(ChannelOption.TCP_NODELAY, true)
       .handler(new ChannelInitializer<SocketChannel>() {          @Override
         public void initChannel(SocketChannel ch) throws Exception {
           ChannelPipeline p = ch.pipeline();
           p.addLast(new StringDecoder());
           p.addLast(new StringEncoder());
           p.addLast(client);
         }
       });    try {
     b.connect("localhost", 8088).sync();
   } catch (InterruptedException e) {
     e.printStackTrace();
   }
 }
}

There are 2 A way , Create a proxy and initialize the client .

Initialize client logic : Create a Netty The client of , And connect to the provider , And set up a custom handler, And some of the String Type of codec .

Create proxy logic : Use JDK Dynamic proxy technology , In the proxy object invoke The method is as follows : If client Not initialized , Then initialize client, This client both handler , Also a Callback. Set the parameters into client , Using thread pool calls client Of call Method and block waiting for data to return .

have a look HelloClientHandler The implementation of the :

public class HelloClientHandler extends ChannelInboundHandlerAdapter implements Callable {  private ChannelHandlerContext context;  private String result;  private String para;  @Override
 public void channelActive(ChannelHandlerContext ctx) {
   context = ctx;
 }  /**
  *  Receive server data , Wake up waiting thread 
  */
 @Override
 public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) {
   result = msg.toString();
   notify();
 }  /**
  *  Writing data , Start waiting to wake up 
  */
 @Override
 public synchronized Object call() throws InterruptedException {
   context.writeAndFlush(para);
   wait();    return result;
 }  void setPara(String para) {    this.para = para;
 }
}

This class caches ChannelHandlerContext, For next use , There are two properties : Return results and request parameters .

When connected successfully , cache ChannelHandlerContext, When calling call Method time , Send the request parameters to the server , wait for . When the server receives and returns data , call channelRead Method , Assign the return value to result, And wake up and wait in call Thread on method . here , Proxy object returns data .

Let's look at the design of the test class :

public class ClientBootstrap {  public static final String providerName = "HelloService#hello#";  public static void main(String[] args) throws InterruptedException {

   RpcConsumer consumer = new RpcConsumer();    //  Create a proxy object 
   HelloService service = (HelloService) consumer
       .createProxy(HelloService.class, providerName);    for (; ; ) {
     Thread.sleep(1000);
     System.out.println(service.hello("are you ok ?"));
   }
 }
}

The test class first creates a proxy object , Then call the proxy's hello Method , And print the results returned by the server .

test result

Successfully printed .

4、 summary

After watching for so long Netty Source code , We finally achieved our own Netty application , Although the application is simple , Even the code is a little rough , But the function is realized ,RPC To allow remote services to be called just like local services , Need to be transparent to users , So we used a dynamic proxy . And use Netty Of handler Send data and response data , Completed a simple RPC call .

Of course , Or that sentence , Simple code , Mainly thinking , And understand RPC The underlying implementation .

Recent hot article recommends :

1.Java 15 Official release , 14 A new feature , Refresh your mind !!

2. Finally, I got it through open source projects IntelliJ IDEA Activation code , It's delicious !

3. I use Java 8 Wrote a piece of logic , I can't understand it , You try ..

4. To hang up Tomcat ,Undertow It's very powerful !!

5.《Java Development Manual ( Song Mountain version )》 The latest release , Download it quickly !

I think it's good , Don't forget to like it + Forward !

版权声明
本文为[Java technology stack]所创,转载请带上原文链接,感谢
https://cdmana.com/2020/12/20201224091951910u.html

Scroll to Top