應用Dubbo框架打造仿貓眼項目 理解微服務核心思想


1:傳統應用帶來的問題

             單一業務開發的迭代問題
              擴容困難
              部署回滾困難
2:微服務概述

              微服務是一種將業務系統進一步拆分的架構風格
               微服務強調每一個業務都獨立運行
               每個單一服務都應該使用更輕量級的機制保持通信
                服務不強調環境,可以不同語言或數據源
3:微服務種類

                       Dubbo
                       Spring Cloud
                        Zero ICE


4:微服務基本概念

           Provider:服務提供者,提供服務實現

           Consumer:服務調用者,調用Provider提供的服務實現

           同一個服務可以既是Provider,又是Consumer

  5:spring boot 集成dubbo需要引入依賴

<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>


6:相關具體代碼

provider端
@Component
@Service(interfaceClass = FilmServiceApi.class)
public class DefaultFilmServiceImpl implements FilmServiceApi {
}
consumer端
@Reference(interfaceClass = FilmServiceApi.class,check = false)
private FilmServiceApi filmServiceApi;

7:api網關作用

服務聚合 熔斷降級 身份驗證

 

8:啟動添加注解

@SpringBootApplication(scanBasePackages = {"com.stylefeng.guns"})
@EnableDubboConfiguration
public class CinemaApplication {

public static void main(String[] args) {

SpringApplication.run(CinemaApplication.class, args);
}
}

9:提取公共api接口
在api項目中新建一個接口以及一個vo對象
public interface FilmServiceApi {

// 獲取banners
List<BannerVO> getBanners();
}
在gateway和Film項目中分別引用api項目
<dependency>
<groupId>com.stylefeng</groupId>
<artifactId>guns-api</artifactId>
</dependency>

在film項目中即provider中

@Component
@Service(interfaceClass = FilmServiceApi.class)
public class DefaultFilmServiceImpl implements FilmServiceApi {

@Autowired
private MoocBannerTMapper moocBannerTMapper;

@Autowired
private MoocFilmTMapper moocFilmTMapper;


@Override
public List<BannerVO> getBanners() {
List<BannerVO> result = new ArrayList<>();
List<MoocBannerT> moocBanners = moocBannerTMapper.selectList(null);

EntityWrapper<MoocFilmT> entityWrapper = new EntityWrapper<>();
entityWrapper.eq("film_status","1");
entityWrapper.eq("film_source",sourceId);
String catStr = "%#"+catId+"#%";
entityWrapper.like("film_cats",catStr);
Page<MoocFilmT> page = new Page<>(1,nums);
//排序page = new Page<>(nowPage,nums,"film_box_office");page = new Page<>(nowPage,nums,"film_box_office");
List<MoocFilmT> moocFilms = moocFilmTMapper.selectPage(page, entityWrapper);
int totalCounts = moocFilmTMapper.selectCount(entityWrapper);


          }

}
在gateway項目中即consumer中使用即可 同時也可以新建返回到前端對象的vo
@RestController
@RequestMapping("/film/")
public class FilmController {

private static final String img_pre = "http://img.meetingshop.cn/";

@Reference(interfaceClass = FilmServiceApi.class,check = false)
private FilmServiceApi filmServiceApi;
}
}
10:綁定用戶信息到threadlocal中
public class CurrentUser {

// 線程綁定的存儲空間
private static final InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
public static void saveUserId(String userId){
threadLocal.set(userId);
}
public static String getCurrentUser(){
return threadLocal.get();
}
// 將用戶信息放入存儲空間
// public static void saveUserInfo(UserInfoModel userInfoModel){
// threadLocal.set(userInfoModel);
// }
//
// // 將用戶信息取出
// public static UserInfoModel getCurrentUser(){
// return threadLocal.get();
// }

}
 
11:mybatis-plus 使用
public boolean checkUsername(String username) {
EntityWrapper<MoocUserT> entityWrapper = new EntityWrapper<>();
entityWrapper.eq("user_name",username);
Integer result = moocUserTMapper.selectCount(entityWrapper);
if(result!=null && result>0){
return false;
}else{
return true;
}
}
12:負載均衡

在集群負載均衡時,Dubbo 提供了多種均衡策略,缺省為 random 隨機調用。

可以自行擴展負載均衡策略,參見:負載均衡擴展

負載均衡策略

Random LoadBalance

  • 隨機,按權重設置隨機概率。
  • 在一個截面上碰撞的概率高,但調用量越大分布越均勻,而且按概率使用權重后也比較均勻,有利於動態調整提供者權重。

RoundRobin LoadBalance

  • 輪詢,按公約后的權重設置輪詢比率。
  • 存在慢的提供者累積請求的問題,比如:第二台機器很慢,但沒掛,當請求調到第二台時就卡在那,久而久之,所有請求都卡在調到第二台上。

LeastActive LoadBalance

  • 最少活躍調用數,相同活躍數的隨機,活躍數指調用前后計數差。
  • 使慢的提供者收到更少請求,因為越慢的提供者的調用前后計數差會越大。

ConsistentHash LoadBalance

  • 一致性 Hash,相同參數的請求總是發到同一提供者。
  • 當某一台提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
  • 算法參見:http://en.wikipedia.org/wiki/Consistent_hashing
  • 缺省只對第一個參數 Hash,如果要修改,請配置 <dubbo:parameter key="hash.arguments" value="0,1" />
  • 缺省用 160 份虛擬節點,如果要修改,請配置 <dubbo:parameter key="hash.nodes" value="320" />

配置

服務端服務級別

<dubbo:service interface="..." loadbalance="roundrobin" /> 

客戶端服務級別

<dubbo:reference interface="..." loadbalance="roundrobin" /> 

服務端方法級別

<dubbo:service interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:service> 

客戶端方法級別

<dubbo:reference interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:reference>


@Component
@Service(interfaceClass = UserAPI.class,loadbalance = "roundrobin" )
public class UserServiceImpl implements UserAPI{
}


13:
dubbo支持多種協議 默認是dubbo
1、dubbo 協議 (默認)
2、rmi 協議
3、hessian 協議
4、http 協議
5、webservice 協議
6、thrift 協議
7、memcached 協議
8、redis 協議



