第九章 Dubbo

9.1 Dubbo基本原理 9.2 Dubbo相关问题 9.3 Dubbo应用及简单源码实现 9.4 Dubbo SPI基本原理

9.1 Dubbo基本原理

简单说说RPC

远程调用计算机通讯协议,比如HTTP/TCP等都是

简单就是序列化数据发送,对方解序列化数据业务处理,在序列化响应信息返回发送

传统的演变下Dubbo功能

用Nginx配置IP地址进行负载均衡,但是人工成本巨大

Dubbo提供了所有服务端相关信息注册到注册中心,一切自动化负载均衡,开发者只需要关心业务

Dubbo提供
	服务开发(RPC应用开发)
	服务软负载均衡
	服务依赖管理
	服务监控
	服务治理

Dubbo整体架构

注册中心,服务提供者(容器),服务消费者,监控中心

注册中心
	消费者和提供者只在启动时和注册中心交互,负责保存服务名与服务地址映射,服务地址变动会主动通知服务消费者。
	服务提供者挂了,注册中心通过长连接感知并立刻服务消费者。注册中心和监控中心挂了,不影响已经运行的提供者和消费者,消费者有本地缓存。但是提供者挂了,消费者则无法再用会无限等待

服务提供者(容器)
	1 提供服务接口API
	2 完成实现类
	3 注册服务(远程和本地注册)
	4 暴露服务(启动tomcat)

服务消费者
	启动时从注册中心拿服务地址并本地缓存
	负载均衡选出一个服务地址进行服务调用

监控中心
	统计服务调用次数和调用时间,内存汇总每1分钟发一次到监控中心。除监控中心外,其余都是长连接

Dubbo调用工作原理

1 Proxy工厂 invoke(Dubbo还是Tomcat)
2 到客服端
3 到传输层下(header和body)
	header是codec编解码,一般是字符串形式,请求的相关源信息
	body序列化的数据
4 到服务端
5 Dispather请求分发
6 线程池处理
7 执行业务

Dubbo下Zookeeper注册中心原理
在这里插入图片描述

9.2 Dubbo相关问题

1 一般使用什么注册中心,还有别的选择吗

可配置Multicast和Zookeeper

1 Multicast 广播方法

使用单播发送提供者信息给消费者,为了减少广播量。所以想多个消费者同时拿到同一个提供者,消费者需要配置unicast=false在multicast后面

工作原理
	提供方启动时广播自己地址
	消费者启动时广播订阅请求
	互相交互

特点
	不用启动中心节点,只用组播地址,组播有网络结构限制,只适合小规模

2 Zookeeper

curator客户端,可集群,xml内配置多个地址

3 也可以直接配置提供者地址,直连提供者

2
核心配置有哪些-xml内配置信息

服务调用是阻塞的吗-默认是

Dubbo推荐使用什么协议

推荐Dubbo协议,Netty实现。也有hessian,http等协议

如何解决服务调用链过长问题

默认使用的什么通信框架,还有别的选择吗
解释了这些问题

Dubbo集群容错有几种方案

!重要,服务治理、服务降级、失败重试以及超时重试

Dubbo Monitor实现原理

在调用消费者和提供者之前都会先走filter链,filter链中有Monitorfilter

Dubbo用到什么设计模式

Dubbo支持分布式事务吗

Dubbo自己不提供,但是允许整合支持分布式事务。比如加入我上一章写的seata来解决分布式事务

9.3 Dubbo应用及简单源码实现

应用

启动Dubbo三种方式
1 
~ applicationContext = ~ ApplicationContext("spring/dubbo-provider.xml");
applicationContext.start();
2 注解启动
@EnableDubbo(scanBasePackages="xxx.Provider")//扫描这些包下实现类
@PropertySource("classpath:xxx")//和xml一样配置内容
~ applicationContext = ~ ApplicationContext("AnnoationProviderConfiguration.class");
applicationContext.start();
3 API启动

提供者xml配置内容
<dubbo:application name="demo-provider"/>
<dubbo:registry address="zookeeper://127.0.0.1/"> //这里也可以配置广播模式multicast
<dubbo:protocol name="dubbo"/> //使用dubbo协议,也可以用hessian,http等
<bean id="demoService" class="xxx.DemoServiceImpl"/> //提供实现类
<dubbo:service interface="xxx.DemoService" ref="demoService"/> //体现类对应的接口,本地注册并且暴露服务给给消费者

