Java技术升华分布式&高可用

Dubbo学习记录

2020-08-25  本文已影响0人  h2coder

本篇是学习了Dubbo 视频教程全集(30P)| 4 小时从入门到精通后,总结的记录。

Dubbo是什么?

Dubbo

Dubbo作为阿里巴巴提供的分布式RPC和微服务框架,提供了基于接口的远程调用,容错和负载均衡,服务注册与发现功能。

什么是RPC

指远程过程调用,是一种进程间的通信方式。这是一种思想,而不是规范。

Dubbo的三大核心能力

Dubbo的架构

流程图

Dubbo的服务注册与发现流程

调用关系

Dubbo环境搭建

JDK环境

必须有,大部分都已经有了,如果没有的话,自行配置一下

注册中心

Dubbo支持4种,分别为MulticastZookeeperRedisSimple,推荐使用Zookeeper,这里也只用Zookeeper

Zookeeper官方

Zookeeper下载地址,我选择3.4.14版本

配置环境变量

解压压缩包到指定目录,我选择放在User目录下

export ZOOKEEPER_HOME=/Users/wally/zookeeper-3.4.14
export PATH=$PATH:$ZOOKEEPER_HOME/bin
source .bash_profile

配置Zookeeper

进入Zookeeper的目录,找到conf子目录,默认会有一个配置文件模板zoo_sample.cfg,复制一份,重命名为zoo.cfg。

  1. 编辑zoo.cfg,将dataDir属性的值改为../data,就是将数据目录配置到了Zookeeper根目录下的data目录。
  2. 在Zookeeper根目录下新建一个data目录,作为Zookeeper的数据存放目录。
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=../data
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

Zookeeper常用命令

去到bin目录,终端下使用zkServer.sh

//启动
zkServer.sh start
//停止
zkServer.sh stop
//查看状态
zkServer.sh status

使用Zookeeper的客户端cli连接Zookeeper

zkCli.sh  -server localhost

admin管理控制台

为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序,不是必须,其实不装,也不影响Dubbo的使用。

使用案例

有2个服务,用户服务订单服务,订单服务依赖用户服务。所以订单服务为服务消费方,而用户服务为提供方
订单服务初始化订单时,会传入userId,订单服务调用用户服务,获取用户的收货地址列表,再返回。

公共模块

用户地址-实体类

public class UserAddress implements Serializable {
    private Integer id;
    /**
     * 用户地址
     */
    private String userAddress;
    /**
     * 用户Id
     */
    private String userId;
    /**
     * 收货人
     */
    private String consignee;
    /**
     * 电话号码
     */
    private String phoneNum;
    /**
     * 是否为默认地址,Y-是 N-否
     */
    private String idDefault;

    public UserAddress(Integer id, String userAddress, String userId, String consignee, String phoneNum, String idDefault) {
        this.id = id;
        this.userAddress = userAddress;
        this.userId = userId;
        this.consignee = consignee;
        this.phoneNum = phoneNum;
        this.idDefault = idDefault;
    }

    //省略get、set方法

    @Override
    public String toString() {
        return "UserAddress{" +
                "id=" + id +
                ", userAddress='" + userAddress + '\'' +
                ", userId='" + userId + '\'' +
                ", consignee='" + consignee + '\'' +
                ", phoneNum='" + phoneNum + '\'' +
                ", idDefault='" + idDefault + '\'' +
                '}';
    }
}

Service层接口

public interface OrderService {
    /**
     * 初始化订单
     *
     * @param userId 用户Id
     */
    List<UserAddress> initOrder(String userId);
}
public interface UserService {
    /**
     * 按照用户Id,获取用户的所有收货地址
     *
     * @param userId 用户Id
     */
    List<UserAddress> getUserAddressList(String userId);
}

建立服务提供者Provider

服务者模块名:user-service-provider

配置Dubbo依赖,Zookeeper依赖、日志等

<?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.atguigu.gmail</groupId>
    <artifactId>user-service-provider</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- 公共API接口 -->
        <dependency>
            <groupId>com.atguigu.gmail</groupId>
            <artifactId>gmall-interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- 日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <!-- dubbo依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.2</version>
        </dependency>
        <!-- 注册中心zookeeper客户端 -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.12.0</version>
        </dependency>
    </dependencies>