1、dubbo 協議 (默認)
缺省協議,使用基於mina1.1.7+hessian3.2.1的tbremoting交互。

連接個數:單連接
連接方式:長連接
傳輸協議:TCP
傳輸方式:NIO異步傳輸
序列化:Hessian 二進制序列化
適用范圍:傳入傳出參數數據包較小(建議小於100K),消費者比提供者個數多,單一消費者無法壓滿提供者,盡量不要用dubbo協議傳輸大文件或超大字符串。
適用場景:常規遠程服務方法調用
1、dubbo默認采用dubbo協議,dubbo協議采用單一長連接和NIO異步通訊,適合於小數據量大並發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的情況
2、他不適合傳送大數據量的服務,比如傳文件,傳視頻等,除非請求量很低。


2、rmi 協議
Java標准的遠程調用協議。

連接個數:多連接
連接方式:短連接
傳輸協議:TCP
傳輸方式:同步傳輸
序列化:Java標准二進制序列化
適用范圍:傳入傳出參數數據包大小混合,消費者與提供者個數差不多,可傳文件。
適用場景:常規遠程服務方法調用,與原生RMI服務互操作

3、hessian 協議
基於Hessian的遠程調用協議。

連接個數:多連接
連接方式:短連接
傳輸協議:HTTP
傳輸方式:同步傳輸
序列化:表單序列化
適用范圍:傳入傳出參數數據包大小混合,提供者比消費者個數多,可用瀏覽器查看,可用表單或URL傳入參數,暫不支持傳文件。
適用場景:需同時給應用程序和瀏覽器JS使用的服務。
1、Hessian協議用於集成Hessian的服務,Hessian底層采用Http通訊,采用Servlet暴露服務,Dubbo缺省內嵌Jetty作為服務器實現。
2、Hessian是Caucho開源的一個RPC框架:http://hessian.caucho.com,其通訊效率高於WebService和Java自帶的序列化


14:
從v2.7.0開始,Dubbo的所有異步編程接口開始以CompletableFuture為基礎

基於 NIO 的非阻塞實現並行調用,客戶端不需要啟動多線程即可完成並行調用多個遠程服務,相對多線程開銷較小。

/user-guide/images/future.jpg

在 consumer.xml 中配置:

<dubbo:reference id="asyncService" interface="org.apache.dubbo.samples.governance.api.AsyncService"> <dubbo:method name="sayHello" async="true" /> </dubbo:reference>

調用代碼:

// 此調用會立即返回null asyncService.sayHello("world"); // 拿到調用的Future引用,當結果返回后,會被通知和設置到此Future CompletableFuture<String> helloFuture = RpcContext.getContext().getCompletableFuture(); // 為Future添加回調 helloFuture.whenComplete((retValue, exception) -> { if (exception == null) { System.out.println(retValue); } else { exception.printStackTrace(); } });

 

15:spring boot中使用dubbo 中的consumer異步調用

@Reference(interfaceClass = FilmAsyncServiceApi.class,async = true,check = false)
private FilmAsyncServiceApi filmAsyncServiceApi;

    @RequestMapping(value = "films/{searchParam}",method = RequestMethod.GET)
public ResponseVO films(@PathVariable("searchParam")String searchParam,
int searchType) throws ExecutionException, InterruptedException {

// 根據searchType,判斷查詢類型
FilmDetailVO filmDetail = filmServiceApi.getFilmDetail(searchType, searchParam);

if(filmDetail==null){
return ResponseVO.serviceFail("沒有可查詢的影片");
}else if(filmDetail.getFilmId()==null || filmDetail.getFilmId().trim().length()==0){
return ResponseVO.serviceFail("沒有可查詢的影片");
}

String filmId = filmDetail.getFilmId();
// 查詢影片的詳細信息 -> Dubbo的異步調用
// 獲取影片描述信息
// FilmDescVO filmDescVO = filmAsyncServiceApi.getFilmDesc(filmId);
filmAsyncServiceApi.getFilmDesc(filmId);
Future<FilmDescVO> filmDescVOFuture = RpcContext.getContext().getFuture();
// 獲取圖片信息
filmAsyncServiceApi.getImgs(filmId);
Future<ImgVO> imgVOFuture = RpcContext.getContext().getFuture();
// 獲取導演信息
filmAsyncServiceApi.getDectInfo(filmId);
Future<ActorVO> actorVOFuture = RpcContext.getContext().getFuture();
// 獲取演員信息
filmAsyncServiceApi.getActors(filmId);
Future<List<ActorVO>> actorsVOFutrue = RpcContext.getContext().getFuture();

// 組織info對象
InfoRequstVO infoRequstVO = new InfoRequstVO();

// 組織Actor屬性
ActorRequestVO actorRequestVO = new ActorRequestVO();
actorRequestVO.setActors(actorsVOFutrue.get());
actorRequestVO.setDirector(actorVOFuture.get());

// 組織info對象
infoRequstVO.setActors(actorRequestVO);
infoRequstVO.setBiography(filmDescVOFuture.get().getBiography());
infoRequstVO.setFilmId(filmId);
infoRequstVO.setImgVO(imgVOFuture.get());

// 組織成返回值
filmDetail.setInfo04(infoRequstVO);

return ResponseVO.success("http://img.meetingshop.cn/",filmDetail);
}
在Application 中添加注解@EnableAsync啟用異步調用


16:一對多查詢

public class FilmInfoVO implements Serializable {

private String filmId;
private String filmName;
private String filmLength;
private String filmType;
private String filmCats;
private String actors;
private String imgAddress;
private List<FilmFieldVO> filmFields;

}

List<FilmInfoVO> getFilmInfos(@Param("cinemaId") int cinemaId);

