教自己学Dubbo

2021-01-19 11:23:32 浏览数 (1)

本文章中的Demo案例已经同步至gitee,供各位参考。

Demo地址:https://gitee.com/lemon_ant/dubbo-demo.git

zookeeper&dubbo简介

架构

节点角色说明

节点

角色说明

Provider

暴露服务的服务提供方

Consumer

调用远程服务的服务消费方

Registry

服务注册与发现的注册中心

Monitor

统计服务的调用次数和调用时间的监控中心

Container

服务运行容器

调用关系说明

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。

zookeeper安装

下载zookeeper.tar.gz

https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz

上传到centos上、

解压使用命令解压在/usr/local目录下

代码语言:javascript复制
tar -zxvf zookeeper.tar.gz  -C /usr/local   

zookeeper目录下创建data文件夹存放数据,修改配置conf下的zoo_sample.cfg名为zoo.cfg(默认加载zoo.cfg)。

修改zoo.cfgdataDir参数为刚才创建的目录,保存退出。

zookeeper启动

启动的前提是需要安装jdk

作者:疯子加天才 标题:centos安装jdk1.8的三种方法 文章链接:https://www.cnblogs.com/telwanggs/p/11546751.html

在zookeeper目录下使用 ./zkServer.sh start 启动

./zkServer.sh status 查看运行状态 standalone 代表单机版

./zkServer.sh stop 停止

dubbo-admin可视化界面

dubbo-admin:https://github.com/apache/incubator-dubbo/releases/tag/dubbo-2.6.0

安装dubbo-admin.war放在本机tomcat中的wabapps目录,启动tomcat,待dubbo-admin.war解压后,进入目录dubbo-admin-2.6.0WEB-INF找到dubbo.properties配置文件,修改配置为zookeeper的IP地址,重新启动tomcat。

浏览器访问地址:http://127.0.0.1:8080/dubbo-admin-2.6.0 账号信息:root root

里面就是所有信息home > governance > applications能够看到注册上去的接口信息。

Dubbo服务提供方

创建一个空的maven项目,新建modul作为服务提供方。dubbo_provider。

导入pom文件

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.hkrt</groupId>
  <artifactId>dubbo_provider</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>dubbo Maven Webapp</name>
  <url>http://www.example.com</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
      <spring.version>5.0.5.RELEASE</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-framework</artifactId>
      <version>2.8.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-recipes</artifactId>
      <version>2.8.0</version>
    </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>${spring.version}</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
          <version>${spring.version}</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>${spring.version}</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>${spring.version}</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
          <version>${spring.version}</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jms</artifactId>
          <version>${spring.version}</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context-support</artifactId>
          <version>${spring.version}</version>
      </dependency>
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>dubbo</artifactId>
          <version>2.6.2</version>
      </dependency>
      <dependency>
          <groupId>org.apache.zookeeper</groupId>
          <artifactId>zookeeper</artifactId>
          <version>3.4.9</version>
      </dependency>
      <dependency>
          <groupId>com.github.sgroschupf</groupId>
          <artifactId>zkclient</artifactId>
          <version>0.1</version>
      </dependency>
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>1.2.47</version>
      </dependency>
  </dependencies>

  <build>
    <finalName>dubbo</finalName>
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
          <plugin>
              <groupId>org.apache.tomcat.maven</groupId>
              <artifactId>tomcat7-maven-plugin</artifactId>
              <configuration>
                  <port>8081</port>
                  <path>/</path>
              </configuration>
          </plugin>
      </plugins>
  </build>
</project>

创建service层接口和实现类

代码语言:javascript复制
import com.alibaba.dubbo.config.annotation.Service;
@Service //发布服务必须使用Dubbo提供的service注解
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
        return "hello";
    }
}

web.xml

代码语言:javascript复制
<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext*.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

applicationContext-service.xml

代码语言:javascript复制
<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" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--每个Dubbo应用(服务提供方和消费方)都需要指定一个唯一的名称-->
    <dubbo:application name="dubbo_provider"></dubbo:application>

    <!--指定Dubbo的注册中心,部署zookeeper的地方-->
    <dubbo:registry address="zookeeper://IP:2181"></dubbo:registry>

    <!--指定协议和端口,只在提供方配置-->
    <dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
    <!--<dubbo:service interface="com.hkrt.service.HelloService" ref="helloService" protocol="dubbo" />-->
    
    <!--1、注解形式:指定包扫描路径,用于发布dubbo服务-->
    <dubbo:annotation package="com.hkrt.service"></dubbo:annotation>
