文章目录
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 注册中心,因为与本篇重点偏离太大,为避免偏离重点,且为了突出本篇的核心所在,就省略了这部分的讲解。