分布式RPC框架Apache Dubbo

1,架构的演进
1.1 单体架构
全部功能集中在一个项目内(All in one)。
1.2 垂直架构
按照业务进行切割,形成小的单体项目。
1.3 SOA结构
将重复功能或模块抽取成组件的形式,对外提供服务,在项目与服务之间使用ESB(企业服务总线)的形式作为通信的桥梁。
1.4 微服务结构
将系统服务层完全独立出来,抽取为一个一个的微服务。
抽取的粒度更细,遵循单一原则。
采用轻量级框架协议传输
2,Dubbo
2.1 概述
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。
rpc : 远程过程调用。 remote procedure call
进行远程方法调用就像是调用本地方法
需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程。
2.2 Dubbo架构

3,zookeeper
3.1 概述
zookeeper是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
文件系统:
Zookeeper维护一个类似文件系统的数据结构:

每个子目录项都被称作为 znode(目录节点),和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。
3.2 安装zookeeper
第一步:安装 jdk(略)
1,安装jdk,配置环境变量
vim /etc/profile

#set java_home
JAVA_HOME=jdk的安装目录
PATH= J A V A H O M E / b i n : JAVA_HOME/bin: PATH
export JAVA_HOME PATH
第二步:把 zookeeper 的压缩包(zookeeper-3.4.6.tar.gz)上传到 linux 系统
将zookeeper的安装包上传到虚拟机上
sftp:
是一种安全的文件传输协议;
(在CRT软件中)
使用alt + p 组合键打开sftp窗口
使用put 命令上传
使用get命令下载
1.默认上传内容是上传到 linux服务器的当前用户的工作目录中;
2.默认下载内容是下载到 windows本地的 文档目录中
3. 可以通过(cd 目录)修改linux服务器的目录;
4. 可以通过(lcd 目录)修改windows的目录
第三步:解压缩压缩包
​ tar -zxvf zookeeper-3.4.6.tar.gz
如何解开 .tar.gz文件?
tar -zxvf 将要被解压的.tar.gz文件 -C 指定把内容解压到哪个目录下;
第四步:进入zookeeper-3.4.6目录,创建data目录
​ mkdir data
第五步:进入conf目录 ,把zoo_sample.cfg 改名为zoo.cfg
​ cd conf
​ mv zoo_sample.cfg zoo.cfg
第六步:打开zoo.cfg文件, 修改data属性:dataDir=/root/zookeeper-3.4.6/data
3.3 启动zookeeper
进入Zookeeper的bin目录,启动服务命令
启动zookeeper
./zkServer.sh start

关闭zookeeper服务
./zkServer.sh stop

查询zookeeper的状态
./zkServer.sh status

查询进程:
jps : 只能查看java进程

配置zookeeper的环境变量:(任意目录使用)
vim /etc/profile
#set zookeeper home
ZK_HOME=/usr/local/zookeeper-3.4.6
PATH= P A T H : PATH: ZK_HOME/bin
export ZK_HOME PATH
4,Dubbo使用
4.1 服务提供方
1,创建项目(web项目) dubbodemo_provider
2,引入坐标

com.alibaba
dubbo
2.8.4


org.apache.zookeeper
zookeeper
3.4.7


com.github.sgroschupf
zkclient
0.1

3,编写接口
package com.itheima.service;

public interface HelloService {
public String sayHello(String name);
}
4,编写实现类
package com.itheima.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.service.HelloService;
import org.springframework.transaction.annotation.Transactional;

@Service //发布服务必须使用Dubbo提供的Service注解
/*
service注解的作用:
1,将标注的类的对象交给spring IOC容器进行管理
2,发布服务
*/
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "8083 hello " + name;
}
}
5,编写配置文件 (applicationContext-service.xml)

<dubbo:application name=“dubbodemo_provider”></dubbo:application>

<dubbo:registry address=“zookeeper://192.168.25.128:2181”></dubbo:registry>

<dubbo:protocol name=“dubbo” port=“20881”></dubbo:protocol>

<dubbo:annotation package=“com.itheima.service.impl”></dubbo:annotation>
6,web.xml

contextConfigLocation
classpath:applicationContext*.xml


org.springframework.web.context.ContextLoaderListener

4.2 服务消费方
1,创建web项目(Controller层)
2,引入坐标

