前文
SpringCloud —— 服務註冊進 Eureka 集羣
SpringCloud —— SpringCloud Consul 實現服務註冊中心
SpringCloud —— HystrixDashboard 服務監控
文章目錄
概述
爲什麼會出現這個技術?需要解決哪些問題?
在微服務框架中,一個由客戶端發起的請求在後端系統中會經過多個不同的服務節點調用來協同產生最後的請求結果,每一個前端請求會形成一條複雜的分佈式服務調用鏈路,鏈路中的任何一環節出現高延時或錯誤都會引起整個請求最後的失敗
什麼是 Sleuth ?
SpringCloud Sleuth 提供了一套完整的服務跟蹤的解決方案,在分佈式系統中提供追蹤解決方案並且兼容支持了 zipkin
搭建鏈路監控步驟
zipkin
下載
SpringCloud F 版開始已不需要自己構建 Zipkin Server 了,只需要調用 jar 包即可
Zipkin jar 下載地址 https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
本文使用的是 2.12.9 版本
運行 jar
這是下載的 jar 包
運行 jar 包非常簡單,只需要運行以下命令即可
java -jar zipkin.jar
這圖案有點意思啊
運行控制檯
運行 jar 之後就可以訪問 Zipkin 的控制檯了,訪問地址 http://localhost:9411/zipkin/
服務提供者
pom.xml 依賴
<dependencies>
<!--包含了sleuth+zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.yml
server:
port: 8001
spring:
application:
name: demo-provider-payment
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
# 採樣率值介於 0 到 1 之間,則表示全部採集
probability: 1
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 當前數據源操作類型
driver-class-name: com.mysql.jdbc.Driver # MySQL 驅動包
url: jdbc:mysql://localhost:3306/數據庫名?useSSL=false
username: 用戶名
password: 密碼
eureka:
client:
# 表示是否將自己註冊進 EurekaServer 默認爲 true
register-with-eureka: true
# 是否從 EurekaServer 抓取已有的註冊信息,默認爲 true,單節點無所謂,集羣必須設置爲 true 才能配合 Ribbon 使用負載均衡
fetchRegistry: true
service-url:
# 註冊地址
# defaultZone: http://localhost:7001/eureka
# 集羣版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: payment8001
perfer-ip-address: true
server:
# 關閉自我保護機制,保證不可用服務被及時剔除
enable-self-preservation: false
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.java.springcloud.entity # 所有 entity 別名類所在包
主要新增以下配置
業務類
package com.java.springcloud.controller;
import com.java.springcloud.entity.CommonResult;
import com.java.springcloud.entity.Payment;
import com.java.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
/**
* @author Woo_home
* @create 2020/3/24 12:11
*/
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Resource
private DiscoveryClient discoveryClient;
@Value("${server.port}")
private String serverPort;
private Logger logger = Logger.getLogger("log");
@PostMapping(value = "/payment/create")
public CommonResult create(@RequestBody Payment payment) {
int result = paymentService.create(payment);
logger.info("***** 插入結果 :" + result);
if (result > 0) {
return new CommonResult(200,"插入數據庫成功,serverPort: " + serverPort,result);
} else {
return new CommonResult(404,"插入數據失敗",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id) {
Payment payment = paymentService.getPaymentById(id);
logger.info("***** 查詢結果 :" + payment + " " +serverPort);
// 如果不爲 null 表示有數據,查詢成功
if (payment != null) {
return new CommonResult(200,"查詢成功,serverPort:" + serverPort,payment);
} else {
return new CommonResult(404,"沒有對應記錄,查詢ID:" + id,null);
}
}
@GetMapping(value = "/payment/discovery")
public Object discovery() {
List<String> services = discoveryClient.getServices();
for (String service : services) {
logger.info("******element: " + service);
}
List<ServiceInstance> instances = discoveryClient.getInstances("DEMO-PROVIDER-PAYMENT");
for (ServiceInstance instance : instances) {
logger.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
}
return this.discoveryClient;
}
@GetMapping(value = "/payment/lb")
public String getPaymentLB() {
return serverPort;
}
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout() {
// 暫停幾秒鐘線程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return serverPort;
}
@GetMapping("/payment/zipkin")
public String paymentZipkin() {
return "hi, i am payment zipkin server fall back, welcome to Woo_home 哈哈";
}
}
主要新增以下代碼
服務消費者(調用方)
pom.xml 依賴
<dependencies>
<!--包含了sleuth+zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.yml
server:
port: 80
spring:
application:
# 服務名稱
name: demo-order-service
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
# 採樣率值介於 0 到 1 之間,則表示全部採集
probability: 1
eureka:
client:
# 表示是否將自己註冊進 EurekaServer 默認爲 true
register-with-eureka: true
# 是否從 EurekaServer 抓取已有的註冊信息,默認爲 true。單節點無所謂,集羣必須設置爲 true 才能配合 Ribbon 使用負載均衡
fetch-registry: true
service-url:
# 註冊地址
# defaultZone: http://localhost:7001/eureka
# 集羣版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
主要新增以下配置
業務類
package com.java.springcloud.controller;
import com.java.springcloud.entity.CommonResult;
import com.java.springcloud.entity.Payment;
import com.java.springcloud.lb.LoadBalancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.net.URI;
import java.util.List;
/**
* @author Woo_home
* @create 2020/3/24 14:11
*/
@RestController
@Slf4j
public class OrderController {
//public static final String PAYMENT_URL = "http://localhost:8001";
public static final String PAYMENT_URL = "http://DEMO-PROVIDER-PAYMENT";
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
// 引入自定義的負載均衡算法
@Resource
private LoadBalancer loadBalancer;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/create",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id,CommonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPayment2(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> forEntity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
if (forEntity.getStatusCode().is2xxSuccessful()) {
return forEntity.getBody();
} else {
return new CommonResult<>(404,"操作失敗");
}
}
@GetMapping("/consumer/payment/postForEntity/create")
public CommonResult<Payment> create2(Payment payment) {
return restTemplate.postForEntity(PAYMENT_URL + "/payment/create", payment, CommonResult.class).getBody();
}
@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("DEMO-PROVIDER-PAYMENT");
if (instances == null || instances.size() <= 0) {
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri + "/payment/lb",String.class);
}
@GetMapping("/consumer/payment/zipkin")
public String paymentZipkin() {
String result = restTemplate.getForObject("http://localhost:8001" + "/payment/zipkin/", String.class);
return result;
}
}
主要新增以下代碼
依次啓動 7001/8001/80
依次啓動這三個服務
測試
訪問 http://localhost/consumer/payment/zipkin
再次刷新 zipkin 控制檯的時候,就會出現兩個服務名稱
選擇訂單服務點擊查找
會顯示該服務響應的時長,依賴哪個服務
繼續點擊該服務進去可以查看該服務調用的鏈路
還可以查看具體的類、請求方法、地址、路徑
點擊依賴選項還可以對依賴進行分析
代碼已上傳至碼雲 代碼下載地址,感興趣的朋友可以下載運行下
SpringCloud 相關文章閱讀
SpringCloud —— 服務註冊進 Eureka 集羣
SpringCloud —— SpringCloud Consul 實現服務註冊中心