dubbo基礎入門使用與開發


一、使用dubbo的准備工作

1. zookeeper單節點環境

    Demo中選用的zookeeper為zookeeper-3.4.5-cdh5.0.0.tar.gz版本,作為開發環境,在不考慮zookeeper部署多節點的情況下,在本機(windows環境)解壓之后,進入解壓目錄下的conf文件夾,復制一份zoo_sample.cfg文件,修改為zoo.cfg。然后運行bin/zkServer.cmd 啟動zookeeper服務。

備注:(1)zookeeper啟動會自動讀取zoo.cfg文件,該文件主要定義了zookeeper需要暴露的端口,數據和日志的存儲路徑。

   (2)如果啟動cmd之后 閃退,在該cmd中最后一行添加pause,可以查看錯誤信息。

2.zookeeper分布式部署

       如果保證zookeeper的高可用性,需要部署一個zookeeper集群,這里介紹zookeeper集群在linux中部署的方式。

(1)    在三台機器上分別解壓zookeeper-3.4.5-cdh5.0.0.tar.gz包,為了目錄的清晰,可以修改解壓后文件夾的名稱為zookeeper-2181。

(2)    在每台機器的zookeeper解壓目錄中,創建data和logs兩個文件夾,用於zookeeper的數據和日志存放:

 

 

(3)    在每台機器的zookeeper解壓目錄的conf文件夾中,創建一個zoo.cfg文件,文件內容如下:

tickTime=2000
initLimit=5
syncLimit=2
dataDir=/home/nxeop/zookeeper/data
dataLogDir=/home/nxeop/zookeeper/logs
clientPort=2181
server.1= EopApp1:2888:3888
server.2= EopApp2:2888:3888
server.3= EopApp2:2888:3888
maxClientCnxns=60
minSessionTimeout=4000

    注解:

tickTime:心跳時間

initLimit:多少個心跳時間內,允許其他server連接並初始化數據

syncLimit:多少個tickTime內,允許follower節點同步

dataDir:存放內存數據文件目錄,根據實際環境修改

dataLogDir:存放日志文件目錄,根據實際環境修改

clientPort:監聽端口,使用默認2181端口

server.x:配置集群主機信息,[hostname]:[通信端口]:[選舉端口],根據自己的主機信息修改

maxClientCnxns:最大並發客戶端數,用於防止DOS的,設置為0是不加限制

minSessionTimeout:最小的客戶端session超時時間(單位是毫秒)

maxSessionTimeout:最大的客戶端session超時時間(單位是毫秒)

EopApp1、EopApp2、EopApp3是三台需要部署zookeeper節點服務器的hostname,這個zoo.cfg文件在集群中所有節點的配置都是一樣,直接復制就可以了。

server.1、server.2和server.3 表示三個服務器,每個服務器在zookeeper集群中有一個唯一的id,這個id就是server.x中的這個x。

(4)    最關鍵的一步,我們需要配置每台服務器上的zookeeper的id。之前已經在每台機器上的zookeeper解壓目錄中創建了data文件夾和logs文件夾。那么,我們在data文件夾中創建一個文件叫myid,對於第一台服務器,文件的內容為1。依次在各台主機的data目錄下生成myid文件設置id值,myid的內容要與前面配置的zoo.cfg中設置的server.x保持一致。

     

(5)    啟動zookeeper:

在三台服務器上依次執行zookeeper目錄中:

bin/zkServer.sh start 

命令,zookeeper三個節點一次啟動。

全部啟動完成后,可以執行

bin/zkServer.sh status               

去查看每個zk節點在集群中的狀態。

 

至此,zookeeper的集群已經部署完成。

示例1:最簡單dubbo服務注冊與調用

1. 示例場景和思路

       當前實例的設計思路圖如下所示:

 

 

person-center:人員信息中心,通過dubbo對外提供服務;

person-client:客戶端,通過dubbo使用person-center提供的服務;

