手写dubbo案例: https://gitee.com/itmc/dubbo-m.git

注册中心原理

image.png

[

](https://gitee.com/itmc/dubbo-m.git)
说明:

服务提供者

启动服务读取接口信息存入本地缓存表—->解析client请求——>通过获取本地缓存列表—->获取对应的请求接口信息—->通过反射运行接口实现并响应

服务消费者

通过请求封装接口信息(代理)—>请求服务生产者

注册中心

注册中心简单实现可以用zk,redis,或者直接文件,之所以选择zk这种是因为它的watch监听机制,或者使用redis的发布/订阅机制

简单模拟实现

基础原理说明

在我们使用dubbo的时候,客户端如下:

  1. @Component
  2. public class BusinessService {
  3. /**
  4. * 引用服务
  5. */
  6. @Reference
  7. private HelloService helloService;
  8. public void testHello(String name) {
  9. String str = helloService.sayHello(name);
  10. System.out.println("调用结果:" + str);
  11. }
  12. }

通过上面代码可以推测:

  1. 客户端获取到HelloService接口
  2. 验证是否被@Reference注解标注
  3. 如果存在@Reference注解,则说明是引入dubbo服务
  4. 获取通过反射获取接口信息,通过http或者dubbo协议调用服务端

客户端调用服务端需要哪些?

  • 服务端url
  • 接口信息

服务端需要哪些信息?

  • 接口信息

注册中心作用?
服务端向注册中心注册接口信息,客户端拉取地址列表

客户端怎样调用?
简单来说就是拿到接口信息通过http协议或者dubbo协议进行服务的调用

服务端怎样处理?
服务端收到client发来的接口信息,通过注册表查询接口信息.通过反射去创建调用对应的方法

服务端创建

创建HelloService

  1. package com.itmck.dubbom.provider.api;
  2. /**
  3. * 太阳当空照,花儿对我笑
  4. *
  5. * Create by M ChangKe 2021/10/20 15:37
  6. **/
  7. public interface HelloService {
  8. String sayHello(String userName);
  9. }

创建HelloServiceImpl

  1. package com.itmck.dubbom.provider.impl;
  2. import com.itmck.dubbom.provider.api.HelloService;
  3. /**
  4. * 太阳当空照,花儿对我笑
  5. * <p>
  6. * Create by M ChangKe 2021/10/20 15:37
  7. **/
  8. public class HelloServiceImpl implements HelloService {
  9. @Override
  10. public String sayHello(String userName) {
  11. return "Hello: " + userName;
  12. }
  13. }

创建Invocation

Invocation用于存放接口类相关信息

  1. package com.itmck.dubbom.dubbom;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. import java.io.Serializable;
  5. /**
  6. * 太阳当空照,花儿对我笑
  7. * <p>
  8. * Create by M ChangKe 2021/10/20 19:52
  9. **/
  10. @Setter
  11. @Getter
  12. public class Invocation implements Serializable {
  13. private static final long serialVersionUID = -2814851007135975046L;
  14. /**
  15. * 接口名
  16. */
  17. private String interfaceName;
  18. /**
  19. * 方法名
  20. */
  21. private String methodName;
  22. /**
  23. * 方法参数类型列表
  24. */
  25. private Class<?>[] paramTypes;
  26. /**
  27. * 参数列表
  28. */
  29. private Object[] params;
  30. public Invocation(String interfaceName, String methodName, Class<?>[] paramTypes, Object[] params) {
  31. this.interfaceName = interfaceName;
  32. this.methodName = methodName;
  33. this.paramTypes = paramTypes;
  34. this.params = params;
  35. }
  36. }

引入tomcat

  1. <dependency>
  2. <groupId>org.apache.tomcat.embed</groupId>
  3. <artifactId>tomcat-embed-core</artifactId>
  4. <version>9.0.12</version>
  5. </dependency>

创建HttpServer

  1. package com.itmck.dubbom.dubbom.protocol.http;
  2. import org.apache.catalina.*;
  3. import org.apache.catalina.connector.Connector;
  4. import org.apache.catalina.core.StandardContext;
  5. import org.apache.catalina.core.StandardEngine;
  6. import org.apache.catalina.core.StandardHost;
  7. import org.apache.catalina.startup.Tomcat;
  8. public class HttpServer {
  9. // tomcat,jetty
  10. public void start(String hostname, Integer port) {
  11. Tomcat tomcat = new Tomcat();
  12. Server server = tomcat.getServer();
  13. Service service = server.findService("Tomcat");
  14. Connector connector = new Connector();
  15. connector.setPort(port);
  16. Engine engine = new StandardEngine();
  17. engine.setDefaultHost(hostname);
  18. Host host = new StandardHost();
  19. host.setName(hostname);
  20. String contextPath = "";
  21. Context context = new StandardContext();
  22. context.setPath(contextPath);
  23. context.addLifecycleListener(new Tomcat.FixContextListener());
  24. host.addChild(context);
  25. engine.addChild(host);
  26. service.setContainer(engine);
  27. service.addConnector(connector);
  28. tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet());
  29. context.addServletMappingDecoded("/*", "dispatcher");
  30. try {
  31. tomcat.start();
  32. tomcat.getServer().await();
  33. } catch (LifecycleException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }

创建DispatcherServlet

  1. package com.itmck.dubbom.dubbom.protocol.http;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. /**
  8. * 太阳当空照,花儿对我笑
  9. * <p>
  10. * Create by M ChangKe 2021/10/20 19:43
  11. **/
  12. public class DispatcherServlet extends HttpServlet {
  13. @Override
  14. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  15. new HttpServerHandler().handler(req,resp);
  16. }
  17. }

创建HttpServerHandler

当前是服务端实现的重点:

  • 根据HttpServletRequest拿到请求信息Invocation
  • 通过接口名向注册列表中获取class
  • 反射执行方法
  • 将结果写入HttpServletResponse ```java package com.itmck.dubbom.dubbom.protocol.http;

import com.itmck.dubbom.dubbom.Invocation; import com.itmck.dubbom.dubbom.register.LocalRegister; import org.apache.commons.io.IOUtils;

import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ObjectInputStream; import java.lang.reflect.Method;

/**

  • 太阳当空照,花儿对我笑
  • Create by M ChangKe 2021/10/20 19:44 **/ public class HttpServerHandler {
  1. public void handler(HttpServletRequest req, HttpServletResponse resp) {
  2. try {
  3. ObjectInputStream ois = new ObjectInputStream(req.getInputStream());
  4. //获取传进来的接口信息
  5. Invocation invocation = (Invocation)ois.readObject();
  6. //获取接口名
  7. String interfaceName = invocation.getInterfaceName();
  8. //通过本地注册表获取接口类
  9. Class<?> interfaceClass = LocalRegister.get(interfaceName);
  10. //获取方法
  11. Method method = interfaceClass.getMethod(invocation.getMethodName(), invocation.getParamTypes());
  12. //通过反射调用
  13. String result = (String) method.invoke(interfaceClass.newInstance(), invocation.getParams());
  14. System.out.println("tomcat:" + result);
  15. IOUtils.write(result, resp.getOutputStream());
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. }
  19. }

}

  1. <a name="NeUsN"></a>
  2. ### 创建LocalRegister
  3. ```java
  4. package com.itmck.dubbom.dubbom.register;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7. /**
  8. * 太阳当空照,花儿对我笑
  9. * <p>
  10. * Create by M ChangKe 2021/10/20 19:17
  11. **/
  12. public class LocalRegister {
  13. private static Map<String, Class<?>> map = new HashMap<>();
  14. public static void register(String interfaceName, Class<?> implClass) {
  15. map.put(interfaceName, implClass);
  16. }
  17. public static Class<?> get(String interfaceName) {
  18. return map.get(interfaceName);
  19. }
  20. }

客户端创建

创建HttpClient

  1. package com.itmck.dubbom.dubbom.protocol.http;
  2. import com.itmck.dubbom.dubbom.Invocation;
  3. import org.apache.commons.io.IOUtils;
  4. import java.io.InputStream;
  5. import java.io.ObjectOutputStream;
  6. import java.io.OutputStream;
  7. import java.net.HttpURLConnection;
  8. import java.net.URL;
  9. /**
  10. * 太阳当空照,花儿对我笑
  11. * <p>
  12. * Create by M ChangKe 2021/10/21 10:06
  13. **/
  14. public class HttpClient {
  15. /**
  16. *
  17. * @param hostname 主机ip
  18. * @param port 端口
  19. * @param invocation 参数
  20. * @return 响应数据
  21. */
  22. public String send(String hostname, Integer port, Invocation invocation) {
  23. try {
  24. URL url = new URL("http", hostname, port, "/");
  25. HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
  26. httpURLConnection.setRequestMethod("POST");
  27. httpURLConnection.setDoOutput(true);
  28. OutputStream outputStream = httpURLConnection.getOutputStream();
  29. ObjectOutputStream oos = new ObjectOutputStream(outputStream);
  30. oos.writeObject(invocation);
  31. oos.flush();
  32. oos.close();
  33. InputStream inputStream = httpURLConnection.getInputStream();
  34. String result = IOUtils.toString(inputStream);
  35. return result;
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. return null;
  40. }
  41. }

创建代理类

  1. package com.itmck.dubbom.dubbom;
  2. import com.itmck.dubbom.dubbom.register.RemoteMapRegister;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.util.List;
  7. /**
  8. * 太阳当空照,花儿对我笑
  9. * <p>
  10. * Create by M ChangKe 2021/10/21 10:14
  11. **/
  12. public class ProxyFactory {
  13. /**
  14. * @param interfaceClass 被代理接口
  15. * @param <T>
  16. * @return 返回代理对象
  17. */
  18. public static <T> T getProxy(final Class<?> interfaceClass) {
  19. return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
  20. @Override
  21. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  22. Invocation invocation = new Invocation(interfaceClass.getName(), method.getName(), method.getParameterTypes(), args);
  23. HttpClient httpClient = new HttpClient();
  24. String result =httpClient.send("localhost","8080",invocation);
  25. return result;
  26. }
  27. });
  28. }
  29. }