消费者xml配置内容
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://xxx.xxx.xxx.xxx/"> //这里也可以配置广播模式multicast
<bean reference id="demoService" check="false"/> //提供实现类
<dubbo:interface="xxx.DemoService"/>
等

消费者直接正常调用应用

简单源码实现

这里主要几乎http协议的tomcat实现, 代理工厂模式转变netty或者tomcat,netty具体实现换汤不换药

提供者简单实现

1 提供和实现API 即实现HelloService接口
2 暴露接口
public class Provider{
	~ main ~{
		1 本地注册(服务名,实现类)
		LocalRegister.register(HelloService.class.getName(),HelloServiceImpl.class);
		2 远程注册,如上传到Zookeeper
		URL url = new URL("localhost",8080);//机器地址可能集群,所以用list
		List<URL> RemoteMapRegister.register(HelloService.class.getName(),url);
		3 按照我们自己的实现类,启动tomcat,也可以用netty启动。基于xml所选择协议
		//HttpServer httpServer = new ~;
		//httpServer.start("localhost",8080);
		Protocol protocol = ProtocolFactory.getProtocol();
		protocol.start(url);
	}
}

//要序列化URL,封装地址
public class URL implements Serializable{
	String hostname;
	Integer port;
	构造函数
}

//按照我们自己需要的方法启动tomcat
public class HttpServer{
	//使用maven依赖加入的tomcat
	Tomcat tomcat = new Tomcat();
	//根据Tomcat层级依次构造,给tomcat设置connector/engine/host/context/wrapper
	...
	//设置我们自己实现的servlet
	tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet());//我们自己实现的DispatcherServlet
	//配置结束,启动tomcat
	tomcat.start();
	tomcat.getServer().await();
}

//实现自己需要的servlet
public class DispatcherServlet extends HttpServlet{
	@Override
	~ service(HttpServletRequest request, HttpServletResponse response){
		//返回自己实现的servlet handler
		~ new HttpServerHandler().handler(request,response);
	}
}

//实现自己需要的servlet handler
public class HttpServerHandler{
	~ handler(HttpServletRequest req, HttpServletResponse resp){
		//处理请求,返回结果
		//从本地注册表中拿到class实现类,利用反射返回相应结果给消费者
		InputSteam inputStream = req.getInputStream();
		Object ois = ObjectInputStream(inputStream);//接受的数据转成object
		Invocation invocation = ois.readObject();//封装的调用对象参数类,在下面实现了
		Class implClass = LocalRegister.get(invocation.getInterfaceName());//拿到需要接口名称
		Method method = implClass.getMethod(invocation.getMethodName,invocation.getMethodTypes);
		String result = (String) Method.invoke(implClass.newInstance,invocation.getParams());
		IOUtils.write(result,resp.getOutputStream()); //返回httpClient
	}
}

//本地注册表
public class LocalRegister{
	~ Map<String,Class> map = new ~;
	~ void register ~ -> map.put(interfaceName,implClass);
	~ Class get(String interfaceName);
}

//远程注册表
//在这里用map,消费者和提供者不同进程下map数据会不一致。Dubbo则可使用Zookeeper解决不同机器下,类似map的数据一致性
public class RemoteMapRegister{
	map<String,List<URL>> map = new ~;
	register(String interfaceName,URL url)
	URL random(String interfaceName) //利用接口名,map.get(interfaceName)后负载均衡随机算法从list集群中拿一个url
}

消费者简单实现

public class Consumer{
	~ main ~{
		//HttpClient httpClient = new ~;
		//Invocation invocation = new ~(HelloService.class.getName(),"sayHello",new class[](String.class));
		//String result = httpClient.send("localhost",8080,invocation);
		Protocol protocol = ProtocolFactory.getProtocol();//代理工厂,netty则返回netty处理对象,http就是HttpClient
		HelloService helloService = Proxy.getProxy(HelloService.class);
	}
}

