dubbo3.0-Rpc调用案例代码解析
根据周瑜所讲的Dubbo3.0视频原理解析视频和内容,对源码的内容进行总结分析。视频源为【Dubbo3.0】地表最强!Dubbo快速入门教程,通俗易懂(3小时带你快速玩转)
依赖配置
此处使用版本为dubbo官方推荐的3.2.0-beta.4版本,升级至此版本后需要兼容的springboot框架要求较高,而又因springboot版本升级后最低兼容JDK版本为17,所以需要更新JDK至17版本,并且IDEA版本需要提升至2022.3以上。但我相信聪明的你已经解决了这些问题了!
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dubbo</groupId>
<artifactId>dubbo-producter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dubbo-producter</name>
<description>dubbo-producter</description>
<properties>
<java.version>17</java.version>
<dubbo.version>3.2.0-beta.4</dubbo.version>
</properties>
<dependencyManagement>
<dependencies>
<!--dubbo相关依赖通过pom引入,包括dubbo-spring-boot-starter-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
源码
源码分为8个部分:程序入口、http服务、接收处理器、本地接口注册(用于代替bean注入)、请求处理器、实体类、服务注册和负载均衡。以这种方式拆分,可以单独替换任何一部分。文末有各部分的调用关系时序图
1.程序入口
UserController
public class UserController {
public static void main(String[] args) {
UserService userService = ProxyFactory.getProxy(UserService.class);
String testdubbo = userService.testdubbo();
}
}
UserServiceImpl
public class UserServiceImpl implements UserService {
@Override
public String testdubbo() {
return new Random().nextDouble() + "";
}
@SneakyThrows
public static void main(String[] args) {
LocalRegister.regist(UserService.class.getSimpleName(), UserServiceImpl.class);
RegisterUrl localhost = new RegisterUrl(InetAddress.getLocalHost().getHostName(), 8081);
new DubboRegister().add(UserService.class.getSimpleName(),localhost);
new HttpProvider().start(localhost.getHost(),localhost.getPort());
}
}
2.http服务
HttpProvider
import com.dubbo.dubboproducter.web.DispatchServlet;
import lombok.SneakyThrows;
import org.apache.catalina.Server;
import org.apache.catalina.Service;
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 HttpProvider {
@SneakyThrows
public void start(String host, Integer port){
Tomcat tomcat = new Tomcat();
Server server = tomcat.getServer();
Service service = server.findService("Tomcat");
Connector connector = new Connector();
connector.setPort(port);
StandardEngine standardEngine = new StandardEngine();
standardEngine.setDefaultHost(host);
StandardHost standardHost = new StandardHost();
standardHost.setName(host);
StandardContext standardContext = new StandardContext();
standardContext.setPath("");
standardContext.addLifecycleListener(new Tomcat.FixContextListener());
standardHost.addChild(standardContext);
standardEngine.addChild(standardHost);
service.setContainer(standardEngine);
service.addConnector(connector) ;
tomcat.addServlet("","DispatchController", new DispatchServlet());
standardContext.addServletMappingDecoded("/*","DispatchController");
tomcat.start();
tomcat.getServer().await();
}
}
HttpConsumer
import com.dubbo.dubboproducter.utils.DubboObject;
import lombok.SneakyThrows;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class HttpConsumer {
@SneakyThrows
public Object send(String host, Integer port, DubboObject dubboObject){
URL http = new URL(URLEncoder.encode("http", StandardCharsets.UTF_8), URLEncoder.encode(host, StandardCharsets.UTF_8), port, "/");
HttpURLConnection httpURLConnection = (HttpURLConnection) http.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(httpURLConnection.getOutputStream());
objectOutputStream.writeObject(dubboObject);
objectOutputStream.flush();
objectOutputStream.close();
// int responseCode = httpURLConnection.getResponseCode();
// System.out.println("Status Code: "+responseCode);
return new ObjectInputStream(httpURLConnection.getInputStream()).readObject();
}
}
3.servlet请求接收处理
DispatchServlet
import com.dubbo.dubboproducter.reaction.DubboHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DispatchServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new DubboHandler().handler(req,resp);
}
}
DubboHandler
import com.dubbo.dubboproducter.register.LocalRegister;
import com.dubbo.dubboproducter.utils.DubboObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
public class DubboHandler {
@SneakyThrows
public void handler(HttpServletRequest req, HttpServletResponse resp){
//1.获取方法信息
DubboObject dubboObject = (DubboObject) new ObjectInputStream(req.getInputStream()).readObject();
//2.实例化对象并运行方法
Class clazz = LocalRegister.get(dubboObject.getInterfaceName());
//a.获取方法
Method method = clazz.getMethod(dubboObject.getMethodName(), dubboObject.getParamsType());
//b.实例化对象
Object obj = clazz.getConstructor().newInstance();
//c.运行方法
Object invoke = method.invoke(obj, dubboObject.getParams());
new ObjectOutputStream(resp.getOutputStream()).writeObject(invoke);
}
}
4.LocalRegister接口实现类的实现关系本地注册
import java.util.HashMap;
public class LocalRegister {
private static final HashMap<String,Class> map = new HashMap<>();
public static void regist(String className, Class clazz){
map.put(className,clazz);
}
public static void unregist(String className){
map.remove(className);
}
public static Class get(String className){
return map.get(className);
}
}
5.proxy发送请求处理
ProxyFactory
import com.dubbo.dubboproducter.loadblance.RandomLoadBlance;
import com.dubbo.dubboproducter.reaction.HttpConsumer;
import com.dubbo.dubboproducter.register.DubboRegister;
import com.dubbo.dubboproducter.register.RegisterUrl;
import com.dubbo.dubboproducter.utils.DubboObject;
import java.lang.reflect.Proxy;
import java.util.Set;
public class ProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T getProxy(Class interfaceClass){
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class[]{interfaceClass},(o,m,arg) ->{
DubboObject dubboObject = new DubboObject(interfaceClass.getSimpleName(), m.getName(),m.getParameterTypes(),arg);
//获取注册中心的服务信息
Set<RegisterUrl> registerUrls = new DubboRegister().get(interfaceClass.getSimpleName());
//进行消费端负载均衡
RegisterUrl registerUrl = RandomLoadBlance.blacne(registerUrls);
return new HttpConsumer().send(registerUrl.getHost(), registerUrl.getPort(), dubboObject);
});
}
}
6.DubboObject发送请求携带的实例对象
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class DubboObject implements Serializable {
private String interfaceName;
private String methodName;
private Class[] paramsType;
private Object[] params;
}
7.服务注册
DubboRegister
#- 远程服务注册
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.TypeReference;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.util.*;
public class DubboRegister {
public Map<String, Set<RegisterUrl>> DUBBO_REGISTER = new HashMap<>();
@SneakyThrows
public void add(String interfaceName, RegisterUrl registerUrl){
if (DUBBO_REGISTER.containsKey(interfaceName)){
Set<RegisterUrl> registerUrls = DUBBO_REGISTER.get(interfaceName);
registerUrls.add(registerUrl);
}else {
DUBBO_REGISTER.put(interfaceName,new HashSet<>(){{this.add(registerUrl);}});
}
JSONObject jsonObject = new JSONObject(DUBBO_REGISTER);
File file = new File("./dubboTestregist.json");
if (!file.exists()) file.createNewFile();
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(jsonObject.toJSONString());
fileWriter.close();
}
public Set<RegisterUrl> get(String interfaceName){
DUBBO_REGISTER = getJson();
Set<RegisterUrl> registerUrls = DUBBO_REGISTER.get(interfaceName);
return registerUrls;
}
private Map<String, Set<RegisterUrl>> getJson(){
try {
FileReader fileReader = new FileReader("./dubboTestregist.json");
String jsonString = IOUtils.toString(fileReader);
return JSONObject.parseObject(jsonString,new TypeReference<Map<String, Set<RegisterUrl>>>(){{}});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
RegisterUrl
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Objects;
@AllArgsConstructor
@Data
public class RegisterUrl {
private String host;
private Integer port;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RegisterUrl that = (RegisterUrl) o;
return Objects.equals(host, that.host) && Objects.equals(port, that.port);
}
@Override
public int hashCode() {
return Objects.hash(host, port);
}
}
8.负载均衡
RandomLoadBlance
import com.dubbo.dubboproducter.register.RegisterUrl;
import java.util.Optional;
import java.util.Set;
public class RandomLoadBlance {
public static RegisterUrl blacne(Set<RegisterUrl> registerUrls){
Optional<RegisterUrl> any = registerUrls.stream().findAny();
assert any.isPresent() : "1231231";
return any.get();
}
}