Hello Rpc: Writing an RPC from scratch

Keywords: Java Netty Zookeeper Spring Dubbo

In school, we should always take something to practice, read some articles like "Write Rpc from scratch" and "Write Search Engine from scratch" can be tried. Actually, some days ago. tiny4j There are still some things to improve and continue to improve, but everyone likes the new and hates the old. They can't help opening another hole, so this time it's RPC.

Before starting to write, I did a lot of preparation to understand some basic things about RPC. I also found some RPC frameworks to try out, such as GRPC and thrift. They all provide the ability of cross-language invocation. According to some configurations written, I can automatically generate some code. In addition to these two domestic frameworks, there are also excellent ones such as Ali's open source dubbo and Weibo's motan; Weibo's Motan documents are very concise and directly support Spring's annotation form. Immediately try helloworld, Api easy to use, a few annotations to solve all the services, for the specific language of Java, this RPC is really very useful! clone code down to see that the transport layer is using netty, in order to be able to complete this and run to get the "Netty Authority Guide" gnawed most of the book, is a door.

After all, it's the first time to write, so I don't think about cross-language. It's easy to use Spring with motan. The whole RPC calling process can be divided into the following layers:

Communication includes connection establishment, message transmission, encoding and decoding of the content to be transmitted, serialization and deserialization, and finally the realization of the client and the server. The client calls a proxy implementation of a service to initiate a request to the server.
Based on netty 5.0, protostuff, kryo serialization libraries, commons-pool2 connection pool is implemented. Service agents use Proxy or Cglib of jdk itself. On such a bunch of excellent frameworks and class libraries, the whole RPC construction is much simpler.

It's also a practice of writing an RPC from scratch. Source code stamp github Limited level is only a toy level implementation, but through this project we can understand all aspects of RPC; if you have better ideas, you are welcome to exchange.

Simple invocation example

Define services and service implementations

public interface EchoService {
    String hello(String s);
}

public class EchoServiceImpl implements EchoService {
    @Override
    public String hello(String s) {
        return s;
    }
}

Server:

 //Configuration: port number, maximum transmission capacity (unit M), service response Handle
 Server server = new Server.Builder().port(9001).maxCapacity(3).build();
 //Publishing service
 server.addService(EchoService.class, new EchoServiceImpl());
 server.start();

Client:

 Client client = new Client.Builder().host("127.0.0.1").port(9001).maxCapacity(3).build();
 DefaultClientInterceptor interceptor = new DefaultClientInterceptor(client);
 ClientProxy clientProxy = new JdkClientProxy(interceptor);

 EchoService echoService = clientProxy.getProxy(EchoService.class);
 System.out.println(echoService.echo("twogoods"));

Integrating SpringBoot

Server Configuration

Use the @RpcService annotation

public interface EchoService {
    String echo(String s);
}

@RpcService
public class EchoServiceImpl implements EchoService {
    @Override
    public String echo(String s) {
        return s;
    }
}

application.yml configuration

tgrpc:
    host: 127.0.0.1
    port: 9100
    maxCapacity: 3
    requestTimeoutMillis: 8000
    maxTotal: 3
    maxIdle: 3
    minIdle: 0
    borrowMaxWaitMillis: 5000

Start server:

@SpringBootApplication
@EnableAutoConfiguration
@EnableRpcServer //Enable Server
@ComponentScan()
public class ServerApplication {
    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(ServerApplication.class);
    }
}

client configuration

The caller uses the @RpcReferer annotation.

@Component
public class ServiceCall {

    @RpcReferer
    private EchoService echoService;

    public String echo(String s) {
        return echoService.echo(s);
    }
}

Start Client Initiation Call

@SpringBootApplication
@EnableAutoConfiguration
@EnableRpcClient //Enable Client
@ComponentScan(basePackages = {"com.tg.rpc.springsupport.bean.client"})
public class ClientApplication {
    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(ClientApplication.class);
        ServiceCall serviceCall = (ServiceCall) applicationContext.getBean("serviceCall");
        System.out.println("echo return :" + serviceCall.echo("TgRPC"));
    }
}

Registry Center

You can use consul, or zookeeper as the registry. It's very simple to use, just configure the registery attribute in the configuration file, with default (default), consul,zookeeper values.

tgrpc:
    port: 9001
    registery: zookeeper
    zookeeperHost: 127.0.0.1
    zookeeperPort: 2181
    zkServicePath: /tgrpc/services

tgrpc:
    port: 9001
    registery: consul
    consulHost: 127.0.0.1
    consulPort: 8500
    ttl: 10000

TODO

  • netty optimization

Posted by sleepydad on Thu, 11 Apr 2019 21:12:30 -0700