zookeeper:注冊中心,所有的dubbo應用都注冊到這上面;

person-interface:一個接口包,定義了人員信息接口和實體:

person-center引用interface包,用於實現這個接口;

person-client引用interface包,使用里面的接口名稱去調用dubbo服務。

2.編寫person-interface接口包

       (1)創建person-interface的maven工程,默認打包方式為jar包。

       (2)編寫實體類PersonInfo和接口IPersonInfoService,示例代碼結構入下所示:

 

(3)IPersonInfoService定義兩個方法:(這里雖然定義了PersonInfo,不過demo1先不使用,只做最簡單的接口)

public interface IpersonInfoService {
    /**
     * 查詢全部人員信息
     */
    public String queryPersonInfoAll();
    /**
     * 根據人員編號查詢人員信息
     */
    public String queryPersonInfoByNumber(String personNumber);
}

 

3.編寫person-center 服務端

工程參考結構如下:

 

 

(1)創建person-center的maven工程,默認打包方式為jar包。

(2)在pom文件中需要添加的核心依賴jar包如下:

       a、person-interface,引用公共接口,用於實現這些接口:

    <!-- 引入實現編寫好的person接口層 -->
        <dependency>
            <groupId>test.dubbo.interface</groupId>
            <artifactId>person-interface</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

 

     b、dubbo,引用dubbo依賴,客戶端和服務端都使用同一個依賴:

<!-- 引入dubbo框架(服務端、客戶端通用) -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.8.4</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
            </exclusions>
        </dependency>

 

       c、zkclient,引用zookeeper客戶端,dubbo會自動使用zkclient去和zookeeper進行連接:

<!-- 因為dubbo服務端需要注冊服務到zk中,因此依賴zkClient包 -->
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>

 

(3)編寫實現person-interface接口的代碼:

       其中,PersonInfoServiceImpl實現了person-interface包中的IPersonInfoService接口的方法:

      

@Service
public class PersonInfoServiceImpl implements IpersonInfoService {

    @Override
    public String queryPersonInfoAll() {
        System.out.println("===================================");
        System.out.println("接口實現:queryPersonInfoAll()");
        System.out.println("===================================");
        return "from PersonInfoServiceImpl : some person Info";
    }
    @Override
    public String queryPersonInfoByNumber(String personNumber) {
        System.out.println("===================================");
        System.out.println("接口實現:queryPersonInfoByNumber(String personNumber)");
        System.out.println("===================================");
        return "from PersonInfoServiceImpl :" + personNumber + " 's Info.";
    }

 

(4)把這個person-center工程配置為一個dubbo的應用。

       在工程的src/main/resources下面創建文件(包括文件夾):

META-INF/spring/applicationContext.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:dubbo="http://code.alibabatech.com/schema/dubbo"
    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">
    備注1:引入dubbo配置相關的xml的命名空間

    <!-- 自動掃描注解:通過dubbo實現 -->
    <dubbo:annotation package="test.dubbo.*" />
    <!-- 必須加上:dubbo應用的名稱 -->
    <dubbo:application name="person-center" />
    <!—dubbo應用注冊到zk的地址 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />
    備注2:應用名稱+zookeeper注冊地址,讓普通應用變成一個dubbo應用。

    <!-- 用dubbo協議在20880端口暴露服務 -->
    <dubbo:protocol name="dubbo" port="20880" />
    備注3:如果該dubbo應用想成為服務端,那么配置一個dubbo協議的端口。
    <!-- 服務端聲明需要對外開放提供服務的接口 -->
    <dubbo:service interface="test.dubbo.itf.IpersonInfoService"
        protocol="dubbo" ref="personInfoService" />
    <!-- 服務端實現接口的bean -->
    <bean id="personInfoService"
        class="test.dubbo.impl.PersonInfoServiceImpl" />
    備注4:服務端聲明需要對外開放的服務接口,並且對接口關聯一個實現類。
</beans>

 

(5)編寫一個啟動類StartDubboServer,啟動這個dubbo應用。

import com.alibaba.dubbo.container.Main;

public class StartDubboServer {
    public static void main(String[] args) {
        /**
         * 通過dubbo的啟動程序,自動加載一個spring的context文件;
         * 配置文件默認指定路徑為 resources/META-INF/spring/applicationContext.xml
         */
        Main.main(null);
    }
}