</project>
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

dubbo支持多个实现类来做多版本,一般用来做灰度发布使用。

注意,这个@Service注解是Spring的,不是Dubbo的,不需要导错包了。

//1.0.0版本,用户接口实现类
@Service
public class UserServiceImpl implements UserService {
    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println("---------- 线上版本订单服务,版本1.0.0 ----------");

        UserAddress userAddress1 = new UserAddress(1,
                "北京市昌平区xx科技园综合楼3层",
                userId,
                "李老师",
                "010-yyyyy",
                "Y");
        UserAddress userAddress2 = new UserAddress(2,
                "深圳市宝安区西部硅谷大厦B座3层",
                userId,
                "王老师",
                "010-xxxxx",
                "N"
        );
        return Arrays.asList(userAddress1, userAddress2);
    }
}
//2.0.0版本的实现
@Service
public class UserServiceImpl2 implements UserService {
    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println("---------- 灰度订单服务,版本2.0.0 ----------");

        UserAddress userAddress1 = new UserAddress(1,
                "北京市昌平区xx科技园综合楼3层",
                userId,
                "李老师",
                "010-yyyyy",
                "Y");
        UserAddress userAddress2 = new UserAddress(2,
                "深圳市宝安区西部硅谷大厦B座3层",
                userId,
                "王老师",
                "010-xxxxx",
                "N"
        );
        return Arrays.asList(userAddress1, userAddress2);
    }
}

配置一个服务提供者,需要6步,最后一步不是必须的,可以不配置监控中心。

  1. 配置当前服务/应用的名字
  2. 指定注册中心的位置,有2种写法,第一种是协议名和地址合起来一起写,第二种是分开属性写
  3. 指定通信的规则,就是协议和端口
  4. 暴露服务,就是暴露刚才上面写的接口实现,并且标签里面指定暴露的服务方法
  5. dubbo支持多版本发布,常用来做灰度发布,和第四步一样,就是version属性指定不同的版本
  6. 指定配置中心的地址和端口,可以直接使用一个registry关键字,让dubbo自己查找
<?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-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 1.配置当前服务/应用的名字 -->
    <dubbo:application name="user-service-provider"/>

    <!-- 2.指定注册中心的位置 -->
    <!--    <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry>-->
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>

    <!-- 3.指定通信规则(通信协议、通信端口) -->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- 4.暴露服务,interface属性指定服务接口,ref指定服务具体实现 -->
    <dubbo:service interface="com.atguigu.gmail.service.UserService" ref="userServiceImpl1"
                   version="1.0.0">
        <dubbo:method name="getUserAddressList" timeout="1000"/>
    </dubbo:service>
    <!-- 服务实现 -->
    <bean id="userServiceImpl1" class="com.atguigu.gmail.service.impl.UserServiceImpl"/>

    <!-- 5.配置一个灰度服务实现 -->
    <dubbo:service interface="com.atguigu.gmail.service.UserService" ref="userServiceImpl2"
                   version="2.0.0"/>
    <bean id="userServiceImpl2" class="com.atguigu.gmail.service.impl.UserServiceImpl2"/>

    <!-- 6.配置监控中心,2种方式,protocol属性写协议,如果写registry,则代表自动从注册中心查找监控中心的位置 -->
    <dubbo:monitor protocol="registry"/>
    <!-- 或者手动指定位置 -->
    <!--    <dubbo:monitor address="127.0.0.1:7070"/>-->
</beans>

提供者启动,为了后续的消费方能调用到,先用System.in.read()阻塞,不马上结束程序。

public class ProviderApplication {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
        context.start();
        //阻塞
        System.in.read();
    }
}

建立服务消费者Consumer

<?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.atguigu.gmail</groupId>
    <artifactId>order-service-consumer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- 公共API -->
        <dependency>
            <groupId>com.atguigu.gmail</groupId>
            <artifactId>gmall-interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- 日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <!-- dubbo依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.2</version>
        </dependency>
        <!-- 注册中心zookeeper客户端 -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.12.0</version>
        </dependency>
    </dependencies>
</project>

和上面服务提供者的一样,拷贝一份即可,不再赘述

