SpringBoot系列之集成Dubbo示例教程
一、分布式基本理論
1.1、分布式基本定義
《分布式系統(tǒng)原理與范型》定義:
“分布式系統(tǒng)是若干獨(dú)立計(jì)算機(jī)的集合,這些計(jì)算機(jī)對于用戶來說就像單個(gè)相關(guān)系統(tǒng)”
分布式系統(tǒng)(distributed system)是建立在網(wǎng)絡(luò)之上的軟件系統(tǒng)。
1.2 架構(gòu)發(fā)展演變
架構(gòu)的發(fā)展是由最初的單一應(yīng)用架構(gòu)構(gòu)建的,一般就是ORM框架方便數(shù)據(jù)庫操作。
不過隨著系統(tǒng)越來越復(fù)雜,單一應(yīng)用架構(gòu)會(huì)變得難以維護(hù),所以架構(gòu)逐漸演變出了垂直應(yīng)用架構(gòu),所謂垂直應(yīng)用架構(gòu)其實(shí)就是安裝業(yè)務(wù)模板進(jìn)行拆分,比如可以安裝業(yè)務(wù)將一個(gè)電商系統(tǒng)分為訂單模塊,用戶信息管理模塊,商品管理模塊等等,這時(shí)候MVC框架就派上用場,MVC框架可以協(xié)助系統(tǒng)更好的按業(yè)務(wù)拆分,不過業(yè)務(wù)拆分后雖然是比單一應(yīng)用架構(gòu)更好維護(hù)了。
不過隨著系統(tǒng)越來約復(fù)雜,發(fā)現(xiàn)很多共用的模塊很難復(fù)用起來,這時(shí)候分布式服務(wù)架構(gòu)登場了,分布式架構(gòu)是將一些核心業(yè)務(wù)抽取出來,作為獨(dú)立的服務(wù),逐漸形成穩(wěn)定的服務(wù)中心,當(dāng)應(yīng)用需要時(shí),就去服務(wù)中心調(diào)服務(wù)就可以,而實(shí)現(xiàn)這種服務(wù)注冊的肯定是RPC框架了。
當(dāng)服務(wù)越來越多,容量的評估,小服務(wù)資源的浪費(fèi)等問題逐漸顯現(xiàn),此時(shí)需增加一個(gè)調(diào)度中心基于訪問壓力實(shí)時(shí)管理集群容量,提高集群利用率,這時(shí)候就需要流動(dòng)計(jì)算架構(gòu)(SOA)[ Service Oriented Architecture],用于提高機(jī)器利用率的資源調(diào)度,SOA是一個(gè)治理中心,綜上所述,到目前,軟件系統(tǒng)架構(gòu)演變經(jīng)歷了:單一應(yīng)用架構(gòu)->垂直應(yīng)用架構(gòu)->分布式應(yīng)用架構(gòu)->流動(dòng)計(jì)算架構(gòu),下面Dubbo官網(wǎng)的圖片可以很好的描述
1.3、RPC簡介
RPC概念
RPC【Remote Procedure Call】是指遠(yuǎn)程過程調(diào)用,是一種進(jìn)程間通信方式,他是一種技術(shù)的思想,而不是規(guī)范。它允許程序調(diào)用另一個(gè)地址空間(通常是共享網(wǎng)絡(luò)的另一臺(tái)機(jī)器上)的過程或函數(shù),而不用程序員顯式編碼這個(gè)遠(yuǎn)程調(diào)用的細(xì)節(jié)。
RPC核心模塊
RPC有兩個(gè)核心模塊:通信和序列化
二、Dubbo理論簡介
Apache Dubbo (incubating) |?d?b??| 是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向接口的遠(yuǎn)程方法調(diào)用,智能容錯(cuò)和負(fù)載均衡,以及服務(wù)自動(dòng)注冊和發(fā)現(xiàn)。
官網(wǎng):
http://dubbo.apache.org/

Dubbo的服務(wù)治理:
Dubbo原理圖片,圖片來自Dubbo官網(wǎng):

