OpenFeign组件的使用
遇到的问题
1)P28-(11:27),为什么要使用OpenFeign代替Ribbon完成服务间的通信。(46:00),解决集群的问题,图1。
2)P29-(10:30),。(14:30),类别服务调用商品服务,传递多个参数。(35:30),@PostMapping。
3)P30-(22:03),接收集合类型的参数,vo:用来传递数据的对象称为值对象。
4)P31-(29:00)
,响应处理返回的JSON字符串,怎么转换成想要的对象?需要用到自定义格式解析。
5)P32-19:30
,OpenFeign的日志展示。
1.RestTemplate存在的问题:①路径写死。②不能自动转换响应结果为对应的对象。③必须集成Ribbon实现负载均衡。
2.OpenFeign能够解决RestTemplate存在的问题,实现系统间服务的通信问题。Feign默认集成了Ribbon,实现请求的负载均衡。使用简单:①只需要写一个接口+一个注解。②调用服务代码更加简单,自动完成数据传递过程中的对象转换。
3.服务间通信问题:①http协议:使用rest方式通信,效率低,但解耦合,推荐。②RPC技术:使用传输层协议通信,效率高,Dubbo框架。
开发步骤
1)创建两个独立的SpringBoot应用,并注册到服务注册中心 consul。2)引入服务注册中心的依赖。3)修改配置文件。4)入口类加入注解。5)使用openfeign进行调用,①在服务调用方引入openfeign依赖,②在服务调用方入口类加入注解,开启Feign的调用支持。③开发客户端接口。
pom.xml
<parent>
<artifactId>springcloud_parent</artifactId>
<groupId>com.jun</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sc06_category</artifactId>
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--consul-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--actuator-->
<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>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
</dependencies>
application.properties
server.port=8787
spring.application.name=CATEGORY
#注册到consul server
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
# 配置类别调用商品服务openfeign默认超时时间,单位毫秒
#feign.client.config.PRODUCT.connectTimeout=5000 #配置指定服务连接超时
#feign.client.config.PRODUCT.readTimeout=5000 #配置指定服务等待超时
# 配置openfeign默认调用所有服务的超时时间
feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000
# 开启openfeign中调用商品服务日志展示
feign.client.config.PRODUCT.loggerLevel=full
feign.client.config.ORDER.loggerLevel=none
# 展示openfeign日志
logging.level.com.jun.feignclient=debug
#---------------------------------------
server.port=8788
spring.application.name=PRODUCT
#注册到consul server
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
#mybatis
#redis
#es
#mq
启动类
@SpringBootApplication
@EnableDiscoveryClient//开启服务注册
@EnableFeignClients //开启openfein客户端调用
public class CategoryApplication {}
@SpringBootApplication
@EnableDiscoveryClient
public class ProductApplication {}
CategoryController.java
//com.jun.entity
public class Product {
private Integer id;
private String name;
private Double price;
private Date bir;}
//com.jun.controller
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
private ProductClient productClient;
@GetMapping("/category")
//public String category() {
//public Product category() {
//public List<Product> category() {
//public Map<String, Object> category() {
public String category() {
log.info("category service……");
//1.RestTemplate 2.RestTemplate+Ribbon() 3.OpenFeign
//String result = productClient.test("小陈", 23); //【P29-10:30,这里可以变通】
//String result = productClient.test1(21,"xiaoming"); //(P29-31:27)
//String result = productClient.test2(new Product(1,"钢笔",24.3,new Date()));
//String result = productClient.test3(new String[]{"21","22","23"});
//String result = productClient.test4(new String[]{"21","23","24"});
//return "category ok!! " + result;
/*Product product = productClient.product(21);
log.info("product: {}",product);
return product;*/
/*List<Product> products = productClient.findByCategoryId(1);
products.forEach(product -> log.info("product: {}",product));
return products;*/
/**
*在http://localhost:8787/category请求页面可以拿到数据(total:1000,rows:集合)。
*尝试将页面中的数据打印出来。出错:java.util.LinkedHashMap cannot be cast to ......Product
* 原因是:页面中的数据是JSON格式的字符串,P31-30:00
*/
/*Map<String, Object> objectMap = productClient.findByCategoryIdAndPage(1, 5, 1);
System.out.println(objectMap.get("total"));
List<Product> rows = (List<Product>) (objectMap.get("rows"));
rows.forEach(product -> System.out.println(product));
return objectMap;*/
/*String result = productClient.findByCategoryIdAndPage(1, 5, 1);
System.out.println(result);
//自定义json反序列化(json字符串转为对象),对象转为json叫序列化(JSONObject.toJSON)。
JSONObject jsonObject = JSONObject.parseObject(result);
System.out.println(jsonObject.get("total"));
Object rows = jsonObject.get("rows");
System.out.println(rows);
//二次json反序列化
List<Product> products = jsonObject.parseArray(rows.toString(), Product.class);
products.forEach(product -> {
log.info("product:{}",product);
});
return result;*/
String result = productClient.product();
//出现异常:Read timed out executing GET http://PRODUCT/product
return result; //P31,openFeign默认超时处理
}
}
ProductClient.java
//com.jun.feignclient
@FeignClient(value="PRODUCT") //value:用来书写调用服务的id
public interface ProductClient { //调用商品服务的接口
//P31-30:00声明调用商品服务根据类别id查询分页查询商品信息以及总条数
@GetMapping("/productList")
//Map<String,Object> findByCategoryIdAndPage
String findByCategoryIdAndPage(@RequestParam("page") Integer page,
@RequestParam("rows") Integer rows,
@RequestParam("categoryId") Integer categoryId);
//P31-声明调用商品服务根据类别id查询一组商品信息
@GetMapping("/products")
List<Product> findByCategoryId(@RequestParam("categoryId") Integer categoryId);
//P31-声明调用根据id查询商品信息接口
@GetMapping("/product/{id}")
Product product(@PathVariable("id") Integer id);
//P30-声明调用商品服务中的test4接口,传递一个list集合类型的参数
//http://localhost:8788/test4?ids=21&ids=22&ids=23
@GetMapping("/test4")
String test4(@RequestParam("ids") String[] ids);
//P30-声明调用商品服务中test3接口,传递一个数组类型 queryString /test3?ids=21&ids=22
@GetMapping("/test3")
String test3(@RequestParam("ids") String[] ids);
//3.声明调用商品服务中test2接口 传递一个商品对象
@PostMapping(value = "/test2")
String test2(@RequestBody Product product);
//2.声明调用商品服务中test1接口:
//路径传递参数,在openfeign接口声明中必须给参数加入注解:@PathVariable
@GetMapping("/test1/{id}/{name}")
String test1(@PathVariable("id") Integer id, @PathVariable("name") String name);
//1.声明调用商品服务中的test接口传递name,age两个参数,test?name=xxx&age=23
//queryString方式传递参数:在openfeign接口声明中必须给参数加入注解。@RequestParam
@GetMapping("/test")
String test(@RequestParam("name") String name, @RequestParam("age") Integer age);
//调用商品服务
@GetMapping("/product")
String product();
@GetMapping("/list")
String list();
}
ProductController.java
//sc07_product:com.jun.vos
//定义用来接收集合类型参数的对象
public class CollectionVO {
private List<String> ids;//接收集合声明在这里
public List<String> getIds() {return ids;}
public void setIds(List<String> ids) {this.ids = ids;}
}
//com.jun.controller
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
/**
* 【P31-17:00】
* 参数:page表示当前页,rows代表每页响应的记录数,(page-1)*rows代表起始条数
* 1.根据类别id分页查询符合当前页集合数据,返回类型:List<Product>:
* select * from t_product where categoryId=? limt ?(page-1)*rows,?(rows)
* 2.根据类别id查询当前类别下总条数totalCount,返回类型:int或Long类型,
* select count(id) from t_product where categoryId=?
* 【返回值类型】:①可以自己定义一个对象接收:ProductDTO{List<Product>,Long totalCount}
* ②Map<String,Object>
*/
@GetMapping("/productList")
public Map<String,Object> findByCategoryIdAndPage(Integer page, Integer rows, Integer categoryId){
log.info("当前页: {},每页显示记录数:{},当前类别id:{} ",page,rows,categoryId);
Map<String, Object> map = new HashMap<>();
List<Product> products = new ArrayList<>();
products.add(new Product(1,"辣条",10.42,new Date()));
products.add(new Product(2,"果冻",10.42,new Date()));
products.add(new Product(3,"冰棍",10.42,new Date()));
int total = 1000;
map.put("rows",products);
map.put("total", total);
return map;
}
//P31-声明调用商品服务根据类别id查询一组商品信息
@GetMapping("/products")
public List<Product> findByCategoryId(Integer categoryId){
log.info("类别id: {}",categoryId);
//调用业务逻辑根据类别id查询商品列表
List<Product> products = new ArrayList<>();
products.add(new Product(1,"面包",23.23,new Date()));
products.add(new Product(2,"牛奶",23.23,new Date()));
products.add(new Product(3,"可乐",23.23,new Date()));
return products;
}
//P31-定义一个接口接收id类型参数,返回一个基于id查询的对象
@GetMapping("/product/{id}")
public Product product(@PathVariable("id") Integer id){
log.info("id:{}",id);
return new Product(id,"彩笔",23.23,new Date());
}
/**5.定义一个接口接受集合类型参数
*springmvc不能直接接受集合类型参数,如果想要接收集合类型参数必须将集合放入对象中,得使用对象的方式接收才行
*oo:oriented(面向) object(对象) 面向对象
*vo(value object):用来传递数据对象称之为值对象
*dto:(data transfer(传输) object):数据传输对象
*/
@GetMapping("/test4")
public String test4(CollectionVO collectionVO){
collectionVO.getIds().forEach(id-> log.info("id:{} ",id));
return "test4 ok,当前服务端口为: "+port;
}
//4.定义个接口接受数组类型参数
@GetMapping("/test3")
public String test3(String[] ids){
for (String id : ids) {
log.info("id: {}",id);
}
//手动转为list List<String> strings = Arrays.asList(ids); (P30-8:25)
return "test3 ok,当前服务端口为: "+port;
}
//3.定义一个接受对象类型参数接口
@PostMapping("/test2")
public String test2(@RequestBody Product product){
log.info("product:{}",product);
return "test2 ok,当前服务端口为: "+port;
}
//2.定义一个接受零散类型参数接口 路径传递参数
@GetMapping("/test1/{id}/{name}")
public String test1(@PathVariable("id") Integer id, @PathVariable("name") String name){
log.info("id:{},name:{}",id,name);
return "test1 ok,当前提供服务的端口为: "+port;
}
//1.定义一个接受零散类型参数接口,queryString(?的方式)
@GetMapping("/test")
public String test(String name, Integer age){
log.info("name:{},age:{}",name,age);
return "test ok,当前提供服务的端口为: "+port;
}
@GetMapping("/product")
public String product() throws InterruptedException {
log.info("进入商品服务.....");
Thread.sleep(2000); //P32,openFeign的默认超时处理
//出现异常:Read timed out executing GET http://PRODUCT/product
return "product ok,当前提供服务的端口:"+port;
}
@GetMapping("/list")
public String list(HttpServletRequest request, String color){
String header = request.getHeader("User-Name");
System.out.println("获取对应请求参数 color: "+color);
System.out.println("获取请求头信息: "+header);
log.info("商品列表服务");
return "list ok当前提供服务端口: "+port;
}
}
OpenFeign实现服务间通信响应处理
使用OpenFeign调用服务,并返回对象
//P31-声明调用根据id查询商品信息接口
@GetMapping("/product/{id}")
Product product(@PathVariable("id") Integer id);
使用OpenFeign调用服务,并返回集合
//P31-声明调用商品服务根据类别id查询一组商品信息
@GetMapping("/products")
List<Product> findByCategoryId(@RequestParam("categoryId") Integer categoryId);
使用OpenFeign调用服务,返回JSON格式的字符串
//声明调用商品服务根据类别id查询分页查询商品信息以及总条数
@GetMapping("/productList")
String findByCategoryIdAndPage(@RequestParam("page") Integer page,
@RequestParam("rows") Integer rows,
@RequestParam("categoryId") Integer categoryId);
OpenFeign的默认超时处理
- 默认的超时处理:使用OpenFeign组件在进行服务间通信时要求被调用服务必须在1s内给予响应,一旦服务执行业务逻辑时间超过1s,OpenFeign组件将直接报错:Read timed out executing GEThttp://PRODUCT/product
- 修改OpenFeign的超时时间。P32-15:00