@Service
public class OrderServiceImpl implements OrderService {
    /**
     * 注入用户服务
     */
    @Autowired
    private UserService userService;

    public List<UserAddress> initOrder(String userId) {
        //1.查询用户的搜索地址
        return userService.getUserAddressList(userId);
    }
}

本地存根,用于调用远程服务时,先走一遍自己的本地实现,可以在这里做参数验证、拦截、容错以及结果缓存等操作。
类似于一个静态代理的拦截器,或者说AOP的效果。

public class UserServiceStub implements UserService {
    private final UserService userService;

    /**
     * 必须提供一个有参构造器,dubbo会传入远程服务的代理对象,通过这个对象,来进行调用远程方法
     *
     * @param userService 远程服务的代理对象
     */
    public UserServiceStub(UserService userService) {
        this.userService = userService;
    }

    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println("UserServiceStub.getUserAddressList() => 本地存根被调用...");
        try {
            if (StringUtils.isEmpty(userId)) {
                throw new RuntimeException("传入的用户Id不能为空!");
            }
            return userService.getUserAddressList(userId);
        } catch (Exception e) {
            e.printStackTrace();
            //容错处理,如果调用出错,返回空
            return new ArrayList<UserAddress>();
        }
    }
}

配置一个服务消费者,需要5步,最后一步监控中心可省略。

  1. 开启包扫描,这个是Spring的配置
  2. 配置服务消费者的应用名称
  3. 指定注册中心的地址。有一个check属性,配置注册中心不存在时,是否报错,默认为true,就是会报错
  4. 声明需要远程调用的远程服务接口,生成远程服务代理
  5. 配置监控中心的地址和端口,也可以指定为registry,代表dubbo自动搜索监控中心
<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns:context="http://www.springframework.org/schema/context"
        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://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 1.配置包扫描 -->
    <context:component-scan base-package="com.atguigu.gmail.service.impl"/>

    <!-- 2.配置服务消费者的应用名称 -->
    <dubbo:application name="order-service-consumer"/>

    <!-- 3.指定注册中心的地址 -->
    <!-- 3.1check属性,配置注册中心不存在时,是否报错,默认为true,就是会报错 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" check="false"/>

    <!-- 4.声明需要远程调用的远程服务接口,生成远程服务代理 -->
    <!-- 4.1 check属性,启动时是否检查依赖的服务是否启动,默认为true,启动会检查,会导致如果依赖的服务没有启动,就会报错,一般我们设置为false,调用的时候,才去检查对方服务状态 -->
    <!-- 4.2 timeout属性,配置消费者调用服务提供者的方法超过一定时间没响应,就判定为超时,避免大量的阻塞,单位为毫秒,默认值为1000,就是一秒 -->
    <!-- 4.3 retries属性,重试次数,调用远程方法超时时,进行重试,不包含第一次调用(如果服务提供方有多个服务,重试请求会分散到不同的服务中) -->
    <!-- 幂等方法(设置重试次数,来提升系统的性能),非幂等方法(不能设置重试次数) -->
    <!-- 幂等:就是方法无论运行多少次,都是一个效果,例如数据库的查询、删除、修改 -->
    <!-- 非幂等:每次方法运行都是产生一个新的效果,例如数据库新增 -->
    <!-- 如不需要重试,设置值为0即可,代表不重试 -->
    <!-- 4.4 dubbo还支持多版本,就是灰度,version属性指定使用调用哪个版本的实现,还支持*通配符来表示随机选一个来调用,来达到灰度 -->
    <!-- 4.5 配置本地存根,使用stub属性配置本地存根的全路径类名 -->
    <dubbo:reference id="userService" interface="com.atguigu.gmail.service.UserService"
                     check="false" timeout="3000" retries="3"
                     version="*" stub="com.atguigu.gmail.service.stub.UserServiceStub"/>

    <!-- 4.2每个去配置check属性是很麻烦的,我们可以统一配置所有消费者的缺省配置,我们可以默认所有服务都不检查,如果有某一项服务不一样,则单独配置即可 -->
    <!-- 4.3 timeout属性,为所有的消费者提供缺省设置,而不需要去dubbo:reference下每个都配置 -->
    <dubbo:consumer check="false" timeout="1000"/>

    <!-- 5.配置监控中心,2种方式,protocol属性写协议,如果写registry,则代表自动从注册中心查找监控中心的位置 -->
    <dubbo:monitor protocol="registry"/>
    <!-- 或者手动指定位置 -->
    <!--    <dubbo:monitor address="127.0.0.1:7070"/>-->