com.alibaba dubbo 2.8.4 org.apache.zookeeper zookeeper 3.4.7 com.github.sgroschupf zkclient 0.1 3,编写 service接口,要和服务提供方的接口一样 package com.itheima.service; ​ public interface HelloService { public String sayHello(String name); } 4,编写controller package com.itheima.controller; ​ import com.alibaba.dubbo.config.annotation.Reference; import com.itheima.service.HelloService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; ​ @Controller @RequestMapping("/hello") public class HelloController {

@Reference //查找服务(远程注入)
private HelloService helloService;

@RequestMapping("/sayHello")
@ResponseBody
public String sayHello(String name){
return helloService.sayHello(name);
}
}
5,配置文件 (applicationContext-web.xml)
<dubbo:application name=“dubbodemo_consumer”></dubbo:application>
<dubbo:registry address=“zookeeper://192.168.25.128:2181”></dubbo:registry>
<dubbo:annotation package=“com.itheima.controller”></dubbo:annotation>
6,web.xml

springmvc
org.springframework.web.servlet.DispatcherServlet

contextConfigLocation classpath:applicationContext-web.xml 1 springmvc *.do 思考一:上面的Dubbo入门案例中我们是将HelloService接口从服务提供者工程(dubbodemo_provider)复制到服务消费者工程(dubbodemo_consumer)中,这种做法是否合适?还有没有更好的方式? 答:这种做法显然是不好的,同一个接口被复制了两份,不利于后期维护。更好的方式是单独创建一个maven工程,将此接口创建在这个maven工程中。需要依赖此接口的工程只需要在自己工程的pom.xml文件中引入maven坐标即可。 思考二:在服务消费者工程(dubbodemo_consumer)中只是引用了HelloService接口,并没有提供实现类,Dubbo是如何做到远程调用的? 答:Dubbo底层是基于代理技术为HelloService接口创建代理对象,远程调用是通过此代理对象完成的。可以通过开发工具的debug功能查看此代理对象的内部结构。另外,Dubbo实现网络传输底层是基于Netty框架完成的。 思考三:上面的Dubbo入门案例中我们使用Zookeeper作为服务注册中心,服务提供者需要将自己的服务信息注册到Zookeeper,服务消费者需要从Zookeeper订阅自己所需要的服务,此时Zookeeper服务就变得非常重要了,那如何防止Zookeeper单点故障呢? 答:Zookeeper其实是支持集群模式的,可以配置Zookeeper集群来达到Zookeeper服务的高可用,防止出现单点故障。
  1. Dubbo管理控制台
    我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。我们可以通过部署一个管理中心来实现。其实管理中心就是一个web应用,部署到tomcat即可。
    5.1 安装
    安装步骤:
    (1)将资料中的dubbo-admin-2.6.0.war文件复制到tomcat的webapps目录下
    (2)启动tomcat,此war文件会自动解压
    (3)修改WEB-INF下的dubbo.properties文件,注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号
    dubbo.registry.address=zookeeper://192.168.134.129:2181
    ​ dubbo.admin.root.password=root
    ​ dubbo.admin.guest.password=guest
    (4)重启tomcat
    5.2 使用
    操作步骤:
    (1)访问http://localhost:8080/dubbo-admin-2.6.0/,输入用户名(root)和密码(root)

  2. Dubbo相关配置说明
    6.1 包扫描
    <dubbo:annotation package=“com.itheima.service” />
    服务提供者和服务消费者都需要配置,表示包扫描,作用是扫描指定包(包括子包)下的类。
    如果不使用包扫描,也可以通过如下配置的方式来发布服务:

    <dubbo:service interface=“com.itheima.api.HelloService” ref=“helloService” />
    作为服务消费者,可以通过如下配置来引用服务:

<dubbo:reference id=“helloService” interface=“com.itheima.api.HelloService” />
上面这种方式发布和引用服务,一个配置项(dubbo:service、dubbo:reference)只能发布或者引用一个服务,如果有多个服务,这种方式就比较繁琐了。推荐使用包扫描方式。
6.2 协议
<dubbo:protocol name=“dubbo” port=“20880”/>
一般在服务提供者一方配置,可以指定使用的协议名称和端口号。
其中Dubbo支持的协议有:dubbo、rmi、hessian、http、webservice、rest、redis等。
推荐使用的是dubbo协议。
dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
也可以在同一个工程中配置多个协议,不同服务可以使用不同的协议,例如:

<dubbo:protocol name=“dubbo” port=“20880” />
<dubbo:protocol name=“rmi” port=“1099” />

