dubbo学习笔记(小白,有错请指出谢谢)
简述
dubbo(阿里巴巴出品,之前开源,阿里巴巴不提供维护,但后来由于使用广泛,阿里又重新开始维护)是一个用来跨网络访问另外一台服务器的技术(RPC),在我看来,其实是对httpClient的一种封装,是调用者可以更加快捷方便,常用于微服务,dubbo中有服务的提供者、服务的消费者,其中消费者是通过调用接口,来获得服务,更大程度的解藕,服务的提供者类似于ssm框架中的serviceImpl,接口类似于service,消费者类似于controller。
1、各点说明:
- Provider: 服务的提供者(serviceImpl)。
- Consumer: 消费者(controller)。
- Registry: 注册中心(中间站)。
- Monitor: 监控中心(统计服务调用次数和时间)。
- Container: 服务运行容器。
2、调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接(http中/1.1以后使用的,用于保持连接性,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,响应头中会加入Connection:keep-alive)推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法(轮询,随机,哈希,最快响应,最小并发),选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
大概了解了dubbo中几个关键的组件和调用过程后,来通过手写一个dubbo进行更深入了解
手写dubbo
Client为服务的消费者
infterface为中间的接口
Server为服务的提供者
package com.jt.cjt.service;
public interface CartService {
public String findCartById(Long id);
}
package com.jt.cjt.service;
public class CartServiceImpl implements CartService {
@Override
public String findCartById(Long id) {
return "cart";
}
}
package com.jt.cjt.service;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
public class MyDubboServer {
public static void main(String[] args) {
try {
//监听9000端口号
ServerSocket serverSocket = new ServerSocket(9000);
System.out.println("启动服务器");
while(true) {
//接收用户的请求
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
//读取接口名
String interfaceName = objectInputStream.readUTF();
//读取方法名
String methodName = objectInputStream.readUTF();
//读取参数类型
Class[] type = (Class[])objectInputStream.readObject();
//读取参数
Object[] p = (Object[])objectInputStream.readObject();
//通过反射执行方法
//根据接口找到实现类
Class implClassInfo = CartServiceImpl.class;
//通过反射创建对象
Object implObject = implClassInfo.newInstance();
//通过反射执行方法
Method method = implClassInfo.getMethod(methodName, type);
Object result = method.invoke(implObject, p);
//把执行结果返回给客户端
OutputStream outOutputStream = socket.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outOutputStream);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
package com.jt.cjt.myDubboClient;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import com.jt.cjt.service.CartService;
public class CartController {
public static void main(String[] args) {
//1,得到动态代理对象
Object proxyObject=getProxyObject(CartService.class);
//2,通过动态代理对象调用目标方法
CartService cartService=(CartService)proxyObject;
cartService.findCartById(100L);
//3,调了目标方法,java虚拟机会执行invoke()
}
//内部类
static class MyInvocationHandler implements InvocationHandler{
String interfaceName;
public MyInvocationHandler(String interfaceName) {
super();
this.interfaceName = interfaceName;
}
//由java虚拟机自动调用invoke
public Object invoke(Object proxy,Method method, Object[] args) throws Throwable {
//spring框架调@before的方法,调method
//System.out.println(method.getName());
try {
//建立和服务器连接
Socket socket = new Socket("127.0.0.1",9000);
//发送接口名
OutputStream outputStream = socket.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeUTF(interfaceName);
//发送方法名
objectOutputStream.writeUTF(method.getName());
//发送参数类型
objectOutputStream.writeObject(method.getParameterTypes());
//发送参数
objectOutputStream.writeObject(args);
//接收服务器结果
InputStream inputStream = socket.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Object serverReturnResult = objectInputStream.readObject();
System.out.println(serverReturnResult);
} catch (Exception e) {
e.printStackTrace();
}
//System.out.println(args[0]);
//联网发送方法名,参数
return null;
}
}
//返回代理对象
static Object getProxyObject(Class interfaceInfo) {
//类加载器作用加载类
ClassLoader classLoader = interfaceInfo.getClassLoader();
//动态代理对象根据动态类创建
//普通类 在虚拟机看来就是字符串class 类名{方法}
//动态类有一个接口,通过反射得到接口中的方法 class 类名{findCartById()}
//放的类信息,有方法信息,根据方法创建动态类
Class[] methodInfo={interfaceInfo};
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(interfaceInfo.getName());
//java中实现动态代理是用proxy.newProxyInstance
return Proxy.newProxyInstance(classLoader,methodInfo, myInvocationHandler);
}
}
调用顺序
getProxyObject(CartService.class)获得CartService接口的代理对象–>调用代理对象接口中的方法,根据动态代理的执行顺序,会在执行方法前,先执行invoke方法,其中将接口名,方法名,参数类型,参数发送过去,在server中接收接口名,方法名,参数类型,参数,然后通过反射创建对象,执行方法,并将结果返回给server,接着server
接收返回结果
dubbo中的注册中心
注册中心有很多,在这里我们来说下zookeeper
zookeeper官方介绍
- ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
- ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户
- ZooKeeper包含一个简单的原语集, [1] 提供Java和C的接口。
- ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在zookeeper-3.4.3\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本
在这里不做深入讨论,简单描述下,在配置好zookeeper后(我是在linux上部署的,具体部署步骤可自行百度),通过客户端可进行查看,其中有服务的提供者信息,服务的消费者信息,并且根据权重自行进行轮询,可手动更改权重大小,来使请求更多的发送到配置较好的服务器上,zookeeper在本地会有缓存,所以当zookeeper服务宕机后,消费者仍可以获得提供者的服务,zookeeper是cp原则(CAP原则,分布式设计原则,c为一致性,a为可用性,p为分区容错性,其中p是必须实现的原则),这点跟eureka不同,eureka是ap原则(具体区别将在后续中进行讨论),zookeeper是基于心跳检测机制来查看服务是否还在运行,由于它的一致性,所以当有服务宕机时,在列表中会立即删除该服务
dubbo+zookeeper整合
首先先导入dubbo和zookeeper所需要的jar包(这里不做展示)
- consumer:消费者
- interface:接口
- provider1:提供者1
- provider2:提供者2
package com.jt.cjt.cart.service;
public interface CartService {
public String findCartById(Long userId);
}
package com.jt.cjt.cart.service;
public class CartServiceImpl implements CartService{
@Override
public String findCartById(Long userId) {
return "第一个提供着:cart";
}
}
package com.jt.cjt.dubboProvider1;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
try {
System.out.println("提供者1开始");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-provider.xml");
context.start();
System.out.println("提供者1启动");
while(true)
{
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 微服务提供者配置文件,作用是把提供者的端口,实现类信息发给服务注册中心zookeeper -->
<!-- 1,设置应用(微服务)名称 -->
<dubbo:application name="provider1-of-cart"/>
<!-- 2,设置zookeeper的ip地址和端口号 -->
<dubbo:registry timeout="超时时间" address="zookeeper://IP地址:端口号">
</dubbo:registry>
<!-- 3,设置微服务的端口号 -->
<dubbo:protocol port="端口号" name="dubbo"></dubbo:protocol>
<!-- 4,设置实现类 -->
<bean id="CartServiceImpl" class="com.jt.cjt.cart.service.CartServiceImpl">
</bean>
<!-- 5,设置客户端访问服务的地址,地址是接口名 -->
<dubbo:service timeout="900000" interface="com.jt.cjt.cart.service.CartService" ref="CartServiceImpl">
</dubbo:service>
</beans>
package com.jt.cjt.dubboConsumer;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.jt.cjt.cart.service.CartService;
public class CartController {
public static void main(String[] args) {
try {
//启动spring框架
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("applicationContext-consumer.xml");
//从spring框架取cartService对象
//以前是从spring容器中取对象,对象是当前项目实现类的对象
//用了dubbo,加了<refrence 指定了接口名CartService>
//1,连接zookeeper,得到提供者信息
//2,网络连接一个提供者,由提供者执行实现类,返回结果
CartService cartService = (CartService)context.getBean("cartService");
//调用提供者
while(true)
{
String cart=cartService.findCartById(90L);
System.out.println("消费者:"+cart);
Thread.currentThread().sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 1,设置应用(微服务)名称 -->
<dubbo:application name="consumer-of-cart"/>
<!-- 2,设置zookeeper的ip地址和端口号 -->
<dubbo:registry timeout="900000" address="zookeeper://ip:port">
</dubbo:registry>
<!-- 3,那个接口的对象由dubbo管理 check=false 消费者启动时不会检查提供者是否启动 -->
<!-- cartController @autowired CartService cartservice -->
<dubbo:reference timeout="90000" check="false" id="cartService" interface="com.jt.cjt.cart.service.CartService">
</dubbo:reference>
</beans>
下面为duubo和其他技术整合的示意图
duubo+zookeeper
dubbo+zookeeper+nginx+mycat
dubbo+zookeeper+nginx+redis+mycat