[manual RPC framework] spring boot + netty4 implements the RPC framework

Time:2021-12-3

[manual RPC framework] spring boot + netty4 implements the RPC framework

Thread model

Netty high performance architecture design

For a brief understanding of the react thread model, refer to the article [five minutes to quickly understand the reactor model]

Example: three thread models of reactor

Thread model 1: traditional blocking I / O service model

[manual RPC framework] spring boot + netty4 implements the RPC framework

Model features:

  • Use blockingIOMode to get the input data
  • Each link requires an independent thread to complete data input, business processing and data return.

Problem analysis:

  • When the number of concurrent threads is large, a large number of threads will be created, occupying a large amount of system resources
  • After the connection is created, if the current thread has no data readable temporarily, the thread will block inreadOperation, resulting in a waste of thread resources.

Thread model 2: reactor mode

For the two shortcomings of the traditional blocking I / O service model, the solutions are as follows:

  • be based onI/OReuse model: multiple connections share a blocking object. Applications only need to wait on one blocking object without blocking all connections. When a connection has new data to process, the operating system notifies the application that the thread returns from the blocking state and starts business processing.ReactorCorresponding name: 1. Reactor mode 2. Distributor mode(Dispatcher)3. Notifier mode(notifier)
  • Reuse thread resources based on thread pool: it is no longer necessary to create threads for each connection. The business processing tasks after the connection are allocated to threads for processing. One thread can process the business of multiple connections.
[manual RPC framework] spring boot + netty4 implements the RPC framework

Single reactor single thread

[manual RPC framework] spring boot + netty4 implements the RPC framework

model analysis

  • advantage:The model is simple. There are no problems of multithreading, process communication and competition. All of them are completed in one thread
  • Disadvantages:Performance problem, there is only one thread, which can not fully play the performance of multi-core CPU. When the handler handles the business on a connection, the whole process cannot handle other connection events, which can easily lead to performance bottlenecks
  • Disadvantages:Reliability problems, unexpected thread termination, or entering an endless loop will lead to the unavailability of the communication module of the whole system, unable to receive and process external messages, resulting in node failure
  • Usage scenario:The number of clients is limited and business processing is very fast. For example, redis has a time complexity of O (1) in business processing

Single reactor multithreading

[manual RPC framework] spring boot + netty4 implements the RPC framework

model analysis

  • advantage: multi core can be fully utilizedcpuProcessing capacity of
  • shortcoming: multi thread data sharing and access are complex,reactorHandle the listening and response of all events. When running in a single thread, it is prone to performance bottlenecks in high concurrency scenarios

Master slave reactor multithreading

[manual RPC framework] spring boot + netty4 implements the RPC framework

model analysis

  • advantage:The data interaction between the parent thread and the child thread is simple, and the responsibilities are clear. The parent thread only needs to receive new connections, and the child thread completes the subsequent business processing.
  • advantage:The data interaction between the parent thread and the child thread is simple. The reactor main thread only needs to pass the new connection to the child thread, and the child thread does not need to return data
  • Disadvantages:High programming complexity
  • Combined with examples:This model is widely used in many projects, including nginx master-slave reactor multiprocess model, memcached master-slave multithreading, and netty master-slave multithreading model

First, implement simple netty communication

Server example

