The RPC framework is called the remote calling framework. The core principle of its implementation is that the consumer uses a dynamic proxy to proxy an interface (Dynamic proxy based on JDK, of course if Using CGLib, you can directly use methods without interface classes). By adding network transmission programming, the transmission call interface method name and method parameters are obtained by the provider, and then through reflection, the method of the interface is executed, and then the reflection is executed. The results are sent back to the consumer through network programming.
Now let’s implement these concepts in turn. Here we do the simplest implementation. Network programming uses BIO. You can use Netty in Reactor mode to rewrite it in a way with better performance. The serialization and deserialization used in network transmission are also native to Java. Of course, such transmission bytes are relatively large and can be processed using Google's protoBuffer or kryo. This is just for convenience to explain the principle.
pom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <? xml version = "1.0" encoding = "UTF-8" ?>
< project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >
< modelVersion >4.0.0</ modelVersion >
< groupId >com.guanjian</ groupId >
< artifactId >rpc-framework</ artifactId >
< version >1.0-SNAPSHOT</ version >
< build >
< plugins >
< plugin >
< groupId >org.apache.maven.plugins</ groupId >
< artifactId >maven-compiler-plugin</ artifactId >
< version >3.7.0</ version >
< configuration >
< source >1.8</ source >
< target >1.8</ target >
< encoding >UTF-8</ encoding >
</ configuration >
</ plugin >
</ plugins >
</ build >
</ project >
|
Copy after login
First of all, of course, the interface and the interface method we want to call remotely.
1 2 | public interface HelloService {
String sayHello(String content);}
|
Copy after login
Interface implementation class
1 2 | public class HelloServiceImpl implements HelloService { public String sayHello(String content) { return "hello," + content; }
}
|
Copy after login
Dynamic proxy on the consumer side, If you write the provider and consumer in two projects, the provider side needs the above interface and implementation classes, while the consumer side only needs the above interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | public class ConsumerProxy {
@SuppressWarnings( "unchecked" )
public static <T> T consume(final Class<T> interfaceClass,final String host,final int port) {
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
new Class[]{interfaceClass}, (proxy,method,args) -> {
Socket socket = new Socket(host, port);
try {
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
output.writeUTF(method.getName());
output.writeObject(args);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
input.close();
}
} finally {
output.close();
}
} finally {
socket.close();
}
}
);
}
}
|
Copy after login
For information about JDK dynamic proxy, please refer to AOP principles and self-implementation. For BIO, please refer to the comparison between traditional IO and NIO.
Provider-side network transmission and remote calling services
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | public class ProviderReflect {
private static final ExecutorService executorService = Executors.newCachedThreadPool();
public static void provider( final Object service,int port) throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
final Socket socket = serverSocket.accept();
executorService.execute(() -> {
try {
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
try {
String methodName = input.readUTF();
Object[] args = (Object[]) input.readObject();
Class[] argsTypes = new Class[args.length];
for (int i = 0;i < args.length;i++) {
argsTypes[i] = args[i].getClass();
}
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
Class<?>[] interfaces = service.getClass().getInterfaces();
Method method = null;
for (int i = 0;i < interfaces.length;i++) {
method = interfaces[i].getDeclaredMethod(methodName,argsTypes);
if (method != null) {
break ;
}
}
Object result = method.invoke(service, args);
output.writeObject(result);
} catch (Throwable t) {
output.writeObject(t);
} finally {
output.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
input.close();
}
} finally {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
}
|
Copy after login
Start the network listening and remote calling on the provider side
1 2 3 4 5 6 | public class RPCProviderMain {
public static void main(String[] args) throws Exception {
HelloService service = new HelloServiceImpl();
ProviderReflect.provider(service,8083);
}
}
|
Copy after login
Start the dynamic proxy call of the consumer
1 2 3 4 5 6 7 8 9 10 | public class RPCConsumerMain {
public static void main(String[] args) throws InterruptedException {
HelloService service = ConsumerProxy.consume(HelloService. class , "127.0.0.1" ,8083);
for (int i = 0;i < 1000;i++) {
String hello = service.sayHello( "你好_" + i);
System.out.println(hello);
Thread.sleep(1000);
}
}
}
|
Copy after login
Running result
hello, hello _0
hello, hello_1
hello, hello_2
hello, hello_3
hello, hello_4
hello, hello_5
.....
If you want to extend it into a high-performance RPC framework with Netty ProtoBuffer, you can refer to the related writing methods of Netty integrating Protobuffer.
Recommended tutorial: "PHP"
The above is the detailed content of Java handwriting an RPC framework. For more information, please follow other related articles on the PHP Chinese website!