Java RPC原理及Dubbo的實踐應用

[TOC]java

1、RPC原理

RPC(Remote Procedure Call)即遠程過程調用,也就是說兩臺服務器A,B,一個應用部署在A服務器上,想要調用B服務器上應用提供的函數/方法,因爲不在一個內存空間,不能直接調用,須要經過網絡來表達調用的語義和傳達調用的數據。git

1.框架原理

在RPC框架中主要有三個角色:Provider、Consumer和Registry。以下圖所示: github

RPC原理

節點角色說明:web

  • Server: 暴露服務的服務提供方。算法

  • Client: 調用遠程服務的服務消費方。spring

  • Registry: 服務註冊與發現的註冊中心。apache

2.RPC的調用該流程

RPC調用流程

1.調用客戶端句柄;執行傳送參數

2.調用本地系統內核發送網絡消息

3.消息傳送到遠程主機

4.服務器句柄獲得消息並取得參數

5.執行遠程過程

6.執行的過程將結果返回服務器句柄

7.服務器句柄返回結果,調用遠程系統內核

8.消息傳回本地主機

9.客戶句柄由內核接收消息

10.客戶接收句柄返回的數據
複製代碼

3.服務註冊與發現

服務提供者啓動後主動向註冊中心註冊機器ip、port以及提供的服務列表;json

服務消費者啓動時向註冊中心獲取服務提供方地址列表,可實現軟負載均衡和Failover;api

2.使用到的技術要點

一、動態代理

生成 client stub和server stub須要用到 Java 動態代理技術 ,咱們可使用JDK原生的動態代理機制,可使用一些開源字節碼工具框架 如:CgLib、Javassist等。

二、序列化

爲了能在網絡上傳輸和接收 Java對象,咱們須要對它進行 序列化和反序列化操做。

* 序列化:將Java對象轉換成byte[]的過程,也就是編碼的過程;

* 反序列化:將byte[]轉換成Java對象的過程;

可使用Java原生的序列化機制,可是效率很是低,推薦使用一些開源的、成熟的序列化技術,例如:protobuf、Thrift、hessian、Kryo、Msgpack

關於序列化工具性能比較能夠參考:jvm-serializers

三、NIO

當前不少RPC框架都直接基於netty這一IO通訊框架,好比阿里巴巴的HSF、dubbo,Hadoop Avro,推薦使用Netty 做爲底層通訊框架。

四、服務註冊中心
複製代碼

可選技術:瀏覽器

  • Redis

  • Zookeeper

  • Consul

  • Etcd

5.RPC功能目標

PC 的主要功能目標是讓構建分佈式計算(應用)更容易,在提供強大的遠程調用能力時不損失本地調用的語義簡潔性。 爲實現該目標,RPC 框架需提供一種透明調用機制讓使用者沒必要顯式的區分本地調用和遠程調用。 下面咱們將具體細化 stub 結構的實現。

6. RPC調用分類

RPC 調用分如下兩種:

1. 同步調用
客戶方等待調用執行完成並返回結果。
2. 異步調用
客戶方調用後不用等待執行結果返回,但依然能夠經過回調通知等方式獲取返回結果。 若客戶方不關心調用返回結果,則變成單向異步調用,單向調用不用返回結果。
複製代碼

7.RPC結構拆分

RPC結構拆分

RPC 服務方經過 RpcServer 去導出(export)遠程接口方法,而客戶方經過 RpcClient 去引入(import)遠程接口方法。 客戶方像調用本地方法同樣去調用遠程接口方法,RPC 框架提供接口的代理實現,實際的調用將委託給代理 RpcProxy 。 代理封裝調用信息並將調用轉交給 RpcInvoker 去實際執行。 在客戶端的 RpcInvoker 經過鏈接器 RpcConnector 去維持與服務端的通道 RpcChannel, 並使用 RpcProtocol 執行協議編碼(encode)並將編碼後的請求消息經過通道發送給服務方。

RPC 服務端接收器 RpcAcceptor 接收客戶端的調用請求,一樣使用 RpcProtocol 執行協議解碼(decode)。 解碼後的調用信息傳遞給 RpcProcessor 去控制處理調用過程,最後再委託調用給 RpcInvoker 去實際執行並返回調用結果。

8.Java 經常使用的RPC框架

