dubbo:被称作开源 Java RPC框架
RPC:对于java程序员而言,RPC就是远程的方法调用;
远程方法调用:我们经常使用的是本地方法调用,相对的远程方法调用性质上差不多,也是方法调用。但是是通过网络去连接,两个不同线程之间的调用。
往泛了说像http网络协议传输数据,TCP协议输出数据都算的上是RPC。
Dubbo:
1、dubbo协议 数据格式 netty
2、http协议 数据格式 tomcat,jetty
手写Dubbo
概念
在java程序中,远程调用方法;
服务提供者:
1、提供服务的接口
2、提供实现类
3、注册服务(注册中心,本地注册)
4、暴露服务(tomcat,nettyServer接受及处理请求)
代码实现
服务提供者主要功能就是方法的提供者
(远程方法的提供者)
需要暴露方法出去,就需要启动tomcat暴露我们的方法,
tomcat是个Servlet容器,我们还需要写一个Servlet来接受请求。
public class DispatcherServlet extends HttpServlet {
定义一个HttpServlet,handler(req, resp);就是处理请求的方法。
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServerHandler().handler(req, resp);
}
}
那我们怎么才能访问一个服务呢?
需要给定以下四个东西才能指定服务,并且有效的调用
1、接口名
2、方法名
3、方法参数类型列表
4、方法值列表
我们把这四个必要的东西构建成一个对象,
Invocation对象
private String interfaceName;
private String methodName;
private Class[] paramTypes;
private Object[] params;
//和上面一一对应
handler要处理请求肯定是要得到上面这四个值,我们要怎么获得Invocation对象呢?
现在我们想提供HelloService这个服务
public interface HelloService {
String sayHello(String userName);
}
假设我们这个服务只有一个实现
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String userName) {
return "Hello: " + userName;
}
}
在我们的提供者中是需要注册服务的,我们这边先把服务注册到本地
这就要使用到注册相关的类
public class LocalRegister {
private static Map<String, Class> map = new HashMap<>();
public static void regist(String interfaceName, Class implClass) {
map.put(interfaceName, implClass);
}
//传我一个接口名和实现类,保存到map中
public static Class get(String interfaceName) {
return map.get(interfaceName);
}
//通过接口名获得对应实现类
}
本地注册
有了这个注册类;
使用这行代码,就可以把Invocation存起来了。
LocalRegister.regist(HelloService.class.getName(), HelloServiceImpl.class);
再之后就可以再handler中使用get方法获得Invocation类了。
获得了invocation方法就可以执行请求了
Method method = implClass.getMethod(invocation.getMethodName(), invocation.getParamTypes());
var result = (String) method.invoke(implClass.newInstance(), invocation.getParams());
到此我们就得到结果了
然后返回结果
System.out.println(“tomcat:” + result);
IOUtils.write(result, resp.getOutputStream());
如果需要不通服务的实现类,可以通过添加多个版本实现。
服务消费者
发送数据方法,
发送的地址,哪个端口,发送的数据是什么
地址:hostname
端口:port
要发送的数据就是我们的:Invocation
利用jdk自带的http方式发送数据即可。
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 (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
我们消费者去调用这个方法
HttpClient httpClient=new HttpClient();
Invocation invocation=new Invocation(HelloService.class,"sayhello",new Class[]{String.class},new Object[]{"方法"});
String result =httpClient.send("localhost",8080,invocation);
这就完成了远程的方法调用,但是这种调用方式对于消费者而言非常不友好。
这里我们利用jdk的动态代理
HelloService helloService = ProxyFactory.getProxy(HelloService.class);
String xxx = helloService.sayHello("xxx");
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String mock = System.getProperty("mock");
if (mock != null && mock.startsWith("return:")) {
String result = mock.replace("return:", "");
return result;
}
Invocation invocation = new Invocation(interfaceClass.getName(), method.getName(), method.getParameterTypes(), args);
List<URL> urlList = RemoteMapRegister.get(interfaceClass.getName());
URL url = LoadBalance.random(urlList);
Protocol protocol = ProtocolFactory.getProtocol();
String result = protocol.send(url, invocation);
return result;
}
把方法写活,只利用HelloService helloService = ProxyFactory.getProxy(HelloService.class);方法,然后再传入String xxx = helloService.sayHello(“xxx”);值就可以实现调用了。