</beans>

我们为了演示方便,直接在启动时,获取服务实例,调用远程服务。

public class ConsumerApplication {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
        OrderService orderService = context.getBean(OrderService.class);
        orderService.initOrder("1");
        System.out.println("调用完成");
        //阻塞
        System.in.read();
    }
}

整合SpringBoot的改造

上面的Dubbo使用比较原始,一般在SpringBoot出现前使用,现在基本都用SpringBoot,所以Dubbo也提供了和SpringBoot整合的方式。

建立服务提供者Provider

建立模块:boot-user-service-provider

<?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.atguigu.gmail</groupId>
    <artifactId>boot-user-service-provider</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- 依赖基础接口 -->
        <dependency>
            <groupId>com.atguigu.gmail</groupId>
            <artifactId>gmall-interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- spring boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--热启动-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!-- Dubbo Spring Boot 的 Starter -->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>0.2.0</version>
        </dependency>
        <!-- 服务容错 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

相当于将之前的provider.xml的内容挪到这里

#配置应用名称
dubbo.application.name=boot-user-service-provider

#配置注册中心的位置和注册中心协议
dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper

#配置通信规则,协议名以及端口
dubbo.protocol.name=dubbo
dubbo.protocol.port=20881

#配置监控中心
dubbo.monitor.protocol=registry

#配置包扫描,@EnableDubbo注解是配置这个包扫描功能
#dubbo.scan.base-packages=com.atguigu.gmail

dubbo还提供了全局配置文件,该文件一般是配置公共配置,application.xx的配置可以覆盖该配置。

#配置公共端口号,当在虚拟机参数、xml或application.properties覆盖相同属性时,会进行覆盖
#虚拟机参数:-Ddubbo.protocol.port,值为20880
#优先级:虚拟机参数 > xml或application.properties > dubbo.properties
#一般dubbo.properties文件配置的是公共配置
dubbo.protocol.port=20882

SpringBoot抛弃了XML,推荐注解和JavaConfig的方式配置,所以Dubbo也有对应的注解进行配置。

这里的@Service注解,是dubbo的注解,用来暴露服务的,同时我们也要Spring管理该Bean,为了和Spring的@Service不重名,我们使用@Component注解。

//1.0.0版本

//注解@Service,标识暴露给dubbo的服务,timeout属性配置远程服务调用超时时间,version属性配置多版本功能,可实现灰度
@Service(timeout = 3000, version = "*")
//因为和spring的@Service注解同名,这里用@Component注解也是一样的
@Component("userService1")
public class BootUserServiceImpl implements UserService {
    /**
     * Hystrix容错处理,该方法会被Hystrix代理,来处理容错异常
     *
     * @param userId 用户Id
     */
    @HystrixCommand
    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println("---------- 线上版本订单服务,版本1.0.0 ----------");

        UserAddress userAddress1 = new UserAddress(1,
                "北京市昌平区xx科技园综合楼3层",
                userId,
                "李老师",
                "010-yyyyy",
                "Y");
        UserAddress userAddress2 = new UserAddress(2,
                "深圳市宝安区西部硅谷大厦B座3层",
                userId,
                "王老师",
                "010-xxxxx",
                "N"
        );
        return Arrays.asList(userAddress1, userAddress2);
    }
}

