实现一个简单的远程调用,需要四个项目,如下
api提供接口,service提供接口的实现,provider和consumer分别是提供者和消费者。
同时还需要一个zookeeper作为注册中心
具体思路:
provider启动后,将服务信息注册到zookeeper,同时开启socker监听,(这里的provider是作为socket中的服务端)。启动consumer,consumer启动后,立即从注册中心获取所需的服务信息(服务信息指提供者的地址和端口号,因为接下来要使用它们进行socket连接),然后使用获取到的服务信息,建立socket连接,得到返回的结果。
实现细节:
api模块
pom依赖
<dependencies>
<!-- zkclient 用于与zookeeper建立连接 -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
</dependency>
</dependencies>
Request封装的是请求信息,由consumer发送给provider,provider根据请求信息,通过反射找到具体的方法,并执行方法,返回结果给consumer
//因为Request类要通过socket传递,所以要实现序列化接口
public class Request implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1364252361112203421L;
/**
* 接口名称
*/
private String interfaceName;
/**
* 方法名称
*/
private String methodName;
/**
* 参数
*/
private Object[] args;
public String getInterfaceName() {
return interfaceName;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
@Override
public String toString() {
return "Request [interfaceName=" + interfaceName + ", methodName=" + methodName + ", args="
+ Arrays.toString(args) + "]";
}
public Request(String interfaceName, String methodName, Object[] args) {
super();
this.interfaceName = interfaceName;
this.methodName = methodName;
this.args = args;
}
public Request() {
super();
// TODO Auto-generated constructor stub
}
}
这里的AddService就是服务接口,其中只写了一个简单的add方法
public interface AddService {
/**
*
* @Title: add
* @Description: TODO
* @param @param a
* @param @param b
* @param @return
* @return int
* @throws
*/
int add(Integer a,Integer b);
}
ProxyUtils,consumer从此类中获取一个代理类,请求的封装,发出,和接收都由代理类完成
public class ProxyUtils {
// 获取一个代理类,由代理类封装请求信息,发送请求,接收请求,最终返回result结果
public static <T> T getProxy(Class<T> interfaces) {
@SuppressWarnings("unchecked")
T proxy = (T)Proxy.newProxyInstance(ProxyUtils.class.getClassLoader(), new Class<?>[] {interfaces}, new InvocationHandler() {
/**
* 代理对象核心方法,proxy代理类本身,method代理方法,args参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("toString".equals(method.getName())) {
return interfaces.getName()+"$proxy";
}
if ("equals".equals(method.getName())) {
return Object.class.equals(this);
}
if ("hashCode".equals(method.getName())) {
return Object.class.hashCode();
}
//封装请求信息
Request request = new Request();
request.setInterfaceName(interfaces.getName());
request.setMethodName(method.getName());
request.setArgs(args);
//发送request
Object result = null;
Socket socket = null;
InputStream inputStream = null;
OutputStream outputStream = null;
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
List<String> serviceList = ZkUtils.discover(interfaces.getName());
String one = loadBalance(serviceList);
String address = one.split(":")[0]; // 获取提供者地址
Integer port = Integer.valueOf(one.split(":")[1]); // 获取提供者端口号
socket = new Socket(address, port); //创建socket
outputStream = socket.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(request); // 将对象写入对象流
//接受提供者计算出的答案
inputStream = socket.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);
result = objectInputStream.readObject(); // 从对象流中读取结果
System.out.println("收到"+port+"的回复:");
} catch (Exception e) {
e.printStackTrace();
} finally {
close(objectInputStream,inputStream,objectOutputStream,outputStream,socket);
}
return result;
}
});
return proxy; // 返回结果给消费者
}
// 关闭资源
public static void close(Closeable... closeables) {
for (Closeable resource : closeables) {
if(null != resource) {
try {
resource.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
resource = null;
}
}
}
}
// 实现简单的负载均衡
public static String loadBalance(List<String> uris) {
if(null == uris || uris.size() == 0) {
return null;
}
Random random = new Random();
int index = random.nextInt(uris.size());
return uris.get(index);
}
}
ZkUtils,封装了zookeeper的服务注册和服务发现
public class ZkUtils {
//初始化zk
private static ZkClient zkClient = null;
private static final String SERVER_URL = "114.55.27.97:2181";
static {
zkClient = new ZkClient(SERVER_URL, 5000, 30000);
}
/**
*
* @Title: register
* @Description: 服务的注册
* @param @param serviceName
* @param @param serviceAddress
* @return void
* @throws
*/
public static void register(String serviceName,String serviceAddress) {
if(null == serviceName || serviceName.equals("")) {
throw new RuntimeException("服务名称不能为空");
}
if(!zkClient.exists("/"+serviceName)) {
zkClient.createPersistent("/"+serviceName);
}
if(!zkClient.exists("/"+serviceName+"/"+serviceAddress)) {
zkClient.createEphemeral("/"+serviceName+"/"+serviceAddress);
}
System.out.println(serviceName+"在"+SERVER_URL+"注册成功,节点为:"+"/"+serviceName+"/"+serviceAddress);
}
/**
*
* @Title: discover
* @Description: 服务的发现
* @param @param serviceName
* @param @return
* @return List<String>
* @throws
*/
public static List<String> discover(String serviceName){
if(null == serviceName || serviceName.equals("")) {
throw new RuntimeException("服务名称不能为空");
}
if(!zkClient.exists("/"+serviceName)) {
return null;
}
List<String> childrens = zkClient.getChildren("/"+serviceName);
return childrens;
}
public static void main1(String[] args) {
register("憨批", "001");
register("憨批", "002");
register("憨批", "003");
try {
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
List<String> childrens = discover("憨批");
System.out.println(childrens);
}
}
service模块
pom依赖,依赖api
<dependencies>
<dependency>
<groupId>com.scaler7</groupId>
<artifactId>api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
没什么好说的,就是实现了api接口
public class AddServiceImpl implements AddService {
@Override
public int add(Integer a, Integer b) {
return a+b;
}
}
provider模块
pom依赖,它依赖api和service
<dependencies>
<dependency>
<groupId>com.scaler7</groupId>
<artifactId>api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.scaler7</groupId>
<artifactId>service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
Provider,提供者,启动后先去zookeeper注册,然后阻塞监听socket
public class Provider {
public static void main(String[] args) throws Exception {
Integer port = 6666;
ServerSocket serverSocket = new ServerSocket(6666);
// 提供者启动后就去zookeeper中注册
ZkUtils.register(AddService.class.getName(), "localhost:"+port);
System.out.println("提供者6666已启动!");
// 循环监听
listener(serverSocket);
}
private static void listener(ServerSocket serverSocket) {
while (true) {
// 等待消费者连接,这一步是阻塞的
Socket accept = null;
try {
accept = serverSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
InputStream inputStream = null;
ObjectInputStream objectInputStream = null;
OutputStream outputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
inputStream = accept.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);
Request request = (Request) objectInputStream.readObject();
Object result = Invoker.invoker(request); // 通过代理类,得到结果
// 将响应结果写入objectOutputStream,发送给消费者
outputStream = accept.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
ProxyUtils.close(objectOutputStream, outputStream, objectInputStream, inputStream);
}
}
}
}
Invoker类,调用此类中的invoker方法,即可通过反射找出接口的实现类中的具体方法,调用并返回结果
public class Invoker {
public static Object invoker(Request question) {
System.out.println("开始计算");
String interfaceName = question.getInterfaceName();
String methodName = question.getMethodName();
Object[] args = question.getArgs();
String className = getClassNameByInterfaceName(interfaceName);
Object result = null;
try {
Class<?> clazz = Class.forName(className); // 实现类
Object obj = clazz.newInstance(); // 实现类的对象
Class<?>[] argsType = null; // 参数类型数组
if(args != null && args.length != 0) {
argsType = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
argsType[i] = args[i].getClass();
}
}
Method method = clazz.getMethod(methodName, argsType);
result = method.invoke(obj, args);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static String getClassNameByInterfaceName(String interfaceName) {
int endIndex = interfaceName.lastIndexOf('.'); // 获得最后一个 '.' 的索引
StringBuilder sb = new StringBuilder();
sb.append(interfaceName.subSequence(0, endIndex)); // com.scaler7.service
sb.append(".impl."); // com.scaler7.service.impl.
sb.append(interfaceName.substring(endIndex+1)); // com.scaler7.service.impl.AddService
sb.append("Impl"); // com.scaler7.service.impl.AddServiceImpl
return sb.toString();
}
}
consumer模块
pom依赖
<dependencies>
<dependency>
<groupId>com.scaler7</groupId>
<artifactId>api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.rpc</groupId>
<artifactId>provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
Consumer类
public class Consumer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//获取一个代理类
AddService addService = ProxyUtils.getProxy(AddService.class);
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//调用add方法,实际是代理类将接口和方法封装入request,通过socker传给provider进行计算,返回结果
Object result = addService.add(1, 2);
System.out.println("答案是:"+result);
}
}
}
运行效果
启动三个provider
启动consumer,随机访问三个provider中的一个,模拟实现负载均衡
至此,一个简单的远程方法调用(RPC)就完成了