Java implements simple RPC framework and understanding
- consumer
- RPC framework
-
- Registration center
-
- Service registration
-
- Local service registration
- Remote service registration
- service discovery
- service call
-
- dynamic proxy
- load balancing
- Retry mechanism
- Service distribution
- service provider
-
- interface
- Implementation class
Consumer
As shown in the following code, consumers who want to call HelloService and ByeService of remote services only need to pass parameters and method calls according to the interface definition, and then specify the required service version number, and use the dynamic proxy function implemented by the rpc framework. Make remote method calls as if they were local methods
public class Consumer {<!-- --> public static void main(String[] args) {<!-- --> HelloService helloService = ProxyFactory.getProxy(HelloService.class, "1.0"); String res1 = helloService.sayHello("lujx"); HelloService helloService2 = ProxyFactory.getProxy(HelloService.class, "2.0"); String res2 = helloService2.sayHello("lujx"); ByeService byeService = ProxyFactory.getProxy(ByeService.class, "1.0"); String res3 = byeService.sayBye("lujx"); log.info("\\ " + res1 + "\\ " + res2 + "\\ " + res3); } }
RPC framework
Registration Center
The core function of the rpc framework is the registration center. The three elements of the registration center are service registration, service discovery, and service invocation.
Service registration
The registration center needs to implement a sharing mechanism and cannot be a resource unique to a certain thread. Therefore, you can choose reids or zookeeper as the data management middleware of the registration center.
/** * Manual registration, automatic registration can be achieved through annotations * */ public void afterPropertiesSet() throws Exception {<!-- --> String host = "localhost"; //Local registration LocalRegister.register(HelloService.class.getName(), "1.0", HelloServiceImpl.class); LocalRegister.register(HelloService.class.getName(), "2.0", HelloServiceImpl2.class); LocalRegister.register(ByeService.class.getName(), "1.0", ByeServiceImpl.class); //Registration center registration URL url = new URL("http", host, port, "/inner"); MapRemoteRegister.register(HelloService.class.getName(), "1.0", url); MapRemoteRegister.register(HelloService.class.getName(), "2.0", url); MapRemoteRegister.register(ByeService.class.getName(), "1.0", url); MapRemoteRegister.serviceMap.forEach((key, value) -> JedisUtil.put("Registration Center", key, JSON.toJSONString(value))); }
Local service registration
- The service node is responsible for registering all externally provided services locally.
- When a consumer requests a service from the service node through a remote service call, the service provider can quickly find the specified service class through the local service registration center.
/** * Local service registration value stores class information, and remote access url information * */ public static void register(String interfaceName, String version, Class<?> clazz) {<!-- --> List<Class<?>> clazzs = serviceMap.get(interfaceName + version); if (clazzs == null) {<!-- --> serviceMap.put(interfaceName + version, Collections.singletonList(clazz)); } else {<!-- --> clazzs.add(clazz); serviceMap.put(interfaceName + version, clazzs); } }
Remote service registration
- Responsible for registering in the registration center all services provided by the service node.
Service discovery
Data format for service registration to the registration center
- key: registration center
- field: interface name + version number
- value: url information
When a dynamic proxy makes a service call, it only needs to use the above format to obtain the service node in redis.
Service call
Dynamic proxy
public class ProxyFactory {<!-- --> public static <T> T getProxy(Class<?> interfaceClass, String version) {<!-- --> Object o = Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{<!-- -->interfaceClass}, (proxy, method, args) -> {<!-- --> Invocation invocation = new Invocation(interfaceClass.getName(), version, method.getName(), method.getParameterTypes(), args); //Service discovery List<URL> urls = RegisterUtil.getServiceUrl(interfaceClass.getName(), version); //Load balancing URL url = LoadBalanceStrategy.loadBalance(urls); //service call RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); //Set request headers headers.setContentType(MediaType.MULTIPART_FORM_DATA); HttpEntity httpEntity = new HttpEntity(ByteUtil.getByteArray(invocation), headers); ResponseEntity<String> entity = restTemplate.postForEntity(url.toURI(), httpEntity, String.class); return entity.getBody(); }); return (T) o; } }
Load balancing
When the dynamic proxy completes service discovery, it obtains the service node URL information. If there is more than one node at this time, a load balancing strategy can be designed to select the appropriate node for service requests.
- What the author implements is a simple random strategy
Retry mechanism
If the service call fails, consider implementing a retry mechanism
- Sleep for n seconds and continue calling
- Set the maximum number of retries
- Select other service nodes to implement the load balancing strategy again, and then make service calls.
Service distribution
When the dynamic proxy sends a request to the service node through the http protocol, the service node needs to implement a servlet that specifically handles internal node services. If it is a node that specifically handles internal services, it can filter out other illegal requests.
The author borrows the server started by SpringBoot. If it is started directly using the Tomcat package, the following annotations cannot be used.
- The /inner path represents the internal service
- @WebServlet
- Intercept service requests at the specified path
- Used in conjunction with @ServletComponentScan, configured on the SpringBoot startup class
@WebServlet(urlPatterns = {<!-- -->"/inner"}) public class MyServlet extends HttpServlet {<!-- --> @Override protected void service(HttpServletRequest req, HttpServletResponse resp) {<!-- --> //echo test new EchoTestHandler().handler(req, resp); //Heartbeat request new HeartServerHandler().handler(req, resp); //rest interface normal request processing new HttpServerHandler().handler(req, resp); } }
Service Provider
Interface
public interface ByeService {<!-- --> String sayBye(String name); }
Implementation class
public class ByeServiceImpl implements ByeService {<!-- --> @Override public String sayBye(String name) {<!-- --> return name + "says bye"; } }
ps: time flies so fast