Java中的RPC框架比較多,各有特點,普遍使用的有RMI、Hessian、Dubbo等。

8.開源的優秀RPC框架

2、Dubbo框架

1.框架介紹

1.1 基本介紹

Dubbo是阿里巴巴SOA服務化治理方案的核心框架,天天爲2,000+個服務提供3,000,000,000+次訪問量支持,並被普遍應用於阿里巴巴集團的各成員站點。 Dubbo[]是一個分佈式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案,以及SOA服務治理方案。

其核心部分包含:

  • 遠程通信: 提供對多種基於長鏈接的NIO框架抽象封裝,包括多種線程模型,序列化,以及「請求-響應」模式的信息交換方式。
  • 集羣容錯: 提供基於接口方法的透明遠程過程調用,包括多協議支持,以及軟負載均衡,失敗容錯,地址路由,動態配置等集羣支持。
  • 自動發現: 基於註冊中心目錄服務,使服務消費方能動態的查找服務提供方,使地址透明,使服務提供方能夠平滑增長或減小機器。

1.2 背景

隨着互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已沒法應對,分佈式服務架構以及流動計算架構勢在必行,亟需一個治理系統確保架構有條不紊的演進

當服務愈來愈多時,服務URL配置管理變得很是困難,F5硬件負載均衡器的單點壓力也愈來愈大。

當進一步發展,服務間依賴關係變得錯蹤複雜,甚至分不清哪一個應用要在哪一個應用以前啓動,架構師都不能完整的描述應用的架構關係。

接着,服務的調用量愈來愈大,服務的容量問題就暴露出來,這個服務須要多少機器支撐?何時該加機器?

1.3Dubbo架構

Dubbo結構

Dubbo架構

Dubbo

節點角色說明:

  • Provider: 暴露服務的服務提供方。
  • Consumer: 調用遠程服務的服務消費方。
  • Registry: 服務註冊與發現的註冊中心。
  • Monitor: 統計服務的調用次調和調用時間的監控中心。
  • Container: 服務運行容器。 調用關係說明
0. 服務容器負責啓動,加載,運行服務提供者。

1. 服務提供者在啓動時,向註冊中心註冊本身提供的服務。

2. 服務消費者在啓動時,向註冊中心訂閱本身所需的服務。

3. 註冊中心返回服務提供者地址列表給消費者,若是有變動,註冊中心將基於長鏈接推送變動數據給消費者。

4. 服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,若是調用失敗,再選另外一臺調用。

5. 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。
複製代碼

3、項目實例

1.1 項目背景介紹

此項目以SpringBoot爲基礎,集成aliaba dubbo-spring-boot-stater ,不以apache最新孵化Dubbo項目爲搭建環境,採用注入風格,前提須要準備zookeeper環境,確保服務可以注入到zookeeper節點。 完整項目源碼GitHub 地址:Dubbo-SpringBoot

1.2 項目模塊說明

項目模塊分爲 dubbo-api、springBoot-dubbo-consumer、springboot-dubbo-provider 三個模塊、將接口抽取出來爲dubbo-api,便於消費者使用,提供者實現。 項目結構以下圖所示:

AoKy8g.png
詳細結構說明以下:

.Dubbo
├── dubbo-api
│   ├── dubboapi.iml
│   ├── pom.xml
│   ├── src
│   │   ├── main
│   │   └── test
│   └── target
│       ├── classes
│       ├── generated-sources
│       └── maven-status
├── Dubbo.iml
├── pom.xml
├── springboot-dubbo-consumer
│   ├── HELP.md
│   ├── pom.xml
│   ├── springboot-dubbo-consumer.iml
│   ├── src
│   │   ├── main
│   │   └── test
│   └── target
│       ├── classes
│       ├── generated-sources
│       ├── generated-test-sources
│       ├── maven-archiver
│       ├── maven-status
│       ├── springboot-dubbo-consumer-0.0.1-SNAPSHOT.jar
│       ├── springboot-dubbo-consumer-0.0.1-SNAPSHOT.jar.original
│       ├── surefire-reports
│       └── test-classes
└── springboot-dubbo-provider
    ├── HELP.md
    ├── pom.xml
    ├── springboot-dubbo-provider.iml
    ├── src
    │   ├── main
    │   └── test
    └── target
        ├── classes
        ├── generated-sources
        ├── generated-test-sources
        ├── maven-archiver
        ├── maven-status
        ├── springboot-dubbo-provider-0.0.1-SNAPSHOT.jar
        ├── springboot-dubbo-provider-0.0.1-SNAPSHOT.jar.original
        ├── surefire-reports
        └── test-classes