<dubbo:service interface=“com.itheima.api.HelloService” ref=“helloService” protocol=“dubbo” />

<dubbo:service interface=“com.itheima.api.DemoService” ref=“demoService” protocol=“rmi” />
6.3 启动时检查
<dubbo:consumer check=“false”/>
上面这个配置需要配置在服务消费者一方,如果不配置默认check值为true。Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题。可以通过将check值改为false来关闭检查。
建议在开发阶段将check值设置为false,在生产环境下改为true。
6.4 负载均衡
负载均衡(Load Balance):其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务。
在集群负载均衡时,Dubbo 提供了多种均衡策略(包括随机、轮询、最少活跃调用数、一致性Hash),缺省为random随机调用。
配置负载均衡策略,既可以在服务提供者一方配置,也可以在服务消费者一方配置,如下:
@Controller
@RequestMapping("/demo")
public class HelloController {
//在服务消费者一方配置负载均衡策略
@Reference(check = false,loadbalance = “random”)
private HelloService helloService;

@RequestMapping("/hello")
@ResponseBody
public String getName(String name){
//远程调用
String result = helloService.sayHello(name);
System.out.println(result);
return result;
}
}
//在服务提供者一方配置负载均衡
@Service(loadbalance = “random”)
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}
可以通过启动多个服务提供者来观察Dubbo负载均衡效果。
注意:因为我们是在一台机器上启动多个服务提供者,所以需要修改tomcat的端口号和Dubbo服务的端口号来防止端口冲突。
在实际生产环境中,多个服务提供者是分别部署在不同的机器上,所以不存在端口冲突问题。
7. 解决Dubbo无法发布被事务代理的Service问题
前面我们已经完成了Dubbo的入门案例,通过入门案例我们可以看到通过Dubbo提供的标签配置就可以进行包扫描,扫描到@Service注解的类就可以被发布为服务。
但是我们如果在服务提供者类上加入@Transactional事务控制注解后,服务就发布不成功了。原因是事务控制的底层原理是为服务提供者类创建代理对象,而默认情况下Spring是基于JDK动态代理方式创建代理对象,而此代理对象的完整类名为com.sun.proxy.$Proxy42(最后两位数字不是固定的),导致Dubbo在发布服务前进行包匹配时无法完成匹配,进而没有进行服务的发布。
7.1 问题展示
在入门案例的服务提供者dubbodemo_provider工程基础上进行展示
操作步骤:
(1)在pom.xml文件中增加maven坐标

mysql
mysql-connector-java
5.1.47


com.alibaba
druid
1.1.6


org.mybatis
mybatis-spring
1.3.2

(2)在applicationContext-service.xml配置文件中加入数据源、事务管理器、开启事务注解的相关配置

上面连接的数据库可以自行创建 (3)在HelloServiceImpl类上加入@Transactional注解 (4)启动服务提供者和服务消费者,并访问

上面的错误为没有可用的服务提供者
查看dubbo管理控制台发现服务并没有发布,如下:

可以通过断点调试的方式查看Dubbo执行过程,Dubbo通过AnnotationBean的postProcessAfterInitialization方法进行处理

7.2 解决方案
通过上面的断点调试可以看到,在HelloServiceImpl类上加入事务注解后,Spring会为此类基于JDK动态代理技术创建代理对象,创建的代理对象完整类名为com.sun.proxy.$Proxy35,导致Dubbo在进行包匹配时没有成功(因为我们在发布服务时扫描的包为com.itheima.service),所以后面真正发布服务的代码没有执行。
解决方式操作步骤:
(1)修改applicationContext-service.xml配置文件,开启事务控制注解支持时指定proxy-target-class属性,值为true。其作用是使用cglib代理方式为Service类创建代理对象

<tx:annotation-driven transaction-manager=“transactionManager” proxy-target-class=“true”/>

(2)修改HelloServiceImpl类,在Service注解中加入interfaceClass属性,值为HelloService.class,作用是指定服务的接口类型
@Service(interfaceClass = HelloService.class)
@Transactional
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}
此处也是必须要修改的,否则会导致发布的服务接口为SpringProxy,而不是HelloService接口,如下:


单体架构:
在这里插入图片描述
配置说明:

在这里插入图片描述

发布了71 篇原创文章 · 获赞 1 · 访问量 1161

猜你喜欢

转载自blog.csdn.net/weixin_44993313/article/details/103570685