1. 程式人生 > >Java RPC 程式設計:Motan 實現示例,叢集配置

Java RPC 程式設計:Motan 實現示例,叢集配置

在多個伺服器程序之間的通訊,目前使用的技術一般是 RPC(Remote Procedure Call Protocol,遠端過程呼叫協議)。

使用 RPC 可以訪問遠端主機的程序服務,不需要清楚底層網路通訊機制,只需要關注服務本身即可。RPC 是目前分散式開發技術中一種常用的技術,其在分散式開發中能更簡單地呼叫遠端服務,就像本地開發一樣。

Motan 是新浪微博開源的 RPC 輕量級框架,其底層網路通訊使用了 Netty 網路框架,序列化協議支援 Hessian 和 Java 的序列化,網路通訊協議可以支援 Motan、Http、TCP 等。

Motan Github 連結:
https://github.com/weibocom/motan

建立 webapp 專案參見連結:https://blog.csdn.net/qq_39384184/article/details/85406037

新增 pom 依賴

<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 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.youyoo</groupId>
    <artifactId>Motan</artifactId>
    <packaging>war</packaging>
    <version>1.0</version>
    <name>Motan Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <repositories>
        <repository>
            <id>spy</id>
            <name>36</name>
            <layout>default</layout>
            <url>http://repo1.maven.org/maven2</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <!--Motan-->
        <dependency>
            <groupId>com.weibo</groupId>
            <artifactId>motan-core</artifactId>
            <version>0.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.weibo</groupId>
            <artifactId>motan-transport-netty</artifactId>
            <version>0.0.1</version>
        </dependency>
        <!-- 叢集相關 -->
        <!-- consul -->
        <dependency>
            <groupId>com.weibo</groupId>
            <artifactId>motan-registry-consul</artifactId>
            <version>0.0.1</version>
        </dependency>
        <!-- zookeeper -->
        <dependency>
            <groupId>com.weibo</groupId>
            <artifactId>motan-registry-zookeeper</artifactId>
            <version>0.0.1</version>
        </dependency>
        <!-- dependencies blow were only needed for spring-based features -->
        <dependency>
            <groupId>com.weibo</groupId>
            <artifactId>motan-springsupport</artifactId>
            <version>0.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.0.5.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>Motan</finalName>
    </build>
</project>

為服務方的呼叫方建立介面

  • FooService
package server;

import java.util.List;
import java.util.Map;


public interface FooService {
	String hello(String name);

	int helloInt(int number1);

	double helloDouble(double number2);

	List<String> helloList(List<String> list);

	Map<String, List<String>> helloMap(Map<String, List<String>> map);

	DemoBean helloJavabean(DemoBean bean);
}

  • DemoBean
package server;

import java.io.Serializable;

public class DemoBean implements Serializable {
	private static final long serialVersionUID = -8088381880917660322L;
	private long id;
	private String name;
	private double score;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getScore() {
		return score;
	}

	public void setScore(double score) {
		this.score = score;
	}

}

服務方實現這個介面的邏輯

  • FooServiceImpl
package server;

import java.util.List;
import java.util.Map;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class FooServiceImpl implements FooService {

	public static void main(String[] args) throws InterruptedException {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
				"classpath:motan_server.xml");
		System.out.println("server start...");
	}

	public String hello(String name) {
		System.out.println("invoked rpc service " + name);
		return "hello " + name;
	}

	public int helloInt(int number1) {
		System.out.println("invoked rpc service " + number1);
		return number1;
	}

	public double helloDouble(double number2) {
		System.out.println("invoked rpc service " + number2);
		return number2;
	}

	public List<String> helloList(List<String> list) {
		System.out.print("invoked rpc service ");
		for (String string : list) {
			System.out.print(string + ",");
		}
		System.out.println();
		return list;
	}

	public Map<String, List<String>> helloMap(Map<String, List<String>> map) {
		System.out.print("invoked rpc service ");
		for (String key : map.keySet()) {
			System.out.print(key + ":[");
			for (String list : map.get(key)) {
				System.out.print(list + ",");
			}
			System.out.print("],");
		}
		System.out.println();
		return map;
	}

	public DemoBean helloJavabean(DemoBean bean) {
		System.out.print("invoked rpc service " + bean);
		System.out.print("," + bean.getId());
		System.out.print("," + bean.getName());
		System.out.print("," + bean.getScore());
		System.out.println();
		return bean;
	}

}

配置服務方暴露介面

  • motan_server.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:motan="http://api.weibo.com/schema/motan"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd">
    <!-- service implemention bean -->
    <bean id="serviceImpl" class="server.FooServiceImpl"/>
    <!-- exporting service by motan -->
    <motan:service interface="server.FooService" ref="serviceImpl"
                   export="8002"/>

    <!--叢集配置:registry 定義-->
    <!--consul-->
    <!--<motan:registry regProtocol="consul" name="my_consul"-->
    <!--address="127.0.0.1:8003"></motan:registry>-->
    <!--consul server-->
    <!--<motan:service interface="server.FooService" ref="serviceImpl"-->
    <!--registry="my_consul" export="8002"/>-->
    <!--zookeeper 單節點-->
    <!--<motan:registry regProtocol="zookeeper" name="my_zookeeper"-->
    <!--address="127.0.0.1:2181"></motan:registry>-->
    <!--zookeeper 多節點叢集-->
    <!--<motan:registry regProtocol="zookeeper" name="my_zookeeper"-->
    <!--address="127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183"></motan:registry>-->
    <!--zookeeper server-->
    <!--<motan:service interface="server.FooService" ref="serviceImpl"-->
    <!--registry="my_zookeeper" export="8002"/>-->