//2.0.0版本
//注解@Service,标识暴露给dubbo的服务,timeout属性配置远程服务调用超时时间,version属性配置多版本功能,可实现灰度
@Service(timeout = 3000, version = "*")
//因为和spring的@Service注解同名,这里用@Component注解也是一样的
@Component("userService2")
public class BootUserServiceImpl2 implements UserService {
    public List<UserAddress> getUserAddressList(String userId) {
        System.out.println("---------- 灰度订单服务,版本2.0.0 ----------");

        UserAddress userAddress1 = new UserAddress(1,
                "北京市昌平区xx科技园综合楼3层",
                userId,
                "李老师",
                "010-yyyyy",
                "Y");
        UserAddress userAddress2 = new UserAddress(2,
                "深圳市宝安区西部硅谷大厦B座3层",
                userId,
                "王老师",
                "010-xxxxx",
                "N"
        );
        return Arrays.asList(userAddress1, userAddress2);
    }
}
  1. dubbo整合springboot的三种方式

    • 导入dubbo的starter,在application.properties配置属性,使用@Service暴露服务,使用@Reference引用服务,并且Application入口类必须加上@EnableDubbo注解来开启注解功能
    • 导入dubbo的starter,保留dubbo的xml配置文件,再在Application入口类上使用@ImportResource注解,引入xml配置文件,使用原始方式,就不需要用注解了
    • 使用注解API的方式,就是SpringJavaConfig的方式提供类对象,将每个组件手动创建放到Spring容器中,让dubbo扫描出组件
  2. 注意!!!上面3种配置方式,不能共存,只能选一种,否则会出现重复定义的异常,例如注解和JavaConfig的方式都做了,而@EnableDubbo内部又使用@DubboComponentScan注解,导致2种配置方式都开启了,出现了重复定义
    服务容错,使用Hystrix,使用@EnableHystrix注解开启Hystrix,在需要保护的方法上使用@HystrixCommand注解,在调用方,也依赖Hystrix,同样使用@EnableHystrix注解开启Hystrix,在调用的方法上,也加上@HystrixCommand注解

推荐使用第一种和第三种方式,就是注解方式或者JavaConfig的方式,我更倾向于第一种,配置文件方式会比JavaConfig简洁一些。

//1)注解方式,使用@EnableDubbo注解,开启基于注解的dubbo功能,例如@Service注解才能生效
@EnableDubbo
//2)xml方式,使用@ImportResource注解导入配置
//@ImportResource(locations = "classpath:provider.xml")
//3)JavaConfig配置方式,需要使用@DubboComponentScan注解配置扫描包的包名,用来扫描到Dubbo的Java配置类,用@EnableDubbo也是可以的,@EnableDubbo注解就已经引用了@DubboComponentScan注解
//@DubboComponentScan(basePackages = "com.atguigu.gmail")
//开启服务容错
@EnableHystrix
@SpringBootApplication
public class BootProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootProviderApplication.class, args);
    }
}

注意:不能和其他2种同时存在!!

//SpringJavaConfig的方式,配置dubbo的组件
@Configuration
public class MyDubboConfig {
    /**
     * 提供Application标签的功能
     * 相当于:dubbo.application.name=boot-user-service-provider
     */
    @Bean
    public ApplicationConfig applicationConfig() {
        ApplicationConfig config = new ApplicationConfig();
        config.setName("boot-user-service-provider");
        return config;
    }

    /**
     * 提供注册中心配置
     * 相当于:
     * dubbo.registry.address=127.0.0.1:2181
     * dubbo.registry.protocol=zookeeper
     */
    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig config = new RegistryConfig();
        config.setAddress("127.0.0.1:2181");
        config.setProtocol("zookeeper");
        return config;
    }

    /**
     * 提供通信协议的配置
     * 相当于:
     * dubbo.protocol.name=dubbo
     * dubbo.protocol.port=20881
     */
    @Bean
    public ProtocolConfig protocolConfig() {
        ProtocolConfig config = new ProtocolConfig();
        config.setName("dubbo");
        config.setPort(20881);
        return config;
    }

    /**
     * 提供暴露服务的配置
     * 相当于
     * <dubbo:service interface="com.atguigu.gmail.service.UserService" ref="userServiceImpl1"
     * version="1.0.0">
     * <dubbo:method name="getUserAddressList" timeout="1000"/>
     * </dubbo:service>
     */
    @Bean
    public ServiceConfig<UserService> userServiceConfig(@Qualifier("userService1") UserService userService) {
        ServiceConfig<UserService> serviceConfig = new ServiceConfig<>();
        //配置暴露的服务的接口
        serviceConfig.setInterface(UserService.class);
        //配置暴露的服务的具体实现,spring会自动注入到参数上
        serviceConfig.setRef(userService);
        //设置版本
        serviceConfig.setVersion("1.0.0");
        //配置每一个方法的信息
        MethodConfig methodConfig = new MethodConfig();
        //设置方法名
        methodConfig.setName("getUserAddressList");
        //设置超时时间
        methodConfig.setTimeout(1000);
        //将MethodConfig关联到Service的配置中
        serviceConfig.setMethods(Arrays.asList(methodConfig));
        return serviceConfig;
    }

    /**
     * 提供监控中心的配置
     */
    @Bean
    public MonitorConfig monitorConfig() {
        MonitorConfig config = new MonitorConfig();
        config.setProtocol("registry");
        return config;
    }
}

