手写dubbo案例: https://gitee.com/itmc/dubbo-m.git
注册中心原理

[
](https://gitee.com/itmc/dubbo-m.git)
说明:
服务提供者
启动服务读取接口信息存入本地缓存表—->解析client请求——>通过获取本地缓存列表—->获取对应的请求接口信息—->通过反射运行接口实现并响应
服务消费者
通过请求封装接口信息(代理)—>请求服务生产者
注册中心
注册中心简单实现可以用zk,redis,或者直接文件,之所以选择zk这种是因为它的watch监听机制,或者使用redis的发布/订阅机制
简单模拟实现
基础原理说明
在我们使用dubbo的时候,客户端如下:
@Componentpublic class BusinessService {/*** 引用服务*/@Referenceprivate HelloService helloService;public void testHello(String name) {String str = helloService.sayHello(name);System.out.println("调用结果:" + str);}}
通过上面代码可以推测:
- 客户端获取到HelloService接口
- 验证是否被@Reference注解标注
- 如果存在@Reference注解,则说明是引入dubbo服务
- 获取通过反射获取接口信息,通过http或者dubbo协议调用服务端
客户端调用服务端需要哪些?
- 服务端url
- 接口信息
服务端需要哪些信息?
- 接口信息
注册中心作用?
服务端向注册中心注册接口信息,客户端拉取地址列表
客户端怎样调用?
简单来说就是拿到接口信息通过http协议或者dubbo协议进行服务的调用
服务端怎样处理?
服务端收到client发来的接口信息,通过注册表查询接口信息.通过反射去创建调用对应的方法
服务端创建
创建HelloService
package com.itmck.dubbom.provider.api;/*** 太阳当空照,花儿对我笑** Create by M ChangKe 2021/10/20 15:37**/public interface HelloService {String sayHello(String userName);}
创建HelloServiceImpl
package com.itmck.dubbom.provider.impl;import com.itmck.dubbom.provider.api.HelloService;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/10/20 15:37**/public class HelloServiceImpl implements HelloService {@Overridepublic String sayHello(String userName) {return "Hello: " + userName;}}
创建Invocation
Invocation用于存放接口类相关信息
package com.itmck.dubbom.dubbom;import lombok.Getter;import lombok.Setter;import java.io.Serializable;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/10/20 19:52**/@Setter@Getterpublic class Invocation implements Serializable {private static final long serialVersionUID = -2814851007135975046L;/*** 接口名*/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;}}
引入tomcat
<dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>9.0.12</version></dependency>
创建HttpServer
package com.itmck.dubbom.dubbom.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 {// tomcat,jettypublic 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();}}}
创建DispatcherServlet
package com.itmck.dubbom.dubbom.protocol.http;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/10/20 19:43**/public class DispatcherServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {new HttpServerHandler().handler(req,resp);}}
创建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 {
public void handler(HttpServletRequest req, HttpServletResponse resp) {try {ObjectInputStream ois = new ObjectInputStream(req.getInputStream());//获取传进来的接口信息Invocation invocation = (Invocation)ois.readObject();//获取接口名String interfaceName = invocation.getInterfaceName();//通过本地注册表获取接口类Class<?> interfaceClass = LocalRegister.get(interfaceName);//获取方法Method method = interfaceClass.getMethod(invocation.getMethodName(), invocation.getParamTypes());//通过反射调用String result = (String) method.invoke(interfaceClass.newInstance(), invocation.getParams());System.out.println("tomcat:" + result);IOUtils.write(result, resp.getOutputStream());} catch (Exception e) {e.printStackTrace();}}
}
<a name="NeUsN"></a>### 创建LocalRegister```javapackage com.itmck.dubbom.dubbom.register;import java.util.HashMap;import java.util.Map;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/10/20 19:17**/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);}}
客户端创建
创建HttpClient
package com.itmck.dubbom.dubbom.protocol.http;import com.itmck.dubbom.dubbom.Invocation;import org.apache.commons.io.IOUtils;import java.io.InputStream;import java.io.ObjectOutputStream;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.URL;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/10/21 10:06**/public class HttpClient {/**** @param hostname 主机ip* @param port 端口* @param invocation 参数* @return 响应数据*/public String send(String hostname, Integer port, Invocation invocation) {try {URL url = new URL("http", hostname, port, "/");HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();httpURLConnection.setRequestMethod("POST");httpURLConnection.setDoOutput(true);OutputStream outputStream = httpURLConnection.getOutputStream();ObjectOutputStream oos = new ObjectOutputStream(outputStream);oos.writeObject(invocation);oos.flush();oos.close();InputStream inputStream = httpURLConnection.getInputStream();String result = IOUtils.toString(inputStream);return result;} catch (Exception e) {e.printStackTrace();}return null;}}
创建代理类
package com.itmck.dubbom.dubbom;import com.itmck.dubbom.dubbom.register.RemoteMapRegister;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.List;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/10/21 10:14**/public class ProxyFactory {/*** @param interfaceClass 被代理接口* @param <T>* @return 返回代理对象*/public static <T> T getProxy(final Class<?> interfaceClass) {return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Invocation invocation = new Invocation(interfaceClass.getName(), method.getName(), method.getParameterTypes(), args);HttpClient httpClient = new HttpClient();String result =httpClient.send("localhost","8080",invocation);return result;}});}}
测试
- 启动 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 {
public static void main(String[] args) {//注册服务信息到本地注册表URL url = new URL("localhost", 8080);LocalRegister.register(HelloService.class.getName(), HelloServiceImpl.class);RemoteMapRegister.regist(HelloService.class.getName(), url);Protocol protocol = ProtocolFactory.getProtocol();protocol.start(url);}
}
- 启动 Consumer```javapackage com.itmck.dubbom.consumer;import com.itmck.dubbom.dubbom.ProxyFactory;import com.itmck.dubbom.provider.api.HelloService;/*** 太阳当空照,花儿对我笑* <p>* Create by M ChangKe 2021/10/21 9:57**/public class Consumer {public static void main(String[] args) {HelloService helloService = ProxyFactory.getProxy(HelloService.class);String result = helloService.sayHello("mck");System.out.println(result);}}
优化代码
- 上面的注册列表待完善.参考RemoteMapRegister
- 客户端的url获取.随机负载均衡参考LoadBalance
- 使用http还是dubbo协议. 参考ProtocolFactory