public static void main(String[] args) {
    //Create a connection thread group with 1 threads. Only handle connection requests
    NioEventLoopGroup boss = new NioEventLoopGroup(1);
    //Create a worker thread group. The number of threads is assumed to be the number of CPU cores * 2 by default. Processing and client business processing
    NioEventLoopGroup worker = new NioEventLoopGroup();
    //Create a server-side startup object
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    //Configure thread group
    serverBootstrap.group(boss, worker)
        //Nioserversocketchannel is used as the channel implementation of the server
        .channel(NioServerSocketChannel.class)
        //Initialize the processor to the worker thread group
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                socketChannel.pipeline()
                    //Add codec for string
                    .addLast(new StringDecoder())
                    .addLast(new StringEncoder())
                    //Add the codec of the object. Classresolvers.weakcachingcurrentresolver sets the weak reference weakreferencemap cache class loader to prevent memory overflow
                    .addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())))
                    .addLast(new ObjectEncoder())
                    //Add custom business processor
                    .addLast(new SimpleChannelInboundHandler<Object>() {
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            Log.info ("client connected... Client address: {}", CTX. Channel(). Remoteaddress());
                        }
                        @Override
                        protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
                            Log.info ("data received by the server: {}", o.tostring());
                            //100 million AI code
                            String str = o.toString();
                            STR = str.replace ("yes", "");
                            str = str.replace("?", "!");
                            str = str.replace("? ", "! ");
                            channelHandlerContext.writeAndFlush(str);
                        }
                    });
            }
        });
    //Start and listen
    ChannelFuture channelFuture = serverBootstrap.bind(8888).syncUninterruptibly();
    //Monitor closed channel
    channelFuture.channel().closeFuture();
}

Client example

public static void main(String[] args) {
    //Setting up client worker threads
    NioEventLoopGroup worker = new NioEventLoopGroup();
    //Create client startup object
    Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(worker)
        //Channel connector
        .channel(NioSocketChannel.class)
        //Initialize the processor to the worker thread group
        .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                socketChannel.pipeline()
                    //Add codec for string
                    .addLast(new StringDecoder())
                    .addLast(new StringEncoder())
                    //Add the codec of the object. Classresolvers.weakcachingcurrentresolver sets the weak reference weakreferencemap cache class loader to prevent memory overflow
                    .addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())))
                    .addLast(new ObjectEncoder())
                    //Add custom business processor
                    .addLast(new SimpleChannelInboundHandler<Object>() {

                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            Ctx.writeandflush ("hahaha");
                        }

                        @Override
                        protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
                            Log.info ("data received by the client: {}", o.tostring());
                        }
                    });
            }
        });

    ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8888).syncUninterruptibly();
    //The client needs to enter information to create a scanner
    Scanner scanner = new Scanner(System.in);
    while (scanner.hasNextLine()) {
        String msg = scanner.nextLine();
        //Send to the server through channel
        channel.writeAndFlush(msg + "\r\n");
    }
    channelFuture.channel().closeFuture();
}

Start up and try, but it should be noted that you have to start the server first~

Spring boot + netty4 implement RPC framework

Well, let’s get to the point. Let’s use our knowledge to implement a simple RPC framework

In brief, RPC (remote procedure call) remote procedure call is simply understood as a node requesting services provided by another node. Call between two services is like calling a local method.

RPC sequence diagram:

[manual RPC framework] spring boot + netty4 implements the RPC framework

QQ screenshot 20210421170511.png

RPC process:

  1. [Client] initiate call
  2. [Client] data coding
  3. [Client] sends encoded data to the server
  4. [server] receives the data sent by the client
  5. [server] decodes the data
  6. [server] processes the message business and returns the result value
  7. [server] encode the result value
  8. [server] returns the encoded result value to the client
  9. [Client] receive result value
  10. [Client] decoding result value
  11. [Client] process the returned data service

Introduce dependency

<dependencies>
    <!--  Springboot dependency -- >
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!--  Spring container context -- >
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <!--  Spring configuration -- >
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- Netty4 -->
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.58.Final</version>
    </dependency>
    <!--  Tools -- >
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.5.8</version>
    </dependency>
</dependencies>

Write server

Custom message protocol:

/**
 * @author zc
 * @date 2021/3/1 17:43
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RpcMessage implements Serializable {
    private static final long serialVersionUID = 430507739718447406L;
    /**
     *Interface interface name
     */
    private String name;
    /**
     *Method name
     */
    private String methodName;
    /**
     *Parameter type
     */
    private Class<?>[] parTypes;
    /**
     *Parameters
     */
    private Object[] pars;
    /**
     *Result value
     */
    private Object result;
}

