zoukankan      html  css  js  c++  java
  • Web基础之Dubbo

    Dubbo

    RPC即Remote Procedure Call,即为远程调用。这和Java的远程代理RMI有点类似,不过RMI只能在Java系统之间进行调用,并且是使用序列化对象的方式进行通信。相比之下,RPC模式的Dubbo性能更高一些,由于使用HTTP进行通信,因此可以在不同语言的服务之间进行调用。

    快速入门

    首先导入Spring以及Dubbo相关依赖:

    maven依赖
    <properties>      
        <spring.version>5.0.5.RELEASE</spring.version>
    </properties>
    <dependencies>
        <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-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
            </dependency>
        <!-- dubbo相关 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.7</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>  
    

    创建公共接口并安装到maven仓库:

    公共接口单独打包
    package cn.bilibili.dubbo.service;
    
    public interface HellobilibiliService {
        String sayHello(String name);
    }
    

    服务方开发

    打包为war包,并在web.xml中配置监听器:

    web.xml
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    <web-app>
        <display-name>Archetype Created Web Application</display-name>
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext-service.xml</param-value>
        </context-param>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    </web-app>
    

    引入公共接口依赖并对接口进行实现:

    接口实现类
    package cn.bilibili.dubbo.service.impl;
    
    import cn.bilibili.dubbo.service.HelloService;
    import com.alibaba.dubbo.config.annotation.Service;
    
    //这个Service注解是dubbo的,不是spring的
    @Service
    public class HelloServiceImpl implements HelloService {
        @Override
        public String sayHello(String name) {
            System.out.println("-------HelloService运行了-----------");
            return "hello " + name;
        }
    }
    

    创建spring配置文件,并在其中配置dubbo:

    applicationContext-service.xml
    <?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:p="http://www.springframework.org/schema/p"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
           xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:dubdo="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://www.springframework.org/schema/mvc
             http://www.springframework.org/schema/mvc/spring-mvc.xsd
             http://code.alibabatech.com/schema/dubbo
             http://code.alibabatech.com/schema/dubbo/dubbo.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    	<!-- 当前应用名称,用于注册中心计算应用间依赖关系,消费者和提供者应用名不能一样 -->
    	<dubbo:application name="dubbo-service-provider" />
    	<!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址-->
    	<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    	<!-- 注册  协议和port   端口默认是20880 -->
    	<dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
    	<!-- 扫描指定包,加入@Service注解的类会被发布为服务  -->
    	 <dubbo:annotation package="cn.bilibili.dubbo.service.impl" />
    </beans>
    

    消费方开发

    在web后台中,一般是web层依赖Service,因此直接使用Controller进行调用。
    创建消费方工程并打war包,然后在web.xml中配置SpringMVC:

    web.xml
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
      <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 -->
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:applicationContext-consumer.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
    </web-app>
    

    然后创建一个Controller:

    具体调用对象
    @Controller
    @RequestMapping("/demo")
    public class HelloController {
        //这里的注解是Dubbo提供
        @Reference
        private HelloService helloService;
    
        @RequestMapping("/hello")
        @ResponseBody
        public String getName(String name){
            //远程调用
            String result = helloService.sayHello(name);    
            return result;
        }
    }
    

    然后在spring中配置dubbo参数:

    applicationContext-consumer.xml
    <?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:p="http://www.springframework.org/schema/p"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    	xmlns:mvc="http://www.springframework.org/schema/mvc"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    			http://www.springframework.org/schema/beans/spring-beans.xsd
    			http://www.springframework.org/schema/mvc
    			http://www.springframework.org/schema/mvc/spring-mvc.xsd
    			http://code.alibabatech.com/schema/dubbo
    			http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    			http://www.springframework.org/schema/context
    			http://www.springframework.org/schema/context/spring-context.xsd">
    
    	<!-- 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样 -->
    	<dubbo:application name="dubbo-service-consumer" />
    	<!-- 连接服务注册中心zookeeper ip为zookeeper所在服务器的ip地址-->
    	<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    	<!-- 扫描的方式controller  -->
    	<dubbo:annotation package="cn.bilibili.dubbo.controller" />
    </beans>
    

    两个Tomcat和JMX端口号不能一样。

    然后便可以在浏览器访问该Controller。


    dubbo还有一个叫dubbo-admin的可视化后台管理工具,可以单独运行在一个Tomcat中。

    Dubbo参数调整

    配置方式调整

    上面使用的是包扫描的方式,其实也可以使用xml直接配置bean:

    <!-- 注册一个Bean -->
    <bean id="helloService" class="cn.bilibili.dubbo.service.impl.HelloServiceImpl">
    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="cn.bilibili.dubbo.service.HelloService" ref="helloService"/>
    

    消费方也可以使用xml的方式配置:

    <!-- 引用远程服务代理,可以和本地bean一样使用helloService -->
    <dubbo:reference id="helloService" interface="cn.bilibili.dubbo.service.HelloService"/>
    

    在Controller中改为自动注入:

    @Controller
    @RequestMapping("/demo")
    public class HelloController {
        @Autowired
        private HelloService helloService;
    }
    

    服务协议

    <dubbo:protocol name="dubbo" port="20880"/>
    

    Dubbo支持的协议有:dubbo、rmi、hessian、http、webservice、rest、redis等。推荐使用的是dubbo协议。dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
    同一个工程中可以配置不同的协议。

    启动时检查

    消费方在启动时默认会检查依赖的服务是否可用,可以使用下面的方式关闭启动检查(配置在消费方):

    <dubbo:consumer check="false"/>
    

    负载均衡

    在集群负载均衡时,Dubbo 提供了多种均衡策略(包括随机(random)、轮询(roundrobin)、最少活跃调用数、一致性Hash),缺省为random随机调用。

    负载均衡可以在消费方配置也可以在服务方配置:

    消费方:

    /**
        * 使用轮询负载均衡策略
        * check = false 启动时候不检查服务提供者
        */
    @Reference(check = false, loadbalance = "roundrobin")
    private DemoService demoService;
    

    服务方:

    //在服务提供者一方配置负载均衡
    @Service(loadbalance = "roundrobin")
    public class HelloServiceImpl implements HelloService {
        public String sayHello(String name) {
            return "hello " + name;
        }
    }
    

    和Dubbo其他的配置类似,多个配置是有覆盖关系的:

    1. 方法级优先,接口级次之,全局配置再次之。
    2. 如果级别一样,则消费方优先,提供方次之。

    所以,4种配置的优先级是:

    1. 客户端方法级别配置。
    2. 客户端接口级别配置。
    3. 服务端方法级别配置。
    4. 服务端接口级别配置。

    服务超时

    超时机制:在设置的超时时间内,如果 consume 端没有接收到 provider 的返回值,认为本次调用失败。默认超时时间 1秒

    消费端设置超时:

    //设置整个服务的超时时间为5秒
    @Reference(timeout = 5000)
    private HelloService helloSerice;
    

    xml形式:

    <!-- 方式一:设置整个服务的超时时间为5秒 -->
    <dubbo:reference id="helloService" interface="cn.bilibili.dubbo.service.HelloService" timeout="5000"/>
    <!-- 方式二:设置服务的某个具体方法的超时时间 -->
    <dubbo:reference id="helloService" interface="cn.bilibili.dubbo.service.HelloService">
        <!-- 设置sayHello方法的超时时间为5秒 -->
        <dubbo:method name="sayHello" timeout="5000"></dubbo:method>
    </dubbo:reference>
    

    服务端设置超时:

    //设置整个服务的超时时间为5秒
    @Service(timeout = 5000)
    public class HelloServiceImpl implements HelloService {
    }
    

    xml形式:

    <!--方式一:设置整个服务的超时时间为5秒-->
    <dubbo:service interface="cn.bilibili.dubbo.service.HelloService" ref="helloService" timeout="5000"/>
           
    <!--方式二:设置服务的某个具体方法的超时时间 -->
    <dubbo:service interface="cn.bilibili.dubbo.service.HelloService" ref="helloService">
        <!-- 设置sayHello方法的超时时间为5秒 -->
        <dubbo:method name="sayHello" timeout="5000"></dubbo:method>
    </dubbo:service>
    

    优先在服务端配置超时。

    服务重试

    当调用某个服务的方法失败的后, dubbo默认重试2次(不包括默认的第一次调用),在设置的重试次数内,请求都失败,认为此次请求异常,抛出异常 。我们可以通过retries参数修改重试次数。
    dubbo会在服务提供端出现异常进行再次重试调用。这个并不代表服务提供端完全执行失败了。所以不是所有接口都适合重试,如果一个服务是不等幂,那么不适合重试的机制,因为会存在重复提交的问题,否则是可以进行重试的。比如提交一个订单的接口是不能进行重试的,而查询类型的接口是可以重试的。(关于幂等性可以看看MDN和这篇博客
    服务重试需要慎重使用。

    消费端重试设置:

    //设置整个服务失败后的重试次数为3,实际调用是4次
    @Reference(retries = 3)
    private HelloService helloSerice;
    

    xml形式

    <!--方式一:设置整个服务失败后的重试次数为3,实际调用是4次-->
    <dubbo:reference id="helloService" interface="cn.bilibili.dubbo.service.HelloService" retries="3"/>
    <!--方式二:设置服务的某个具体方法的重试次数-->
     <dubbo:reference id="helloService" interface="cn.bilibili.dubbo.service.HelloService">
        <!-- 设置sayHello方法的重试次数为3,实际调用是4次 -->
        <dubbo:method name="sayHello" retries="3"></dubbo:method>
    </dubbo:reference>
    

    服务端设置:

    //设置整个服务失败后的重试次数为3,实际调用是4次
    @Service(retries = 3)
    public class HelloServiceImpl implements HelloService {
    }
    

    xml形式

    <!--方式一:设置整个服务失败后的重试次数为3,实际调用是4次-->
    <dubbo:service interface="cn.bilibli.dubbo.service.HelloService" ref="helloService" retries="3"/>
           
    <!--方式二:设置服务的某个具体方法的重试次数-->
    <dubbo:service interface="cn.bilibili.dubbo.service.HelloService" ref="helloService">
        <!-- 设置sayHello方法的重试次数为3,实际调用是4次 -->
        <dubbo:method name="sayHello" retries="3"></dubbo:method>
    </dubbo:service>
    

    事务

    但是我们如果在服务提供者类上加入@Transactional事务控制注解后,服务就发布不成功了。原因是事务控制的底层原理是为服务提供者类创建代理对象,而默认情况下Spring是基于JDK动态代理方式创建代理对象,而此代理对象的完整类名为com.sun.proxy.$Proxy42(最后两位数字不是固定的),导致Dubbo在发布服务前进行包匹配时无法完成匹配,进而没有进行服务的发布。

    解决方法:

    开启事务时开启proxy-target-class,强制使用cglib代理:

    proxy-target-class在spring事务、aop、缓存这几块都有设置,其作用都是一样的:

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    
    <aop:config proxy-target-class="true">
    
    <cache:annotation-driven proxy-target-class="true"/>
    

    然后需要在服务提供者的接口上指定服务接口类型:

    //指定服务接口类型
    @Service(interfaceClass = HelloService.class)
    @Transactional
    public class HelloServiceImpl implements HelloService {
        public String sayHello(String name) {
            return "hello " + name;
        }
    }
    
  • 相关阅读:
    Xcode下 gdb 调试命令
    Xcode 获取本地IP
    Linux终端提示符PS1设置(颜色)
    linux 客户端 Socket 非阻塞connect编程
    关于SIGPIPE导致的程序退出
    jQuery MiniUI开发系列之:数据验证
    支持Java Spring MVC
    支持ASP.NET MVC
    支持ASP.NET WebService
    jQuery MiniUI开发系列之:Ajax处理超时、服务端错误
  • 原文地址:https://www.cnblogs.com/lixin-link/p/11540934.html
Copyright © 2011-2022 走看看