Dubbo角色:
Provider:暴露服務(wù)的服務(wù)提供者
Container:服務(wù)運(yùn)行的容器
Consumer:調(diào)用遠(yuǎn)程服務(wù)的消費(fèi)者
Registry:服務(wù)注冊和發(fā)現(xiàn)的注冊中心
Minitor:統(tǒng)計(jì)服務(wù)調(diào)用次數(shù)和時(shí)間的監(jiān)控中心
調(diào)用過程:
下面根據(jù)我的理解說明一下
0:服務(wù)器容器負(fù)責(zé)啟動(dòng)、加載、運(yùn)行服務(wù)提供者
1:服務(wù)提供者在啟動(dòng)后就可以向注冊中心暴露服務(wù)
2:服務(wù)消費(fèi)者在啟動(dòng)后就可以向注冊中心訂閱想要的服務(wù)
3:注冊中心向服務(wù)消費(fèi)者返回服務(wù)調(diào)用列表
4:服務(wù)消費(fèi)者基于軟負(fù)載均衡算法調(diào)用服務(wù)提供者的服務(wù),這個(gè)服務(wù)提供者有可能是一個(gè)服務(wù)提供者列表,調(diào)用那個(gè)服務(wù)提供者就是根據(jù)負(fù)載均衡來調(diào)用了
5:服務(wù)提供者和服務(wù)消費(fèi)者定時(shí)將保存在內(nèi)存中的服務(wù)調(diào)用次數(shù)和服務(wù)調(diào)用時(shí)間推送給監(jiān)控中心
三、Dubbo環(huán)境搭建
3.1 Zookeeper搭建
搭建Zookeeper,首先是搭建分布式架構(gòu)的注冊中心Zookeeper,當(dāng)然也可以用Redis等等來做服務(wù)注冊中心,不過本博客只介紹Zookeeper的,因?yàn)闆]有l(wèi)inux服務(wù)器,所以只介紹window版的搭建
1、下載Zookeeper:
網(wǎng)址?https://archive.apache.org/dist/zookeeper/zookeeper-3.4.13/2、解壓Zookeeper
解壓Zookeeper之后,運(yùn)行bin目錄里的zkServer.cmd,發(fā)現(xiàn)報(bào)錯(cuò)了,提示找不到配置文件,所以需要繼續(xù)步驟33、配置Zookeeper
因?yàn)閆ookeeper的conf文件夾下面只提供zoo_sample.cfg文件,需要自己修改命名為zoo.cfg
對于配置文件需要注意:
dataDir=./ 臨時(shí)數(shù)據(jù)存儲(chǔ)的目錄(可寫相對路徑)
clientPort=2181 zookeeper的端口號(hào)
4、使用zkCli.cmd測試
修改配置文件后,重新啟動(dòng)zkServer.cmd,啟動(dòng)bin目錄下面的zkCli.cmd,很顯然這是個(gè)客戶端程序,注意zkServer.cmd是服務(wù)端程序,必須啟動(dòng)
ok,簡單在zkCli.cmd敲幾個(gè)命令測試一下:
ls /:列出zookeeper根下保存的所有節(jié)點(diǎn)
create –e /testNode 12345678:創(chuàng)建一個(gè)testNode節(jié)點(diǎn),值為12345678
get /testNode:獲取/testNode節(jié)點(diǎn)的值
3.2 Dubbo管理頁面搭建
搭建了服務(wù)注冊中心后,就需要搭建Dubbo-admin了,最近看了一下,dubbo的Github項(xiàng)目已經(jīng)進(jìn)行了更新,管理平臺(tái)已經(jīng)做了比較大的改動(dòng),而我學(xué)習(xí)的時(shí)候,平臺(tái)是比較簡單的,所以本dubbo-admin搭建是以舊版master的為準(zhǔn),不過以學(xué)習(xí)為目的的,只需要知道具體原理和操作技巧就可以
下載dubbo-admin
去下載一下dubbo-admin,可以找主干master分支的,找到dubbo-admin,git clone到本地
https://github.com/apache/incubator-dubbo-ops
因?yàn)槲掖罱〞r(shí)候(ps:不是博客寫作時(shí)間),dubbo還沒做比較大改動(dòng),所以我以比較舊的版本為例子,現(xiàn)在新的具體參考dubbo官方的教程,本博客只是做記錄
修改dubbo-admin
修改 src\main\resources\application.properties 指定zookeeper地址
Maven package dubbo-admin
mvn clean package -Dmaven.test.skip=true
運(yùn)行dubbo-admin的jar
maven打包之后,就去target里找到j(luò)ar,然后cmd運(yùn)行
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
運(yùn)行成功之后,訪問:?http://127.0.0.1:7001,輸入默認(rèn)的賬號(hào)密碼root/root,登錄成功