<!-- 一對多的查詢 -->
<resultMap id="getFilmInfoMap" type="com.stylefeng.guns.api.cinema.vo.FilmInfoVO">
<result column="film_id" property="filmId"></result>
<result column="film_name" property="filmName"></result>
<result column="film_length" property="filmLength"></result>
<result column="film_language" property="filmType"></result>
<result column="film_cats" property="filmCats"></result>
<result column="actors" property="actors"></result>
<result column="img_address" property="imgAddress"></result>
<collection property="filmFields" ofType="com.stylefeng.guns.api.cinema.vo.FilmFieldVO">
<result column="UUID" property="fieldId"></result>
<result column="begin_time" property="beginTime"></result>
<result column="end_time" property="endTime"></result>
<result column="film_language" property="language"></result>
<result column="hall_name" property="hallName"></result>
<result column="price" property="price"></result>
</collection>
</resultMap>

<select id="getFilmInfos" parameterType="java.lang.Integer" resultMap="getFilmInfoMap">
SELECT
info.film_id,
info.`film_name`,
info.`film_length`,
info.`film_language`,
info.`film_cats`,
info.`actors`,
info.`img_address`,
f.`UUID`,
f.`begin_time`,
f.`end_time`,
f.`hall_name`,
f.`price`
FROM
mooc_hall_film_info_t info
LEFT JOIN
mooc_field_t f
ON f.`film_id` = info.`film_id`
AND f.`cinema_id` = ${cinemaId}
</select>

17:
緩存類型
  • lru 基於最近最少使用原則刪除多余緩存,保持最熱的數據被緩存。
  • threadlocal 當前線程緩存,比如一個頁面渲染,用到很多 portal,每個 portal 都要去查用戶信息,通過線程緩存,可以減少這種多余訪問。
  • jcache 與 JSR107 集成,可以橋接各種緩存實現。
  • 配置

    <dubbo:reference interface="com.foo.BarService" cache="lru" /> 

    或:

    <dubbo:reference interface="com.foo.BarService"> <dubbo:method name="findBar" cache="lru" /> </dubbo:reference>

18:連接控制

服務端連接控制
限制服務器端接受的連接不能超過10 個 :
<dubbo:provider protocol="dubbo" accepts="10" />

<dubbo:protocol name="dubbo" accepts="10" />

客戶端連接控制
限制客戶端服務使用連接不能超過 10 個 [2]:
<dubbo:reference interface="com.foo.BarService" connections="10" />

<dubbo:service interface="com.foo.BarService" connections="10" />
如果 <dubbo:service> 和 <dubbo:reference> 都配了 connections,<dubbo:reference>
優先,參見:配置的覆蓋策略

 19並發控制

  配置樣例
樣例 1
限制 com.foo.BarService 的每個方法,服務器端並發執行(或占用線程池線程數)不能超過 10 個:
<dubbo:service interface="com.foo.BarService" executes="10" />
樣例 2
限制 com.foo.BarService 的 sayHello 方法,服務器端並發執行(或占用線程池線程數)不能超過 10 個:
<dubbo:service interface="com.foo.BarService">
<dubbo:method name="sayHello" executes="10" />
</dubbo:service>
樣例 3
限制 com.foo.BarService 的每個方法,每客戶端並發執行(或占用連接的請求數)不能超過 10 個:
<dubbo:service interface="com.foo.BarService" actives="10" />

<dubbo:reference interface="com.foo.BarService" actives="10" />
樣例 4
限制 com.foo.BarService 的 sayHello 方法,每客戶端並發執行(或占用連接的請求數)不能超過 10 個:
<dubbo:service interface="com.foo.BarService">
<dubbo:method name="sayHello" actives="10" />
</dubbo:service>

