具体实现代码如下,原则仅仅是为了便于展示调用过程。
// 接口类
public interface SayHelloService {
/**
* say hello method
* @param helloArgs args
* @return string
*/
String sayHello(String helloArgs);
}
// 接口实现类
public class SayHelloServiceImpl implements SayHelloService {
@Override
public String sayHello(String helloArgs) {
String cmpArgs = "hello";
if (cmpArgs.equals(helloArgs)){
return "hello";
} else {
return "bye bye";
}
}
}
// 服务提供类
public class ServiceProvider {
void monitor() throws Exception {
Map<String,Object> services = new HashMap<>();
services.put("com.loongshawn.test.service.SayHelloService",new SayHelloServiceImpl());
ServerSocket sever = new ServerSocket(1234);
while (true) {
Socket socket = sever.accept();
System.out.println("server socket accept");
// 读取服务信息
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
String interfaceName = input.readUTF();
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[])input.readObject();
Object[] arguments = (Object[])input.readObject();
System.out.println(interfaceName + "," + methodName + "," + parameterTypes + "," + arguments);
// 执行调用
Class servicesInterfaceClass = Class.forName(interfaceName);
Object service = services.get(interfaceName);
Method method = servicesInterfaceClass.getMethod(methodName,parameterTypes);
Object result = method.invoke(service,arguments);
System.out.println("server method execute result:" + result);
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(result);
}
}
// 服务消费类
public class ServiceConsumer {
void doSend() throws Exception{
String interfaceName = SayHelloService.class.getName();
Method method = SayHelloService.class.getMethod("sayHello",java.lang.String.class);
Object[] arguments = {"hello"};
Socket socket = new Socket("127.0.0.1",1234);
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeUTF(interfaceName);
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(arguments);
System.out.println(interfaceName + "," + method.getName() + "," + method.getParameterTypes() + "," + arguments);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
Object result = input.readObject();
System.out.println("client request result:" + result);
}
基于TCP socket这种方式实现RPC更多的是与输入输出流打交道,将数据写入outputstream,从inputstream中读数据,都是一些底层的工作。在现在这个应用呈现爆炸性扩张的年代,这种方式的开发效率显得低下,同时也面临并发同步等挑战,因此来看看另外一种实现。
基于http协议的RPC实现,构建图与socket类似,只不过具体请求的代码被httpclient相关代码取代。
具体实现代码如下,通过springboot工程部署server端服务,仅展示服务提供端方法,这个方法在一个Controller中,用来响应http请求。
@RequestMapping(value = "/service")
@ResponseBody
Object doGet(@RequestParam("interfaceName") String interfaceName,
@RequestParam("method") String methodName,
@RequestParam("parameterTypes") String parameterTypes,
@RequestParam("arguments") String arguments
) throws Exception {
Map<String,Object> services = new HashMap<>();
services.put("com.loongshawn.test.service.SayHelloService",new SayHelloServiceImpl());
System.out.println(interfaceName + "," + methodName + "," + parameterTypes + "," + arguments);
// 执行调用
Class servicesInterfaceClass = Class.forName(interfaceName);
Object service = services.get(interfaceName);
Method method = servicesInterfaceClass.getMethod(methodName,parameterTypes.getClass());
Object result = method.invoke(service,arguments);
System.out.println("server method execute result:" + result);
return result;
}
客户端请求代码如下,将参数的传递、请求、结果响应交给httpclient工具,这个Apache下面的一个工具包。类似的http工具包还有unirest工具,比httpclient使用起来更简洁。
void doGet() throws Exception{
String interfaceName = SayHelloService.class.getName();
Method method = SayHelloService.class.getMethod("sayHello",java.lang.String.class);
String arguments = "hello";
String url = "http://127.0.0.1:8080/service?" +
"interfaceName=" + interfaceName + "&method=" + method.getName() +
"¶meterTypes=" + "java.lang.String" + "&arguments=" + arguments;
HttpClientServiceImpl httpClientService = new HttpClientServiceImpl();
String result = httpClientService.doGet(url);
System.out.println(url);
System.out.println("client request result:" + result);
}
http实现的RPC方便之处在于还能通过浏览器测试:
http://127.0.0.1:8080/service?interfaceName=com.loongshawn.test.service.SayHelloService&method=sayHello¶meterTypes=java.lang.String&arguments=hello
http://127.0.0.1:8080/service?interfaceName=com.loongshawn.test.service.SayHelloService&method=sayHello¶meterTypes=java.lang.String&arguments=bye
展示的两种RPC实现当然不能应对大规模的SOA构架实现,但服务端方法调用机制还是能够借用。高并发环境下的SOA构架,利用zookeeper进行RPC服务治理是一种选择,目前阿里内部使用的是HSF框架(https://blog.csdn.net/loongshawn/article/details/73903709 ),对外开源的是dubbo来进行服务治理。产品太多,也不可能都学一遍,可以先学学zookeeper的工作机制,至少能搭个可用的demo起来。不同的企业拥抱的技术栈和方案不同,了解了相关的机制时切换时也会容易些。