四、Dubbo服務(wù)注冊發(fā)現(xiàn)例子
經(jīng)典例子(引用尚硅谷教程例子進(jìn)行改寫):
4.1、業(yè)務(wù)場景
某個(gè)電商系統(tǒng),訂單服務(wù)需要調(diào)用用戶服務(wù)獲取某個(gè)用戶的所有地址;
我們現(xiàn)在 需要?jiǎng)?chuàng)建兩個(gè)服務(wù)模塊進(jìn)行測試
| 模塊 | 功能 |
|---|---|
| 訂單服務(wù)模塊 | 創(chuàng)建訂單等 |
| 用戶服務(wù)模塊 | 查詢用戶地址等 |
測試預(yù)期結(jié)果:
訂單服務(wù)web模塊在A服務(wù)器,用戶服務(wù)模塊在B服務(wù)器,A可以遠(yuǎn)程調(diào)用B的功能
4.2、api工程創(chuàng)建
創(chuàng)建工程:
建議將服務(wù)接口,服務(wù)模型,服務(wù)異常等均放在 API 包中,因?yàn)榉?wù)模型及異常也是 API 的一部分,同時(shí),這樣做也符合分包原則:重用發(fā)布等價(jià)原則(REP),共同重用原則(CRP)。
創(chuàng)建一個(gè)API工程,將實(shí)體類和接口都放在api工程
maven新建一個(gè)shop-api-common工程:
用戶地址DTO類:
package com.test.dubbo.bean;
import java.io.Serializable;
public class UserAddress implements Serializable {
private Integer id;
private String userAddress; //用戶地址
private String userId; //用戶id
private String consignee; //收貨人
private String phoneNum; //電話號(hào)碼
private String isDefault; //是否為默認(rèn)地址 Y-是 N-否
public UserAddress() {
super();
}
public UserAddress(Integer id, String userAddress, String userId, String consignee, String phoneNum,
String isDefault) {
super();
this.id = id;
this.userAddress = userAddress;
this.userId = userId;
this.consignee = consignee;
this.phoneNum = phoneNum;
this.isDefault = isDefault;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getConsignee() {
return consignee;
}
public void setConsignee(String consignee) {
this.consignee = consignee;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
public String getIsDefault() {
return isDefault;
}
public void setIsDefault(String isDefault) {
this.isDefault = isDefault;
}
}
用戶信息服務(wù)接口:
package com.test.dubbo.service;
import java.util.List;
import com.test.dubbo.bean.UserAddress;
/**
* 用戶服務(wù)
*/
public interface UserService {
/**
* 按照用戶id返回所有的收貨地址
* @param userId
* @return
*/
public List getUserAddressList(String userId);
}
訂單信息服務(wù)接口:
package com.test.dubbo.service;
import java.util.List;
import com.test.dubbo.bean.UserAddress;
public interface OrderService {
/**
* 初始化訂單
* @param userId
*/
public List initOrder(String userId);
}
ok,創(chuàng)建好api工程
4.3、服務(wù)提供者工程
要實(shí)現(xiàn)服務(wù)提供,配置文件主要需要配置如下:
Dubbo提供者加載過程(Dubbo容器的啟動(dòng)):
Spring加載xml配置之后暴露服務(wù)的過程:
Exporter方法主要是打開socket的監(jiān)聽,接收客戶的請求
ok,理解了上面的理論知識(shí)后,繼續(xù)創(chuàng)建一個(gè)user-service-provider工程:
參考Dubbo官方例子
2.2.1.RELEASE
2.7.5
2.12.0
org.apache.dubbo
dubbo-spring-boot-starter
${dubbo.version}
org.apache.dubbo
dubbo-dependencies-zookeeper
${dubbo.version}
pom
org.slf4j
slf4j-log4j12
我用的Springboot版本是2.2.1,dubbo starter版本是2.7.5,啟動(dòng)之后,發(fā)現(xiàn)報(bào)錯(cuò)
Caused by: java.lang.: org.apache.curClassNotFoundExceptionator.framework.CuratorFrame
找不到對應(yīng)的類,看起來是缺少jar了?通過網(wǎng)上資料搜索再加上如下配置即可:
org.apache.curator
curator-framework
${curator.version}
org.apache.curator
curator-recipes
${curator.version}
curator是Zookeeper配置需要的
maven配置好之后,就可以進(jìn)行dubbo配置:
#server.port=7010
dubbo.application.name=user-service-provider
dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper
dubbo.protocol.name=dubbo
dubbo.protocol.port=20882
dubbo.monitor.protocol=registry
#dubbo.scan.base-packages=com.example.springboot.dubbo
用戶服務(wù)類:
package com.example.springboot.dubbo.service.impl;
import com.example.spring.dubbo.bean.UserAddress;
import com.example.spring.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Service//暴露服務(wù)
@Component
public class UserServiceImpl implements UserService {
@Override
public List getUserAddressList(String userId) {
UserAddress address1 = new UserAddress(1, "北京市昌平區(qū)", "1", "李老師", "010-56253825", "Y");
UserAddress address2 = new UserAddress(2, "深圳市寶安區(qū)", "1", "王老師", "010-56253825", "N");
return Arrays.asList(address1,address2);
}
}
Springboot的啟動(dòng)類要加上@EnableDubbo注解:
package com.example.springboot.dubbo;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
* Springboot啟動(dòng)類
*
*
* @author nicky
*
* 修改記錄
* 修改后版本: 修改人:修改日期: 2020年01月05日 修改內(nèi)容:
*
*/
@EnableDubbo(scanBasePackages="com.example.springboot.dubbo.service.impl")
@SpringBootApplication
public class UserServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceProviderApplication.class, args);
}
}
啟動(dòng)Springboot類,然后在監(jiān)控平臺(tái)是可以看到服務(wù)注冊成功的

