文章目錄
- 1. 基礎知識介紹
- 2. 環境準備
- 3. 項目結構
- 4. 基本使用操作過程
- 5. OpenFeign 服務調用過程總結
- 6. OpenFeign 與 Ribbon 調用過程的對比
- 7. 超時控制演示
- 7.1 修改 Order80 的 application.yml 文件,設置 OpenFeign 客戶端超時控制
- 7.2 在服務提供者 8001 的控制類中添加超時方法
- 7.3 在服務消費者 80 的 service 接口中加入該超時方法
- 7.4 在服務消費者 80 的 controller 類中添加該方法暴露給瀏覽器
- 7.5 測試 http://localhost/consumerFeign/payment/timeout
- 7.6 更改超時方法的停止時間爲 3 秒
- 7.7 再次測試 http://localhost/consumerFeign/payment/timeout
- 8. 日誌打印演示
- 8.1 在 Order80 中配置日誌 Bean 對象
- 8.1.1 在在 src/main/java/com/atguigu/springcloud 下新建 config 包並新建 FeignConfig 類
- 8.1.2 添加 @Configuration 註解註明該類爲註解類
- 8.1.3 配置日誌打印等級的Bean對象
- 8.2 在 Order80 的 application.yml 文件中開啓 Feign 日誌支持
- 8.3 瀏覽器發出請求
- 8.4 後臺查看日誌打印結果
- 9. 程序源碼
- 9.1 cloud-provider-payment8001
- 9.1.1 PaymentController.java
- 9.1.2 PaymentDao.java
- 9.1.3 PaymentService.java
- 9.1.4 PaymentServiceImpl.java
- 9.1.5 PaymentMain8001.java
- 9.1.6 PaymentMapper.xml
- 9.1.7 application.yml
- 9.2 cloud-provider-payment8003
- 9.2.1 PaymentController.java
- 9.2.2 PaymentDao.java
- 9.2.3 PaymentService.java
- 9.2.4 PaymentServiceImpl.java
- 9.2.5 PaymentMain8003.java
- 9.2.6 PaymentMapper.xml
- 9.2.7 application.yml
- 9.3 cloud-consumer-feign-order80
- 10. 附註說明
1. 基礎知識介紹
1.1 Feign 概述
- Feign是一個聲明式WebService客戶端。 使用Feign能讓編寫Web Service客戶端更加簡單。
- 它的使用方法是定義一個服務接口然後在上面添加註解。
- Feign也支持可拔插式的編碼器和解碼器。
- Spring Cloud對Feign進行了封裝,使其支持了Spring MVC標準註解和HttpMessageConverters。Feign可以與Eureka和Ribbon組合使用以支持負載均衡
1.2 OpenFeign 與 Feign 的比較
1.3 OpenFeign 的使用方法
在Feign的實現下,我們只需創建一個接口並使用註解的方式來配置它(以前是Dao接口 上面標註Mapper註解現在是一個微服務接口 上面標註一個Feign註解即可),即可完成對服務提供方的接口綁定,簡化了使用Spring cloud Ribbon時,自動封裝服務調用客戶端的開發量。
Feign集成了Ribbon,利用Ribbon維護了Payment的服務列表信息,並且通過輪詢實現了客戶端的負載均衡。
1.4 OpenFeign 的超時控制
OpenFeign 默認支持 Ribbon,可以進行超時控制,當發出請求後等待響應的時間超過設定的閾值的時候,就會報錯。
1.5 OpenFeign 的日誌打印
1.5.1 概述
Feign提供了日誌打印功能,我們可以通過配置來調整日誌級別,從而瞭解Feign中Http請求的細節,說白了就是對Feign接口的調用情況進行監控和輸出。
1.5.2 級別
- NONE: 默認的,不顯示任何日誌
- BASIC:僅記錄請求方法、URL、響應狀態及執行時間
- HEADERS:BASIC + 請求和響應頭的信息
- FULL:HEADERS + 請求和相應的正文及元數據
2. 環境準備
-
操作系統:macOS Catalina 10.15.3
-
IDEA:IntelliJ IDEA 2019.1 (Ultimate Edition)
-
JDK:1.8
-
SpringBoot:2.2.6
-
SpringCloud:Hoxton.SR3
3. 項目結構
3.1 cloud-provider-payment8001
3.2 cloud-provider-payment8003
3.3 cloud-consumer-feign-order80
4. 基本使用操作過程
3.1 新建微服務服務提供者cloud-provider-payment8001 和 8003
因爲 OpenFeign 是用在客戶端的,不是本篇的重點,所以服務提供者(服務端)的構建過程,此處略過,後面會貼上源碼。
3.2 新建微服務服務消費者cloud-consumer-feign-order80
3.2.2 在父工程下構建Module
- 使用 Maven 建立 Module
- 命名cloud-consumer-feign-order80
- 完成創建 Module
3.2.3 導入 POM 文件座標
與其他的 SpringCloud 微服務模塊的 POM 文件區別不是很大,這裏最重要的是我們要導入 openfeign 的座標。
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--api-->
<dependency> <groupId>com.atguigu.com.atguigu.com.atguigu.com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--devtools-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
3.2.4 在 resource 下新建 application.yml 配置文件
- 設定端口80
server:
port: 80
- 註冊進 Eureka 註冊中心
eureka:
client:
register-with-eureka: true #註冊進註冊中心
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集羣版
3.2.5 編寫主啓動類 OrderFeignMain80
- 在 main/java/com/atguigu/springcloud 目錄下新建主啓動類
- 編寫SpringBoot的應用入口
@SpringBootApplication
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
3.2.6 編寫業務接口
cloud-consumer-feign-order80 是服務消費者,這裏我們調用的服務的來自服務提供者的 8001 和 8003
- 在 src/main/java/com/atguigu/springcloud 下新建 service 包並新建 PaymentFeignService 接口
業務接口中我們要寫的服務要對應到服務提供者8001和8003提供的服務,因爲8001和8003提供的服務的一致,所以我們這裏只需要來看 8001 的控制類提供了什麼服務。
- 查看 8001 服務提供者所提供的服務
@PostMapping(value = "/payment/create")
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("*****插入結果:"+result);
if(result > 0){
return new CommonResult(200,"插入數據成功, serverPort:"+serverPort,result);
}else{
return new CommonResult(444,"插入數據庫失敗",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
log.info("**********查詢結果:"+payment+"\t"+"哈哈哈哈");
if (payment != null){
return new CommonResult(200,"查詢成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"沒有對應記錄,查詢ID:"+id,null);
}
}
我們這裏可以發現8001主要提供了 2 個服務,一個是 create,一個是 getPaymentById。
- 在 order80 的 PaymentFeignService 接口上添加註解 @FeignClient,指明要使用的服務提供者的名稱
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
備註:這個名稱的設定是在 8001 的 application.yml 文件中設定的。
- 在order80 的 PaymentFeignService 接口中編寫服務方法
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import feign.Param;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author Hedon Wang
* @create 2020-05-02 10:00
*/
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
@PostMapping(value = "/payment/create")
public CommonResult create(@RequestBody Payment payment);
}
3.2.7 編寫控制器類
控制器類是要來接受瀏覽器端發過來的請求,然後做出相應的響應。我們這樣裏要寫的就是暴露給瀏覽器對應這兩個服務的兩個路徑,當瀏覽器通過這兩個路徑傳過來請求的時候,控制器就調用 service 層接口中的方法,service 層接口中的方法就去查找服務提供者 8001 和 8003 提供的對應的方法並執行,然後將方法執行結果返回給瀏覽器。
- 在 src/main/java/com/atguigu/springcloud 下新建 controller 包並新建 OrderFeignController 類
- 爲 OrderFeignController 類 添加 @RestController 註解,註明這是一個控制類
- 爲 OrderFeignController 類 添加 @Slf4j 註解,提供日誌打印功能
- 注入 PaymentFeignService 服務調用接口
//注入服務調用接口
@Resource
private PaymentFeignService paymentFeignService;
- 編寫調用兩個服務的方法
@GetMapping(value = "/consumerFeign/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentFeignService.getPaymentById(id); //調用服務
}
@GetMapping(value ="/consumerFeign/payment/create")
public CommonResult<Payment> create(Payment payment){
return paymentFeignService.create(payment); //調用服務
}
3.2.8 主啓動類添加@EnableFeignClients 註解激活並開啓 Feign
@SpringBootApplication
@EnableFeignClients //使用Feign激活並開啓
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
3.2.9 測試
-
啓動 7001、7002 服務註冊中心(可選)
-
啓動服務提供者 8001、8003
-
再啓動服務消費者 80
-
測試 create 服務:http://localhost:80/consumerFeign/payment/create
- 測試 getPaymentById 服務:http://localhost:80/consumerFeign/payment/get/31
5. OpenFeign 服務調用過程總結
6. OpenFeign 與 Ribbon 調用過程的對比
6.1 OpenFeign
6.2 Ribbon
7. 超時控制演示
7.1 修改 Order80 的 application.yml 文件,設置 OpenFeign 客戶端超時控制
#設置Feign客戶端的超時時間(OpenFeign默認支持Ribbon)
ribbon:
#指的是建立連接所用的時間,適用於網絡狀況正常的情況下,兩端連接所需要的時間
ConnectTimeout: 5000 #改成5s
#指的是建立連接後從服務器讀取到可用資源所用的時間
ReadTimeout: 5000 #改成5秒
7.2 在服務提供者 8001 的控制類中添加超時方法
- 第一次我們設置程序暫停10秒鐘,超過了我們前面設定的閾值
/**
* 故意設置暫停程序,造成超時
*/
@GetMapping("/payment/feign/timeout")
public String paymentFeignTimeout(){
try{
//停10秒鐘
TimeUnit.SECONDS.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
return serverPort;
}
7.3 在服務消費者 80 的 service 接口中加入該超時方法
@GetMapping("/payment/feign/timeout")
public String paymentFeignTimeout();
7.4 在服務消費者 80 的 controller 類中添加該方法暴露給瀏覽器
@GetMapping(value = "/consumerFeign/payment/timeout")
public String paymentFeignTimeout(){
//openfeign-client 客戶端一般默認等待 1 秒鐘,但是我們這裏故意讓它暫停了 3 秒鐘
return paymentFeignService.paymentFeignTimeout();
}
7.5 測試 http://localhost/consumerFeign/payment/timeout
- 出現超時報錯
7.6 更改超時方法的停止時間爲 3 秒
/**
* 故意設置暫停程序,造成超時
*/
@GetMapping("/payment/feign/timeout")
public String paymentFeignTimeout(){
try{
//停3秒鐘
TimeUnit.SECONDS.sleep(3);
}catch (InterruptedException e){
e.printStackTrace();
}
return serverPort;
}
7.7 再次測試 http://localhost/consumerFeign/payment/timeout
- 在閾值範圍內,沒有問題
8. 日誌打印演示
8.1 在 Order80 中配置日誌 Bean 對象
8.1.1 在在 src/main/java/com/atguigu/springcloud 下新建 config 包並新建 FeignConfig 類
8.1.2 添加 @Configuration 註解註明該類爲註解類
8.1.3 配置日誌打印等級的Bean對象
這裏我們設置最高級 FULL
package com.atguigu.springcloud.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Hedon Wang
* @create 2020-05-02 11:27
*/
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
8.2 在 Order80 的 application.yml 文件中開啓 Feign 日誌支持
logging:
level:
#Feign日誌以什麼級別監控哪個接口
com.atguigu.springcloud.service.PaymentFeignService: debug
8.3 瀏覽器發出請求
8.4 後臺查看日誌打印結果
9. 程序源碼
9.1 cloud-provider-payment8001
9.1.1 PaymentController.java
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.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;
/**
* @author Hedon Wang
* @create 2020-04-22 21:33
*/
@RestController
//打日誌
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
/**
* 服務發現
*/
@Resource
private DiscoveryClient discoveryClient;
/**
* Controller是傳給前端的,我們傳CommonResult而不傳Payment
*/
@PostMapping(value = "/payment/create")
//consumer是客戶端,提交有關payment過來,我們服務端要搞一個RequestBody才能接收
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("*****插入結果:"+result);
if(result > 0){
return new CommonResult(200,"插入數據成功, serverPort:"+serverPort,result);
}else{
return new CommonResult(444,"插入數據庫失敗",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
log.info("**********查詢結果:"+payment+"\t"+"哈哈哈哈");
if (payment != null){
return new CommonResult(200,"查詢成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"沒有對應記錄,查詢ID:"+id,null);
}
}
/**
* 通過服務發現來獲得自己的信息
*/
@GetMapping("/payment/discovery")
public Object discovery(){
//① 盤點一下在Eureka註冊過的微服務有哪些
List<String> services = discoveryClient.getServices();
for(String element:services)
{
log.info("**********service: "+element);
}
//② 通過微服務的信息進一步獲得微服務的相關信息
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for(ServiceInstance instance:instances){
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return this.discoveryClient;
}
/**
* 故意設置暫停程序,造成超時
*/
@GetMapping("/payment/feign/timeout")
public String paymentFeignTimeout(){
try{
//停3秒鐘
TimeUnit.SECONDS.sleep(3);
}catch (InterruptedException e){
e.printStackTrace();
}
return serverPort;
}
}
9.1.2 PaymentDao.java
package com.atguigu.springcloud.dao;
import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @author Hedon Wang
* @create 2020-04-22 21:12
*/
//推薦用Mybatis的@Mapper,而不用Spring的@Repository
@Mapper
public interface PaymentDao {
/**
* 寫操作
* @param payment
* @return
*/
public int create(Payment payment);
/**
* 讀操作
* @param id
* @return
*/
public Payment getPaymentById(@Param("id") Long id);
}
9.1.3 PaymentService.java
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Param;
/**
* @author Hedon Wang
* @create 2020-04-22 21:29
*/
public interface PaymentService {
/**
* 寫操作
* @param payment
* @return
*/
public int create(Payment payment);
/**
* 讀操作
* @param id
* @return
*/
public Payment getPaymentById(@Param("id") Long id);
}
9.1.4 PaymentServiceImpl.java
package com.atguigu.springcloud.service.impl;
import com.atguigu.springcloud.dao.PaymentDao;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author Hedon Wang
* @create 2020-04-22 21:30
*/
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
private PaymentDao paymentDao;
@Override
public int create(Payment payment) {
return paymentDao.create(payment);
}
@Override
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}
9.1.5 PaymentMain8001.java
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author Hedon Wang
* @create 2020-04-22 20:48
*/
/**
* 主程序
*/
@SpringBootApplication
@EnableEurekaClient //註冊進Eureka
@EnableDiscoveryClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
9.1.6 PaymentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.atguigu.springcloud.dao.PaymentDao">
<!--增
前面有在application.yml配置了別名,所以這裏只需要寫Payment,而不需要寫com.atguigu.com.atguigu.com.atguigu.com.atguigu.springcloud.entities.Payment
useGenerateKeys = true 的原因:
因爲我們插入數據庫,必然會返回一個數字
如果這個數字大於0,說明我們插入成功
如果這個數據小於等於0,說明我們插入失敗
這也是我們create返回值爲int的原因
keyProperty: 主鍵
-->
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
INSERT INTO payment(serial) values (#{serial});
</insert>
<!--查-->
<resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"></id>
<result column="serial" property="serial" jdbcType="VARCHAR"></result>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
SELECT * FROM payment where id=#{id};
</select>
</mapper>
9.1.7 application.yml
server:
port: 8001
spring:
application:
name: cloud-payment-service #微服務的名稱,別改動,重要
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 當前數據源操作類型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql驅動包
url: jdbc:mysql://localhost:3306/cloudDB?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities # 所有Entity別名類所在包
#配置註冊金eureka
eureka:
client:
register-with-eureka: true #註冊金Eureka
#是否從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 #集羣版
instance:
instance-id: payment8001
prefer-ip-address: true
#Eureka客戶端向服務端發送心跳的時間間隔:默認30秒
# lease-renewal-interval-in-seconds: 1
#Eureka服務端在收到最後一次心跳後等待時間上限:默認90秒
# lease-expiration-duration-in-seconds: 2
9.2 cloud-provider-payment8003
9.2.1 PaymentController.java
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.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;
/**
* @author Hedon Wang
* @create 2020-04-22 21:33
*/
@RestController
//打日誌
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@Resource
private DiscoveryClient discoveryClient;
/**
* Controller是傳給前端的,我們傳CommonResult而不傳Payment
*/
@PostMapping(value = "/payment/create")
//consumer是客戶端,提交有關payment過來,我們服務端要搞一個RequestBody才能接收
// public CommonResult create(@RequestBody Payment payment){
public CommonResult create(Payment payment){
int result = paymentService.create(payment);
log.info("*****插入結果:"+result);
if(result > 0){
return new CommonResult(200,"插入數據成功, serverPort:"+serverPort,result);
}else{
return new CommonResult(444,"插入數據庫失敗",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
log.info("**********查詢結果:"+payment+"\t"+"哈哈哈哈");
if (payment != null){
return new CommonResult(200,"查詢成功,serverPort: "+serverPort,payment);
}else{
return new CommonResult(444,"沒有對應記錄,查詢ID:"+id,null);
}
}
@GetMapping("/payment/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();
for(String element:services){
log.info(element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for(ServiceInstance instance:instances){
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return this.discoveryClient;
}
}
9.2.2 PaymentDao.java
package com.atguigu.springcloud.dao;
import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @author Hedon Wang
* @create 2020-04-22 21:12
*/
//推薦用Mybatis的@Mapper,而不用Spring的@Repository
@Mapper
public interface PaymentDao {
/**
* 寫操作
* @param payment
* @return
*/
public int create(Payment payment);
/**
* 讀操作
* @param id
* @return
*/
public Payment getPaymentById(@Param("id") Long id);
}
9.2.3 PaymentService.java
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Param;
/**
* @author Hedon Wang
* @create 2020-04-22 21:29
*/
public interface PaymentService {
/**
* 寫操作
* @param payment
* @return
*/
public int create(Payment payment);
/**
* 讀操作
* @param id
* @return
*/
public Payment getPaymentById(@Param("id") Long id);
}
9.2.4 PaymentServiceImpl.java
package com.atguigu.springcloud.service.impl;
import com.atguigu.springcloud.dao.PaymentDao;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author Hedon Wang
* @create 2020-04-22 21:30
*/
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
private PaymentDao paymentDao;
@Override
public int create(Payment payment) {
return paymentDao.create(payment);
}
@Override
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}
9.2.5 PaymentMain8003.java
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author Hedon Wang
* @create 2020-04-22 20:48
*/
/**
* 主程序
*/
@SpringBootApplication
@EnableEurekaClient //註冊進Eureka
@EnableDiscoveryClient
public class PaymentMain8003 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8003.class,args);
}
}
9.2.6 PaymentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.atguigu.springcloud.dao.PaymentDao">
<!--增
前面有在application.yml配置了別名,所以這裏只需要寫Payment,而不需要寫com.atguigu.com.atguigu.com.atguigu.com.atguigu.springcloud.entities.Payment
useGenerateKeys = true 的原因:
因爲我們插入數據庫,必然會返回一個數字
如果這個數字大於0,說明我們插入成功
如果這個數據小於等於0,說明我們插入失敗
這也是我們create返回值爲int的原因
keyProperty: 主鍵
-->
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
INSERT INTO payment(serial) values (#{serial});
</insert>
<!--查-->
<resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"></id>
<result column="serial" property="serial" jdbcType="VARCHAR"></result>
</resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
SELECT * FROM payment where id=#{id};
</select>
</mapper>
9.2.7 application.yml
server:
port: 8003
spring:
application:
name: cloud-payment-service #微服務的名稱,別改動,重要
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 當前數據源操作類型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql驅動包
url: jdbc:mysql://localhost:3306/cloudDB?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities # 所有Entity別名類所在包
#配置註冊金eureka
eureka:
client:
register-with-eureka: true #註冊金Eureka
#是否從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 #集羣版
instance:
instance-id: payment8003
prefer-ip-address: true
9.3 cloud-consumer-feign-order80
9.3.1 FeignConfig.java
package com.atguigu.springcloud.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Hedon Wang
* @create 2020-05-02 11:27
*/
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
9.3.2 OrderFeignController.java
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @author Hedon Wang
* @create 2020-05-02 10:04
*/
@RestController
@Slf4j
public class OrderFeignController {
//注入服務調用接口
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/consumerFeign/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentFeignService.getPaymentById(id); //調用服務
}
@GetMapping(value ="/consumerFeign/payment/create")
public CommonResult<Payment> create(Payment payment){
return paymentFeignService.create(payment); //調用服務
}
@GetMapping(value = "/consumerFeign/payment/timeout")
public String paymentFeignTimeout(){
//openfeign-client 客戶端一般默認等待 1 秒鐘,但是我們這裏故意讓它暫停了 3 秒鐘
return paymentFeignService.paymentFeignTimeout();
}
}
9.3.3 PaymentFeignService.java
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import feign.Param;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author Hedon Wang
* @create 2020-05-02 10:00
*/
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
//RequestBody要搭配PostMapping
@PostMapping(value = "/payment/create")
//consumer是客戶端,提交有關payment過來,我們服務端要搞一個RequestBody才能接收
public CommonResult create(@RequestBody Payment payment);
@GetMapping("/payment/feign/timeout")
public String paymentFeignTimeout();
}
9.3.4 OrderFeignMain80.java
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author Hedon Wang
* @create 2020-05-02 09:54
*/
@SpringBootApplication
@EnableFeignClients //使用Feign激活並開啓
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
9.3.5 application.yml
server:
port: 80
eureka:
client:
register-with-eureka: true #註冊進註冊中心
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集羣版
#設置Feign客戶端的超時時間(OpenFeign默認支持Ribbon)
ribbon:
#指的是建立連接所用的時間,適用於網絡狀況正常的情況下,兩端連接所需要的時間
ConnectTimeout: 5000 #改成5s
#指的是建立連接後從服務器讀取到可用資源所用的時間
ReadTimeout: 5000 #改成5秒
logging:
level:
#Feign日誌以什麼級別監控哪個接口
com.atguigu.springcloud.service.PaymentFeignService: debug
10. 附註說明
此處還用到 Eureka 註冊中心,因爲與本篇重點偏離太大,爲避免偏離重點,且爲了突出本篇的核心所在,就省略了這部分的講解。