  通過dubbo提供的一個容器啟動工具Main,可以啟動一個spring的context容器,並且如果不指定配置文件的話,會自動找如下路徑的文件:resources/META-INF/spring/applicationContext.xml

找到spring的xml文件后,容器加載dubbo相關配置,自動啟動一個dubbo服務。啟動成功后效果如下:

 

  這個時候,從zookeeper中可以看到,dubbo節點自動創建,並且有一個接口服務已經登記到了dubbo節點下,此時:該接口節點下面consumer節點為空,沒有消費方,provider節點有1個子節點,就是我們剛剛啟動的那個服務端:

 

 

 

至此,一個dubbo的服務端已經成功創建完成。

4.編寫person-client 客戶端

工程參考結構如下:

 

 

 

(1)創建person-center的maven工程,默認打包方式為jar包。

(2)在pom文件中需要添加的核心依賴和服務端一模一樣,不過person-interface這個jar包的依賴是用於接口的調用;

(3)編寫一個使用person-interface接口的代碼,person-interface接口通過注入的方式引用:

@Service
public class CheckPersonStatus {
    /**
     * 這個bean的實現來源於dubbo的服務
     */
    @Autowired
    private IpersonInfoService personInfoService;

    public String checkAllPersonStatus() {
        return personInfoService.queryPersonInfoAll();
    }

    public String checkPersonStatusByPersonNumber(String personNumber) {
        return personInfoService.queryPersonInfoByNumber(personNumber);
    }
}

 

(4)把這個person-center工程配置為一個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://code.alibabatech.com/schema/dubbo"
    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">
    備注1:引入dubbo配置相關的xml的命名空間

    <!-- 自動掃描注解:通過dubbo實現 -->
    <dubbo:annotation package="test.dubbo.*" />
    <!-- 必須加上:dubbo應用的名稱 -->
    <dubbo:application name="person-client" />
    <!—dubbo應用注冊的zk地址 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />
    備注2:應用名稱+zookeeper注冊地址,讓普通應用變成一個dubbo應用。
     
    <!-- 注冊需要使用的dubbo服務,通過interface指定服務接口 -->
    <dubbo:reference id="personInfoService" interface="test.dubbo.itf.IpersonInfoService"
        timeout="10000" check="false" />
    備注2:dubbo應用作為一個服務使用者,只需要聲明需要的接口服務即可。
</beans>

 

所有dubbo的應用都需要:

a、  添加dubbo應用的名稱;

b、 添加dubbo應用注冊的zookeeper的地址;

對於dubbo的應用:作為一個服務端:

a、添加對外開放的端口號;

b、添加需要開放的接口以及實現;

對於dubbo的應用:作為一個客戶端端:

a、添加需要使用的接口;

 

(5)啟動這個dubbo應用,為了使用方便,通過applicationContext進行spring的啟動:

 

public class MainClientRunner {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "META-INF/spring/applicationContext.xml");
        /**
         * 啟動spring容器,同時也會啟動dubbo的客戶端
         */
        context.start();
        /**
         * 獲取bean
         */
        CheckPersonStatus checkPersonStatus = (CheckPersonStatus) context.getBean("checkPersonStatus");
        /** 調用checkPersonStatus,checkPersonStatus調用了dubbo服務 **/
        String allInfo = checkPersonStatus.checkAllPersonStatus();
        String oneInfo = checkPersonStatus.checkPersonStatusByPersonNumber("12345678");
        System.out.println(allInfo);
        System.out.println(oneInfo);

