【如何實現一個簡單的RPC框架】系列文章:java
【遠程調用框架】如何實現一個簡單的RPC框架(一)想法與設計
【遠程調用框架】如何實現一個簡單的RPC框架(二)實現與使用
【遠程調用框架】如何實現一個簡單的RPC框架(三)優化一:利用動態代理改變用戶服務調用方式
【遠程調用框架】如何實現一個簡單的RPC框架(四)優化二:改變底層通訊框架
【遠程調用框架】如何實現一個簡單的RPC框架(五)優化三:軟負載中心設計與實現
第一個優化以及第二個優化修改後的工程代碼可下載資源 如何實現一個簡單的RPC框架web
參考【遠程調用框架】如何實現一個簡單的RPC框架(一)想法與設計,對應四個模塊一共建立了四個Java工程,他們分別是:spring
【ServiceAccessCenter】:一個Java Web應用,服務註冊查找中心apache
【LCRPC】:LCRPC服務框架的核心部分,最終利用Maven生成一個jar包提供服務發佈以及遠程調用功能json
【LCRPCTest】:服務端的測試部分,發佈一個計算器服務數組
【LCRPCClientTest】:客戶端的測試部分,利用服務發佈者提供的二方包,遠程調用該計算器服務緩存
一個Java Web應用,服務註冊查找中心tomcat
{ "interfaceName":"interfaceName", "version":"version", "implClassName":"imlClassName", "ip":"ip" }
(3)響應結果:true(表明註冊成功)false(表明註冊失敗)多線程
["ip","ip"]
{"interfaceName":"interfaceName","version":"version","implClassName":"imlClassName","ips":["ip","ip"],"ip":"ip"}
核心代碼主要是對註冊服務集合的管理,包括增長以及查詢。注意多線程操做的問題。
- (1)描述服務信息的DO:ServiceInfoDOmvc
package whu.edu.lcrpc.servicecenter.entity; /** * Created by apple on 17/3/26. */ import lombok.Data; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * 服務的描述信息,包括: * 服務惟一標識\實現類全限定名\ip地址列表等 */ @Data public class ServiceInfoDO { private String interfaceName;//服務對應接口名稱 private String version;//版本號 private String implClassName;//實現該接口的類 private Set<String> ips = new HashSet<>();//該服務的地址 private String ip;//某一次註冊服務的地址 }
public class ServicesSingle { private static ServicesSingle servicesSingle = null; private Map<String,ServiceInfoDO> services = null; private ServicesSingle(){ services = new ConcurrentHashMap<>(); } public static ServicesSingle getServiceSingle(){ synchronized (ServicesSingle.class){ if (servicesSingle == null){ servicesSingle = new ServicesSingle(); } } return servicesSingle; } }
public interface IServiceAccess { /** * 根據用戶提供的服務信息,進行服務的註冊 * @param serviceInfo 要註冊的服務信息 * @return */ public boolean serviceRegistry(ServiceInfoDO serviceInfo); /** * 根據服務的惟一標識ID查詢服務的地址列表 * @param serviceID * @return */ public Set<String> queryServiceIPsByID(String serviceID); /** * 根據服務的惟一標識ID查詢服務的信息 * @param serviceID * @return */ public ServiceInfoDO queryServiceInfoByID(String serviceID); }
/** * 完成服務的管理操做:註冊\查詢 */ public class ServiceAccessImpl implements IServiceAccess{ @Override public boolean serviceRegistry(ServiceInfoDO serviceInfo) { if (serviceInfo.getInterfaceName() == null || serviceInfo.getInterfaceName().length() ==0 || serviceInfo.getImplClassName() == null || serviceInfo.getImplClassName().length() ==0 || serviceInfo.getVersion() == null || serviceInfo.getVersion().length() ==0 || serviceInfo.getIp() == null || serviceInfo.getIp().length() ==0) return false; String serviceID = serviceInfo.getInterfaceName() + "_" + serviceInfo.getVersion(); if (ServicesSingle.getServiceSingle().getServices().containsKey(serviceID)){ ServicesSingle.getServiceSingle().getServices().get(serviceID).getIps().add(serviceInfo.getIp()); }else { serviceInfo.getIps().add(serviceInfo.getIp()); ServicesSingle.getServiceSingle().getServices().put(serviceID,serviceInfo); } return true; } @Override public Set<String> queryServiceIPsByID(String serviceID) { if (!ServicesSingle.getServiceSingle().getServices().containsKey(serviceID)) return null; return ServicesSingle.getServiceSingle().getServices().get(serviceID).getIps(); } @Override public ServiceInfoDO queryServiceInfoByID(String serviceID) { if (!ServicesSingle.getServiceSingle().getServices().containsKey(serviceID)) return null; return ServicesSingle.getServiceSingle().getServices().get(serviceID); } }
LCRPC服務框架的核心部分,最終利用Maven生成一個jar包提供服務發佈以及遠程調用功能
整個工程是由maven以及spring開發構建的,是一個Java工程,最終利用maven構建jar包提供用戶使用。
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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>whu.edu.lcrpc.</groupId> <artifactId>lcrpc-core</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>lcrpc-core</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.3.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!--spring依賴--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!--gson依賴--> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.2.4</version> </dependency> <!--lombok依賴--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <!--json解析依賴--> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
(看不清可訪問服務框架UML類圖)
服務調用端提供LCRPCConsumerImpl類給用戶使用,該類中包含兩個必要的屬性變量分別是interfaceNeme(接口名)、version(版本號),用來標識某一個LCRPCConsumer實例對象是對哪個服務的調用,同時提供方法serviceConsumer,用戶經過傳入要調用的方法名稱以及參數信息,實現對該接口某一個方法的遠程調用。LCRPCConsumer類使用一個幫助類ConsumerServiceImpl來實現整個調用過程。同時,設計了一些自定義異常,用於在程序出錯時拋出給用戶進行捕獲。
核心代碼
@Data public class LCRPCConsumerImpl implements ILCRPCConsumer { /** * 如下兩個變量共同組成服務的惟一標識 */ private String interfaceName;//服務對應接口的全限定名 private String version;//服務版本號 private IConsumerService consumerService;//初始化客戶端輔助類 public LCRPCConsumerImpl(){ consumerService = new ConsumerServiceImpl(); } @Override public Object serviceConsumer(String methodName, List<Object> params) throws LCRPCServiceIDIsIllegal,LCRPCServiceNotFound,LCRPCRemoteCallException { //若服務惟一標識沒有提供,則拋出異常 if (interfaceName == null || interfaceName.length() == 0 || version == null || version.length() == 0) throw new LCRPCServiceIDIsIllegal(); //step1. 根據服務的惟一標識獲取該服務的ip地址列表 String serviceID = interfaceName + "_" + version; Set<String> ips = consumerService.getServiceIPsByID(serviceID); if (ips == null || ips.size() == 0) throw new LCRPCServiceNotFound(); //step2. 路由,獲取該服務的地址,路由的結果會返回至少一個地址,因此這裏不須要拋出異常 String serviceAddress = consumerService.getIP(serviceID,methodName,params,ips); //step3. 根據傳入的參數,拼裝Request對象,這裏必定會返回一個合法的request對象,因此不須要拋出異常 LCRPCRequestDO requestDO = consumerService.getRequestDO(interfaceName,version,methodName,params); //step3. 傳入Request對象,序列化並傳入服務端,拿到響應後,反序列化爲object對象 Object result = null; try { result = consumerService.sendData(serviceAddress,requestDO); }catch (Exception e){ //在服務調用的過程種出現問題 throw new LCRPCRemoteCallException(e.getMessage()); } if (result == null)throw new LCRPCRemoteCallException(Constant.SERVICEUNKNOWNEXCEPTION); //step4. 返回object對象 return result; } }
public class ConsumerServiceImpl implements IConsumerService { @Override public Set<String> getServiceIPsByID(String serviceID) { //調用服務註冊查找中心的服務,獲取ip列表 Set<String> ips = new HashSet<>(); String url = "http://" + Constant.SERVICEACCESSCENTER_IP + ":" + Constant.SERVICEACCESSCENTER_PORT + "/" + Constant.QUERYSERVICEIPSBYID + "?serviceID=" + serviceID; Set status = new HashSet<Integer>(); status.add(200); StringBuilder response = new StringBuilder(); GetRemoteInfo.getRemoteInfo(url,"GET",null,null,response,status); if (response.length() == 0)return ips; ips = new Gson().fromJson(response.toString(),ips.getClass()); return ips; } @Override public String getIP(String serviceID, String methodName,List<Object> params, Set<String> ips) { //能夠根據接口\方法\參數進行路由,這裏咱們先簡單實現,選出列表的第一個,模擬路由的過程 String[] temparr = new String[ips.size()]; ips.toArray(temparr); return temparr[0]; } @Override public LCRPCRequestDO getRequestDO(String interfaceName, String version, String methodName, List<Object> params) { LCRPCRequestDO requestDO = new LCRPCRequestDO(); requestDO.setInterfaceName(interfaceName); requestDO.setMethodName(methodName); requestDO.setParams(params); requestDO.setVersion(version); return requestDO; } @Override public Object sendData(String ip,LCRPCRequestDO requestDO) throws IOException, ClassNotFoundException { ObjectOutputStream objectOutputStream = null; Socket socket = null; ObjectInputStream objectInputStream = null; try { socket = new Socket(ip, Constant.PORT);//向遠程服務端創建鏈接 objectOutputStream = new ObjectOutputStream(socket.getOutputStream());//得到輸出流 objectOutputStream.writeObject(requestDO);//發送序列化結果 objectOutputStream.flush(); socket.shutdownOutput(); //等待響應 objectInputStream = new ObjectInputStream(socket.getInputStream());//得到輸入流 Object result = objectInputStream.readObject();//序列化爲Object對象 objectInputStream.close(); objectOutputStream.close(); socket.close(); return result; }catch (Exception e){ throw e; }finally { try { if(objectInputStream != null)objectInputStream.close(); if (objectOutputStream != null)objectOutputStream.close(); if (socket !=null )socket.close(); } catch (IOException e) { e.printStackTrace(); throw e; } } } }
服務發佈部分,主要包括兩部分,第一是服務的監聽,第二是服務的註冊與處理。服務的監聽,經過開啓一個socket監聽線程ServerThread來實現。當有客戶端請求後,開啓新的處理線程ServerProcessThread,來進行數據的解析、服務的調用、數據響應等操做。提供給用戶使用的是類LCRPCProviderImpl,主要包含四個必要屬性變量(interfaceName、version、implClassName、ip),對要發佈的服務進行描述。該類主要提供的方法是服務的註冊。同時還設計了自定義異常類,用於在出錯時拋出,他們分別是:LCRPCServiceInfoNotComplete(要發佈的服務的信息不完整)、LCRPCServiceListenFailed(服務監聽失敗)、LCRPCServiceRegistryFailed(服務註冊失敗)。
核心代碼
@Data public class LCRPCProviderImpl implements ILCRPCProvider{ /** * 如下變量爲發佈一個服務的必要變量 */ private String interfaceName;//服務對應接口的全限定名 private String version;//服務版本號 private String implClassName;//實現該服務接口的類 private String ip;//發佈該服務的地址 private static boolean isListened = false;//是否已經開啓監聽 private IProviderService providerService; public LCRPCProviderImpl(){ providerService = new ProviderServiceImpl(); //開啓服務監聽端口 if (!isListened ){ if (providerService.startListen()) isListened = true; else throw new LCRPCServiceListenFailed(); } } public void checkInfo(){ //先判斷服務參數信息是否完整 if (interfaceName == null || interfaceName.length() == 0 || version == null || version.length() == 0 || implClassName == null || implClassName.length() ==0 || ip == null || ip.length() ==0) throw new LCRPCServiceInfoNotComplete(); } @Override public boolean servicePublish() { checkInfo(); //step1. 註冊服務.註冊服務以前先判斷服務是否開啓,若沒有開啓,則首先開啓服務 synchronized (LCRPCProviderImpl.class){ if (!isListened){ if (providerService.startListen()) isListened = true; else throw new LCRPCServiceListenFailed(); } providerService.serviceregistry(interfaceName,version,implClassName,ip); } return true; } }
public class ServerThread implements Runnable{ @Override public void run() { try { ServerSocket serverSocket = new ServerSocket(Constant.PORT); System.out.println("已經開始監聽,能夠註冊服務了"); while (true){ Socket socket = serverSocket.accept(); new Thread(new ServerProcessThread(socket)).start();//開啓新的線程進行鏈接請求的處理 } } catch (IOException e) { e.printStackTrace(); } } }
public class ServerProcessThread implements Runnable { private Socket socket; public ServerProcessThread(Socket socket){ this.socket = socket; } @Override public void run() { //獲取客戶端的數據,並寫回 //等待響應 ObjectInputStream objectInputStream = null; ObjectOutputStream objectOutputStream = null; try { //step1. 將請求數據反序列化爲request對象 objectInputStream = new ObjectInputStream(socket.getInputStream());//得到輸入流 LCRPCRequestDO requestDO = (LCRPCRequestDO) objectInputStream.readObject();//序列化爲Object對象 //step2. 獲取服務接口實現類的信息 ServiceInfoDO serviceInfoDO = ServicesSingle.getServiceSingle().getServices().get(requestDO.getInterfaceName() + "_" + requestDO.getVersion()); //step3.利用反射建立對象,調用方法,獲得結果 Class clz = Class.forName(serviceInfoDO.getImplClassName()); Method methodethod = null; Object result = null; if (requestDO.getParams() != null && requestDO.getParams().size() > 0){ Class []classes = new Class[requestDO.getParams().size()]; Object []obj = requestDO.getParams().toArray(); int i = 0; for (Object object:requestDO.getParams()){ if(object instanceof Integer){ classes[i] = Integer.TYPE; }else if(object instanceof Byte){ classes[i] = Byte.TYPE; }else if(object instanceof Short){ classes[i] = Short.TYPE; }else if(object instanceof Float){ classes[i] = Float.TYPE; }else if(object instanceof Double){ classes[i] = Double.TYPE; }else if(object instanceof Character){ classes[i] = Character.TYPE; }else if(object instanceof Long){ classes[i] = Long.TYPE; }else if(object instanceof Boolean){ classes[i] = Boolean.TYPE; }else { classes[i] = object.getClass(); } i++; } methodethod = clz.getDeclaredMethod(requestDO.getMethodName(),classes); result = methodethod.invoke(clz.newInstance(),obj); }else { methodethod = clz.getDeclaredMethod(requestDO.getMethodName()); result = methodethod.invoke(clz.newInstance()); } //step4.將結果序列化,寫回調用端 objectOutputStream = new ObjectOutputStream(socket.getOutputStream());//得到輸出流 objectOutputStream.writeObject(result);//發送序列化結果 objectOutputStream.flush(); socket.shutdownOutput(); socket.close(); } catch (Exception e) { e.printStackTrace(); }finally { try { if(objectInputStream != null)objectInputStream.close(); if (objectOutputStream != null)objectOutputStream.close(); if (socket !=null )socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
public class ProviderServiceImpl implements IProviderService { @Override public boolean startListen() { new Thread(new ServerThread()).start(); return true; } @Override public boolean serviceregistry(String interfaceName, String version, String implClassName, String ip) { //註冊到服務註冊查找中心的同時也要緩存到內存services //step1. 註冊到服務中心 String url = "http://" + Constant.SERVICEACCESSCENTER_IP + ":" + Constant.SERVICEACCESSCENTER_PORT + "/" + Constant.SERVICEREGISTRY; Map<String,String> headers = new HashMap(); headers.put("Content-Type","application/json"); JSONObject param = new JSONObject(); param.put("interfaceName",interfaceName); param.put("version",version); param.put("implClassName",implClassName); param.put("ip",ip); Set status = new HashSet<Integer>(); status.add(200); StringBuilder response = new StringBuilder(); GetRemoteInfo.getRemoteInfo(url,"POST",headers,param.toString(),response,status); if (response.equals("false")) throw new LCRPCServiceRegistryFailed(); //step2. 存入到緩存 if (ServicesSingle.getServiceSingle().getServices().containsKey(interfaceName + "_" + version)){ ServicesSingle.getServiceSingle().getServices().get(interfaceName + "_" + version).getIps().add(ip); } else { ServiceInfoDO serviceInfoDO = new ServiceInfoDO(); serviceInfoDO.setInterfaceName(interfaceName); serviceInfoDO.setVersion(version); serviceInfoDO.setImplClassName(implClassName); serviceInfoDO.getIps().add(ip); ServicesSingle.getServiceSingle().getServices().put(interfaceName + "_" + version,serviceInfoDO); } System.out.println("成功註冊服務:[" + interfaceName + "]"); return true; } }
服務端的測試部分,發佈一個計算器服務;
該部分爲一個基本Java應用,採用spring+maven的開發方式。
<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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>whu.edu.lcrpc</groupId> <artifactId>lcrpc-test</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>lcrpc-test</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.3.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!--spring依賴--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!--lombok依賴--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <!--對LCRPC服務框架的依賴--> <dependency> <groupId>whu.edu.lcrpc</groupId> <artifactId>lcrpc-core</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
<?xml version="1.0" encoding="UTF-8"?> <beans default-autowire="byName" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--ILCRPCProvider的bean 進行配置後 自動發佈服務--> <bean id="lcrpcProvider" class="whu.edu.lcrpc.server.impl.LCRPCProviderImpl" init-method="servicePublish"> <property name="implClassName" value="whu.edu.lcrpc.service.impl.CaculatorImpl"></property> <property name="version" value="0.1"></property> <property name="ip" value="127.0.0.1"></property> <property name="interfaceName" value="whu.edu.lcrpc.service.ICaculator"></property> </bean> </beans>
其中四個屬性均是必要的。同時init-method的配置也是必要的,該函數經過用戶配置爲屬性信息進行服務的發佈。
若是是在web容器中進行服務的發佈,例如tomcat,則只須要在web.xml中配置spring的監聽,就能夠進行服務的發佈。或者在main函數中利用ApplicationContext進行配置文件的讀取。
在本例中,爲了簡單,採用main函數的方式。代碼以下:
package whu.edu.lcrpc.app; /** * Created by apple on 17/3/26. */ import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 服務端測試類,調用LCRPC接口,進行服務的發佈 */ public class ProviderTest { public static void main(String[] args) { //直接讀取spring的配置文件就好 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); } }
package whu.edu.lcrpc.service; /** * Created by apple on 17/3/26. */ import whu.edu.lcrpc.entity.MyParamDO; import whu.edu.lcrpc.entity.MyResultDO; /** * 服務對應接口 * 該服務實現一個簡單的計算器服務,實現加減乘除四個基本功能 */ public interface ICaculator { /** * 加 * @param n1 * @param n2 * @return */ public double add(double n1,double n2); /** * 減 * @param n1 * @param n2 * @return */ public MyResultDO minus(double n1, double n2); /** * 乘 * @param myParamDO * @return */ public MyResultDO multiply(MyParamDO myParamDO); /** * 除 * @param myParamDO * @return */ public double divide(MyParamDO myParamDO); }
接口實現類的代碼以下:
package whu.edu.lcrpc.service.impl; import whu.edu.lcrpc.entity.MyParamDO; import whu.edu.lcrpc.entity.MyResultDO; import whu.edu.lcrpc.service.ICaculator; /** * Created by apple on 17/3/26. */ /** * 服務對應接口的實現類 */ public class CaculatorImpl implements ICaculator { @Override public double add(double n1, double n2) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return n1 + n2; } @Override public MyResultDO minus(double n1, double n2) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } MyResultDO myResultDO = new MyResultDO(); myResultDO.setResult(n1 - n2); return myResultDO; } @Override public MyResultDO multiply(MyParamDO myParamDO) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } MyResultDO myResultDO = new MyResultDO(); myResultDO.setResult(myParamDO.getN1() * myParamDO.getN2()); return myResultDO; } @Override public double divide(MyParamDO myParamDO) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return myParamDO.getN2() / myParamDO.getN1(); } }
最終該工程會生成一個二方包提供給服務調用端使用。
客戶端的測試部分,利用LCRPC提供的包以及服務發佈者提供的二方包,遠程調用該計算器服務。
<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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>whu.edu.lcrpc</groupId> <artifactId>lcrpc-clienttest</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>lcrpc-clienttest</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.3.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!--spring依賴--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!--lombok依賴--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <!--對服務發佈者二方包的依賴--> <dependency> <groupId>whu.edu.lcrpc</groupId> <artifactId>lcrpc-test</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--對LCRPC服務框架的依賴--> <dependency> <groupId>whu.edu.lcrpc</groupId> <artifactId>lcrpc-core</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
<?xml version="1.0" encoding="UTF-8"?> <beans default-autowire="byName" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="rpcConsumer" class="whu.edu.lcrpc.server.impl.LCRPCConsumerImpl"> <property name="interfaceName" value="whu.edu.lcrpc.service.ICaculator" ></property> <property name="version" value="0.1"></property> </bean> <bean id="ConsumerTest" class="whu.edu.lcrpc.app.ConsumerTest"></bean> </beans>
屬性interfaceName以及version是必要的,標識該bean是對哪個遠程服務的調用。
package whu.edu.lcrpc.app; /** * Created by apple on 17/3/26. */ import lombok.Data; import whu.edu.lcrpc.entity.MyParamDO; import whu.edu.lcrpc.entity.MyResultDO; import whu.edu.lcrpc.server.ILCRPCConsumer; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * 客戶端測試類,調用LCRPC接口,進行服務的調用 */ @Data public class ConsumerTest { @Resource ILCRPCConsumer rpcConsumer; public void add() { //調用接口 進行加減乘除 List<Object> params = new ArrayList<>(); params.add(1.0); params.add(2.0); Double result = (Double) rpcConsumer.serviceConsumer("add",params); System.out.println("add: " + result); } public void minus(){ List<Object> params = new ArrayList<>(); params.add(1.0); params.add(2.0); MyResultDO result = (MyResultDO) rpcConsumer.serviceConsumer("minus",params); System.out.println("minus: " + result.getResult()); } public void multiply(){ List<Object> params = new ArrayList<>(); MyParamDO myParamDO = new MyParamDO(); myParamDO.setN1(1.0); myParamDO.setN2(2.0); params.add(myParamDO); MyResultDO result = (MyResultDO) rpcConsumer.serviceConsumer("multiply",params); System.out.println("multiply: " + result.getResult()); } public void divide(){ List<Object> params = new ArrayList<>(); MyParamDO myParamDO = new MyParamDO(); myParamDO.setN1(1.0); myParamDO.setN2(2.0); params.add(myParamDO); Double result = (Double) rpcConsumer.serviceConsumer("divide",params); System.out.println("divide: " + result); } }
package whu.edu.lcrpc.app; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by apple on 17/3/27. */ public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); ConsumerTest consumerTest = (ConsumerTest) context.getBean("ConsumerTest"); new Thread(()->{ consumerTest.add(); }).start(); new Thread(()->{ consumerTest.minus(); }).start(); new Thread(()->{ consumerTest.multiply(); }).start(); new Thread(()->{ consumerTest.divide(); }).start(); } }
注意:全部工程代碼可訪問資源:實現本身的遠程調用框架,進行下載
未完待續。。。。這只是服務框架的初步實現版本,期待優化提高後的第二個版本~
值得期待的是: (1)怎樣利用動態代理,使得用戶與訪問本地接口同樣調用遠程服務 (2)當鏈接增多,服務是否能夠支撐?須要改變IO模式。