Custom RPC annotation:

/**
 * @author zc
 * @date 2021/3/2 15:36
 */
@Target(value = {ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RpcServer {
}

definitionServerHandleService processor:

/**
 *Netty server handle processing class, message body rpcmessage
 *Implement the applicationcontextaware interface: this interface can load and obtain all spring beans.
 *Beans that implement this interface will automatically inject ApplicationContext when the spring container is initialized
 *
 * @author ZC
 * @date 2021/3/1 22:15
 */
@Slf4j
@ChannelHandler.Sharable
public class ServerHandle extends SimpleChannelInboundHandler<RpcMessage> implements ApplicationContextAware {
    private Map<String, Object> serviceMap;

    /**
     *Setapplicationaware is automatically executed when the class is loaded by the spring container
     *
     *@ param ApplicationContext spring context
     *@ throws beansexception exception information
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //Get all beans collections with @ rpcserver annotation from spring container, map < name (object type, object full pathname), instance object >
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(RpcServer.class);
        Log.info ("bean loaded by @ rpcserver annotation: {}", beanswithannotation);
        if (beansWithAnnotation.size() > 0) {
            Map<String, Object> map = new ConcurrentHashMap<>(16);
            for (Object o : beansWithAnnotation.values()) {
                //Gets the interface class implemented by the instance object
                Class<?> anInterface = o.getClass().getInterfaces()[0];
                //Get the interface class name as the key and the instance object as the value
                map.put(anInterface.getName(), o);
            }
            //Using variables to catch maps
            serviceMap = map;
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Log.info ("client connected: {}", CTX. Channel(). Remoteaddress());
        super.channelActive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Log.error ("exception information");
        cause.printStackTrace();
        super.exceptionCaught(ctx, cause);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcMessage rpcMessage) throws Exception {
        Log.info ("message sent by client: {}", rpcmessage);
        //Get instance object from map
        Object service = serviceMap.get(rpcMessage.getName());
        //Get call method
        Method method = service.getClass().getMethod(rpcMessage.getMethodName(), rpcMessage.getParTypes());
        method.setAccessible(true);
        //Reflection calls the instance object method to get the return value
        Object result = method.invoke(service, rpcMessage.getPars());
        rpcMessage.setResult(JSONUtil.toJsonStr(result));
        Log.info ("message returned to client: {}", rpcmessage);
        //The netty server writes the data to the channel and sends it to the client. At the same time, a listener is added. When all data packets are sent, the channel is closed
        channelHandlerContext.writeAndFlush(rpcMessage).addListener(ChannelFutureListener.CLOSE);
    }
}

definitionNettyServerEnd:

/**
 *Netty server
 *
 * @author zc
 * @date 2021/2/24 13:23
 **/
@Slf4j
public class NettyServer {

    /**
     *Server processor
     */
    private final ServerHandle serverHandle;
    /**
     *Server channel
     */
    private Channel channel;

    /**
     *Constructor
     *
     *@ param serverhandle server processor
     */
    public NettyServer(ServerHandle serverHandle) {
        this.serverHandle = serverHandle;
    }

    /**
     *Start
     *
     *@ param port startup port
     */
    public void start(int port) {
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    .addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())))
                                    .addLast(new ObjectEncoder())
                                    .addLast(serverHandle);
                        }
                    });

            final ChannelFuture channelFuture = serverBootstrap.bind(port).syncUninterruptibly();
            Log.info ("server startup - port: {}", port);
            channel = channelFuture.channel();
            channel.closeFuture().syncUninterruptibly();
        } catch (Exception e) {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    /**
     *Close current channel
     */
    public void stop() {
        channel.close();
    }
}

Custom RPC configuration attribute class:

/**
 * @author zc
 * @date 2021/3/4 23:38
 */