        try {
            Thread.sleep(3600 * 24);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        /**
         * 關閉spring的容器
         */
        context.close();
    }
}

至此為止,一個dubbo的客戶端已經完成。

客戶端運行效果如下:

 

這個時候,服務端的顯示如下:

 

說明客戶端調用的dubbo服務,服務的實現,是在dubbo服務端完成的。

Zookeeper中的dubbo節點如下顯示:

 

可以看出,此時,該接口增加了一個消費者。

示例2:dubbo集群實驗

Dubbo服務的分布式主要體現在兩個方面:

(1)    多個dubbo應用,可以為同一個接口提供服務。

這個意思就是說,我們編寫了一個dubbo應用person-center,對外提供了一個test.dubbo.itf.IpersonInfoService這個接口,那么我們可以部署任意多個person-center服務端並且配置為同一套zookeeper環境,所有的服務端,都會注冊到這個zookeeper中,也就意味着有多個provider提供了這個接口服務。直接避免了單點故障,而且能夠線性提高接口服務的性能。

(2)    復雜業務系統,拆分成多個基於dubbo服務的業務子系統。

比如對於一個業務系統,可以拆分成多個子系統,子系統對外通過dubbo進行服務交互,實現功能上的分布式構建,降低系統耦合性,而且可以通過多節點,對每個功能實現集群構建。

 

1. 多節點dubbo服務實驗

(1)給person-center這個服務端添加一個調用次數統計的代碼:

 

 

通過統計調用次數,可以觀察多個服務提供方的負載均衡情況。

(2)用過eclipse啟動person-center服務端,此時配置文件中,提供服務的端口號為20880。

 

 

(3) 分別修改端口號為20881和20882之后再次啟動服務端。這樣,person-center這個服務端已經啟動了三個節點,從zookeeper中能看到:

 

 

也就是說,對於接口test.dubbo.itf.IPersonInfoService具有三個服務提供方同時提供。

(4)客戶端調用:客戶端通過一個for循環持續調用這個接口:

 

       啟動客戶端之后,客戶端會循環調用服務端1000次,這個時候,我們觀察每一個服務端的調用統計情況:

 

當客戶端停止調用時,三個服務端分別被調用了338、335、327次,說明,三個節點的調用基本上是負載均衡。

       也就是說,dubbo的服務端集群的負載均衡是在客戶端完成的,對於服務端來說是沒有感知,集群中每個節點之間是透明的,不存在類似zookeeper的leader和follower的概念。

 

示例3:dubbo構建REST服務

1. 完善person-interface接口包

之前我們創建的person-interface是一個純粹的接口代碼包,只能作為一個普通的接口,現在我們要把這個接口改造成為可以額外開放成為REST服務的一個接口包。

(1)pom文件中添加2個依賴javax.ws.rs-api和dubbo:

        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.0</version>
        </dependency>

這個依賴主要是定義了REST服務接口的注解。

Dubbo的具體依賴和上文是一樣的。

(3)    改造接口:

代碼中黃色背景部分是新增內容,下面對新增內容一一介紹如下:

 

package test.dubbo.itf;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType;

/**
 * 
 * @author ShengGang 人員數據操作接口
 *
 */
@Path("personInfoService")
@Consumes({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})
@Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})
public interface IpersonInfoService {
    /**
     * 查詢全部人員信息
     * @return
     */
    @GET
    @Path("queryPersonInfoAll")
    public String queryPersonInfoAll();

    /**
     * 根據人員編號查詢人員信息
     * @return
     */
    @POST
    @Path("queryPersonInfoByNumber/{personNumber}")
    public String queryPersonInfoByNumber(@PathParam("personNumber") String personNumber);
}

 

