【如何实现一个简单的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模式。