目录
简介
服务提供者
服务注册:注册中心
HttpServerHandler处理远程调用请求
consumer服务消费端
简介
RPC(Remote Procedure Call)——远程过程调用,它是一种通过网络从远程计算机程序上请求服务, 而不需要了解底层网络技术的协议,在面向对象的编程语言中,远程过程调用即是远程方法调用
基本实现思路如下:
项目结构:
- provider服务提供
- consumer服务消费
- registry注册
-
protocol协议
服务提供者
- 定义服务接口
接口HelloService
public interface HelloService {
String sayHello(String message);
}
- 实现类HelloServiceImpl
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return name+ "调用了myRPC的服务";
}
}
服务注册:注册中心
此处注册中心我们将服务注册在map集合中,结构:Map<String,Map<URL,Class>> 外边map的key存储 服务接口的全类名,URL封装了调用服务的ip和port,里边value指定指定具体实现类 注册中心类提供注册服务并暴露服务和发现服务功能:
public class URL {
private String hostname;
private Integer port;
@Override
public boolean equals(Object obj) {
if(obj==null){
return false;
}
if(!(obj instanceof URL)){
return false;
}
URL url = (URL) obj;
if(hostname.equals(((URL) obj).getHostname()) && port.intValue() == url.port.intValue()){
return true;
}
return false;
}
@Override
public int hashCode() {
return hostname.hashCode();
}
}
public class NativeRegistry {
private static Map<String, Map<URL,Class>> registCenter = new HashMap<>();
/**
* 注册服务
* @param url
* @param interfaceName
* @param implClass
*/
public static void regist(URL url,String interfaceName,Class implClass){
Map<URL,Class> map = new HashMap<>();
map.put(url,implClass);
registCenter.put(interfaceName,map);
}
/**
* 从注册中心获取服务
* @param url
* @param interfaceName
* @return
*/
public static Class get(URL url,String interfaceName){
return registCenter.get(interfaceName).get(url);
}
}
- 注册服务
public class ServiceProvider {
public static void main(String[] args) {
//创建URL
URL url = new URL("localhost", 8080);
//注册中心中注册服务
NativeRegistry.regist(url, HelloService.class.getName(), HelloServiceImpl.class);
//启动并暴露服务
HttpServer httpServer = new HttpServer();
httpServer.start(url.getHostname(),url.getPort());
}
}
- 暴露服务
服务之间调用的通信协议采用http协议,所以在服务provider中启动tomcat暴露服务
添加内嵌tomcat的依赖
<!--内嵌tomcat-->
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.12</version>
</dependency>
</dependencies>
- 创建HttpServer
public class HttpServer {
/**
* tomcat服务启动
* 参考tomcat配置
* <Server port="8005" shutdown="SHUTDOWN">
* <Service name="Catalina">
* <Connector port="8080" protocol="HTTP/1.1"
* connectionTimeout="20000"
* redirectPort="8443"
* URIEncoding="UTF-8"/>
* <Engine name="Catalina" defaultHost="localhost">
* <Host name="localhost" appBase="webapps"
* unpackWARs="true" autoDeploy="true">
* <Context path="" doBase="WORKDIR" reloadable="true"/>
* </Host>
* </Engine>
* </Service>
* </Server>
*/
/**
* 启动服务
* @param hostname
* @param port
*/
public void start(String hostname,int port){
// 实例一个tomcat
Tomcat tomcat = new Tomcat();
// 构建server
Server server = tomcat.getServer();
// 获取service
Service service = server.findService("Tomcat");
// 构建Connector
Connector connector = new Connector();
connector.setPort(port);
connector.setURIEncoding("UTF-8");
// 构建Engine
Engine engine = new StandardEngine();
engine.setDefaultHost(hostname);
// 构建Host
Host host = new StandardHost();
host.setName(hostname);
// 构建Context
String contextPath = "";
Context context = new StandardContext();
context.setPath(contextPath);
context.addLifecycleListener(new Tomcat.FixContextListener());// 生命周期监听器
// 然后按照server.xml,一层层把子节点添加到父节点
host.addChild(context);
engine.addChild(host);
service.setContainer(engine);
service.addConnector(connector);
// service在getServer时就被添加到server节点了
// tomcat是一个servlet,设置路径与映射
tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet());
context.addServletMappingDecoded("/*","dispatcher");
try {
tomcat.start();// 启动tomcat
tomcat.getServer().await();// 接受请求
}catch (LifecycleException e){
e.printStackTrace();
}
}
}
- DispatcherServlet
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServerHandler().handle(req,resp);
}
}
HttpServerHandler处理远程调用请求
public class HttpServerHandler {
/**
* 服务的处理
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void handle(HttpServletRequest req, HttpServletResponse resp){
try {
//服务请求的处理逻辑
//1 通过请求流获取请求服务调用的参数
InputStream inputStream = req.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Invocation invocation = (Invocation) objectInputStream.readObject();
//2 从注册中心获取服务的列表
Class implCass = NativeRegistry.get(new URL("localhost", 8080), invocation.getInterfaceName());
//3 调用服务 反射
Method method = implCass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
String result = (String) method.invoke(implCass.newInstance(), invocation.getParams());
//4 结果返回
IOUtils.write(result,resp.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
- 封装调用参数Invocation
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Invocation implements Serializable {
private String interfaceName;
private String methodName;
private Object[] params;
private Class[] paramTypes;
}
- 启动服务
public class ServiceProvider {
public static void main(String[] args) {
//创建URL
URL url = new URL("localhost", 8080);
//注册中心中注册服务
NativeRegistry.regist(url, HelloService.class.getName(), HelloServiceImpl.class);
//启动并暴露服务
HttpServer httpServer = new HttpServer();
httpServer.start(url.getHostname(),url.getPort());
}
}
consumer服务消费端
- 封装HttpClient对象,发起远程调用
public class HttpClient {
/**
* 远程方法调用
* @param hostname :远程主机名
* @param port :远程端口号
* @param invocation :封装远程调用的信息
*/
public String post(String hostname, int port, Invocation invocation) {
try {
URL url = new URL("http", hostname, port, "/client/");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);// 必填项
//发送调用的信息
OutputStream os = connection.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(invocation);
oos.flush();
oos.close();
// 将输入流转为字符串(此处可是java对象) 获取远程调用的结果
InputStream is = connection.getInputStream();
return IOUtils.toString(is);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
- 调用测试
public class Consumer {
public static void main(String[] args) {
//封装一个invocation
Invocation invocation = new Invocation(HelloService.class.getName(), "sayHello2",
new Object[]{"Test"}, new Class[]{String.class});
//远程调用服务
String result = new HttpClient().post("localhost", 8080, invocation);
System.out.println("远程调用执行的结果result="+result);
}
}