测试

  • 启动 Provider ```java package com.itmck.dubbom;

import com.itmck.dubbom.dubbom.Protocol; import com.itmck.dubbom.dubbom.ProtocolFactory; import com.itmck.dubbom.dubbom.URL; import com.itmck.dubbom.dubbom.register.LocalRegister; import com.itmck.dubbom.dubbom.register.RemoteMapRegister; import com.itmck.dubbom.provider.api.HelloService; import com.itmck.dubbom.provider.impl.HelloServiceImpl;

public class Provider {

  1. public static void main(String[] args) {
  2. //注册服务信息到本地注册表
  3. URL url = new URL("localhost", 8080);
  4. LocalRegister.register(HelloService.class.getName(), HelloServiceImpl.class);
  5. RemoteMapRegister.regist(HelloService.class.getName(), url);
  6. Protocol protocol = ProtocolFactory.getProtocol();
  7. protocol.start(url);
  8. }

}

  1. - 启动 Consumer
  2. ```java
  3. package com.itmck.dubbom.consumer;
  4. import com.itmck.dubbom.dubbom.ProxyFactory;
  5. import com.itmck.dubbom.provider.api.HelloService;
  6. /**
  7. * 太阳当空照,花儿对我笑
  8. * <p>
  9. * Create by M ChangKe 2021/10/21 9:57
  10. **/
  11. public class Consumer {
  12. public static void main(String[] args) {
  13. HelloService helloService = ProxyFactory.getProxy(HelloService.class);
  14. String result = helloService.sayHello("mck");
  15. System.out.println(result);
  16. }
  17. }

优化代码

  • 上面的注册列表待完善.参考RemoteMapRegister
  • 客户端的url获取.随机负载均衡参考LoadBalance
  • 使用http还是dubbo协议. 参考ProtocolFactory