</beans>

通过maven插件启动服务provider,查看dubbo-admin发现已经存在HelloService的相关信息。

Dubbo服务消费方

新建modul作为服务提供方。dubbo_consumer

同样和provider相同的pom文件依赖。

创建一个和服务提供方相同的包package,创建controller

代码语言:javascript复制
import com.alibaba.dubbo.config.annotation.Reference;

@RestController
@RequestMapping("/hello")
public class HelloController {

    @Reference   //订阅zookeeper,查找相关服务
    private HelloService helloService;

    @ResponseBody
    @RequestMapping("/say")
    public String sayHello(String name){
        System.out.println("响应成功");
        return helloService.sayHello(name);
    }
}

HelloService.class

代码语言:javascript复制
public interface HelloService {
    public String sayHello(String name);
}

web.xml

代码语言:javascript复制
<web-app>
  <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*:springmvc-servlet.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>
</web-app>

springmvc-servlet.xml

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 自动扫描包,实现支持注解的IOC -->
    <context:component-scan base-package="com" />

    <!-- Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler />

    <!-- 支持mvc注解驱动 -->
    <mvc:annotation-driven />

    <!--每个Dubbo应用(服务提供方和消费方)都需要指定一个唯一的名称-->
    <dubbo:application name="dubbo_customer" />

    <!--指定Dubbo的注册中心,部署zookeeper的地方-->
    <dubbo:registry  address="zookeeper://IP:2181" />

    <!--1、注解形式:指定包扫描路径,用于发布dubbo服务-->
    <dubbo:annotation package="com.hkrt.controller" />
</beans>

通过maven插件启动服务consumer,查看dubbo-admin发现已经存在HelloService的相关信息。

案例回顾

需要在impl类上添加注解@service

注意这里的service已经是 spring提供的 现在要使用Dubbo提供的service注解

代码语言:javascript复制
import com.alibaba.dubbo.config.annotation.Service

问题:案例中服务提供方和服务消费方里面都会存在HelloService接口,这样做是否合理?有没有更好的方法?

:这种做法不好,同一个接口复制了两份,放于两个服务中,不利于维护,更好的方式是单独创建一个maven工程,将接口全部放到这个maven工程中,需要依赖此接口的工程只需要在自己的pom.xml中引入坐标即可。

问题:在服务的消费方里面只是引用了HelloService接口,并没有实现类,那么Dubbo是如何实现远程调用的?

:Dubbo底层是基于代理技术为HelloService接口创建代理对象,远程调用时通过此代理对象完成的,可以通过开发工具的Debug功能查看此代理对象的内部结构,另外,Dubbo实现网络传输是基于Netty框架完成的。

问题:使用zookeeper作为服务的注册中心,默认单机版运行,如何防止zookeeper单点故障呢?

:zookeeper支持集群模式,可以配置zookeeper集群来达到zookeeper服务的高可用,防止单点故障的发生。

加入log4j日志

创建log4j.properties文件放于resources下,spring自动加载

代码语言:javascript复制
### 设置###
log4j.rootLogger = debug, stdout

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Threshold = DEBUG
log4j.appender.stdout.Target = System.err
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = C:/Users/Surpass/Desktop/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log 
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

Dubbo相关配置

包扫描

代码语言:javascript复制
<dubbo:annotation package="com.hkrt.service"></dubbo:annotation>

表示包扫描,作用是扫描改包下的所有了类,存在相关的注解。

如果不使用包扫描,可以通过配置的方式进行注册发布服务。

服务提供方:

代码语言:javascript复制
<!--推荐注解开发,二者选其一-->
<!--1、注解形式:指定包扫描路径,用于发布dubbo服务-->
<dubbo:annotation package="com.hkrt.service"></dubbo:annotation>

<!--2、配置形式:通过bean的方式进行服务的注册(这是服务提供方)-->
<bean class="com.hkrt.service.impl.HelloServiceImpl" id="helloService"></bean>
<dubbo:service interface="com.hkrt.service.HelloService" ref="helloService"/>

服务消费方:

代码语言:javascript复制
<!--推荐注解开发,两者只存在其一-->
<!--1、注解形式:指定包扫描路径,用于发布dubbo服务-->
<dubbo:annotation package="com.hkrt.controller" />

<!--2、配置形式:配置reference进行实例,相当于<bean/>-->
<dubbo:reference interface="com.hkrt.service.HelloService" id="helloService"/>

