Java uses netty to implement simple RPC

Time:2020-9-27

Build a wheel to realize RPC call

After writing a simple example of netty to realize communication, I germinated the idea of realizing RPC call, so I started to work on netty RPC and realized a simple RPC call project.

If you are also interested in building wheels, you can take a look at the blog about using java to realize netty communication.

Build an RPC wheel


get ready

First of all, you need to understand the following knowledge.

Netty

Handle communication between services.

Zookeeper

Service registration and discovery.

SpringBoot

At present, it’s just a start-up project.

Cglib Proxy & Jdk Reflect

The exposed interface is instantiated by proxy, and the implementation of interface method is called through reflection.


RPC processing flow

Java uses netty to implement simple RPC

  1. The service provider exposes the corresponding port through netty.
  2. At the same time, the provider registers the service information that needs to be exposed to zookeeper. The node information registered by zookeeper is the class path of the interface, and the registered data is the exposed port. In addition, the corresponding interface implementation class needs to be saved in the context of ApplicationContext.
  3. At this point, the consumer will discover in zookeeper according to the class path of the corresponding interface.
  4. According to the class path of the corresponding interface, the corresponding exposed port information is obtained through ReadData in zookeeper.
  5. Get the port information and send the request through netty.
  6. After netty initiates the request reception, it calls the corresponding interface method through the previous ApplicationContext context.
  7. After the ApplicationContext call is successful, netty returns the result of the response.
  8. The service consumer gets the response result and the RPC process ends.

Do it

Based on the article of using java to implement netty communication, we create the following project information.

Java uses netty to implement simple RPC

rpc-sample-api

Dependent information

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>netty-rpc-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

code

Only one hiservice interface needs to be defined.

public interface HiService {

    public String hi(String msg);
}

rpc-sample-server

Dependent information

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>rpc-sample-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>netty-rpc-server</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

application.yaml

register.address: 127.0.0.1:3000

code

  • First, the hiservice interface is implemented.
@RpcServer(cls = HiService.class)
public class HiServiceImpl implements HiService {

    public String hi(String msg) {
        return "hello, I'm Rpc, I want say : " + msg;
    }
}
  • Server class.
@Component
public class Server implements ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(Server.class);

    @Value("${register.address}")
    private String registerAddress;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> serviceBean = new HashMap<String, Object>();

        Map<String, Object> objectMap = applicationContext.getBeansWithAnnotation(RpcServer.class);

        for (Object object : objectMap.values()) {
            try {
                RpcServer annotation = object.getClass().getAnnotation(RpcServer.class);

                serviceBean.put("/yanzhenyidai/" + annotation.cls().getName(), object);

                String[] split = registerAddress.split(":");

                new NettyServer(split[0], Integer.valueOf(split[1])).server(serviceBean);
            } catch (Exception e) {
                logger.error("[server-start] fail ", e);
            }
        }
    }
}

realizationApplicationContextAwareTo get the spring context, scan the rpcserver annotation to get the service information to be exposed this time, and open the port service exposure and zookeeper registration of nettyserver.

  • Application startup class
@SpringBootApplication
public class RpcServerApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(RpcServerApplication.class)
                .web(WebApplicationType.NONE)
                .run(args);
    }
}

Start springboot without web startup.

rpc-sample-client

rely on

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>netty-rpc-client</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>com.yanzhenyidai</groupId>
    <artifactId>rpc-sample-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.2</version>
</dependency>

<dependency>
    <groupId>asm</groupId>
    <artifactId>asm</artifactId>
    <version>3.3.1</version>
</dependency>

code

  • Client
public class Client {

    public <T> T create(final Class<?> cls) {

        return (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, new InvocationHandler() {
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

                Request request = new Request();
                request.setInterfaceName("/yanzhenyidai/" + cls.getName());
                request.setRequestId(UUID.randomUUID().toString());
                request.setParameter(objects);
                request.setMethodName(method.getName());
                request.setParameterTypes(method.getParameterTypes());

                Response response = new NettyClient().client(request);
                return response.getResult();
            }
        });
    }
}

The cglib dynamic proxy is used to instantiate the interface information. When calling, the request is sent to nettyserver through nettyclient. The nettyserver processes and discovers the zookeeper node and invokes the interface implementation method by reflection.

  • context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.yanzhenyidai.config.Client"></bean>

</beans>

Inject the bean object information of the client.

  • Application
public class RpcClientApplication {

    public static void main(String[] args) {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("context.xml");

        Client client = context.getBean(Client.class);

        HiService hiService = client.create(HiService.class);
        String msg = hiService.hi("msg");
        System.out.println(msg);

    }
}

Operation results

Java uses netty to implement simple RPC


summary

The above is just a simple RPC process. Compared with Dubbo RPC, it will be more intuitive to see. I hope it can help you. I also welcome you to build wheels with me.

GitHub address of the project: netty RPC