<dubbo:reference interface="com.foo.BarService">
<dubbo:method name="sayHello" actives="10" />
</dubbo:service>
如果 <dubbo:service> 和 <dubbo:reference> 都配了actives,<dubbo:reference> 優先,參見:配置的覆蓋策略。
20:ftp 獲取json文件封裝
引入
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
在配置文件中配置
ftp:
host-name: 192.168.1.5
port: 2100
user-name: ftp
password: ftp
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "ftp")
public class FTPUtil {

// 地址 端口 用戶名 密碼
private String hostName;
private Integer port;
private String userName;
private String password;

private FTPClient ftpClient = null;

private void initFTPClient(){
try{
ftpClient = new FTPClient();
ftpClient.setControlEncoding("utf-8");
ftpClient.connect(hostName,port);
ftpClient.login(userName,password);
}catch (Exception e){
log.error("初始化FTP失敗",e);
}
}

// 輸入一個路徑,然后將路徑里的文件轉換成字符串返回給我
public String getFileStrByAddress(String fileAddress){
BufferedReader bufferedReader = null;
try{
initFTPClient();
bufferedReader = new BufferedReader(
new InputStreamReader(
ftpClient.retrieveFileStream(fileAddress))
);

StringBuffer stringBuffer = new StringBuffer();
while(true){
String lineStr = bufferedReader.readLine();
if(lineStr == null){
break;
}
stringBuffer.append(lineStr);
}

ftpClient.logout();
return stringBuffer.toString();
}catch (Exception e){
log.error("獲取文件信息失敗",e);
}finally {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
使用
@Autowired
private FTPUtil ftpUtil;

// 讀取位置圖,判斷seats是否為真
String fileStrByAddress = ftpUtil.getFileStrByAddress(seatPath);

// 將fileStrByAddress轉換為JSON對象
JSONObject jsonObject = JSONObject.parseObject(fileStrByAddress);
// seats=1,2,3 ids="1,3,4,5,6,7,88"
String ids = jsonObject.get("ids").toString();

21:總所周知,java在浮點型運算時是非精確計算,如下demo
  1.  
    System.out.println( 0.05 + 0.01);// 0.060000000000000005
  2.  
    System.out.println( 1.0 - 0.42);// 0.5800000000000001
  3.  
    System.out.println( 4.015 * 100);// 401.49999999999994
  4.  
    System.out.println( 123.3 / 100);// 1.2329999999999999

在商業運算中,這點微小的誤差有可能造成非常嚴重的后果。 
所以在商業應用開發中,涉及金額等浮點數計算的數據,全部使用BigDecimal進行加減乘除計算

private static double getTotalPrice(int solds,double filmPrice){
BigDecimal soldsDeci = new BigDecimal(solds);
BigDecimal filmPriceDeci = new BigDecimal(filmPrice);

BigDecimal result = soldsDeci.multiply(filmPriceDeci);

// 四舍五入,取小數點后兩位
BigDecimal bigDecimal = result.setScale(2, RoundingMode.HALF_UP);

return bigDecimal.doubleValue();
}
 22:分組聚合
按組合並返回結果 [1],比如菜單服務,接口一樣,但有多種實現,用group區分,現在消費方需從每種group中調用一次返回結果,合並結果返回,這樣就可以實現聚合菜單項。

配置
搜索所有分組
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true" />
合並指定分組
<dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" />
指定方法合並結果,其它未指定的方法,將只調用一個 Group
<dubbo:reference interface="com.xxx.MenuService" group="*">
<dubbo:method name="getMenuItems" merger="true" />
</dubbo:reference>
某個方法不合並結果,其它都合並結果
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true">
<dubbo:method name="getMenuItems" merger="false" />
</dubbo:reference>

在springboot中沒法使用merge的配置 需要自己手動合並結果
@Reference(
interfaceClass = OrderServiceAPI.class,
check = false,
group = "order2018")
private OrderServiceAPI orderServiceAPI;

@Reference(
interfaceClass = OrderServiceAPI.class,
check = false,
group = "order2017")
private OrderServiceAPI orderServiceAPI2017;
Page<OrderVO> result = orderServiceAPI.getOrderByUserId(Integer.parseInt(userId), page);
Page<OrderVO> result2017 = orderServiceAPI2017.getOrderByUserId(Integer.parseInt(userId), page);
// 合並結果
int totalPages = (int)(result.getPages() + result2017.getPages());
// 2017和2018的訂單總數合並
List<OrderVO> orderVOList = new ArrayList<>();
orderVOList.addAll(result.getRecords());
orderVOList.addAll(result2017.getRecords());


23:
多版本
當一個接口實現,出現不兼容升級時,可以用版本號過渡,版本號不同的服務相互間不引用。
老版本服務提供者配置:
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
新版本服務提供者配置:
<dubbo:service interface="com.foo.BarService" version="2.0.0" />
老版本服務消費者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
新版本服務消費者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />
如果不需要區分版本,可以按照以下的方式配置 [1]:
<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />

24:
用的限流算法有兩種:漏桶算法和令牌桶算法

1、漏桶算法

      漏桶算法思路很簡單,水(請求)先進入到漏桶里,漏桶以一定的速度出水,當水流入速度過大會直接溢出,可以看出漏桶算法能強行限制數據的傳輸速率。因此,漏桶算法對於存在突發特性的流量來說缺乏效率.

2:令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一樣但方向相反的算法,更加容易理解.隨着時間流逝,系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了.新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務.

令牌桶的另外一個好處是可以方便的改變速度. 一旦需要提高速率,則按需提高放入桶中的令牌的速率. 一般會定時(比如100毫秒)往桶中增加一定數量的令牌, 有些變種算法則實時的計算應該增加的令牌的數量.

 

25:具體實現

// 因為令牌桶對業務有一定的容忍度
public class TokenBucket {

private int bucketNums=100; // 桶的容量
private int rate=1; // 流入速度
private int nowTokens; // 當前令牌數量
private long timestamp=getNowTime(); // 時間

private long getNowTime(){
return System.currentTimeMillis();
}

private int min(int tokens){
if(bucketNums > tokens){
return tokens;
}else{
return bucketNums;
}
}

public boolean getToken(){
// 記錄來拿令牌的時間
long nowTime = getNowTime();
// 添加令牌【判斷該有多少個令牌】
nowTokens = nowTokens + (int)((nowTime - timestamp)*rate);
// 添加以后的令牌數量與桶的容量那個小
nowTokens = min(nowTokens);
System.out.println("當前令牌數量"+nowTokens);
// 修改拿令牌的時間
timestamp = nowTime;
// 判斷令牌是否足夠
if(nowTokens < 1){
return false;
}else{
nowTokens -= 1;
return true;
}
}
}
private static TokenBucket tokenBucket = new TokenBucket();
if(tokenBucket.getToken()) {
//獲取令牌
}
else
{
//沒獲取令牌
}
Google開源工具包Guava提供了限流工具類RateLimiter,該類基於令牌桶算法來完成限流,非常易於使用。

26:spring boot 整合hystrix

 

 
        
添加依賴包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>


添加開啟注解
@EnableHystrixDashboard
@EnableCircuitBreaker
@EnableHystrix

配置示例
@HystrixCommand(fallbackMethod = "error", commandProperties = {
@HystrixProperty(name="execution.isolation.strategy", value = "THREAD"),
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value
= "4000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = ”50")
}, threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "1"),
@HystrixProperty(name = "maxQueueSize", value = "10"),
@HystrixProperty(name = "keepAliveTimeMinutes", value = "1000"),
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "8"),
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1500")
})

public ResponseVO error(Integer fieldId,String soldSeats,String seatsName){
return ResponseVO.serviceFail("抱歉,下單的人太多了,請稍后重試");
}


27:InheritableThreadLocal

  InheritableThreadLocal用於子線程能夠拿到父線程往ThreadLocal里設置的值。使用代碼如下:

public class Test { public static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<Integer>(); public static void main(String args[]) { threadLocal.set(new Integer(456)); Thread thread = new MyThread(); thread.start(); System.out.println("main = " + threadLocal.get()); } static class MyThread extends Thread { @Override public void run() { System.out.println("MyThread = " + threadLocal.get()); } } } 

 

 

輸出結果如下圖:
 
輸出結果

如果把上面的InheritableThreadLocal換成ThreadLocal的話,在子線程里的輸出將為是空。

  • InheritableThreadLocal之所以能夠完成線程間變量的傳遞,是在new Thread()的時候對inheritableThreadLocals對像里的值進行了復制。
  • 子線程通過繼承得到的InheritableThreadLocal里的值與父線程里的
28: dubbo的本地存根
 