配置的方式只能进行单一的接口配置,如果有多个服务,那么就很繁琐了。

协议

代码语言:javascript复制
<dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>

相关协议的介绍请移步博客

作者:xiaojin21cen 名称:dubbo 支持的9种协议 文章地址:https://blog.csdn.net/xiaojin21cen/article/details/79834222

启动时检查

代码语言:javascript复制
<dubbo:consumer check="false"/>

这个配置主要是用在服务的消费端,如果不配置则默认为true,Dubbo缺省会在启动的时候检查依赖的服务是否可用,不可用是会抛出异常,阻止Spring进行初始化完成,以便上线时,及时发现问题,可以通过将check的值修改为false关闭检查。

待上线时设置为true

负载均衡

Dubbo内置了4种负载均衡策略:

  1. RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的默认负载均衡策略。
  2. RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。
  3. LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机。活跃数指调用前后计数差。使慢的 Provider 收到更少请求,因为越慢的 Provider 的调用前后计数差会越大。
  4. ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。
代码语言:javascript复制
//在服务消费方配置负载均衡
@Reference(check=false,loadbalance="random")
private HelloService helloService;

@ResponseBody
@RequestMapping("/say")
public String sayHello(String name){
	System.out.println("响应成功");
	return helloService.sayHello(name);
}

//在服务提供者配置负载均衡
@Service(loadbalance = "random")   //发布服务必须使用Dubbo提供的service注解
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
        return "hello" name;
    }
}

更多负载均衡,请移步Dubbo官方文档:

http://dubbo.apache.org/zh-cn/blog/dubbo-loadbalance.html

这个案例里面,修改端口号来模拟多台并行的服务提供方,启动服务provider和consumer,访问consumer。

解决Dubbo无法发布被事务代理的service问题

如果在服务提供者注册的类上面添加事务注解@Transactinal事务控制后,发现服务就发布不成功了,原因是因为事务控制的底层原理是为服务提供者类创建代理对象,而默认情况下Spring是基于JDK动态代理方式创建代理对象,而代理对象的完成类名为com.sunproxy.$Proxy–(后面是数字),导致Dubbo在发布服务的时候进行包匹配匹配不到。

问题展示

使用dubbo坐标版本

代码语言:javascript复制
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>dubbo</artifactId>
	<version>2.6.0</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.9</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.4</version>
</dependency>
<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.40</version>
</dependency>

在applicationContext-service.xml中添加事务管理器和连接池相关配置

代码语言:javascript复制
<!--连接数据库-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
	<property name="url" value="jdbc:mysql://127.0.0.1:3306/dow_jones"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
</bean>

<!--事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>
    <!--开启事务管理器-->
<tx:annotation-driven transaction-manager="transactionManager"/>

然后在HelloServiceImpl类添加@Transactinal,完成后启动provider,在dubbo-admin中发现并没有服务提供者进行注册,只有一个服务消费者。

如果修改成cglib动态代理,回事什么样子?

修改配置applicationContext-service.xml

代码语言:javascript复制
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

这样子就使用的是cglib代理。那么启动provider看到有注册信息。

但是它的注册信息是这样的。

代码语言:javascript复制
org.springframework.aop.SpringProxy

这样子还是访问不到的。

解决方案

在之前的基础上,需要在注册服务类上添加注解参数。

代码语言:javascript复制
@Service(interfaceClass = HelloService.class)   //发布服务必须使用Dubbo提供的service注解
@Transactional
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
        return "hello" name;
    }
}

明确该服务实现的是HelloService.class的这个类,然后启动,查看dubbo-admin看结果,这时候发现provider注册的就是正确的接口服务了。

代码语言:javascript复制
//服务
com.hkrt.service.HelloService
    
//服务地址
dubbo://ip:20880/com.hkrt.service.HelloService?anyhost=true&application=dubbo_provider&dubbo=2.6.0&generic=false&interface=com.hkrt.service.HelloService&methods=sayHello&pid=24892&side=provider×tamp=1603959532358

另述

前面到在问题展示那里,我指定了dubbo坐标的版本号是2.6.0,博主在这个案例中发现,2.6.0以上的版本,即使使用jdk动态代理,也是能够将被事务管理的服务接口注册到zookeeper上去,并且能够成功访问。

尾言

您的支持和鼓励是我最大的动力,麻烦动动您高贵的小手,点赞,评论和收藏,感谢!

0 人点赞