Dubbo框架
Dubbo框架: 基础
昨天简单介绍了REST的内容;纵观博主的博客,你可能会觉得基础,并且快 ,过几天一个框架,一个技术,最快的就是Mybatis了,两天就介绍完了,真的这么简单吗? 不是的 — 博主目前没有扩展深度,博主只是在扩展广度,也就是先要开拓视野; 你没了解Vue,你可能就不懂为啥要使用RESTful风格,费力不讨好,
Dubbo和Spring Cloud都是分布式的相关框架,何为分布式?这个后面会多次提到。
【这段时间,博主的重心在***上,该阶段还是会快速介绍很多需要使用的框架、中间件 — 可能有的就和之前的JQuery很快带过】
分布式 RPC remote procedure call 远程过程调用
分布式系统是若干独立的系统的集合,但是用户使用的体验就是一个系统; 随着业务的规模的逐步扩大,单台计算机不能承受住【such as 双十一的流量】 – 那就多个一起抗
应用的架构
- 单一架构: 当网站流量很小的时候,将所有的应用业务放在一台服务器上,打包运行即可;开发和部署简单; 但是扩展不容易【 怎么处理日益增长的流量?】维护不容易,性能提升困难
- 垂直应用架构: 将大应用拆分为小应用【按照业务拆分】,根据各个业务的访问频率决定各自业务部署的服务器的数量, 部署容易; 但是可能页面改变,就需要整个项目重新部署,业务和界面没有分隔开,服务器的交互效率问题,还有业务之间的关联关系
- 分布式架构 基于RPC远程过程调用: 将业务拆分,用某种方式实现各个业务模块的远程调用和复用,这是一个好的RPC框架就决定了分布式架构的性能,怎么调用? 服务器挂了? 需要一个框架来管理(对于大流量,大公司各自使用不同的RPC框架来处理,而没有顶住就会宕机)
Dubbo
Dubbo时一个高性能的RPC框架【alibaba开发】,解决分布式的调用的问题
Apache Dubbo是一款高性能的、轻量级的java RPC框架,三大核心功能: 面向接口的远程方法调用,只能的容器和负载均衡、服务器的自动注册和发现; 是一个分布式服务的框架,提供高性能和透明化的RPC远程调用方案、服务治理方案
Dubbo特性: 面向接口代理的高性能RPC调用,服务是接口
Dubbo的注册中心可以管理调度,这里举一个例子【假设有100台服务器,50台处理用户业务,50台处理订单业务,但是上线后发现分配不合理,需要修改比例,就可以借助Dubbo实现】
高性能
高性能是因为Dubbo是一个RPC框架,就是远程过程调用,这里就涉及到了计算机网络的传输
远程方法调用,决定性能的关键因素就是序列化和网络通信:
序列化: java网络中就提到过: 本地的对象要在网络上传输,就要实现Serilizable接口,必须序列化,Dubbo就是采用的效率最高的二进制流进行序列化
网络通信: Http通信需要进行7步走【三次握手,四次挥手】---- 安全; Dubbo使用的是Socket即时通信机制一步到位,可以建立长连接,不用反复连接,直接进行传输
Dubbo Architecture
Container就是spring的容器,所以将provider注册到容器中,provider将服务注册到注册中心中,消费者就可以订阅相关的服务,订阅成功后,注册中心通知消费者订阅成功,同时消费者创建本地代理类来使用provider提供的服务
- Consumer: 调用远程服务的服务消费方:, Consumer在启动的时候,向注册中心订阅自己所需要的服务,服务消费者,从提供者的地址列表中,基于软负载均衡算法,选择一台提供者进行调用,如果调用失败,就换一个
- Monitor: 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心【哨兵】
dubbo支持协议
dubbo支持多种协议,比如dubbo、http、webservice、redis等,推荐使用dubbo协议,默认端口号20880
如果使用dubbo,使用的SSM架构,那么在Spring的配置文件中加入
<dubbo:protocol name="dubbo" port=“20880”/>
不依赖注册中心【直连方式】
在实际的操作中,是可以没有注册中心的,提供者直接提供服务,然后消费者事先知道有哪些服务,直接进行调用即可
首先这里就简单使用Spring创建项目,不创建SpringBoot项目,普通的maven项目即可
要想在项目中使用Dubbo,那么原始的Spring就需要在pom中手动添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.12</version>
</dependency>
当然项目中的容器就是spring的容器,所以需要spring的相关依赖,context和webmvc
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
这里目前来说有点魔幻,mvc中有context,但是不配置context;就会报错jar包冲突.....后面再康康
同时dubbo底层是依赖netty实现的,所以要加入netty的依赖: java.lang.ClassNotFoundException: io.netty.channel.EventLoopGroup
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.32.Final</version>
</dependency>
创建服务提供者的相关对象和方法
package chuan.model;
public class Student {
private int stuNo;
private String stuName;
private String stuClass;
package chuan.service;
import chuan.model.Student;
public interface StudentService {
Student queryStudentByName(String userName);
}
provider提供的服务就是该业务方法,因为Dubbo的特性就是面向接口,接口为最小的粒度【其实就是代理模式,所以最终执行的还是实现类,创建一个实现类】
package chuan.service.impl;
import chuan.model.Student;
import chuan.service.StudentService;
public class StudentServiceImpl implements StudentService {
@Override
public Student queryStudentByName(String userName) {
Student student = new Student();
student.setStuNo(1);
student.setStuName("Cfeng");
student.setStuClass("HC2001");
return student;
}
}
配置provider的核心配置文件【声明、协议、暴露服务 application protocol service 】
provider要将服务给暴露出去,就必须进行配置【最后会分享springBoot的简化版】,配置的内容包括服务提供者的名称applicaiton, 服务的协议protocol,暴露的服务接口service — 如果不使用注册中心,那么就通过registry属性为N/A; 服务包括接口,和其具体的实现类对象
<?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://dubbo.apache.org/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="chuan.service"/>
<dubbo:application name="stuServiceProvider"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="chuan.service.StudentService" ref="studentService" registry="N/A"/>
</beans>
- 接下来就是将这个spring配置文件【容器】在项目启动的时候加载;SSM中就需要在web.xml中手动进行配置,将spring容器挂载到全局的监听器中
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dubbo-stuService-provider.xml</param-value>
</context-param>
这样项目启动的时候就会扫描spring容器并且加载这个provider【provider依赖的Spring容器】
创建消费者consumer【不同的服务器上,port不同】
创建的步骤和提供者类似,首先就是建立一个项目【这是分布式框架,不是在同一个服务器或者项目中】
- 创建pom文件,添加dubbo和spring的依赖
这里需要使用另外一个项目的服务,那么就就必须加入依赖…,所以先对项目进行jar打包到本地仓库,然后导入依赖
//首先将上面的服务提供module打包jar包 【服务提供者没有controller是因为这是分布式架构,服务者就只是分担服务压力而已】
<dependency>
<groupId>Chuan</groupId>
<artifactId>dubbo-001</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
Caused by: java.lang.NoSuchMethodError: ‘org.springframework.context.ApplicationContext org.springframework.web.servlet.handler.AbstractHandlerMapping.obtainApplicationContext()’
这个错误是经典报错,所以记录一下,这是因为依赖项冲突,也就是重复的依赖, 这里很明显,因为导入的前一个项目已经将很多的依赖添加过了,所以直接删除即可;只保留该依赖项
配置dubbo的核心配置文件【声明,引用服务】
这里的配置和上面还是稍微一些不同,上面需要暴露服务,而这里只是需要引用服务
但是还是要声明应用的名称来让dubbo进行标识【至于为什么是application可以看到这是每一个module项目就一个配置,所以当然就是application了,dubbo管理的就是一个个应用,只是这些应用发挥的功能不同,分布式提供服务】
还要使用reference来指明要使用的provider的服务,id指定要使用的实现类对象,这个不是用来标识实现类对象的,是根据version来标识的,id不能重复,只是对于这个引用的服务进行id命名而已、interface指定服务者提供的接口interface,url就是默认的协议dubbo://localhost:20880 , 同时还要表明为直连方式registry为N/A
<?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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="serviceConsumer"/>
<dubbo:reference id="studentService" interface="chuan.service.StudentService" registry="N/A" url="dubbo://localhost:20880"/>
</beans>
这里的springMVC的配置文件懒得新开了,就写一起了,反正也是父子关系【SpringBoot也没有分开了】,就一个组件扫描器和注解驱动
id就是标识这个引用的,就类似之前的bean,这里因为只有一个实现类对象,所以引用的时候就会直接调用Dubbo中的service实现类对象,如果有多个就通过vetrsion进行标识
所以就简单两行即可
<context:component-scan base-package="Clei.controller"/>
<mvc:annotation-driven/>
- 编写controller来使用这个服务;【需要在web.xml中配置中央调度器】
package clei.controller;
import chuan.model.Student;
import chuan.service.StudentService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class stuController {
@Resource
private StudentService studentService;
@RequestMapping("/user")
public Student queryStudent() {
return studentService.queryStudentByName("Clei");
}
}
配置中央调度器,直接通过init-param来指定两个文件
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dubbo-consumer.xml,classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
java.lang.IllegalStateException: Serialized class chuan.model.Student must implement java.io.Serializable
这里很明显,因为dubbo对象的传输依赖的是序列化,实体类必须实现Seriali接口
public class Student implements Serializable {
这里可能会有疑问?
在Consumer中直接使用接口实现类不就行了?
public Student queryStudent() {
StudentService studentService = new StudentServiceImpl();
return studentService.queryStudentByName("Clei");
}
但是这样做和Dubbo就没有关系了,这就是一个简单的包依赖和调用
接口工程
所以Dubbo就要求工程中有一个接口工程: 就是一个maven java工程;要求接口工程中存放的内容:
- 对外暴露的服务接口service的interface
- 实体bean对象
这样子中间有了接口工程来中转,就不需要再导入依赖,并且不会产生为何不直接使用对象的方法的困惑了,因为届时就不能这样操作了
Dubbo服务化
分包【中间接口module】
dubbo中建议将所有的服务接口、服务模型放在公共包中【业务接口工程】
Dubbo标签
Dubbo中常用的标签,分为3个类别: 公用标签、服务提供者标签和服务消费者标签
公用标签
application: 指定应用module的名称【dubbo的唯一标识】 ----- 配置应用信息
registry: 指定注册中心 ----- 配置注册中心
<dubbo:applicaiton name="服务名称"/>
<dubbo:registry address="ip:port" protocol="协议"/>
粒度
服务接口的粒度要尽可能大,每个服务方法中应该代表一个功能,而不是一个功能的步骤
服务接口建议是按照业务场景为单位划分,并且要对相近的业务进行抽象,防止接口数量爆满
不适用过于抽象的通用接口,比如Map query(Map) 这样的接口没有具体的意义,不方便维护
稳定性
Dubbo具有高稳定性,当服务提供者宕机了,那么消费者就是不能正常进行访问,只有提供者状态正常,消费者才能够正常使用服务
配置属性
在服务提供者配置访问参数,因为服务提供者更了解服务的各种参数
consumer的check属性
dubbo缺省,启动的时候会自动检查服务是否可用,不可用会抛出异常,阻止spring容器的初始化,也就是应用服务的属性;一般在开发的时候关闭自动检测
关闭某个服务的自动检查
<dubbo:refrence interface="xxxx" id="" url ="" check= "false"/>
关闭注册中心启动的自动检查
<dubbo:registry adress="" check="false"/>
重试次数retries
消费者访问提供者,如果访问失败,就会尝试访问其他的服务器,但是重试的延迟更大,访问时间变长,用户体验不好,可以通过retries设置
<dubbo:service interface="" ref vresion retries="2"/> 这里可以不用设置
超时时间timout
重试次数有点不方便,可以直接设置超时时间,最长加载多少毫秒就失败
<dubbo:service interface="" ref vresion timout="2000"/>
版本【同一个接口的不同实现类】 Service的version属性
每个接口都应定义版本号,区分统一接口的不同实现;
<dubbo:service interface= "XXX" version="1.0"/> 不同的ref
项目改造–直边方式【使用接口工程做中转】
这里新建一个普通的maven项目,也就是一个quickstart的项目,这种方式主要的就是将公共的内容剥离到接口工程中;也就是实体bean和公共的业务接口
首先就先表明和上面的不同的地方:
- 创建了新的module—接口工程 【将之前的provider中的实体bean和业务接口放入】
- provider只需要对接口工程的接口进行实现【配置文件的service的interface指定】
- 接口工程的pom.xml只需要dubbo的依赖和netty-all依赖
- provider和consumer都依赖接口工程,consumer中的配置文件和controller的接口引入修改位置
provider
controller的服务的实现类
package chuan.service.impl;
import Csheng.model.Student;
import Csheng.service.StudentService;
import org.springframework.stereotype.Service;
@Service("studentService")
public class StudentServiceImpl implements StudentService {
@Override
public Student queryStudentByName(String userName) {
Student student = new Student();
student.setStuNo(1);
student.setStuName("Cfeng++");
student.setStuClass("HC2001");
return student;
}
}
配置文件【声明,协议、暴露服务】
<context:component-scan base-package="chuan.service"/>
<dubbo:application name="stuServiceProvider"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="Csheng.service.StudentService" ref="studentService" registry="N/A"/>
然后在web.xml中使用监听器将spring容器创建即可;容器中有service的实现类对象;Dubbo协议传输该对象
interface proj
实体bean【一定要序列化】
package Csheng.model;
import java.io.Serializable;
public class Student implements Serializable {
private int stuNo;
private String stuName;
private String stuClass;
服务接口【适当的抽象化】
package Csheng.service;
import Csheng.model.Student;
public interface StudentService {
Student queryStudentByName(String userName);
}
consumer
pom中要添加接口工程的依赖,因为要订阅服务【接口】;可能使用到其中的实体bean
controller示例:
package clei.controller;
import Csheng.model.Student;
import Csheng.service.StudentService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
@Controller
public class stuController {
@Resource
private StudentService studentService;
@RequestMapping("/hello")
@ResponseBody
public Student queryStudent() {
System.out.println("nihao");
return studentService.queryStudentByName("Clei");
}
}
配置文件【 web.xml配置中央处理器就不演示了】
<dubbo:application name="serviceConsumer"/>
<dubbo:reference id="studentService" interface="Csheng.service.StudentService" registry="N/A" url="dubbo://localhost:20880"/>
这里使用的service对象就是之前的provider的spring容器中的,遵循dubbo协议,直接使用这个对象
这样运行provider和consumer就可以正常进行显示;需要注意的是: 使用接口工程就解决了之前的困惑,因为consumer不依赖provider,那么就不能够直接new provider中的接口实现类; 必须通过Dubbo的方式来使用这个对象
监控中心
Dubbo的使用,只需要有消费者、提供者和注册中心三者即可,当时不能看到有哪些消费者和提供者,为了更好发现问题,引入dubbo-admin,可以对…进行管理
dubbo-admin — 配合注册中心图形化管理服务
图形化的服务管理页面,安装需要指定注册中心地址,就可以从注册中心获取到所有的提供者和消费者进行配置管理
注册中心Zookeeper
上面的接口工程就已经稍微优化了直连的方式,但是对于服务提供方,需要发布服务,由于应用系统的复杂性,服务的数量和类型都是不断膨胀的,接口工程不方便管理庞大的服务;服务消费方只是需要使用到需要使用到的服务;对于提供方和消费方来说,可能既要提供服务,也要消费服务
所以需要将服务统一管理起来,可以有效优化内部应用对服务发布和使用的管理,服务注册中心可以通过特定的i下而已完成服务对外的统一,Dubbo提供的注册中心有: Multicast、Redis、Simple、zookeeper;推荐使用Zookeeper是一个树形的目录服务,支持变更推送,适合Dubbo服务的注册中心【就是一个记录服务的module】
可以看到最上面的就是Dubbo根结点,下面就是一个个服务【接口】,接口下面就是该服务的消费者和提供者,在下面的叶子结点就是provider和consumer的url
下载配置Zookeeper
进入官网下载即可: Apache ZooKeeper 【ZooKeeper 动物园管理者】 — 动物就是服务【接口】
需要注意的是,从3.5版本之后,普通的tar.gz只是源码,不能直接运行,所以需要下载带有bin的;这里给一个直接下载的地址: Index of /dist/zookeeper/zookeeper-3.8.0 (apache.org)
下载之后解压到相关的目录,接下来就是修改一个配置文件,将zoo-cfg复制,创建修改
dataDir=D:/zookeeper/apache-zookeeper-3.8.0/data #创建了一个data目录
clientPort=2181
admin.serverPort = 8888 #设置运行的程序的端口号,默认是8080,修改为8888
点击bin下面的server.cmd就可以直接运行
2022-04-07 22:06:11,895 [myid:] - INFO [main:o.a.z.s.NIOServerCnxnFactory@660] - binding to port 0.0.0.0/0.0.0.0:2181
使用zookeeper示例
之前我们已经使用了一个接口工程来优化直连的方式,但是这样子还是不方便直观的管理规模庞大的provider和consumer;这里就可以引入zookeeper;还是先来说相比之前的接口直边方式的不同之处
- 引入zookeeper的依赖curator,这里就将依赖放在接口工程中
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
- provider和consumer的配置文件都将对应的registry属性删掉,然后添加一个registry结点,指定address
<dubbo:registry address="zookeeper://localhost:2181" //这里的端口就是之前占用的端口
在linux中进行部署的区别就是不同的ip地址即可
所以没有太大的变化;在开启的时候,将不同的module运行起来并且将zookeeper运行起来
版本号version — 同一接口不同实现
在实际开发中可能出现的问题就是,版本的迭代,因为面向接口编程,然后对于同一个服务接口,可能有了更好的实现类,但是按照之前的接口工程,在暴露服务的时候interface指向接口,ref指向实现类; 只是暴露了一个服务;现在版本迭代,想要多个版本的服务都支持,那么就要使用暴露多个服务
这里和上面相比的不同点
- provider配置文件中要暴露多个服务【不同的实现类】,增加一个verison属性,ref引用不同的对象注入到对应的version中
- consumer的配置文件中,可以同时接收同一接口的不同的实现服务;通过id标识当前应用的服务,version指定版本,这样就可以通过dubbo将对应的Service对象传输到Consumer中使用
|