@Component
@ConfigurationProperties(prefix = "netty")
@Data
public class NettyRpcProperties {
    private int serverPort;
}`

To create a server side startup configuration class:

/**
 *Nettyserver server configuration class
 *
 * @author zc
 * @date 2021/3/1 18:24
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(NettyRpcProperties.class)
public class ServerBeanConfig {

    private final NettyRpcProperties nettyRpcProperties;

    @Autowired
    public ServerBeanConfig(NettyRpcProperties nettyRpcProperties) {
        this.nettyRpcProperties = nettyRpcProperties;
    }

    /**
     *Configure serverhandle
     *
     *@ return serverhandle processing class
     */
    @Bean
    public ServerHandle serverHandle() {
        return new ServerHandle();
    }

    /**
     *Configuring nettyserver
     *
     *@ param handle serverhandle handling class
     * @return NettyServer
     */
    @Bean
    public NettyServer nettyServer(ServerHandle handle) {
        NettyServer nettyServer = new NettyServer(handle);
//        nettyServer.start(nettyRpcProperties.getServerPort());
        return nettyServer;
    }

    /**
     *Solve the problem that the springboot port cannot listen
     */
    @Component
    static class NettyServerStart implements ApplicationRunner {
        private final NettyServer nettyServer;
        private final NettyRpcProperties properties;

        @Autowired
        NettyServerStart(NettyServer nettyServer, NettyRpcProperties properties) {
            this.nettyServer = nettyServer;
            this.properties = properties;
        }

        @Override
        public void run(ApplicationArguments args) throws Exception {
            log.info("===============ApplicationRunner");
            if (nettyServer != null) {
                nettyServer.start(properties.getServerPort());
            }
        }
    }
}

Inject spring container

At this time, there are two ways to make the configuration automatically injected into the spring container take effect:

  1. Automatic injection

    Create the meta-inf directory under the resource directory and the spring.factories file

    Write in the document

    Org. Springframework. Boot. Autoconfigure. Enableautoconfiguration = ${package path: XXX. XXX. XXX}. ${configuration class: serverbean config}

    After configuration, the configuration class will be automatically loaded when springboot starts.

  2. Injection by annotation

    /**
     *Custom springboot startup annotation
     *Inject serverbeanconfig configuration class
     *
     * @author ZC
     * @date 2021/3/1 23:48
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @ImportAutoConfiguration({ServerBeanConfig.class})
    public @interface EnableNettyServer {
    }

Write client


Create client processor ` clienthandle

/**
 * @author zc
 * @date 2021/3/2 15:19
 */
@Slf4j
@ChannelHandler.Sharable
public class ClientHandle extends SimpleChannelInboundHandler<RpcMessage> {
    /**
     *Define the message map, with the connection channel as the key and the message return value as the value
     */
    private final ConcurrentMap<Channel, RpcMessage> rpcMessageConcurrentMap;

    public ClientHandle(ConcurrentMap<Channel, RpcMessage> rpcMessageConcurrentMap) {
        this.rpcMessageConcurrentMap = rpcMessageConcurrentMap;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcMessage rpcMessage) throws Exception {
        Log.info ("client receives server message: {}", rpcmessage);
        rpcMessageConcurrentMap.put(channelHandlerContext.channel(), rpcMessage);
    }
}

Create client startup classNettyClient

/**
 * @author ZC
 * @date 2021/3/1 23:30
 */
@Slf4j
public class NettyClient {

    private Channel channel;
    /**
     *Store the mapping relationship between the request number and the response object
     */
    private final ConcurrentMap<Channel, RpcMessage> rpcMessageConcurrentMap = new ConcurrentHashMap<>();

    public RpcMessage send(int port, final RpcMessage rpcMessage) {
        //The client needs an event loop group
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    .addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())))
                                    .addLast(new ObjectEncoder())
                                    .addLast(new ClientHandle(rpcMessageConcurrentMap));
                        }
                    });
            final ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", port).syncUninterruptibly();
            Log.info ("connection to the server succeeded:" + channelfuture. Channel(). Remoteaddress());
            channel = channelFuture.channel();
            channel.writeAndFlush(rpcMessage);
            Log.info ("sending data succeeded: {}", rpcmessage);
            channel.closeFuture().syncUninterruptibly();
            return rpcMessageConcurrentMap.get(channel);
        } catch (Exception e) {
            log.error("client exception", e);
            return null;
        } finally {
            group.shutdownGracefully();
            //Remove the direct mapping relationship between the request number and the response object
            rpcMessageConcurrentMap.remove(channel);
        }
    }

    public void stop() {
        channel.close();
    }
}

