When I first came to know Dubbo, ALL I knew was that it was an RPC framework, so what is RPC?

1. What is RPC

Wikipedia defines RPC as follows:

In distributed computing, Remote Procedure Call (RPC) is a computer communication protocol. This protocol allows a program running on one computer to call a subroutine in another address space (usually a computer on an open network) without the programmer having to program for this interaction as if it were a native program (no attention to detail). RPC is a Server – Client (Client/Server) mode. The classical implementation is a system of sending request – receiving response for information exchange. If the software involved uses object-oriented programming, remote procedure calls can also be called remote calls or remote method calls, such as Java RMI.

So for A Java program, RPC is a remote method call.

1.1 Relative to local method calls

Remote method calls are relative to local method calls, which refer to method calls within a process, and remote method calls, which refer to methods within two processes that call each other.

1.2 Network-based Transmission

As can be seen from the above statement, remote method call refers to method call within two processes. If remote method call is to be realized, it is basically called through network transmission data.

So there you have it

  • RPC over Http: Transmits data over Http
  • PRC over Tcp: transmits data over Tcp

1.3 Data Transmission

The transmitted data can be defined through negotiation by both sides of RPC, which basically includes:

  • Which class or interface is invoked
  • Which method is called, method name and method parameter type (consider method overloading)
  • Call the method’s input parameter

2. What is Dubbo

2.1 define

Apache Dubbo is a high-performance, lightweight, open source Java service framework.

A few months ago, Apache Dubbo was a high-performance, lightweight, open source Java RPC framework.

Dubbo was originally positioned as RPC, focusing on calls between two services. However, with the popularity of microservices, Dubbo is also moving beyond service invocation into service governance, service monitoring, service gateways, etc. Dubbo is now aiming to become more than just an RPC framework, but a service framework similar to Spring Cloud.

2.2 Basic Components

Dubbo consists of the following modules: registry, service provider, container, service consumer, and monitoring center.

2.2.1 Registry

Those of you who have used SpringCloud or Zookeeper will remember that one of the most important components of a distributed service is the registry, which manages the entire service of the system, including service discovery and service registration.

2.2.2 Service Provider

As mentioned earlier, the RPC framework needs to transfer data over the network. This data can be represented in Java as an interface, and the service provider’s role is to provide this interface and register itself with the registry so that the service can be exposed for service consumers to invoke. Both the service provider and the service consumer need to agree on an agreement to handle the service consumer’s requests.

2.2.3 Container (Container)

Dubbo’s Container module is an independent Container. Services do not need the features of Web containers such as Tomcat/JBoss, so there is no need to load services using Web containers. Dubbo has jetty and Spring built in to start the service, but also provides a container extension. Users can customize the container to start the service, so you can use Tomcat to start the service, but it is not recommended.

2.2.4 Service Consumers

The service consumer needs to send a request to the service provider to get the final data.

2.2.5 Monitoring center

If a distributed service wants to run stably, it needs to monitor the running status of the service on the whole link. The service without monitoring is really terrible. The author has encountered the service without monitoring, and the process of finding and troubleshooting problems is really painful.

2.2.6 Basic Call process

  • The service container is responsible for starting, loading, and running the service provider.
  • At startup, service providers register their services with the registry.
  • At startup, service consumers subscribe to the registry for the services they need.
  • The registry returns a list of service provider addresses to the consumer, and if there are changes, the registry pushes the change data to the consumer based on the long connection.
  • The service consumer, from the provider address list, selects one provider to call based on the soft load balancing algorithm. If the call fails, selects another one to call.
  • Service consumers and providers accumulate calls and call times in memory and regularly send statistics to the monitoring center every minute.

3. Implement a simple Dubbo yourself

With that said, it’s time to show the real technology, and let’s start by implementing a simple version of Dubbo ourselves.

3.1 Requirement Analysis

First of all, we conduct demand analysis. We design a framework, and what modules we hope the framework has and what functions of each module are. We can refer to Dubbo’s architecture for design.

Because of the simple version, we only need four modules: registry, service provider, service consumer, and container.

3.2 Module function analysis

3.2.1 Registry

  • Provides registration service functions
  • Provides the function of obtaining registered services

3.2.2 Service providers

  • Provide an invocation interface for service consumers to invoke
  • Register services with the registry at startup

3.2.3 container

  • The service container is responsible for starting, loading, and running the service provider

3.2.4 Service consumers

  • Subscribe to the registry at startup
  • Invokes the interface provided by the service provider

3.3 Overall project structure

The whole project is divided into three packages: Consumer, Provider and framework.

  • Consumer: Contains a consumer launch class consumer
  • Provider: contains the provided interface UserService, interface implementation class UserServiceImpl, and provider startup class Provider
  • Framework: contains protocol package, Proxy package, and Register package
    • Protocol: The protocol layer, this time using Http, HttpClient, HttpServer, HttpServerHandler, DispatcherServlet, and Invocation
    • Proxy: contains a proxy factory, ProxyFactory
    • Register: contains a LocalRegister