查看服務(wù)接口的詳細(xì)信息:

4.4、服務(wù)消費(fèi)者工程
然后服務(wù)已經(jīng)注冊了,現(xiàn)在創(chuàng)建一個(gè)消費(fèi)者工程order-service-comsumer
maven加上配置
2.2.1.RELEASE
2.7.5
2.12.0
org.springframework.boot
spring-boot-starter-web
org.apache.dubbo
dubbo-spring-boot-starter
${dubbo.version}
org.apache.dubbo
dubbo-dependencies-zookeeper
${dubbo.version}
pom
org.slf4j
slf4j-log4j12
org.apache.curator
curator-framework
${curator.version}
org.apache.curator
curator-recipes
${curator.version}
com.example.springboot
shop-api-common
0.0.1-SNAPSHOT
消費(fèi)者配置文件:
server.port=8081
dubbo.application.name=order-service-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.monitor.protocol=registry
訂單服務(wù)類:
package com.example.springboot.dubbo.service.impl;
import com.example.spring.dubbo.bean.UserAddress;
import com.example.spring.dubbo.service.OrderService;
import com.example.spring.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import java.util.List;
/**
*
* 訂單服務(wù)
*
*
* @author nicky
*
* 修改記錄
* 修改后版本: 修改人:修改日期: 2020年01月05日 修改內(nèi)容:
*
*/
@Service
public class OrderServiceImpl implements OrderService{
@Reference(loadbalance="random",timeout=1000) //dubbo直連
UserService userService;
@Override
public List initOrder(String userId) {
//1、查詢用戶的收貨地址
List addressList = userService.getUserAddressList(userId);
return addressList;
}
}
Springboot啟動(dòng)
package com.example.springboot.dubbo;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
* Springboot啟動(dòng)類
*
*
* @author nicky
*
* 修改記錄
* 修改后版本: 修改人:修改日期: 2020年01月05日 修改內(nèi)容:
*
*/
@EnableDubbo(scanBasePackages="com.example.springboot.dubbo.service.impl")
@SpringBootApplication
public class OrderServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceProviderApplication.class, args);
}
}
寫一個(gè)Controller類進(jìn)行測試:
package com.example.springboot.dubbo.controller;
import com.example.spring.dubbo.bean.UserAddress;
import com.example.spring.dubbo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class OrderController {
@Autowired
OrderService orderService;
@ResponseBody
@RequestMapping("/initOrder")
public List initOrder(@RequestParam("uid")String userId) {
return orderService.initOrder(userId);
}
}
在監(jiān)控平臺(tái)看,消費(fèi)者啟動(dòng)也是可以的


騰訊、阿里、滴滴后臺(tái)面試題匯總總結(jié) — (含答案)
面試:史上最全多線程面試題 !
最新阿里內(nèi)推Java后端面試題
JVM難學(xué)?那是因?yàn)槟銢]認(rèn)真看完這篇文章

關(guān)注作者微信公眾號(hào) —《JAVA爛豬皮》
了解更多java后端架構(gòu)知識(shí)以及最新面試寶典


看完本文記得給作者點(diǎn)贊+在看哦~~~大家的支持,是作者源源不斷出文的動(dòng)力
作者:SmileNicky
出處:https://www.cnblogs.com/mzq123/p/12153889.html