Define netty client bean post processor

/**
 *Netty client bean post processor
 *Implement the spring post processor interface: beanpostprocessor
 *After the bean object is instantiated and dependency injected, add our own logic before and after calling the initialization method. Note that it is triggered after bean instantiation and dependency injection
 *
 * @author ZC
 * @date 2021/3/2 23:00
 */
@Slf4j
public class NettyClientBeanPostProcessor implements BeanPostProcessor {

    private final NettyClient nettyClient;

    public NettyClientBeanPostProcessor(NettyClient nettyClient) {
        this.nettyClient = nettyClient;
    }

    /**
     *After instantiation and dependency injection, complete some customized initialization tasks before calling the displayed initialization
     *Note: method return value cannot be null
     *If NULL is returned, the null pointer exception will be reported in the subsequent initialization method, or the bean instance object cannot be obtained through the getBean () method
     *Because the post processor took the bean instance from the spring IOC container, the object was not put back into the IOC container again
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, @Nullable String beanName) throws BeansException {
        //Get instance class
        Class<?> beanClass = bean.getClass();
        do {
            //Get all fields of this class
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                //Judge whether the field has @ rpcserver
                if (field.getAnnotation(RpcServer.class) != null) {
                    field.setAccessible(true);
                    try {
                        //Get the proxy object of this class through JDK dynamic proxy
                        Object o = Proxy.newProxyInstance(field.getType().getClassLoader(), new Class[]{field.getType()}, new ClientInvocationHandle(nettyClient));
                        //Inject the proxy class into this field
                        field.set(bean, o);
                        Log.info ("create proxy class = = > > > {}", beanname);
                    } catch (IllegalAccessException e) {
                        log.error(e.getMessage());
                    }
                }
            }
        } while ((beanClass = beanClass.getSuperclass()) != null);
        return bean;
    }

    /**
     *Execute when instantiation, dependency injection and initialization are completed
     *Note: method return value cannot be null
     *If NULL is returned, the null pointer exception will be reported in the subsequent initialization method, or the bean instance object cannot be obtained through the getBean () method
     *Because the post processor took the bean instance from the spring IOC container, the object was not put back into the IOC container again
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //You can perform different processing operations according to the beanname
        return bean;
    }

    /**
     *JDK dynamic agent processor
     */
    static class ClientInvocationHandle implements InvocationHandler {
        private final NettyClient nettyClient;

        public ClientInvocationHandle(NettyClient nettyClient) {
            this.nettyClient = nettyClient;
        }

        /**
         *Proxy method call
         *
         *@ param proxy proxy class
         *@ param method method
         *@ param args parameter
         *@ return return return value
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) {
            //Assembling netty parameters
            RpcMessage rpcMessage = RpcMessage.builder()
                    .name(method.getDeclaringClass().getName())
                    .methodName(method.getName())
                    .parTypes(method.getParameterTypes())
                    .pars(args)
                    .build();
            //Call netty to send data
            RpcMessage send = nettyClient.send(1111, rpcMessage);
            Log.info ("received server data: {}, returned result value = = = =" {} ", send, send. Getresult());
            return send.getResult();
        }
    }
}

Define client configuration classes

/**
 * @author zc
 * @date 2021/3/1 18:24
 */
@Configuration
public class ClientBeanConfig {

    @Bean
    public NettyClient nettyClient() {
        return new NettyClient();
    }