dubbo的本地存根的原理是:遠程服務后,客戶端通常只剩下接口,而實現全在服務器端,但提供方有些時候想在客戶端也執行部分邏輯,那么就在服務消費者這一端提供了一個Stub類,然后當消費者調用provider方提供的dubbo服務時,客戶端生成 Proxy 實例,這個Proxy實例就是我們正常調用dubbo遠程服務要生成的代理實例,然后消費者這方會把 Proxy 通過構造函數傳給 消費者方的Stub ,然后把 Stub 暴露給用戶,Stub 可以決定要不要去調 Proxy。會通過代理類去完成這個調用,這樣在Stub類中,就可以做一些額外的事,來對服務的調用過程進行優化或者容錯的處理。附圖:

實現步驟:

 1. 定義一個服務接口和服務實現類

public interface UserInterface {
    
    
       public User getUserById(Integer id) ;

}
復制代碼
public class UserService implements UserInterface {

    public User getUserById(Integer id) {
        User user  = new User() ;
        user.setId(id);
        user.setName("hu");
        return user;
    }  

}
復制代碼

 

2. 服務分布配置

<dubbo:service  interface="org.huxin.dubbo.test.user.service.UserInterface" ref="userService" protocol="dubbo"    retries="0"/>
        

    <bean id="userService" class="org.huxin.dubbo.test.user.service.impl.UserService" />

 

3.服務消費者的Stub類

復制代碼
public class UserServiceStub implements UserInterface {
    
    //必須定義這個接口,以便接收dubbo在調用遠程服務生成的服務代理類
    private UserInterface userLocalService ;
    
    
    
    //這個構造函數必須要提供,dubbo框架會在消費者這一方調用這個方法
    public UserServiceStub(UserInterface userLocalService ) {
        this.userLocalService = userLocalService  ;
    }

    public User getUserById(Integer id) {

        User user = null ;
        try {
          if (id == 1) {
            user = this.userLocalService.getUserById(id) ;
          }else {
            user = new User(); 
            user.setName("系統用戶");
          }
        }catch(Exception e) {
          user = new User(); 
          user.setName("異常用戶");
        }

              return user ;

    }
}
復制代碼

 

4. 服務消費方的配置

 <dubbo:reference id="userService" interface="org.huxin.dubbo.test.user.service.UserInterface" 
                    stub="org.huxin.dubbo.test.UserServiceStub" protocol="dubbo"/>

 

5.測試代碼

1
2
3
4
5
6
7
8
@Test
     public  void  testGetUserById(){
         Integer id =  2  ;
             UserInterface  userService = context.getBean(UserInterface. class ) ;
         User user = userService.getUserById( id) ;
         System.out.println(user.getName()); 
         
     }

  

總結:上述代碼當調用服務出錯時,消費方會返回“異常用戶”,起到了容錯的作用

29:本地偽裝

本地偽裝通常用於服務降級,例如某驗權服務,當服務提供方全部掛掉后,客戶端不拋出異常,而是通過 Mock 數據
返回授權失敗。使用方式如下,mock指定的實現類在Provider拋出RpcException異常時執行(一定要拋出RpcException異常才執行),取代遠程返回結果:

<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" version="1.0.0" mock="com.alibaba.dubbo.demo.consumer.mock.DemoServiceMock"/>

DemoServiceMock實現源碼:

public class DemoServiceMock implements DemoService { public String sayHello(String name) { return "mock-value"; } }
30:
隱式參數

可以通過 RpcContext 上的 setAttachment 和 getAttachment 在服務消費方和提供方之間進行參數的隱式傳遞。 [1]

/user-guide/images/context.png

在服務消費方端設置隱式參數

setAttachment 設置的 KV 對,在完成下面一次遠程調用會被清空,即多次遠程調用要多次設置。

RpcContext.getContext().setAttachment("index", "1"); // 隱式傳參,后面的遠程調用都會隱式將這些參數發送到服務器端,類似cookie,用於框架集成,不建議常規業務使用
xxxService.xxx(); // 遠程調用
// ...

在服務提供方端獲取隱式參數

public class XxxServiceImpl implements XxxService { public void xxx() { // 獲取客戶端隱式傳入的參數,用於框架集成,不建議常規業務使用 String index = RpcContext.getContext().getAttachment("index"); } }

31:分布式事務

事務簡介

事務是用來保證一組數據操作的完整性和一致性
滿足ACID
具有四種隔離級別、七種傳播行為

分布式事務

將多個節點的事務看成一個整體處理
由事務參與者、資源服務器、事務管理器等組成
常見的分布式事務:支付、下單

 

實現思路

兩段式事務2PC和三段式事務3PC(基礎,生產環境中基本沒人用)
基於XA的分布式事務
基於消息的最終一致性方案(常用)
TCC編程式補償性事務(目前最好)

 

2PC、3PC

  • 當需要一個事務的時候事務管理器(看下圖)(相當於居委會大媽,是一個管理者身份)就通知全部資源管理器:“要搞事務啦,你們【准備】一下。”

 

  • 資源管理器接收到“大媽”的通知后就開始處理業務,進行事務操作,操作結束后,但是還沒有提交,就告訴“大媽”,我的事務已經做好了【就緒】,等待提交。

 

  • “大媽”收到全部資源管理器的【就緒】消息后,就進入第二階段,“大媽”說:“看來你們都就緒了,那就提交吧”,

 

