zk實戰--rpc框架集羣化 頂 原 薦

在看此篇內容時需要瀏覽下面內容 netty實戰--手寫rpc框架

前文功能簡介以及功能擴充

利用netty來實現一個點對點的rpc調用。客戶端和服務端都是靠手寫地址進行socket同學的,無法1對多,也無法把服務拆分到不同的機器上進行壓力分攤,做不到調用的時候水平擴展。現實的場景是機器單臺作戰能力比較差勁,但是機器多,可以把不同的運算交給不同的機器來做,這樣達到機器的利用。所以有了如下的改造。

  1. 支持服務水平擴展。
  2. 支持不同的服務分佈在不同的機器上。

這時候就需要引入zookeeper來做管理。

主體思路

  1. 上線的數據都寫入到臨時節點裏
  2. 通過臨時節點來確保,服務器停止之後,節點消失
  3. 自動發現服務的功能,依靠zk的Watcher來發現服務的上線和下線功能。

數據組織形式

zk數據

   /xp
    |------ip:port---services
    |    
    |------ip:port---services
    |       
    |------ip:port---services

zk的數據按照如上情況進行組織,xp爲一個永久節點,下面的ip:port組成一個臨時節點。分別代表服務的ip和port,當服務停止或者意外被殺以後,對應的ip:host就會消失。

xp是一個永久節點,我們對他進行watch,只要有新的服務啓動或者停止都會收到事件。

client維護的結構

|-service---client
|
|-service---client

client維護的結構是service和client連接的對應,一般情況下都是一個server裏有多個服務,水平擴展的時候啓動多個server即可。所以組織的時候是按照service組織方便查找。

代碼實現要點

客戶端對永久節點進行watch,這裏加入了一個callback,當發生了節點變化的時候重新更新消息。

	public List<String> getInofAndWatcher(final String path, final InfoCallBack callBack) throws Exception {
		List<String> nodeList = zk.getChildren(path, new Watcher() {
			@Override
			public void process(WatchedEvent event) {
				if (event.getType() == Event.EventType.NodeChildrenChanged) {
					try {
						List<String> nodeList = zk.getChildren(path, false);
						callBack.getLastList(nodeList);
					} catch (Exception e) {
						e.printStackTrace();
					}

				}
			}
		});
		return nodeList;
	}

發佈到zk的信息,就是ip,port,service。

public class ServiceInfo {
	private String ip;
	private int port;
	private List<String> interfaces;
}

會把這些數據通過json格式寫入到zk,這裏需要寫明文,方便查看zk下有哪些服務

	public void addService(ServiceInfo info) {
		zkUtil.create(CommonContext.PATH + "/" + info.toString(), JSONObject.toJSONString(info).getBytes());

	}

客戶端在啓動或者事件發生的時候進行service更替。

	public void getLastList(List<String> lists) {
		Map<String, Set<Client>> serviceTmp = new HashMap<>();
		if (lists != null && !lists.isEmpty()) {
			for (String node : lists) {
				String info;
				try {
					info = new String(zkUtil.getData(CommonContext.PATH + "/" + node));
					ServiceInfo parse = JSONObject.parseObject(info, ServiceInfo.class);
					String address = parse.toString();
					List<String> interfaces = parse.getInterfaces();
					if (interfaces != null && !interfaces.isEmpty()) {
						for (String service : interfaces) {
							Set<Client> set = serviceTmp.get(service);
							if (set == null) {
								set = new HashSet<Client>();
							}
							if (!set.contains(address)) {
								Client client = new Client();
								client.connect(parse.getIp(), parse.getPort());
								set.add(client);
							}
							serviceTmp.put(service, set);

						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}

			}

		}

		services = serviceTmp;

	}

數據發送的時候選擇隨機找服務,這樣不至於壓力都在一個服務上。

	public static void send(RpcRequest request, String className) {
		Set<Client> set = services.get(className);
		if (set != null && !set.isEmpty()) {
			Client[] array = set.toArray(new Client[0]);
			//random 
			Client client = array[new Random().nextInt(array.length)];
			client.write(request);
		}
	}

測試使用

客戶端

		new Observer("127.0.0.1:2181");
		ITest proxy = ProxyInterface.getProxy(ITest.class);
		String message = proxy.getMessage();
		System.out.println(message);

服務端

		ITest test= new TestImpl();	
		Invoker.put(test);
		List<String> list = new ArrayList<String>(Invoker.getServices());
		ServiceInfo info = new ServiceInfo("127.0.0.1", 6161, list);
		Publisher pub = new Publisher("127.0.0.1:2181");
		pub.addService(info);
		Server.bind(6161);

總結

利用zk的註冊功能,讓服務互相發現,並且做到水平擴展。重點就是一個事件機制和臨時節點的機制。代碼如下 https://github.com/xpbob/lightrpc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章