public class HttpClient{
	public String send(String hostName, Integer port,Invocation invocation){
		URL url = new URL("http",hostName,port,"/");
		...
		HttpURLConnection.setRequestMethod("POST");
		HttpURLConnection.setDoOutput(true);
		HttpURLConnection.getOutputStream();转成object给oos
		oos.flush();//发送http封装的对象给提供者,等待提供者HttpServerHandler处理请求
		oos.close();

		//接受HttpServerHandler的response
		InputSteam inputStream = HttpURLConnection.getInputStream();
		String result = IOUtils.toString(inputStream);
		return resutl;
	}
}

//封装调用对象参数
public class Invocation implement Serializable{
	String interfaceName;
	String methodName;
	Class[] paramTypes;
	Object[] params;
}

public class ProxyFactory{
	public static<T> getProxy(Class interfaceClass){
		Proxy.newProxyInstance(xxx) 返回一个代理类
		@Override
		invoke(){
			1Method.getMethod中拿sayHello,new Class[]这些参数,不写死			
			2 URL = 利用RemoteMapResiger.random(interfaceClass.getName()),负载均衡拿port
			3 利用这些参数调用
			HttpClient httpClient = new ~;
			Invocation invocation = new ~(HelloService.class.getName(),"sayHello",new class[](String.class));
			String result = httpClient.send("localhost",8080,invocation);
		}
	}
}
在netty中也经常用自定义协议+编解码器,来处理粘包拆包问题
DubooHttp协议不用手动改代码,直接装配
public interface Protocol{
	void start(URL url);
	String send(URL url, Invocation invocation);
}

public class DubboProtocol implement Protocol{
	@Override
	~ start ~{
		new NettyServer().start(url.getHostName(),url.getPort());
	}
	@Override
	~ send ~{
		return new NettyClient<>().sned(url.getHostName(),url.getPort(),invocation)
	}
}
public class HttpProtocol implement Protocol{
	一样
}

public class ProtocolFactory{
	public static Protocol getProtocol(URL url){
		工厂模式拿new HttpProtocol, new DubboProtocol
	}
}

总结

说那么一大串,简单来说
1 提供者本地注册,远程注册。 
2 消费者从远程注册中找提供者
3 两者之间实现自定义的传递类对象+序列化解序列化,达成交互

9.4 Dubbo SPI基本原理

Java SPI扩展机制

resources下META-INF.services下 
创建framework.Protocol, 
写protocol.dubbo.DubboProtocol则用dubbo Netty
写Http则用Http Tomcat

Dubbo SPI扩展机制

加入依赖注入,AOP等,性能也比JAVA SPI更好

Java SPI中无法写多个自己选择其中一个,Dubbo可以多个命名变量,通过变量名选择自己需要那个
如Dubbo:Protocol.dubbo.Dubbo.Protocol 加了Dubbo这个key

Dubbo SP核心
	AOP = wrapper包类,重写方法添加before after,包装类配置到META-INF下
	IOC = 如@Autowired注入接口,URL封装注入的实现类,URL参数制定哪个实现类
Dubbo SPI
@SPI //调用Dubbo SPI
pubLic interface Car{
	@Adaptive(value="carType") //从url参数获取key carType
	public void getColor(URL url);
}

public class CarDemo{
	~ main ~{
		~ e = ExtensionLoader.getExtensionLoader(Car.class) //对应Java的ServiceLoader
		Car redCar = e.getExtension("red");
		//URL参数注入IOC
		Map<String,String> map = new ~ 
		map.put("carType","black");
		URL url = new URL("","",0,map);
		driver.driveCar(url);
	}
}

META-INF.services 文件配置
red:xxx.impl.RedCar
black:xxx.impl.BlackCar

ExtensionLoader.java 大致源码

一个map工厂,解析文件,加载文件内对应key和实现类的关系map

1 getExtensionLoader(Car.class) 返回 ObjectFactory 实现类/代理类
2 用双Null+锁校验生成Car接口单例对象
3 WETA-INF.service 路径下找实现类
4 检验类是否合法
5 cacheAdaptiveClass(clazz) 看是否使用IOC
6 cacheWrapperClass(clazz) 看是否使用AOP
7 处理依赖注入IOC
8 处理AOP
9 包装类嵌套实例生成
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱肉肉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值