Web基礎之Dubbo

Dubbo

RPC即Remote Procedure Call,即爲遠程調用。這和Java的遠程代理RMI有點相似,不過RMI只能在Java系統之間進行調用,而且是使用序列化對象的方式進行通訊。相比之下,RPC模式的Dubbo性能更高一些,因爲使用HTTP進行通訊,所以能夠在不一樣語言的服務之間進行調用。html

快速入門

首先導入Spring以及Dubbo相關依賴:
java


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倉庫:
git


公共接口單獨打包

package cn.bilibili.dubbo.service;

public interface HellobilibiliService {
    String sayHello(String name);
}

服務方開發github

打包爲war包,並在web.xml中配置監聽器:
web


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>

引入公共接口依賴並對接口進行實現:
redis


接口實現類

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:
spring


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>

消費方開發apache

在web後臺中,通常是web層依賴Service,所以直接使用Controller進行調用。
建立消費方工程並打war包,而後在web.xml中配置SpringMVC:
json


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;
    }
}
相關文章
相關標籤/搜索