“@Path”,類似於webMVC中的@RequestMapping, @Path(“personInfoService”)放在了接口類上面,當訪問這個接口的方法時,相對的路徑則是/personInfoService,也就是說,當這個接口開放為REST服務的時候,請求url就是 http://localhost:9090/personInfoService/xxx;

“@Consumes”、“@Produces”,分別指定這個接口的消費方和請求需要接收和發送哪種類型的數據,ContentType里面可以選擇JSON或者是XML。如果是JSON,那么REST框架會自動進行對象和JSON格式的轉換。

“@Get”、“@Post”,指定具體的接口方法暴露為REST服務的時候是Get請求還是Post請求;

“@Path("queryPersonInfoByNumber/{personNumber}")”,這個就是標准的REST風格的URL的定義方法。請求的參數是放到URL中,{personNumber}是指,這個路徑會對應着這個方法的參數名。如果personNumber為11000123,那么請求的路徑就是

http://localhost:9090/personInfoService/queryPersonInfoByNumber/11000123

“@PathParam(“personNumber”)”,這個注解是可選的,可以指定這個參數的別名。

2. 改造person-center服務端

這個服務端的改造比interface接口包改造要簡單多,在之前的配置文件基礎上額外增加二個配置即可:開放一個REST服務端口,讓之前的接口支持REST協議。

(1)    pom文件中添加如下依賴。

<!-- 通過dubbo提供rest服務,需要引入resteasy框架 -->
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>3.0.7.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>6.1.26</version>
        </dependency>
        <!-- 數據bean的校驗框架 -->
        <dependency>
            <groupId>org.glassfish.hk2.external</groupId>
            <artifactId>bean-validator</artifactId>
            <version>2.4.0</version>
        </dependency>

 

這些依賴的作用分別是

resteasy-client:提供REST服務的封裝;

jetty:開放HTTP服務;

bean-validator:校驗入參和出參的bean;

(2)    在配置文件中添加額外添加一些配置:

 

標記的第一行意思是,這個dubbo應用開放了REST協議,並且端口號為9090。開放的http服務是通過jetty完成的,“contextpath”則是指定了根路徑。

如果需要調用REST服務,則請求路徑為:

http://localhost:9090/service/personInfoService/queryPersonInfoAll

       標記的第二行意思是,這個接口會同時支持dubbo和rest兩種協議,既可以按照一般的dubbo的調用方式去使用這個接口,也可以通過HTTP進行REST服務的調用。

       最簡單的驗證REST服務是否正確開放的方法是,直接通過瀏覽器去訪問開放的REST服務地址:

       訪問GET請求的接口:

 

 

       GET請求在瀏覽器中響應正常。

       訪問POST請求的接口:

 

 

       POST請求在瀏覽器中響應為空,控制台會提示405,意思是該URL只會接收POST請求,而不會接收GET請求。

 

示例4:dubbo服務使用數據庫

       主要改造的工程為person-center服務端,讓這個dubbo服務端真正連接數據庫,通過接口返回數據庫數據。

1.      添加maven依賴

 

<!-- 引入spring的jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <!-- 使用阿里的druid數據源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>0.2.8</version>
            <exclusions>
                <exclusion>
                    <artifactId>tools</artifactId>
                    <groupId>com.alibaba</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>jconsole</artifactId>
                    <groupId>com.alibaba</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- mysql的連接器 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.21</version>
        </dependency>

在person-center項目的pom.xml中,添加如下maven依賴:

其中:

spring-jdbc.jar提供了jdbcTemplate的數據庫操作api;

druid.jar為阿里的數據源;

mysql-connector-java.jar為mysql的java連接驅動;

2.      修改applicationContext.xml文件

修改person-interface下面的META-INF/spring/applicationContext.xml文件,添加如下bean的配置:

 

<!-- 指定spring需要加載的配置文件 -->    
    <bean
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:config.properties" />
    </bean>
    
    <!-- 采用druid的數據源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="initialSize" value="${jdbc.initialSize}" />
        <property name="maxActive" value="${jdbc.maxActive}" />
        <property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
        <property name="validationQuery" value="${jdbc.validationQuery}" />
    </bean>
    
    <!-- jdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