  • 資源管理器就開始執行【提交】,提交完成后,就告訴“大媽”,我【已提交】了。“大媽”就判斷是否收到全部資源管理器的【已提交】請求,一旦沒有收到全部的請求,就表示事務出現了問題。

 

兩段式的問題:

比如在提交時:第一個資源管理器提交成功,並返回了,事務管理器也收到了返回的消息;

但是第二個資源管理器提交時,宕機了!!因此它不會返回任何信息,事務管理器就會一直等待,導致事務最終無法完成!

 

3PC

CanCommit、PreCommit、DoCommit三個階段

 

在2PC的基礎上,添加了超時檢測,在2PC的第一階段與第二階段中間加入准備階段,保證了一旦第三階段出問題我可以全部回滾。

參照着圖看下面的流程

CanCommit階段

3PC的CanCommit階段其實和2PC的准備階段很像。協調者向參與者發送commit請求,參與者如果可以提交就返回Yes響應,否則返回No響應。

1.事務詢問 協調者向參與者發送CanCommit請求。詢問是否可以執行事務提交操作。然后開始等待參與者的響應。
2.響應反饋 參與者接到CanCommit請求之后,正常情況下,如果其自身認為可以順利執行事務,則返回Yes響應,並進入預備狀態。否則反饋No

PreCommit階段

協調者根據參與者的反應情況來決定是否可以記性事務的PreCommit操作。根據響應情況,有以下兩種可能。

假如協調者從所有的參與者獲得的反饋都是Yes響應,那么就會執行事務的預執行。

1.發送預提交請求 協調者向參與者發送PreCommit請求,並進入Prepared階段。
2.事務預提交 參與者接收到PreCommit請求后,會執行事務操作,並將undo和redo信息記錄到事務日志中。
3.響應反饋 如果參與者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令。

假如有任何一個參與者向協調者發送了No響應,或者等待超時之后,協調者都沒有接到參與者的響應,那么就執行事務的中斷。

1.發送中斷請求 協調者向所有參與者發送abort請求。
2.中斷事務 參與者收到來自協調者的abort請求之后(或超時之后,仍未收到協調者的請求),執行事務的中斷。

doCommit階段

該階段進行真正的事務提交,也可以分為以下兩種情況。

執行提交

1.發送提交請求 協調接收到參與者發送的ACK響應,那么他將從預提交狀態進入到提交狀態。並向所有參與者發送doCommit請求。
2.事務提交 參與者接收到doCommit請求之后,執行正式的事務提交。並在完成事務提交之后釋放所有事務資源。
3.響應反饋 事務提交完之后,向協調者發送Ack響應。
4.完成事務 協調者接收到所有參與者的ack響應之后,完成事務。

中斷事務 協調者沒有接收到參與者發送的ACK響應(可能是接受者發送的不是ACK響應,也可能響應超時),那么就會執行中斷事務。

1.發送中斷請求 協調者向所有參與者發送abort請求
2.事務回滾 參與者接收到abort請求之后,利用其在階段二記錄的undo信息來執行事務的回滾操作,並在完成回滾之后釋放所有的事務資源。
3.反饋結果 參與者完成事務回滾之后,向協調者發送ACK消息
4.中斷事務 協調者接收到參與者反饋的ACK消息之后,執行事務的中斷。

當進入第三階段時,由於網絡超時等原因,雖然參與者沒有收到commit或者abort響應,但是他有理由相信:成功提交的幾率很大。

2PC與3PC的區別

相對於2PC,3PC主要解決的單點故障問題,並減少阻塞,因為一旦參與者無法及時收到來自協調者的信息之后,他會默認執行commit。而不會一直持有事務資源並處於阻塞狀態。

但是這種機制也會導致數據一致性問題,因為,由於網絡原因,協調者發送的abort響應沒有及時被參與者接收到,那么參與者在等待超時之后執行了commit操作。這樣就和其他接到abort命令並執行回滾的參與者之間存在數據不一致的情況。

 

XA

X/OPEN推出的方案,實際業務中很少有人用。(和兩段式差不多)

 

基於消息的最終一致性方案

他屬於:最終一致性方案。

存在資源浪費浪費了資源,下游應用會一直等待上游傳遞消息

看圖:

 

 

 

 

 

TCC編程式補償性事務

Try,Confirm,Cancel的首字母縮寫,螞蟻金服一直在用(屬於常用分布式事務解決方案)

調用Try接口,執行完后返回結果,都成功調用Confirm、一旦存在失敗調用Cancel

 

TCC事務機制簡介:

TCC事務機制簡介 | 百特開源​www.bytesoft.org 百特開源​www.bytesoft.org圖標

與基於消息的最終一致性方案的對比百特開源與基於消息的最終一致性方案的對比

  1. 基於消息的事務是存在資源浪費浪費了資源,下游應用會一直等待上游傳遞消息
  2. TCC事務是柔性事務,在try階段要對資源做預留
  3. TCC事務在確認或取消階段釋放資源
  4. TCC的時效性更好
  5. TCC 性能和並發不如消息的好

常用分布式事務框架

如果嫌麻煩,就用阿里巴巴的GTS、DTX吧(我選擇阿里!)

不然就使用開源框架

嘗試使用TCC編程式補償性事務

為什么不用ByteTcc,因為它的star數沒有tcc-transaction高。。更穩定,更好一些。

就是此開源項目:

changmingxie/tcc-transaction​github.com圖標

我們先嘗試一下把他跑起來,體驗一下TCC編程式補償性事務是什么感覺。

這個項目有幾處需要我們手動修改的地方,請仔細。

 

第一步:下載tcc-transaction:git clone git@github.com:changmingxie/tcc-transaction.git

第二步:用IDEA打開下載的工程

第三步:修改幾處配置,如圖所示修改即可。

項目導入后的樣子如圖,目前只需要看sample模塊下面的http子模塊

 

然后導入數據庫SQL語句 tcc是必須的數據庫其他都是業務演示數據庫

把這四個文件導入到你的數據庫

 

 

特別注意示例給的 create_db_tcc.sql 數據庫字段沒有IS_DELETE值,需要在創建完此庫以后再手動執行以下SQL語句:

ALTER TABLE `TCC_TRANSACTION_CAP` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL; ALTER TABLE `TCC_TRANSACTION_ORD` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL; ALTER TABLE `TCC_TRANSACTION_RED` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL; ALTER TABLE `TCC_TRANSACTION_UT` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;

如果你的版本有此字段,就算了。

然后修改此三個子模塊的數據庫配置文件

 

第一個要修改的就是數據庫連接信息,改為你的用戶名與密碼(三處子模塊都要修改),如圖

 

 

第二個就是添加Tomcat部署的時候,把這三個包部署:

 

 

對應部署包后面的應用上下文可以改為:order、cap、red

 

然后部署發布

會有三個工程啟動(速度比較慢)

 

啟動成功

 

 

然后訪問圖示連接,照着網頁提示操作,就可以體驗一把TCC是什么樣子的了


下面看一下dubbo版本的TCC如何啟動

還是以sample為例,現在進入dubbo這個子模塊,數據庫sql語句不用管,上面已經搞過了,修改幾處配置文件即可

 

第一處:依然是此三處的數據庫配置文件,修改為自己的用戶名與密碼

 

如圖

 

然后修改此三處的web.xml文件,一打開web.xml就會看到發紅的<web-app>

其實這是配置的順序寫的不對,把listener放到filter之后就沒問題了,

然后在<display-name>Sample Dubbo Capital</display-name> 下面添加如下的東西

<context-param> <param-name>webAppRootKey</param-name> <param-value>tcc-transaction-dubbo-capital</param-value> </context-param>

同理,三處子模塊都要修改一下

 

然后部署到Tomcat的時候,也有講究,按下圖的順序配置

 

 

因為Tomcat的插件會按此順序加載,不然項目無法啟動。

然后給order的Application context值就給默認的斜杠

另外兩個分別給/red、/cap即可

最后啟動tomcat,訪問127.0.0.1:8080/即可體驗dubbo下的TCC

 

未完待續。。。

 

1、需要提供分布式事務支持的接口上添加@Compensable
2、在對應的接口實現上添加@Compensable
3、在接口實現上添加confirmMethod、cancelMethod、transactionContextEditor
4、實現對應的confirmMethod、cancelMethod
注意: confirm方法和cancel方法必須與try方法在同一個類中

5、主事務的業務都已經實現的差不多的時候才調用子事務




注意:
1、分布式事務里,不要輕易在業務層捕獲所有異常
2、使用TCC-Transaction時,confirm和cancel的冪等性需要自己代碼支持

思考:
為什么要在confirm、cancel里檢查訂單狀態,而不直接修改為結束狀態
因為confirm確認的就是剛剛try方法里新增的一個訂單。

-》 為了保證服務的冪等性

冪等性:使用相同參數對同一資源重復調用某個接口的結果與調用一次的結果相同

 

