第九章 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(){
			1 從Method.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中也經常用自定義協議+編解碼器,來處理粘包拆包問題
Duboo和Http協議不用手動改代碼,直接裝配
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 包裝類嵌套實例生成
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章