和之前的spring文件相比,主要添加了三個bean:

(1)    PropertySourcesPlaceholderConfigurer:這個bean指定了工程路徑里面的resources/config.properties文件,那么spring的xml文件中所有的屬性配置都通過配置文件去讀取和修改;

(2)    DataSource:配置了一個數據源,指定了url、username和passwd等數據庫連接相關配置;

(3)    jdbcTemplate:由spring提供的一個jdbc操作API,可以通過這個實例輕松進行數據庫增刪改查等操作;

 

3.      創建dao層

(1)    在工程中添加一個dao的package,用於編寫dao層,工程結構如下:

 

 

(2)編寫RowMapper類

由於沒有采用Hibernate等持久層框架,而是直接采用的jdbcTemplate操作數據庫,因此並沒有把數據庫字段和實體類PersonInfo進行一一映射。所以,我們需要編寫一個RowMapper類把數據庫的數據字段一一映射成為PersonInfo的字段。該類主要繼承了spring的RowMapper接口,代碼如下:

package test.dubbo.dao.wrapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import test.dubbo.entity.PersonInfo;

public class PersonInfoRowMapper implements RowMapper<PersonInfo> {

    @Override
    public PersonInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
        PersonInfo info = new PersonInfo();
        info.setPersonNumber(rs.getString("person_number"));
        info.setPersonAddr(rs.getString("person_address"));
        info.setPersonName(rs.getString("person_name"));
        info.setPersonPhoneNumber(rs.getString("person_phone_number"));
        info.setPersonStatus(rs.getString("person_status"));
        return info;
    }
}

 

(3)    編寫PersonInfoDao類

package test.dubbo.dao;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import test.dubbo.dao.wrapper.PersonInfoRowMapper;
import test.dubbo.entity.PersonInfo;

@Repository
public class PersonInfoDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public PersonInfo queryPersonInfo(String personNumber) {
        String sql = "select * from person_info where person_number = ?";
        return jdbcTemplate.queryForObject(sql, new PersonInfoRowMapper(), personNumber);
    }

    public List<PersonInfo> queryPersonInfoList() {
        String sql = "select * from person_info";
        return jdbcTemplate.query(sql, new PersonInfoRowMapper());
    }
}    

 

PersonInfoDao使用了jdbcTemplate,從數據庫進行讀寫操作,代碼如下:

這里主要通過jdbcTemplate執行了queryForObject和query兩個操作,因為我們用了之前編寫的繼承了RowMapper接口的PersonInfoRowMapper類,因此,查詢結果直接返回成了PersonInfo對象。

4.      修改Service層的實現類

主要參考代碼:

 

@Service
public class PersonInfoServiceImpl implements IpersonInfoService {
    private static final AtomicInteger count = new AtomicInteger(0);
    @Autowired
    private PersonInfoDao personInfoDao;
    private static final String ERROR_INFO = "{\"errorCode\":\"-1\"}";
    @Override
    public String queryPersonInfoAll() {
        System.out.println("===================================");
        System.out.println("接口實現:queryPersonInfoAll()");
        List<PersonInfo> infoList = personInfoDao.queryPersonInfoList();
        String rtnInfo;
        try {
             rtnInfo = JSON.json(infoList);
        } catch (IOException e) {
            rtnInfo = ERROR_INFO;
            e.printStackTrace();
        }
        System.out.println("===================================");
        return rtnInfo;
    }
    @Override
    public String queryPersonInfoByNumber(String personNumber) {
        count.incrementAndGet();
        System.out.println("===================================");
        System.out.println("接口實現:queryPersonInfoByNumber(String personNumber)");
        System.out.println("接口被調用了"+count.get()+"次。");
        PersonInfo personInfo = personInfoDao.queryPersonInfo(personNumber);
        String rtnInfo;
        try {
             rtnInfo = JSON.json(personInfo);
        } catch (IOException e) {
            rtnInfo = ERROR_INFO;
            e.printStackTrace();
        }
        System.out.println("===================================");
        return rtnInfo;
    }
}