 基礎環境配置詳解

 

1、 將所有的tcc依賴包 api, core, spring, dubbo加入maven本地倉庫

 

2、 添加依賴包

 

3、 添加配置文件

 

4、 修改配置

 

 

 

 

 

5、 Springboot讀取配置文件

 

 

 

6、 添加事務表

CREATE TABLE `TCC_TRANSACTION_TEST_A`

 

業務實現演示

生產端代碼演示

@Component
@Service(interfaceClass = ServiceAPI.class)
public class TransactionServiceImpl implements ServiceAPI {

@Override
@Compensable(confirmMethod = "confirmSendMessage", cancelMethod = "cancelSendMessage", transactionContextEditor = DubboTransactionContextEditor.class)
public String sendMessage(String message) {
System.out.println("this is sendMessage try message="+message);
if(message.equals("123")){
throw new NullPointerException();
}

return "quickstart-provider-message="+message;
}

@Override
@Compensable(confirmMethod = "confirmIsTrueSeats", cancelMethod = "cancelIsTrueSeats", transactionContextEditor = DubboTransactionContextEditor.class)
public boolean isTrueSeats(String seats) {
if(seats.equals("1,2,3")){
throw new IllegalArgumentException();
}else{
return true;
}
}

@Override
@Compensable(confirmMethod = "confirmIsNotSold", cancelMethod = "cancelIsNotSold", transactionContextEditor = DubboTransactionContextEditor.class)
public boolean isNotSold(String seats) {
if(seats.equals("4,5")){
throw new IllegalArgumentException();
}else{
return true;
}
}

/*
千萬千萬注意冪等性問題
*/
@Override
@Compensable(confirmMethod = "confirmSaveOrder", cancelMethod = "cancelSaveOrder", transactionContextEditor = DubboTransactionContextEditor.class)
public String saveOrder(String fieldId, String seats, String seatsNum) {
System.out.println("創建一個待支付狀態的訂單");
return "";
}
public String confirmSaveOrder(String fieldId, String seats, String seatsNum) {
System.out.println("將訂單修改為支付中");
return "";
}
public String cancelSaveOrder(String fieldId, String seats, String seatsNum) {
System.out.println("將訂單修改為已關閉");
return "";
}

public String confirmSendMessage(String message) {
System.out.println("this is confirmSendMessage message="+message);
return "quickstart-provider-message="+message;
}

public String cancelSendMessage(String message) {
System.out.println("this is cancelSendMessage message=" + message);
return "quickstart-provider-message=" + message;
}

public boolean confirmIsTrueSeats(String seats) {
System.out.println("this is confirmIsTrueSeats");
return true;
}
public boolean cancelIsTrueSeats(String seats) {
System.out.println("this is cancelIsTrueSeats");
return true;
}
public boolean confirmIsNotSold(String seats) {
System.out.println("this is confirmIsNotSold");
return true;
}
public boolean cancelIsNotSold(String seats) {
System.out.println("this is cancelIsNotSold");
return true;
}

}

api層代碼
public interface ServiceAPI {

@Compensable
String sendMessage(String message);

/*
背景:傳入購票數量、傳入購買座位、影廳編號
業務:
1、判斷傳入的座位是否存在
2、查詢過往訂單、判斷座位是否已售
3、新增訂單
邏輯:
1、新增一條訂單
2、判斷座位是否存在 & 是否已售
3、任意一條為假,則修改訂單為無效狀態
*/

// 判斷是否為真座位
@Compensable
boolean isTrueSeats(String seats);

// 是否已售
@Compensable
boolean isNotSold(String seats);

// 保存訂單
@Compensable
String saveOrder(String fieldId,String seats,String seatsNum);

}

消費端代碼

@Component
public class TransactionConsumer {

@Autowired
ServiceAPI serviceAPI;

@Compensable(confirmMethod = "confirmSendMessage", cancelMethod = "cancelSendMessage", asyncConfirm = true)
public void sendMessage(String message){
// System.out.println("this is consumer sendMessage message="+message);
//
// System.out.println(serviceAPI.sendMessage(message));

// 測試業務
serviceAPI.saveOrder("001",message,"5");

serviceAPI.isTrueSeats(message);

serviceAPI.isNotSold(message);

}

public void confirmSendMessage(String message){
System.out.println("this is consumer confirmSendMessage message="+message);
// System.out.println(serviceAPI.sendMessage(message));
}

public void cancelSendMessage(String message){
System.out.println("this is consumer cancelSendMessage message="+message);
// System.out.println(serviceAPI.sendMessage(message));
}
}

測試
@SpringBootApplication
@EnableDubboConfiguration
public class ConsumerApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run =
SpringApplication.run(ConsumerApplication.class, args);

// 測試分布式事務使用
TransactionConsumer transactionConsumer = (TransactionConsumer) run.getBean("transactionConsumer");
transactionConsumer.sendMessage("4,5");
// 1,2,3 4,5

}


}


