基础概念
Apache Dubbo是一款高性能、轻量级的Java RPC框架,前身是阿里的开源Java RPC框架,可以和spring无缝集成。
Dubbo提供了三大核心能力:面向接口的远程方法调用、智能容错和负载均衡、服务自动注册和发现。
本章先来了解一下RPC框架
RPC全称为remote procedure call,即远程过程调用。例如两台服务器A和B,A服务器部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。需要注意的是RPC并不是具体的技术,而是指整个网络远程调用过程。
RPC调用过程:
客户端调用、序列化、通过网络发送消息给服务端
服务端反序列化、调用本地服务、处理服务、返回处理结果、将结果序列化、
通过网络返回消息给客户端、客户端反序列化、得到调用结果
常见的RPC框架:
RMI、Hessian、gRPC(google)、bRPC、Dubbo、Thrift(facebook)
Java原生支持的远程调用,应用JRMP作为通信协议,可以认为是纯Java版本的分布式远程调用解决方案。
核心原理:服务器在注册中心注册服务、客户端从注册中心获取服务
先创建一个maven父工程,再创建一个服务端子工程,创建接口继承Remote
public interface UserService extends Remote {
//记得要抛出异常 不然会报错
String sayHello(String name) throws RemoteException;
}
实现远程接口,并且继承UnicastRemoteObject
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
public UserServiceImpl() throws RemoteException {
}
public String sayHello(String name) {
return name+"调用了服务端的服务";
}
}
public class Server {
public static void main(String[] args) throws Exception {
//对外暴露服务
//1.启动rmi注册服务,指定端口号
LocateRegistry.createRegistry(8888);
//2.创建要被访问的远程对象的实例
UserService userService = new UserServiceImpl();
//3.把远程对象实例注册到rmi注册中心
Naming.bind("rmi://localhost:8888/UserService",userService);
System.out.println("服务端开始启动");
}
}
创建maven子工程作为客户端,客户端需要依赖服务接口
public class Client {
public static void main(String[] args) throws Exception{
UserService userService = (UserService) Naming.lookup("rmi://localhost:8888/UserService");
String s = userService.sayHello("客户端");
System.out.println(s);
}
}
注意还要把服务端的UserService接口在客户端的工程中再创建一份,接口的包结构一定要和服务端是一模一样的。
工程的整体结构:注意UserService的接口包路径结构是一样的
Hessian使用C/S方式,基于HTTP协议传输,使用二进制进行序列化
先构建一个服务器子工程,将项目打包默认设为war模式,并创建一个webapp目录,并生成WEB.xml配置文件
<dependencies>
<dependency>
<groupId>com.cauchogroupId>
<artifactId>hessianartifactId>
<version>4.0.7version>
dependency>
dependencies>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
public interface UserService {
String sayHello(String name);
}
public class UserServiceImpl implements UserService {
public String sayHello(String name) {
return name+"调用了hessian的服务端服务";
}
}
<servlet>
<servlet-name>HessianServeltservlet-name>
<servlet-class>com.caucho.hessian.server.HessianServletservlet-class>
<init-param>
<param-name>service-classparam-name>
<param-value>com.sjh.service.impl.UserServiceImplparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>HessianServeltservlet-name>
<url-pattern>/hessianServleturl-pattern>
servlet-mapping>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>8888port>
<path>/path>
<uriEncoding>UTF-8uriEncoding>
configuration>
plugin>
plugins>
build>
<dependencies>
<dependency>
<groupId>com.cauchogroupId>
<artifactId>hessianartifactId>
<version>4.0.7version>
dependency>
dependencies>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
public class Client {
public static void main(String[] args) throws MalformedURLException {
//定义远端连接地址 端口号和服务端定义的一样 路径是服务端web.xml文件servlet设置的路径
String url="http://localhost:8888/hessianServlet";
HessianProxyFactory hessianProxyFactory = new HessianProxyFactory();
//第一个参数为服务接口字节码 第二个参数为url地址
UserService userService= (UserService) hessianProxyFactory.create(UserService.class,url);
String s = userService.sayHello("hessian客户端");
System.out.println(s);
}
}
创建一个maven父工程,然后创建服务提供方和服务消费方两个子工程
public interface HelloService {
String sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return name+"调用了RPC的服务!";
}
}
public class URL {
private String hostName;
private Integer port;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
URL url = (URL) o;
return Objects.equals(hostName, url.hostName) &&
Objects.equals(port, url.port);
}
@Override
public int hashCode() {
return Objects.hash(hostName, port);
}
public URL(String hostName, Integer port) {
this.hostName = hostName;
this.port = port;
}
public String getHostName() {
return hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
}
public class NativeRegistry {
//注册中心 第一个参数是接口全类名 Class是实现类
private static Map<String, Map<URL,Class>> registerCenter=new HashMap<>();
//注册服务
public static void register(String interfaceName,URL url,Class implClass){
Map<URL,Class> map=new HashMap<>();
map.put(url,implClass);
registerCenter.put(interfaceName,map);
}
//获取服务信息
public static Class getRegister(String interfaceName,URL url){
Map<URL, Class> map = registerCenter.get(interfaceName);
Class clazz = map.get(url);
return clazz;
}
}
public class ServiceProvider {
public static void main(String[] args) {
//注册服务
URL url = new URL("localhost", 8080);
NativeRegistry.register(HelloService.class.getName(),url, HelloServiceImpl.class);
//启动tomcat 暴露服务
HttpServer httpServer = new HttpServer();
httpServer.start(url.getHostName(),url.getPort());
}
}
服务之前调用的通信协议采用HTTP,所以在服务中启动tomcat暴露服务
添加内置tomcat的依赖
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<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>
创建HttpServer用来模拟tomcat
public class HttpServer {
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=new Connector();
connector.setPort(port);
connector.setURIEncoding("UTF-8");
//构建Engine
StandardEngine engine = new StandardEngine();
engine.setDefaultHost(hostName);
//构建host
StandardHost host = new StandardHost();
host.setName(hostName);
//构建context
String contextPath="";
StandardContext 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);
//设置路径与映射
tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet());
context.addServletMappingDecoded("/*","dispatcher");
try{
tomcat.start();
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 HttpServerHander().handle(req,resp);
}
}
HttpServerHander类
public class HttpServerHander {
public void handle(HttpServletRequest req, HttpServletResponse resp){
try{
//通过请求流获取请求服务调用的参数
ServletInputStream inputStream = req.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Invocation invocation= (Invocation) objectInputStream.readObject();
//从注册中心获取服务列表
Class implClass= NativeRegistry.getRegister(invocation.getInterfaceName(), new URL("localhost",8080));
//调用服务 反射
Method method=implClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
String result= (String) method.invoke(implClass.newInstance(),invocation.getParams());
//返回结构
IOUtils.write(result,resp.getOutputStream(),"UTF-8");
}catch (Exception e){
e.printStackTrace();
}
}
}
Invocation类封装服务消费方调用服务时需要的参数
//封装调用参数
public class Invocation implements Serializable {
private String interfaceName;//接口名
private String methodName;//方法名
private Object[] params;//参数值列表
private Class[] paramTypes;//参数类型列表
public String getInterfaceName() {
return interfaceName;
}
public Invocation(String interfaceName, String methodName, Object[] params, Class[] paramTypes) {
this.interfaceName = interfaceName;
this.methodName = methodName;
this.params = params;
this.paramTypes = paramTypes;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
public Class[] getParamTypes() {
return paramTypes;
}
public void setParamTypes(Class[] paramTypes) {
this.paramTypes = paramTypes;
}
}
public class HttpClient {
public String post(String hostName, int port, Invocation invocation) throws Exception {
//进行连接
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();
//将输入流转换成字符串获取远程调用接口
InputStream is = connection.getInputStream();
return IOUtils.toString(is, StandardCharsets.UTF_8);
}
}
public class ConsumerTest {
public static void main(String[] args) throws Exception{
//调用需要的参数
Invocation invocation = new Invocation(HelloService.class.getName(), "sayHello", new Object[]{"myRPC的客户端"}, new Class[]{String.class});
//调用服务
HttpClient httpClient = new HttpClient();
String result = httpClient.post("localhost", 8080, invocation);
System.out.println(result);
}
}