3.4 Invocation Process

  • The Provider class starts, registers the service, and starts the container
  • The Consumer class starts and invokes the exposed interface through a dynamic proxy
  • ProxyFactory sends a request through HttpClient to invoke the interface provided by the service provider
  • The service provider receives the interface request and processes the request through HttpServerHandler
  • The HttpServerHandler parses the request parameters and gets the transport entity Invocation
  • Use reflection to get an instance of UserServiceImpl by transferring parameters in the entity
  • Call the method in UserServiceImpl to get the returned data
  • The returned data is written to the Response, which is returned to the service consumer
  • The consumer receives the returned data and presents it

3.5 Implementation

Global dependency file

<? The 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" > The < modelVersion > 4.0.0 < / modelVersion > < groupId > org. Example < / groupId > < artifactId > RPCDemo < / artifactId > <version>1.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> < version > 4.1.16. Final < / version > < / dependency > < the dependency > < groupId > org.. Apache tomcat. Embed < / groupId > < artifactId > tomcat embed - core < / artifactId > < version > 9.0.12 < / version > < / dependency > < the dependency > <groupId>org.apache.commons</groupId> <artifactId> Commons -io</artifactId> <version>1.3.2</version> </dependency> <! -- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId> Lombok </artifactId> <version>1.18.4</version> <scope>provided</scope> </dependency> <! -- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> < artifactId > fastjson < / artifactId > < version > 1.2.51 < / version > < / dependency > < the dependency > < the groupId > org. Apache. Curator < / groupId > < artifactId > curator - framework < / artifactId > < version > 4.1.0 < / version > < / dependency > < the dependency > < groupId > org. Apache. Curator < / groupId > < artifactId > curator - client < / artifactId > < version > 4.1.0 < / version > </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> < version > 4.1.0 < / version > < / dependency > < the dependency > < groupId > org. Apache. Zookeeper < / groupId > < artifactId > zookeeper < / artifactId > < version > 3.4.13 < / version > < / dependency > < the dependency > < groupId > org, apache httpcomponents < / groupId > < artifactId > httpclient < / artifactId > < version > 4.5.13 < / version > < / dependency > < the dependency > < groupId > org, apache httpcomponents < / groupId > < artifactId > httpmime < / artifactId > < version > 4.5.2 < / version > </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> < version > 4.4.14 < / version > < / dependency > < / dependencies > < project >Copy the code

3.5.1 track of the provider layer

Provider class