流程




Dubbo Monitor 使用
Dubbo Monitor 下載
dubbo-monitor-simple-2.5.3-assembly.tar.gz

解壓后, 編輯conf/dubbo.properties文件

復制代碼
dubbo.container=log4j,spring,registry,jetty
dubbo.application.name=simple-monitor
dubbo.application.owner=
dubbo.registry.address=zookeeper://192.168.3.10:2181
dubbo.protocol.port=10089 #這個是monitor自身要發布的接口使用的端口
dubbo.jetty.port=8082 #這個是monitor提供的web界面的接口
dubbo.jetty.directory=/home/tomcat/dubbo-monitor #這個是dubbo monitor的解壓目錄
dubbo.charts.directory=/home/tomcat/dubbo-monitor/charts #這個和下面的目錄, 最好在dubbo monitor解壓目錄下
dubbo.statistics.directory=/home/tomcat/dubbo-monitor/statistics
dubbo.log4j.file=logs/dubbo-monitor-simple.log
dubbo.log4j.level=WARN
復制代碼

4. 啟動/停止

通過 bin/start.sh, stop.sh來啟動停止monitor. 初始啟動, charts的生成需要等幾分鍾

5. 訪問

通過 http://host_ip:8082/ (上面配置的端口)訪問monitor服務

zpkin使用



1.Trace
Zipkin使用Trace結構表示對一次請求的跟蹤,一次請求可能由后台的若干服務負責處理,每個服務的處理是一個Span,Span之間有依賴關系,Trace就是樹結構的Span集合;

2.Span
每個服務的處理跟蹤是一個Span,可以理解為一個基本的工作單元,包含了一些描述信息:id,parentId,name,timestamp,duration,annotations等,例如:

{
      "traceId": "bd7a977555f6b982", "name": "get-traces", "id": "ebf33e1a81dc6f71", "parentId": "bd7a977555f6b982", "timestamp": 1458702548478000, "duration": 354374, "annotations": [ { "endpoint": { "serviceName": "zipkin-query", "ipv4": "192.168.1.2", "port": 9411 }, "timestamp": 1458702548786000, "value": "cs" } ], "binaryAnnotations": [ { "key": "lc", "value": "JDBCSpanStore", "endpoint": { "serviceName": "zipkin-query", "ipv4": "192.168.1.2", "port": 9411 } } ] }

traceId:標記一次請求的跟蹤,相關的Spans都有相同的traceId;
id:span id;
name:span的名稱,一般是接口方法的名稱;
parentId:可選的id,當前Span的父Span id,通過parentId來保證Span之間的依賴關系,如果沒有parentId,表示當前Span為根Span;
timestamp:Span創建時的時間戳,使用的單位是微秒(而不是毫秒),所有時間戳都有錯誤,包括主機之間的時鍾偏差以及時間服務重新設置時鍾的可能性,
出於這個原因,Span應盡可能記錄其duration;
duration:持續時間使用的單位是微秒(而不是毫秒);
annotations:注釋用於及時記錄事件;有一組核心注釋用於定義RPC請求的開始和結束;

cs:Client Send,客戶端發起請求; sr:Server Receive,服務器接受請求,開始處理; ss:Server Send,服務器完成處理,給客戶端應答; cr:Client Receive,客戶端接受應答從服務器;

 

transport 是通過brave實現
dubbo-filter
日志記錄trace跟蹤都是通過filter實現的
dubbo提供了Filter擴展,可以通過自定義Filter來實現這個功能
配置文件
參考:調用攔截擴展
在resources目錄下添加純文本文件META-INF/dubbo/com.alibaba.dubbo.rpc.Filter,內容如下:

修改dubbo的provider配置文件,在dubbo:provider中添加配置的filter,如下


 
        
 
下載zpkin項目源碼並編譯成jar包通過java -jar 啟動 即可
 
項目引入zpkin
父工程引入依賴管理
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-bom</artifactId>
<version>5.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-bom</artifactId>
<version>2.7.9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>


product 引入以下依賴
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-dubbo-rpc</artifactId>
</dependency>

<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-spring-beans</artifactId>
</dependency>

<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
</dependency>

<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-okhttp3</artifactId>
</dependency>

product端注入以下依賴
<bean id="sender" class="zipkin2.reporter.beans.OkHttpSenderFactoryBean">
<property name="endpoint" value="http://localhost:9411/api/v2/spans"/>
</bean>

<bean id="tracing" class="brave.spring.beans.TracingFactoryBean">
<property name="localServiceName" value="hello-service"/>
<property name="spanReporter">
<bean class="zipkin2.reporter.beans.AsyncReporterFactoryBean">
<property name="sender" ref="sender"/>
<!-- wait up to half a second for any in-flight spans on close -->
<property name="closeTimeout" value="500"/>
</bean>
</property>
<property name="currentTraceContext">
<bean class="brave.spring.beans.CurrentTraceContextFactoryBean">
<property name="scopeDecorators">
<bean class="brave.context.slf4j.MDCScopeDecorator" factory-method="create"/>
</property>
</bean>
</property>
</bean>

聲明需要暴露的服務接口需要提供filter="tracing"配置
<!-- 聲明需要暴露的服務接口 -->
<dubbo:service
timeout="10000"
async="true"
interface="com.mooc.jiangzh.dubbo.ServiceAPI"
ref="quickStartService" filter="tracing"/>
 
         
springboot集成zpkin
service和reference都需要添加filter="tracing"
需要多一個依賴
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.0.1-incubating</version>
</dependency>



免責聲明!

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



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