代碼注意事項如下:

(1)    service層注入了personInfoDao這個實例;

(2)    通過調用personInfoDao中的方法,獲取到PersonInfo對象以及對象的List,但是我們需要把對象轉成json格式,這里采用阿里的JSON包---

com.alibaba.dubbo.common.json.*

 

到此為止,dubbo服務端使用數據庫部分已經改造完成。

 

示例5:通過dubbo構建復雜業務場景

1.      模擬的場景結構圖

 

 

這里,作為模擬場景,我們有兩個dubbo服務的提供方,person-center和product-center,這兩個dubbo應用分別表示兩個獨立的系統,但是都注冊到同一套zookeeper注冊中心。order-system-service表示一個第三方綜合系統,會使用person-center和product-center兩個系統的接口,同時,自己會對外開放自己的dubb/REST接口。

  1. 具體的代碼可以參考附件內容,里面所有的配置都是之前已經提到過的內容。

其他:常見異常問題及解決

1.      啟動dubbo服務端后連接zookeeper出現如下異常:

INFO : org.apache.zookeeper.ZooKeeper - Client environment:os.name=Windows 7
INFO : org.apache.zookeeper.ZooKeeper - Client environment:os.arch=amd64
INFO : org.apache.zookeeper.ZooKeeper - Client environment:os.version=6.1
INFO : org.apache.zookeeper.ZooKeeper - Client environment:user.name=zhaijj
INFO : org.apache.zookeeper.ZooKeeper - Client environment:user.home=C:\Users\zhaijj
INFO : org.apache.zookeeper.ZooKeeper - Client environment:user.dir=D:\workdir\person-center
INFO : org.apache.zookeeper.ZooKeeper - Initiating client connection, connectString=192.168.159.5:2181 sessionTimeout=60000 watcher=org.I0Itec.zkclient.ZkClient@2a2b2bd1
INFO : org.apache.zookeeper.ClientCnxn - Opening socket connection to server /192.168.159.5:2181
INFO : org.I0Itec.zkclient.ZkEventThread - Terminate ZkClient event thread.
INFO : org.apache.zookeeper.ClientCnxn - Socket connection established to 192.168.159.5/192.168.159.5:2181, initiating session
INFO : org.apache.zookeeper.ClientCnxn - Session establishment complete on server 192.168.159.5/192.168.159.5:2181, sessionid = 0x158ced3a4380008, negotiated timeout = 40000
INFO : org.apache.zookeeper.ZooKeeper - Session: 0x158ced3a4380008 closed
INFO : org.apache.zookeeper.ClientCnxn - EventThread shut down
org.I0Itec.zkclient.exception.ZkTimeoutException: Unable to connect to zookeeper server within timeout: 5000
    at org.I0Itec.zkclient.ZkClient.connect(ZkClient.java:876)
    at org.I0Itec.zkclient.ZkClient.<init>(ZkClient.java:98)
    at org.I0Itec.zkclient.ZkClient.<init>(ZkClient.java:92)
    at org.I0Itec.zkclient.ZkClient.<init>(ZkClient.java:80)
    at com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperClient.<init>(ZkclientZookeeperClient.java:26)

  然而,zookeeper是正常的,排除任何網絡、防火牆原因。

  原因:zkClient客戶端版本過低,配置的是0.1,引用的zookeeper的jar包是3.3.3,然而服務端的zookeeper版本比較高,達到3.4.6。

  解決方式:升高zkClient的版本:

        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.9</version>
        </dependency>

這個最高版本的zkClient用的是zookeeper-3.4.8.jar 。更換包后,正常運行。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM