RestTemplate+ribbon方式 存在问题:
1.每次调用服务都需要写这些代码,存在大量的代码冗余 String restTemplateForObject = restTemplate.getForObject(“http://服务名/url?参数“ + name, String.class);
2.服务地址如果修改,维护成本增高
3.使用时不够灵活
1、Feign介绍
https://cloud.spring.io/spring-cloud-openfeign/reference/html/
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,默认实现了负载均衡的效果并且springcloud为feign添加了springmvc注解的支持。
2、springcloud-consul-user-openfeign-6107
2.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.morrow</groupId>
<artifactId>springcloud-consul-user-openfeign-6107</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-consul-user-openfeign-6107</name>
<description>springcloud-consul-user-openfeign-6107 project for Spring Boot</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<!--引入consul依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 健康度监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 引入 openfeign 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<!--全局管理springcloud版本,并不会引入具体依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2 yml 文件
server:
port: 6107
spring:
application:
name: users
cloud:
consul:
host: localhost #注册consul服务的主机
port: 8500 #注册consul服务的端口号
discovery:
# register-health-check: true # 开启 关闭consul 服务的健康检查[不推荐]
service-name: ${spring.application.name} #指定注册的服务名称 默认就是应用名
2.3 ProductClient 文件
package com.morrow.springcloudconsuluseropenfeign6107.clients;
import com.morrow.springcloudconsuluseropenfeign6107.entity.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Map;
//value属性用来指定:调用服务名称
@FeignClient("products")
public interface ProductClient {
@GetMapping("/product/findAlls") //书写服务调用路径
String findAlls();
@GetMapping("/product/findOne")
Map<String, Object> findOne(@RequestParam("productId") String productId);
@PostMapping("/product/save")
Map<String,Object> save(@RequestParam("name") String name);
@PostMapping("/product/saveProduct")
Map<String,Object> saveProduct(@RequestBody Product product);
}
2.4 Product 文件
@Data
public class Product {
private Integer id;
private String name;
}
2.5 TestFeignController文件
package com.morrow.springcloudconsuluseropenfeign6107.controller;
import com.morrow.springcloudconsuluseropenfeign6107.clients.ProductClient;
import com.morrow.springcloudconsuluseropenfeign6107.entity.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@Slf4j
public class TestFeignController {
@Autowired
ProductClient productClient;
/**
* Get 请求 无参数
* @return
*/
@GetMapping("testFindAll")
public String findAll(){
log.info("通过使用OpenFeign组件调用商品服务...");
String result = productClient.findAlls();
log.info("成功调用商品服务...:[{}]",result);
return result;
}
/**
* Get 请求 单个参数
* @param id
* @return
*/
@GetMapping("/test1")
public Map<String,Object> test1(String id){
log.info("用来测试Openfiegn的GET方式参数传递");
Map<String, Object> msg = productClient.findOne(id);
log.info("调用返回信息:[{}]",msg);
return msg;
}
/**
* POST 请求 单个参数
* @param name
* @return
*/
@PostMapping("/user/save")
public Map<String,Object> save(String name){
log.info("接收到的商品信息名称:[{}]",name);
Map<String,Object> map = productClient.save(name);
log.info("调用成功返回结果: "+map);
return map;
}
/**
* POST 请求 实体类
* @param product
* @return
*/
@PostMapping("/user/saveProduct")
public Map<String, Object> saveProduct(Product product){
log.info("接收到的商品信息:[{}]",product);
Map<String, Object> map = productClient.saveProduct(product);
log.info("调用成功返回结果: [{}]",map);
return map;
}
}
3、 springcloud-consul-products-6105
3.1 添加 ProductController2 文件
@RestController
@Slf4j
public class ProductController2 {
@Value("${server.port}")
private int port;
@GetMapping("/product/findAlls")
public Map<String,Object> findAlls() throws InterruptedException {
// Thread.sleep(2000);
log.info("商品服务查询所有调用成功,当前服务端口:[{}]",port);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","服务调用成功,服务提供端口为: "+port);
map.put("status",true);
return map;
}
@GetMapping("/product/findOne")
public Map<String,Object> findOne(String productId){
log.info("商品服务查询商品信息调用成功,当前服务端口:[{}]",port);
log.info("当前接收商品信息的id:[{}]",productId);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
map.put("status",true);
map.put("productId",productId);
return map;
}
@PostMapping("/product/save")
public Map<String,Object> save(String name){
log.info("商品服务保存商品调用成功,当前服务端口:[{}]",port);
log.info("当前接收商品名称:[{}]",name);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
map.put("status",true);
map.put("name",name);
return map;
}
@PostMapping("/product/saveProduct")
public Map<String,Object> saveProduct(@RequestBody Product product){
log.info("商品服务保存商品信息调用成功,当前服务端口:[{}]",port);
log.info("当前接收商品名称:[{}]",product);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
map.put("status",true);
map.put("product",product);
return map;
}
}
4、重启项目 测试
5、OpenFeign超时设置
默认情况下,openFiegn在进行服务调用时,要求服务提供方处理业务逻辑时间必须在1S内返回,如果超过1S没有返回则OpenFeign会直接报错,不会等待服务执行,但是往往在处理复杂业务逻辑是可能会超过1S,因此需要修改OpenFeign的默认服务调用超时时间。
5.1 修改OpenFeign默认超时时间
server:
port: 6107
spring:
application:
name: users
cloud:
consul:
host: localhost #注册consul服务的主机
port: 8500 #注册consul服务的端口号
discovery:
# register-health-check: true # 开启 关闭consul 服务的健康检查[不推荐]
service-name: ${spring.application.name} #指定注册的服务名称 默认就是应用名
feign:
client:
config:
products:
connectTimeout: 5000 #配置指定服务连接超时
readTimeout: 5000 #配置指定服务等待超时
# default:
# connectTimeout: 5000 #配置所有服务连接超时
# readTimeout: 5000 #配置所有服务等待超时
6、OpenFeign调用详细日志展示
往往在服务调用时我们需要详细展示feign的日志,默认feign在调用是并不是最详细日志输出,因此在调试程序时应该开启feign的详细日志展示。feign对日志的处理非常灵活可为每个feign客户端指定日志记录策略,每个客户端都会创建一个logger默认情况下logger的名称是feign的全限定名需要注意的是,feign日志的打印只会DEBUG级别做出响应。
我们可以为feign客户端配置各自的logger.level对象,告诉feign记录那些日志logger.lever有以下的几种值
NONE 不记录任何日志
BASIC 仅仅记录请求方法,url,响应状态代码及执行时间HEADERS 记录Basic级别的基础上,记录请求和响应的header
FULL 记录请求和响应的header,body和元数据
6.1 、开启日志展示
server:
port: 6107
spring:
application:
name: users
cloud:
consul:
host: localhost #注册consul服务的主机
port: 8500 #注册consul服务的端口号
discovery:
# register-health-check: true # 开启 关闭consul 服务的健康检查[不推荐]
service-name: ${spring.application.name} #指定注册的服务名称 默认就是应用名
feign:
client:
config:
products:
connectTimeout: 5000 #配置指定服务连接超时
readTimeout: 5000 #配置指定服务等待超时
loggerLevel: full #开启指定服务日志展示
# default:
# connectTimeout: 5000 #配置所有服务连接超时
# readTimeout: 5000 #配置所有服务等待超时
# loggerLevel: full #全局开启服务日志展示
logging:
level:
com:
morrow:
springcloudconsuluseropenfeign6107:
clients: debug #指定feign调用客户端对象所在包,必须是debug级别