</beans>

配置呼叫方呼叫介面

  • client_server.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:motan="http://api.weibo.com/schema/motan"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd">
    <!-- reference to the remote service -->
    <motan:referer id="remoteService" interface="server.FooService"
                   directUrl="localhost:8002"/>

    <!--叢集配置:registry 定義-->
    <!--consul-->
    <!--<motan:registry regProtocol="consul" name="my_consul"-->
    <!--address="127.0.0.1:8500"></motan:registry>-->
    <!--consul client-->
    <!--<motan:referer id="remoteService"-->
    <!--interface="server.FooService"-->
    <!--registry="my_consul"></motan:referer>-->
    <!--zookeeper 單節點-->
    <!--<motan:registry regProtocol="zookeeper" name="my_zookeeper"-->
    <!--address="127.0.0.1:2181"></motan:registry>-->
    <!--zookeeper 多節點叢集-->
    <!--<motan:registry regProtocol="zookeeper" name="my_zookeeper"-->
    <!--address="127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183"></motan:registry>-->
    <!--zookeeper client-->
    <!--<motan:referer id="remoteService"-->
    <!--interface="server.FooService"-->
    <!--registry="my_zookeeper"></motan:referer>-->
</beans>

呼叫方呼叫

  • Client
package client;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import server.DemoBean;
import server.FooService;

public class Client {
	public static void main(String[] args) throws InterruptedException {
		ApplicationContext ctx = new ClassPathXmlApplicationContext(
				"classpath:motan_client.xml");
		// 獲取到service
		FooService service = (FooService) ctx.getBean("remoteService");
		// rpc呼叫
		/** String **/
		String ret1 = service.hello("motan");
		System.out.println(ret1);
		/** int **/
		int ret2 = service.helloInt(110);
		System.out.println(ret2);
		/** double **/
		double ret3 = service.helloDouble(11.2);
		System.out.println(ret3);
		/** list **/
		List<String> list = new ArrayList<String>();
		list.add("hello");
		list.add("motan");
		List<String> ret4 = service.helloList(list);
		for (String string : ret4) {
			System.out.print(string + ",");
		}
		System.out.println();
		/** map **/
		Map<String, List<String>> map = new HashMap<String, List<String>>();
		map.put("key1", Arrays.asList(new String[] { "val1","val2" }));
		map.put("key2", Arrays.asList(new String[] { "val1","val2","val3" }));
		map.put("key3", Arrays.asList(new String[] { "val1","val2","val3","val4" }));
		Map<String, List<String>> ret5 = service.helloMap(map);
		for (String key : ret5.keySet()) {
			System.out.print(key + ":[");
			for (String tmp : map.get(key)) {
				System.out.print(tmp + ",");
			}
			System.out.print("],");
		}
		System.out.println();
		/** javabean **/
		DemoBean bean = new DemoBean();
		bean.setId(1001l);
		bean.setName("motan bean");
		bean.setScore(99.998);
		DemoBean ret6 = service.helloJavabean(bean);
		System.out.print(ret6.getId());
		System.out.print("," + ret6.getName());
		System.out.print("," + ret6.getScore());
		System.out.println();
	}

}

Motan 的服務方建立好服務介面之後,將暴露介面、埠等在配置檔案中寫好,啟動配置檔案即可開啟,呼叫方需要使用與服務方相同的介面(包括包名相同),並正確配置配置檔案,即可在程式碼中呼叫服務方暴露的介面服務。

叢集配置

實現叢集呼叫只需要在上述操作的基礎上做一點改變即可,Motan 的叢集與阿里的 Dubbo 的原理類似,通過註冊方、服務方、呼叫方三方實現。

  • RPC Sever 向 RPC Registry 註冊服務,並每隔一定時間向 RPC Registry 傳送心跳彙報狀態。
  • RPC Client 需要向 RPC Registry 訂閱 RPC 服務,RPC Client 根據 RPC Registry 返回的服務列表,對具體的 RPC Server 進行 RPC 呼叫。
  • 當 RPC Server 發生變更時,RPC Registry 會同步變更,RPC Client 感知後會對本地的服務列表作相應調整。

Motan 支援 consul 和 Zookeeper 兩種外部伺服器發現元件,實現方式見上述程式碼的註釋:需要新增 pom 依賴和在 server 和 client 的配置檔案中增加 registry 的定義。

consul 需要顯示呼叫心跳開關注冊到 consul(zookeeper 不需要)。

MotanSwitcherUtil.setSwitcher(ConsulConstants.NAMING_PROCESS_HEARTBEAT_SWITCHER,true);