手写dubbo案例: https://gitee.com/itmc/dubbo-m.git
注册中心原理
[
](https://gitee.com/itmc/dubbo-m.git)
说明:
服务提供者
启动服务读取接口信息存入本地缓存表—->解析client请求——>通过获取本地缓存列表—->获取对应的请求接口信息—->通过反射运行接口实现并响应
服务消费者
通过请求封装接口信息(代理)—>请求服务生产者
注册中心
注册中心简单实现可以用zk,redis,或者直接文件,之所以选择zk这种是因为它的watch监听机制,或者使用redis的发布/订阅机制
简单模拟实现
基础原理说明
在我们使用dubbo的时候,客户端如下:
@Component
public class BusinessService {
/**
* 引用服务
*/
@Reference
private 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 {
@Override
public 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
@Getter
public 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,jetty
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();
}
}
}
创建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 {
@Override
protected 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
```java
package 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() {
@Override
public 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
```java
package 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