In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
Today, the editor will share with you how to write a RPC framework by hand. The content is detailed and the logic is clear. I believe most people still know too much about this knowledge, so share this article for your reference. I hope you can get something after reading this article. Let's learn about it.
What is the underlying principle of the RPC framework? we know that RPC (remote procedure call) is simply to call remote services just like calling local methods, in which the knowledge used is sequenced and deserialized, dynamic proxy, network transmission, dynamic loading, reflection and so on.
I found that I know something about this knowledge. So I want to try to implement a simple RPC framework, that is, to consolidate the basic knowledge, but also to have a more in-depth understanding of RPC principles.
Of course, a complete RPC framework contains many functions, such as service discovery and governance, gateways and so on. This article simply implements a calling process.
Analysis of parameters transmitted and output
A simple request can be abstracted into two steps
So based on these two steps, what information should we send to the server before the request? What information should the server return to the client after it is processed?
What information should we send to the server before the request?
Since we are calling the interface provided by the server on the client side, we need to transfer the information called by the client side, so we can divide the transmitted information into two categories.
The first is that the server can find the corresponding interface implementation classes and methods based on this information.
The second type is the parameter information transferred by calling this method.
Then we analyze according to the two types of information to be transmitted, what information can find the corresponding method of the corresponding implementation class? To find a method, you must first find the class. Here we can simply use the Bean instance management ApplicationContext provided by Spring to find the class.
So all you need to find an instance of a class is to know the name of the class and find an instance of the class, so how do you find the method?
In reflection, this method can be found based on the method name and parameter type. Then we know the first kind of information at this time, so we set up the corresponding entity class to store this information.
@ Data public class Request implements Serializable {private static final long serialVersionUID = 3933918042687238629L; private String className; private String methodName; private Class [] parameTypes; private Object [] parameters;}
What information should the server return to the client after it has finished processing?
Above we analyzed what information the client should transmit to the server, and what kind of return value should the server send after processing?
Here we will only consider the simplest case, and the thread of the client request will be waiting all the time, and there will be no asynchronous processing, so it is simple to analyze it and return the processing result directly.
@ Data public class Response implements Serializable {private static final long serialVersionUID =-2393333111247658778L; private Object result;}
Since they all involve network transmission, they all have to implement serialized interfaces.
How to get the parameter transfer information and execute it?-client
Above we analyzed what kind of information the client sends to the server. So how do we get this information?
First of all, we call the interface, so we need to write custom annotations and load this information into the Spring container when the program starts.
With this information, then we need to transmit, call the interface, but actually perform the process of network transmission, so we need dynamic proxies. Then it can be divided into two steps
Initialization information phase: register key as the interface name and value as the dynamic interface class in the Spring container
Execution phase: network transmission is actually performed through dynamic agents
Initialize the information phase
Because we use Spring as the management of the Bean, we register the interface and the corresponding proxy class in the Spring container. And how do we find the interface class we want to call? We can customize the annotations to scan. Register all the interfaces you want to call into the container.
Create an annotation class to indicate which interfaces can be Rpc.
@ Target ({ElementType.TYPE}) @ Retention (RetentionPolicy.RUNTIME) public @ interface RpcClient {}
Then create a scan class RpcInitConfig for the @ RpcClient annotation and register it in the Spring container.
Public class RpcInitConfig implements ImportBeanDefinitionRegistrar {@ Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {ClassPathScanningCandidateComponentProvider provider = getScanner (); / / set scanner provider.addIncludeFilter (new AnnotationTypeFilter (RpcClient.class)); / / scan all classes Set beanDefinitionSet = provider.findCandidateComponents ("com.example.rpcclient.client") with @ RpcClient annotations under this package; for (BeanDefinition beanDefinition: beanDefinitionSet) {if (beanDefinition instanceof AnnotatedBeanDefinition) {/ / get parameter information AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition on the annotation String beanClassAllName = beanDefinition.getBeanClassName (); Map paraMap = annotatedBeanDefinition.getMetadata () .getAnnotationAttributes (RpcClient.class.getCanonicalName ()); / / register the factory class of RpcClient in BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition (RpcClinetFactoryBean.class); / / set the value of the constructor in the RpcClinetFactoryBean factory class builder.addConstructorArgValue (beanClassAllName); builder.getBeanDefinition (). SetAutowireMode (AbstractBeanDefinition.AUTOWIRE_BY_TYPE); / / register it in the container registry.registerBeanDefinition (beanClassAllName, builder.getBeanDefinition ()) } / / allow Spring to scan annotations on the interface protected ClassPathScanningCandidateComponentProvider getScanner () {return new ClassPathScanningCandidateComponentProvider (false) {@ Override protected boolean isCandidateComponent (AnnotatedBeanDefinition beanDefinition) {return beanDefinition.getMetadata (). IsInterface () & & beanDefinition.getMetadata (). IsIndependent ();}};}}
Since the factory class is registered above, we set up a factory class RpcClinetFactoryBean that inherits the FactoryBean class in Spring and uniformly creates the @ RpcClient annotated proxy class. It is recommended to read the @ Configuration annotation for Spring Zero configuration.
@ Data public class RpcClinetFactoryBean implements FactoryBean {@ Autowired private RpcDynamicPro rpcDynamicPro; private Class classType; public RpcClinetFactoryBean (Class classType) {this.classType = classType;} @ Override public Object getObject () {ClassLoader classLoader = classType.getClassLoader (); Object object = Proxy.newProxyInstance (classLoader,new Class [] {classType}, rpcDynamicPro); return object;} @ Override public Class getObjectType () {return this.classType;} @ Override public boolean isSingleton () {return false;}}
Notice the getObjectType method here. When you inject the factory class into the container, this method returns the Class type that is registered in the container as the Class type.
Then take a look at the proxy class RpcDynamicPro that we created.
@ Component @ Slf4j public class RpcDynamicPro implements InvocationHandler {@ Override public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {String requestJson = objectToJson (method,args); Socket client = new Socket ("127.0.0.1", 20006); client.setSoTimeout (10000); / / get the output stream of Socket to send data to the server PrintStream out = new PrintStream (client.getOutputStream ()) / / get the input stream of Socket, which is used to receive data sent from the server BufferedReader buf = new BufferedReader (new InputStreamReader (client.getInputStream (); / / send data to the server side out.println (requestJson); Response response = new Response (); Gson gson = new Gson () Try {/ / there is a time limit for receiving data from the server (set by the system or set by itself). After this time, the exception String responsJson = buf.readLine (); response = gson.fromJson (responsJson, Response.class);} catch (SocketTimeoutException e) {log.info ("Timeout, No response") } if (client! = null) {/ / if the constructor establishes a connection, the socket is closed. If the connection is not established, there is no need to close client.close (); / / only socket is closed, and its associated input and output streams are also closed} return response.getResult ();} public String objectToJson (Method method,Object [] args) {Request request = new Request (); String methodName = method.getName () Class [] parameterTypes = method.getParameterTypes (); String className = method.getDeclaringClass (). GetName (); request.setMethodName (methodName); request.setParameTypes (parameterTypes); request.setParameters (args); request.setClassName (getClassName (className)); GsonBuilder gsonBuilder = new GsonBuilder (); gsonBuilder.registerTypeAdapterFactory (new ClassTypeAdapterFactory ()); Gson gson = gsonBuilder.create (); return gson.toJson (request);} private String getClassName (String beanClassName) {String className = beanClassName.substring (beanClassName.lastIndexOf (.) + 1) ClassName = className.substring (0J1). ToLowerCase () + className.substring (1); return className;}}
Our client has finished writing, and we have assembled the information sent to the server. The rest of the work is simple and start writing the server-side code.
What information should the server return to the client after processing?-the server
The code on the server side is simpler than the client side. It can be divided into the following three steps
After you get the interface name, find the implementation class through the interface name
Execute the corresponding method through reflection
Return the information after execution
So let's write the code according to these three steps.
After you get the interface name, find the implementation class through the interface name
How to get the implementation class of the corresponding interface through the interface name? This requires us to load the corresponding information into the server when it starts.
@ Component @ Log4j public class InitRpcConfig implements CommandLineRunner {@ Autowired private ApplicationContext applicationContext; public static Map rpcServiceMap = new HashMap (); @ Override public void run (String... Args) throws Exception {Map beansWithAnnotation = applicationContext.getBeansWithAnnotation (Service.class); for (Object bean: beansWithAnnotation.values ()) {Class clazz = bean.getClass (); Class [] interfaces = clazz.getInterfaces (); for (Class inter: interfaces) {rpcServiceMap.put (getClassName (inter.getName ()), bean); log.info ("loaded Services:" + inter.getName ()) } private String getClassName (String beanClassName) {String className = beanClassName.substring (beanClassName.lastIndexOf (".") + 1); className = className.substring (0Magne1). ToLowerCase () + className.substring (1); return className;}}
At this point, rpcServiceMap stores the correspondence between the interface name and its corresponding implementation class.
Execute the corresponding method through reflection
After getting the corresponding relationship at this time, we can find the method in the corresponding implementation class according to the information sent by the client. Then execute it and return the information.
Public Response invokeMethod (Request request) {String className = request.getClassName (); String methodName = request.getMethodName (); Object [] parameters = request.getParameters (); Class [] parameTypes = request.getParameTypes (); Object o = InitRpcConfig.rpcServiceMap.get (className); Response response = new Response (); try {Method method = o.getClass (). GetDeclaredMethod (methodName, parameTypes); Object invokeMethod = method.invoke (o, parameters); response.setResult (invokeMethod) } catch (NoSuchMethodException e) {log.info ("not found" + methodName);} catch (IllegalAccessException e) {log.info ("execution error" + parameters);} catch (InvocationTargetException e) {log.info ("execution error" + parameters);} return response;}
Now that both of our services are started and invoked on the client side, we find that just invoking the interface can be invoked.
These are all the contents of the article "how to write a RPC Framework by hand". Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.