    @Bean
    public NettyClientBeanPostProcessor nettyClientBeanPostProcessor(NettyClient nettyClient) {
        return new NettyClientBeanPostProcessor(nettyClient);
    }
}

Finally, like the server, inject the spring container

/**
 * @author ZC
 * @date 2021/3/1 23:48
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration({ClientBeanConfig.class})
public @interface EnableNettyClient {
}

So far, our spring boot + netty4 has implemented the simplest RPC framework pattern; Then we can reference our own RPC dependencies.

Finally, execute the Maven command

mvn install

Netty RPC examples example

Interface service

There’s nothing in POM…

Define an interface

/**
 * @author zc
 * @date 2021/3/1 17:55
 */
public interface Test1Api {

    void test();

    void test(int id, String name);

    String testStr(int id);

    Object testObj();
}

RPC server server

Normal springboot project

Introducing POM

<!--  Custom RPC dependency -- >
<dependency>
    <groupId>cn.happyloves.rpc</groupId>
    <artifactId>netty-rpc</artifactId>
    <version>0.0.1</version>
</dependency>
<!--  Interface dependency -- >
<dependency>
    <groupId>cn.happyloves.netty.rpc.examples.api</groupId>
    <artifactId>rpc-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

Configuration properties

#Application name
spring.application.name=rpc-server
#Application service web access port
server.port=8080
netty.server-port=1111

Create an entity class

/**
 * @author ZC
 * @date 2021/3/2 23:59
 */
@Data
public class Account implements Serializable {
    private static final long serialVersionUID = 667178018106218163L;
    private Integer id;

    private String name;
    private String username;
    private String password;
}

Create server to implement test1api interface

/**
 * @author ZC
 * @date 2021/3/2 23:59
 */
@Slf4j
@Service
@RpcServer
public class TestServiceImpl implements Test1Api {
    @Override
    public void test() {
        log.info("111111111");
    }

    @Override
    public void test(int id, String name) {
        log.info("222222222,{},{}", id, name);
    }

    @Override
    public String testStr(int id) {
        log.info("33333333333333333,{}", id);
        return "33333333333333333 " + id;
    }

    @Override
    public Object testObj() {
        log.info("444444444444444444");
        Account account = new Account();
        Account.setname ("Zhang San");
        return account;
    }
}

Finally, @ enablenetyserver is added to the springboot boot class

/**
 * @author ZC
 * @date 2021/3/2 23:55
 */
@EnableNettyServer
@SpringBootApplication
public class RpcServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RpcServerApplication.class, args);
    }
}

RPC server client

Introducing POM dependency

<dependency>
    <groupId>cn.happyloves.rpc</groupId>
    <artifactId>netty-rpc</artifactId>
    <version>0.0.1</version>
</dependency>
<dependency>
    <groupId>cn.happyloves.netty.rpc.examples.api</groupId>
    <artifactId>rpc-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

Create controller

/**
 * @author ZC
 * @date 2021/3/3 0:04
 */
@RestController
public class ClientController {
    @RpcServer
    private Test1Api testServiceImpl;

    @GetMapping("/test1")
    public void test() {
        testServiceImpl.test();
    }

    @GetMapping("/test2")
    public void test(int id, String name) {
        testServiceImpl.test(id, name);
    }

    @GetMapping("/test3")
    public String testStr(int id) {
        return testServiceImpl.testStr(id);
    }

    @GetMapping("/test4")
    public Object testObj() {
        return testServiceImpl.testObj();
    }
}

Finally, add the annotation @ enablenettyclient on the startup class

@EnableNettyClient
@SpringBootApplication
public class RpcClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(RpcClientApplication.class, args);
    }
}

First run the server, then run the client, and then call the client interface to see that the server can receive the message from the client, then the server processes and returns, and the client receives and returns…

At this point, a small demo is completed.

Of course, there are still many needs to be handled in the future. For example, in the current demo, each communication between the client needs to create an instance to connect, the service registration, the client and the server are the same application, and so on. This will be improved slowly later
Zhao xiaopang’s personal blog: https://zc.happyloves.cn:4443/wordpress/