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

Keywords: Java

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

Thread model 1: traditional blocking I/O service model

Model features:

Use blocking IO mode to obtain 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 to read temporarily, the thread will block the read operation, 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:

Based on I/O reuse 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. The corresponding name of reactor: 1. Reactor mode 2. Dispatcher mode 3. Notifier mode
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.

Single Reactor single thread

model analysis

Advantages: 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 give full play to 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 make the communication module of the whole system unavailable, unable to receive and process external messages, resulting in node failure
Usage scenario: the number of clients is limited, and the business processing is very fast. For example, the time complexity of Redis in business processing is O(1)
Single Reactor multithreading

model analysis

Advantages: it can make full use of the processing power of multi-core cpu
Disadvantages: multithreaded data sharing and access are complex. reactor handles 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

model analysis

Advantages: 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.
Advantages: 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 multi process 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 connection... 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("Do you", "");
                            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("Ha ha ha");
                        }

                        @Override
                        protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
                            log.info("Data received by 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:

RPC process:

[Client] initiates the call
[[Client] data coding
[Client] sends encoded data to the server
[Server] receives the data sent by the client
[The server] decodes the data
[The server] processes the message business and returns the result value
[[server] encode the result value
[Server] returns the encoded result value to the client
[Client] receive result value
[[Client] decoding result value
[Client] process the returned data business

Introduce dependency

<dependencies>
    <!-- SpringBoot rely on -->
    <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 to configure -->
    <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>
    <!-- tool -->
    <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;
    /**
     * parameter
     */
    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 {
}

Define the ServerHandle business processor:

/**
 * Netty Server End Handle processing class, message body RpcMessage
 * Implement the ApplicationContextAware interface: this interface can load and obtain all spring bean s.
 * 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 Abnormal 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("cover@RpcServer Annotation loaded Bean: {}", 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 map s
            serviceMap = map;
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("The client is connected: {}", ctx.channel().remoteAddress());
        super.channelActive(ctx);
    }

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

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcMessage rpcMessage) throws Exception {
        log.info("Messages sent by the 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 back 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);
    }
}

Define the NettyServer side:

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

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

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

    /**
     * start-up
     *
     * @param port Boot 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 start-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 Processing 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:

Automatic injection

stay resource Create under directory META-INF Directories, creating spring.factories file

Write in the document

org.springframework.boot.autoconfigure.EnableAutoConfiguration=${Package path:xxx.xxx.xxx}.${Configuration class: ServerBeanConfig}

After configuration, in SpringBoot The configuration class is automatically loaded at startup.

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("The client receives the server message:{}", rpcMessage);
        rpcMessageConcurrentMap.put(channelHandlerContext.channel(), rpcMessage);
    }
}

Create client startup class NettyClient

/**
 * @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("Successfully connected to the server: " + channelFuture.channel().remoteAddress());
            channel = channelFuture.channel();
            channel.writeAndFlush(rpcMessage);
            log.info("Data sent successfully:{}", 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 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:{}, Return 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 rely on -->
<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

# apply 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

Continue to support Remi sauce~~

Article source: https://www.jianshu.com/p/f94a0d971b7b

Posted by cjmling on Thu, 16 Sep 2021 15:07:56 -0700