複製代碼

1.3 項目搭建

  • Step1:

Idea建立一個空的maven工程,在建立一個maven模塊dubbo-api,在dubbo-api 建立一個接口RemoteUserService,用於消費者調用,提供者實現。

package com.yongliang.dubbo.api;

/**
 * @version 1.0.0
 */
public interface RemoteUserService {
    String sayHello(String name);
}
複製代碼
  • Step 2:

建立springBoot模塊 springboot-dubbo-provider, 配置pom.xml 信息以下:

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yongliang.dubbo</groupId>
    <artifactId>springboot-dubbo-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-dubbo-provider</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>
        <!-- dubbo依賴 -->
        <dependency>
            <groupId>com.alibaba.spring.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.6</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <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>Dubbo</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>25.1-jre</version>
        </dependency>
    </dependencies>

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

</project>

複製代碼

編輯application.properites 文件,加入dubbo配置參數

server.port=8082
server.servlet.context-path=/DubboProvider
## dubbo 
spring.dubbo.application.id=dubbo-provider
spring.dubbo.application.name=dubbo-provider
#spring.dubbo.provider.filter=dubboLogFilter
# zookeeper 註冊信息
spring.dubbo.registry.address=zookeeper://10.18.33.158:2181
spring.dubbo.server=true
通訊協議
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20880
複製代碼

在啓動入口增長dubbo自動註解

package com.yongliang.dubbo.provider;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubboConfiguration
@SpringBootApplication
public class SpringbootDubboProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDubboProviderApplication.class, args);
    }

}

複製代碼

在provider 模塊實現接口RemoteUserService

package com.yongliang.dubbo.provider.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.yongliang.dubbo.api.RemoteUserService;
import org.springframework.stereotype.Component;

/**
 * @author zhangyongliang
 * @create 2019-04-08 18:44
 **/
@Service(interfaceClass = RemoteUserService.class,retries = 0,version = "1.0.0")
@Component
public class RemoteServiceimpl implements RemoteUserService {
    @Override
    public String sayHello(String name) {
        return "Hello"+name;
    }
}

複製代碼

retries標識,失敗後重復調用次數,version表明接口實現版本,此版本號與消費者實際依賴保持一致。

  • Step3:

建立springboot-dubbo-consumer 消費者模塊,pom.xml 配置信息以下:

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yongliang.dubbo</groupId>
    <artifactId>springboot-dubbo-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-dubbo-consumer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>
        <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>com.alibaba.spring.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.6</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>Dubbo</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

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

</project>
複製代碼

修改 application.properties 文件,增長相關dubbo配置信息

server.port=8081
server.servlet.context-path=/DubboConsumer
## dubbo
spring.dubbo.application.name=dubbo-consumer
spring.dubbo.application.id=dubbo-consumer
spring.dubbo.registry.address=zookeeper://10.18.33.158:2181
spring.dubbo.consumer.check=false
spring.dubbo.reference.check=false
複製代碼

在啓動入口增長dubbo自動配置註解

package com.yongliang.dubbo.consumer;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubboConfiguration
@SpringBootApplication
public class SpringbootDubboConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDubboConsumerApplication.class, args);
    }

}

複製代碼

實現Rest控制類,實現方法調用:

package com.yongliang.dubbo.consumer.rest;

import com.alibaba.dubbo.config.annotation.Reference;
import com.yongliang.dubbo.api.RemoteUserService;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author zhangyongliang
 * @create 2019-04-08 18:48
 **/
@RestController
public class RemoteUserRest  {
    @Reference(version = "1.0.0")
    private RemoteUserService remoteUserService;

    @GetMapping("/dubboTest")
    public String dubboTest() {
        return remoteUserService.sayHello("dubbo");
    }
}

複製代碼
打開瀏覽器訪問:localhost:8081/DubboConsumer/dubboTest
複製代碼

請求成功後返回:HelloDubbo則證實Dubbo集成成功。 此過程沒有說明父 pom.xml 配置信息,讀者自行參考源碼組織。

相關文章
相關標籤/搜索