和普通方式配置的provider.xml文件一致,所以不在赘述了。

建立服务消费者Consumer

建立模块:boot-order-service-consumer

<?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.atguigu.gmail</groupId>
    <artifactId>boot-order-service-consumer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- 公共API -->
        <dependency>
            <groupId>com.atguigu.gmail</groupId>
            <artifactId>gmall-interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- web项目 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--热启动-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!-- Dubbo Spring Boot 的 Starter -->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>0.2.0</version>
        </dependency>
        <!-- 服务容错 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
#配置端口号
server.port=8081
#配置应用名称
dubbo.application.name=boot-order-service-consumer
#配置注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#配置监控中心
dubbo.monitor.protocol=registry
  1. 和服务提供者注册的服务类似,也用@Service注解进行服务暴露
  2. 使用@Reference注解,注入依赖的服务,例如这里是用户服务
@Service
public class BootOrderServiceImpl implements OrderService {
    /**
     * 依赖注入dubbo的远程服务,因为服务是远程服务,不能用spring的@Autowired注解了
     * 1.url属性,可以实现dubbo直连,直接配置远程服务的ip和端口,不经过注册中心
     * <p>
     * 2.loadbalance属性,负载均衡配置,负载均衡实现有4种,分别是
     * 1)roundrobin:轮训,挨个按顺序分配,第一轮:A-B-C 第二轮:A-B-C
     * 2)leastactive:最小活跃调用数,选中处理速度最快的服务。活跃数指调用前后计数差,优先调用高的,相同活跃数的随机。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
     * 3)random:随机抽取,dubbo模式使用这种
     * 4)一致性哈希,只要请求过1次,后面的请求都会找到上次的机器进行处理
     * <p>
     * 3.check属性,是否启动时检查服务提供方是否存在,默认为true,代表检查,如果发现没启动,会报错
     */
//    @Autowired
//    @Reference(url = "127.0.0.1:20881")
//    @Reference(loadbalance = "roundrobin")
    @Reference(check = false)
    private UserService userService;

    /**
     * 使用@HystrixCommand注解,保护调用失败时,调用容错方法进行返回
     */
    @HystrixCommand(fallbackMethod = "fallback")
    public List<UserAddress> initOrder(String userId) {
        //1.查询用户的搜索地址
        return userService.getUserAddressList(userId);
    }

    /**
     * 容错方法,注意方法参数必须和容错方法一致,否则会抛出找不到方法的异常
     */
    private List<UserAddress> fallback(String userId) {
        UserAddress address = new UserAddress(
                0,
                "测试",
                userId,
                "测试",
                "xxxx",
                "N"
        );
        return Arrays.asList(address);
    }
}

使用@Autowired注解,可以注入暴露的服务,提供initOrder接口,进行调用

@Controller
public class OrderController {
    @Autowired
    private OrderService orderService;

    /**
     * 初始化订单
     */
    @ResponseBody
    @RequestMapping("/initOrder")
    public List<UserAddress> initOrder(@RequestParam("uid") String userId) {
        return orderService.initOrder(userId);
    }
}

重点:使用注解@EnableDubbo,开启dubbo的注解功能。

//注解@EnableDubbo,开启dubbo的注解功能
@EnableDubbo
//开启服务容错
@EnableHystrix
@SpringBootApplication
public class BootConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootConsumerApplication.class, args);
    }
}

总结

代码我上传到了Github,有兴趣的同学可以clone。

上一篇下一篇

猜你喜欢

热点阅读