Dubbo的介绍
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。
Dubbo提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
官网:http://dubbo.apache.org/
什么是RPC?
RPC全称为remote procedure call,即远程过程调用。比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。
需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程。
而不需要了解底层网络技术的协议,在面向对象的编程语言中,远程过程调用即是 远程方法调用
RPC调用过程
java中RPC框架比较多,常见的有RMI、Hessian、gRPC、bRPC、Dubbo等,其实对于RPC框架而言,核心模块就是通讯和序列化接下来我们就分别看一下常见的RPC框架
RMI
1)RMI(remote method invocation)是java原生支持的远程调用,RMI采用JRMP(Java RemoteMessageing Protocol)作为通信协议,可以认为是纯java版本的分布式远程调用解决方案。
2)RMI的核心概念
3)RMI步骤
服务器:
1.创建服务接口
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface UserService extends Remote {
String sayHello(String name) throws RemoteException;
}
2.提供接口的实现类
import Jackie.service.UserService;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class UserServiceImpl extends UnicastRemoteObject implements UserService
{
public UserServiceImpl() throws RemoteException {
}
@Override
public String sayHello(String name) throws RemoteException{
return "成功调用了服务端的服务"+name;
}
}
3.将本地服务暴露出去,供外部调用
import Jackie.service.UserService;
import Jackie.service.impl.UserServiceImpl;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class ServerMain {
public static void main(String[] args) {
try {
//1.启动RMI注册服务,指定端口号
LocateRegistry.createRegistry(5184);
//2.创建要被访问的远程对象的实例
UserService userService = new UserServiceImpl();
3.暴露服务,把远程对象实例注册到RMI注册服务器上
Naming.bind("rmi://localhost:5184/UserService", userService);
System.out.println("服务器启动中。。。。");
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
}
}
}
客户端:
创建服务接口
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface UserService extends Remote {
String sayHello(String name) throws RemoteException;
}
客户端远程调用服务,客户端需要依赖服务接口
import Jackie.service.UserService;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class ClientMain {
public static void main(String[] args) {
UserService userService = null;
try {
userService = (UserService) Naming.lookup("rmi://localhost:5184/UserService");
//userService代理对象
System.out.println(userService);
System.out.println(userService.sayHello("客户端"));
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Hessian
Hessian使用C/S方式,基于HTTP协议传输,使用Hessian二进制序列化。
server端:
1.添加hessian的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">
<parent>
<artifactId>DubboartifactId>
<groupId>Jackie.demogroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<packaging>warpackaging>
<artifactId>Hessian_ServerartifactId>
<dependencies>
<dependency>
<groupId>com.cauchogroupId>
<artifactId>hessianartifactId>
<version>4.0.7version>
dependency>
dependencies>
2.创建跟server端相同的接口UserService
public interface UserService {
String sayHello(String name);
}
3.实现类
import Jackie.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public String sayHello(String name) {
return "调用了hessian服务端的服务" + name;
}
}
4.web.xml中配置HessianServlet
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>HessianServletservlet-name>
<servlet-class>com.caucho.hessian.server.HessianServletservlet-class>
<init-param>
<param-name>service-classparam-name>
<param-value>Jackie.service.impl.UserServiceImplparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>HessianServletservlet-name>
<url-pattern>/hessianServleturl-pattern>
servlet-mapping>
web-app>
5.添加tomcat7插件启动服务
<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">
<parent>
<artifactId>DubboartifactId>
<groupId>Jackie.demogroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<packaging>warpackaging>
<artifactId>Hessian_ServerartifactId>
<dependencies>
<dependency>
<groupId>com.cauchogroupId>
<artifactId>hessianartifactId>
<version>4.0.7version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>5184port>
<path>/path>
<uriEncoding>UTF-8uriEncoding>
configuration>
plugin>
plugins>
build>
project>
客户端:
1.添加hessian的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">
<parent>
<artifactId>DubboartifactId>
<groupId>Jackie.demogroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>Hessian_ClientartifactId>
<dependencies>
<dependency>
<groupId>com.cauchogroupId>
<artifactId>hessianartifactId>
<version>4.0.7version>
dependency>
dependencies>
project>
2.创建跟server端相同的接口UserService
public interface UserService {
String sayHello(String name);
}
3.创建测试类测试
import Jackie.service.UserService;
import com.caucho.hessian.client.HessianProxyFactory;
import java.net.MalformedURLException;
public class ClientTest {
public static void main(String[] args) throws MalformedURLException {
String url = "http://localhost:5184/hessianServlet";
HessianProxyFactory hessianProxyFactory = new HessianProxyFactory();
UserService userService = (UserService) hessianProxyFactory.create(UserService.class, url);
System.out.println(userService.sayHello("hessian客户端"));
}
}
Thrift:FaceBook开源RPC框架,典型的CS架构,支持跨语言,客户端和服务端可以使用不同的
语言开发,thrift通过IDL(Interface Description Language)来关联客户端和服务端。
gRPC google
dubbo
provider服务提供
consumer服务消费
registry注册
protocol协议
服务提供者:
1、定义服务接口
接口HelloService
public interface HelloService {
String sayHello(String message);
}
2、实现类HelloServiceImpl
import Jackie.service.HelloService;
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return name+ "调用了myRPC的服务";
}
}
3、服务注册:注册中心
此处注册中心我们将服务注册在map集合中,结构:Map
外边map的key存储服务接口的全类名;
URL封装了调用服务的ip和port,里边value指定指定具体实现类;
注册中心类提供注册服务并暴露服务和发现服务功能:
public class URL {
private String hostname;
private Integer port;
public URL() {
}
public URL(String hostname, Integer port) {
this.hostname = hostname;
this.port = port;
}
/**
* 获取
* @return hostname
*/
public String getHostname() {
return hostname;
}
/**
* 设置
* @param hostname
*/
public void setHostname(String hostname) {
this.hostname = hostname;
}
/**
* 获取
* @return port
*/
public Integer getPort() {
return port;
}
/**
* 设置
* @param port
*/
public void setPort(Integer port) {
this.port = 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();
}
}
import Jackie.pojo.URL;
import java.util.HashMap;
import java.util.Map;
public class NativeRegistry {
//注册中心 map
private static Map<String, Map<URL, Class>> registCenter = new HashMap<>();
/**
* 注册服务
*
* @param url
* @param interfaceName
* @param implClass
*/
public static void regist(String interfaceName, URL url, 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(String interfaceName, URL url) {
return registCenter.get(interfaceName).get(url);
}
}
注册服务
import Jackie.Tomcat.HttpServer;
import Jackie.pojo.URL;
import Jackie.registry.NativeRegistry;
import Jackie.service.HelloService;
import Jackie.service.impl.HelloServiceImpl;
public class ServiceProvider {
public static void main(String[] args) {
//创建URL
URL url = new URL("localhost", 8080);
//注册中心中注册服务
NativeRegistry.regist(HelloService.class.getName(), url, HelloServiceImpl.class);
//启动tomcat,并暴露服务
HttpServer httpServer = new HttpServer();
httpServer.start(url.getHostname(),url.getPort());
}
}
4、暴露服务
服务之间调用的通信协议采用http协议,所以在服务provider中启动tomcat暴露服务
添加内嵌tomcat的依赖
<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">
<parent>
<artifactId>DubboartifactId>
<groupId>Jackie.demogroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>providerartifactId>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-coreartifactId>
<version>9.0.12version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
dependencies>
project>
创建HttpServer
import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;
public class HttpServer {
/**
* tomcat服务启动
* 参考tomcat配置
*
*
*
*
*
*
*
*
*
*
*/
/**
* 启动服务
* @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
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//HttpServerHandler真正处理远程调用请求
new HttpServerHandler().handle(req,resp);
}
}
HttpServerHandler处理远程调用请求
import Jackie.pojo.Invocation;
import Jackie.pojo.URL;
import Jackie.registry.NativeRegistry;
import org.apache.commons.io.IOUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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(invocation.getInterfaceName(),new URL("localhost", 8080));
//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
import java.io.Serializable;
public class Invocation implements Serializable {
/**
* 接口名
*/
private String interfaceName;
/**
* 方法名
*/
private String methodName;
/**
* 参数值列表
*/
private Object[] params;
/**
* 参数类型列表
*/
private Class[] paramTypes;
public Invocation() {
}
public Invocation(String interfaceName, String methodName, Object[] params, Class[] paramTypes) {
this.interfaceName = interfaceName;
this.methodName = methodName;
this.params = params;
this.paramTypes = paramTypes;
}
/**
* 获取
* @return interfaceName
*/
public String getInterfaceName() {
return interfaceName;
}
/**
* 设置
* @param interfaceName
*/
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
/**
* 获取
* @return methodName
*/
public String getMethodName() {
return methodName;
}
/**
* 设置
* @param methodName
*/
public void setMethodName(String methodName) {
this.methodName = methodName;
}
/**
* 获取
* @return params
*/
public Object[] getParams() {
return params;
}
/**
* 设置
* @param params
*/
public void setParams(Object[] params) {
this.params = params;
}
/**
* 获取
* @return paramTypes
*/
public Class[] getParamTypes() {
return paramTypes;
}
/**
* 设置
* @param paramTypes
*/
public void setParamTypes(Class[] paramTypes) {
this.paramTypes = paramTypes;
}
}
启动服务
import Jackie.Tomcat.HttpServer;
import Jackie.pojo.URL;
import Jackie.registry.NativeRegistry;
import Jackie.service.HelloService;
import Jackie.service.impl.HelloServiceImpl;
public class ServiceProvider {
public static void main(String[] args) {
//创建URL
URL url = new URL("localhost", 8080);
//注册中心中注册服务
NativeRegistry.regist(HelloService.class.getName(), url, HelloServiceImpl.class);
//启动tomcat,并暴露服务
HttpServer httpServer = new HttpServer();
httpServer.start(url.getHostname(),url.getPort());
}
}
4、consumer服务消费端
添加commons-io的依赖
<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">
<parent>
<artifactId>DubboartifactId>
<groupId>Jackie.demogroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>consumerartifactId>
<dependencies>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
dependencies>
project>
封装HttpClient对象,发起远程调用j
import Jackie.pojo.Invocation;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
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 outputStream = connection.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(invocation);
objectOutputStream.flush();
objectOutputStream.close();
// 将输入流转为字符串(此处可是java对象) 获取远程调用的结果
InputStream inputStream = connection.getInputStream();
return IOUtils.toString(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
创建跟provider端相同的接口HelloService
public interface HelloService {
String sayHello(String message);
}
创建跟provider端相同的pojo
import java.io.Serializable;
public class Invocation implements Serializable {
/**
* 接口名
*/
private String interfaceName;
/**
* 方法名
*/
private String methodName;
/**
* 参数值列表
*/
private Object[] params;
/**
* 参数类型列表
*/
private Class[] paramTypes;
public Invocation() {
}
public Invocation(String interfaceName, String methodName, Object[] params, Class[] paramTypes) {
this.interfaceName = interfaceName;
this.methodName = methodName;
this.params = params;
this.paramTypes = paramTypes;
}
/**
* 获取
* @return interfaceName
*/
public String getInterfaceName() {
return interfaceName;
}
/**
* 设置
* @param interfaceName
*/
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
/**
* 获取
* @return methodName
*/
public String getMethodName() {
return methodName;
}
/**
* 设置
* @param methodName
*/
public void setMethodName(String methodName) {
this.methodName = methodName;
}
/**
* 获取
* @return params
*/
public Object[] getParams() {
return params;
}
/**
* 设置
* @param params
*/
public void setParams(Object[] params) {
this.params = params;
}
/**
* 获取
* @return paramTypes
*/
public Class[] getParamTypes() {
return paramTypes;
}
/**
* 设置
* @param paramTypes
*/
public void setParamTypes(Class[] paramTypes) {
this.paramTypes = paramTypes;
}
}
调用测试
import Jackie.pojo.Invocation;
import Jackie.service.HelloService;
public class ConsumerMain {
public static void main(String[] args) {
//封装一个invocation
Invocation invocation = new Invocation(HelloService.class.getName(), "sayHello",
new Object[]{"myRPC的客户端"}, new Class[]{String.class});
//远程调用服务
String result = new HttpClient().post("localhost", 8080, invocation);
System.out.println("远程调用执行的结果result="+result);
}
}