package com.rpc.provider; import com.rpc.framework.protocol.http.HttpServer; import com.rpc.framework.register.LocalRegister; public class Provider { public static void main(String[] args) { String interfaceName = UserService.class.getName(); LocalRegister.register(interfaceName,UserServiceImpl.class); // Start service HttpServer HttpServer = new HttpServer(); httpServer.start("localhost",8080); }}Copy the code

Interface UserService

package com.rpc.provider;

public interface UserService {

    String hello(String name);

}
Copy the code

The implementation class UserServiceImpl

package com.rpc.provider; public class UserServiceImpl implements UserService { @Override public String hello(String name) { return "hello" + name; }}Copy the code

3.5.2 consumer layer

Cousumer class

package com.rpc.consumer; import com.rpc.framework.proxy.ProxyFactory; import com.rpc.provider.UserService; public class Consumer { public static void main(String[] args) { UserService userService = ProxyFactory.gerProxy(UserService.class); String result = userService.hello("PRC"); System.out.println(result); }}Copy the code

3.5.3 framework layer

3.5.3.1 protocol layer

Four classes in the HTTP layer

package com.rpc.framework.protocol.http; import lombok.SneakyThrows; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class DispatcherServlet extends HttpServlet { @SneakyThrows @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { new HttpServerHandler().handler(req,resp); }}Copy the code
package com.rpc.framework.protocol.http; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.rpc.framework.protocol.Invocation; import org.apache.http.HttpEntity; import org.apache.http.ParseException; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.protocol.HttpContext; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.util.EntityUtils; import javax.net.ssl.SSLContext; import java.io.IOException; import java.nio.charset.Charset; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; public class HttpClient { public static SSLContext createSSL() { try { return new SSLContextBuilder().loadTrustMaterial(null, (certificate, authType) -> true).build(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } return null; } /** * build httpClient client, set SSL certificate, */ private static Final CloseableHttpClient httpClient = httpClient.custom ().setSSLContext(createSSL()) .setRetryHandler(new HttpRequestRetryHandler() { @Override public boolean retryRequest(IOException e, int retryCount, HttpContext httpContext) { if (retryCount <= 3) { return true; } return false; } }).build(); /** * Send an HttpPost request, Map * * @Param URL * @param Invocation * @return */ public static String sendPost(String URL, Invocation invocation) { String jsonString = JSON.toJSONString(invocation); StringEntity entity = new StringEntity(jsonString, Charset.forName("UTF-8")); /*entity.setContentEncoding("UTF-8"); entity.setContentType("application/json; charset=UTF-8"); */ HttpPost httppost = new HttpPost(url); */ httppost.setHeader(" content-type ", "application/json; charset=UTF-8"); httppost.setHeader("Accept", "application/json"); httppost.setEntity(entity); CloseableHttpResponse response = null; try { response = httpclient.execute(httppost); } catch (IOException e) { e.printStackTrace(); } HttpEntity httpEntity = response.getEntity(); String result = null; try { result = EntityUtils.toString(httpEntity); } catch (ParseException | IOException e) { e.printStackTrace(); } return result; }}Copy the code
package com.rpc.framework.protocol.http; import org.apache.catalina.*; import org.apache.catalina.connector.Connector; import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardEngine; import org.apache.catalina.core.StandardHost; import org.apache.catalina.startup.Tomcat; /** */ public class HttpServer {public void start(String hostName, Integer port){ Tomcat tomcat = new Tomcat(); Server server = tomcat.getServer(); Service service = server.findService("Tomcat"); Connector connector = new Connector(); connector.setPort(port); Engine engine = new StandardEngine(); engine.setDefaultHost(hostName); Host host = new StandardHost(); host.setName(hostName); String contextPath = ""; Context context = new StandardContext(); context.setPath(contextPath); context.addLifecycleListener(new Tomcat.FixContextListener()); host.addChild(context); engine.addChild(host); service.setContainer(engine); service.addConnector(connector); tomcat.addServlet(contextPath,"dispatcher", new DispatcherServlet()); context.addServletMappingDecoded("/*", "dispatcher"); try { tomcat.start(); tomcat.getServer().await(); } catch (LifecycleException e) { e.printStackTrace(); }}}Copy the code
package com.rpc.framework.protocol.http; import com.alibaba.fastjson.JSONObject; import com.rpc.framework.protocol.Invocation; import com.rpc.framework.register.LocalRegister; import com.rpc.provider.UserService; import com.rpc.provider.UserServiceImpl; import org.apache.commons.io.IOUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class HttpServerHandler { public void handler(HttpServletRequest req, HttpServletResponse resp) throws IOException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {// Handle request Invocation = JSONObject. ParseObject (Req.getinputStream (), Invocation. Class); Class aClass = LocalRegister.get(invocation.getInterfaceName()); try { Method method = aClass.getMethod(invocation.getMethodName(),invocation.getParamTypes()); String result = (String) method.invoke(aClass.newInstance(), invocation.getParams()); IOUtils.write(result,resp.getOutputStream()); } catch (NoSuchMethodException e) { e.printStackTrace(); }}}Copy the code

Transport entity Class

package com.rpc.framework.protocol; import java.io.Serializable; public class Invocation implements Serializable { private String interfaceName; private String methodName; private Class[] paramTypes; private Object[] params; public Invocation(String interfaceName, String methodName, Class[] paramTypes, Object[] params) { this.interfaceName = interfaceName; this.methodName = methodName; this.paramTypes = paramTypes; this.params = params; } public String getInterfaceName() { return interfaceName; } public void setInterfaceName(String interfaceName) { this.interfaceName = interfaceName; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Class[] getParamTypes() { return paramTypes; } public void setParamTypes(Class[] paramTypes) { this.paramTypes = paramTypes; } public Object[] getParams() { return params; } public void setParams(Object[] params) { this.params = params; }}Copy the code

3.5.3.2 proxy layer

ProxyFactory class

package com.rpc.framework.proxy; import com.rpc.framework.protocol.Invocation; import com.rpc.framework.protocol.http.HttpClient; import java.lang.reflect.Proxy; public class ProxyFactory { public static <T> T gerProxy(final Class interfaceClass){ return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, (proxy, method, args) -> { Invocation invocation = new Invocation(interfaceClass.getName(), method.getName(), method.getParameterTypes(),args); String result = httpClient. sendPost("http://localhost:8080", Invocation); return result; }); }}Copy the code

3.5.3.3 register layer

LocalRegister class

package com.rpc.framework.register; import java.util.HashMap; import java.util.Map; public class LocalRegister { private static Map<String, Class> map = new HashMap<>(); public static void register(String interfaceName, Class implClass){ map.put(interfaceName, implClass); } public static Class get(String interfaceName){ return map.get(interfaceName); }}Copy the code

3.6 validation

Start the Provider class, then the Consumer class

The output

helloPRC
Copy the code

Done!!