Feign服务调用学习笔记
1概述
本节简单学习下独立使用Feign的基本用法。Feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。Feign底层默认是使用jdk中的HttpURLConnection发送HTTP请求(没有连接池,保持长连接),feign也提供了httpClient、OKhttp来发送请求。
之所以学习Feign这个功能,主要是考虑:
1.传统采用httpclient(org.apache.httpcomponents)或者okhttp(io.github.openfeign)这样相对较重的框架,对初学者来说编码量与学习曲线都会是一个挑战。
2.而使用spring中RestTemplate,又没有配置化的解决方案。
1.1 Feign底层原理
feign是一个伪客户端,即它不做任何的请求处理。Feign通过处理注解生成request,从而实现简化HTTP API开发的目的,即开发人员可以使用注解的方式定制request api模板,在发送http request请求之前,feign通过处理注解的方式替换掉request模板中的参数,这种实现方式显得更为直接、可理解。
2代码实战
2.1Feign
2.1.1client
2.1.1.1maven依赖
注意点:Feign 10.x 及以上版本是在 Java 8上构建的,应该在 Java 9、10 和 11上工作。对于需要 JDK 6兼容性的用户,请使用 Feign 9.x。
<!--3.1.feign-core-->
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-core -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>9.6.0</version>
</dependency>
<!--3.2.feign-jackson-->
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-jackson -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>9.6.0</version>
</dependency>
2.1.1.2远程服务接口
package com.kikop.mynavivefeign.service.impl;
import com.alibaba.fastjson.JSONObject;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
/**
* @author kikop
* @version 1.0
* @project Name: myspringdemo
* @file Name: RemoveServiceImpl
* @desc 远程服务feign包装的接口
* @date 2021/3/7
* @time 10:50
* @by IDE: IntelliJ IDEA
*/
public interface RemoveServiceImpl {
// 通过@RequestLine指定HTTP协议及URL地址
@RequestLine("GET /myspringbootdemo/myfeign/say")
String say();
// 字符串传参示例
@RequestLine("GET /myspringbootdemo/myfeign/getStrValue?userName={userName}")
String getStrValue(@Param(value = "userName") String userName);
// JSON对象传参示例
@Headers({"Content-Type: application/json", "Accept: application/json"})
@RequestLine("POST /myspringbootdemo/myfeign/getObjectValue")
JSONObject getObjectValue(JSONObject req);
}
2.1.1.3调用测试
package com.kikop.mynavivefeign.test;
import com.alibaba.fastjson.JSONObject;
import com.kikop.mynavivefeign.service.impl.RemoveServiceImpl;
import feign.Feign;
import feign.Request;
import feign.Retryer;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
public class mytest {
public static void main(String[] args) {
try {
for (int i = 0; i <10 ; i++) {
getStrValueTest();
}
} catch (Throwable ex) {
System.out.println("----------ex----------");
ex.printStackTrace();
}
}
private static void sayTest() {
RemoveServiceImpl removeService = Feign.builder()
// options方法指定连接超时时长及响应超时时长
.options(new Request.Options(1000, 3500))
// retryer方法指定重试策略:最大3次
.retryer(new Retryer.Default(5000, 5000, 3))
// target方法绑定接口与服务端地址。返回类型为绑定的接口类型。
.target(RemoveServiceImpl.class, "http://127.0.0.1:8085");
System.out.println(removeService.say());
}
private static void getStrValueTest() {
RemoveServiceImpl removeService = Feign.builder()
// options方法指定连接超时时长及响应超时时长
.options(new Request.Options(1000, 3500))
// retryer方法指定重试策略
.retryer(new Retryer.Default(5000, 5000, 3))
// target方法绑定接口与服务端地址。返回类型为绑定的接口类型。
.target(RemoveServiceImpl.class, "http://127.0.0.1:8085");
String kikop = removeService.getStrValue("kikop");
System.out.println(kikop);
}
private static void getObjectValueTest() {
RemoveServiceImpl removeService = Feign.builder()
// pojo对象编码器
.encoder(new JacksonEncoder())
// pojo对象解码器
.decoder(new JacksonDecoder())
// options方法指定连接超时时长及响应超时时长
.options(new Request.Options(1000, 3500))
// retryer方法指定重试策略
.retryer(new Retryer.Default(5000, 5000, 3))
// target方法绑定接口与服务端地址。返回类型为绑定的接口类型。
.target(RemoveServiceImpl.class, "http://127.0.0.1:8185");
JSONObject req = new JSONObject();
req.put("userName", "kikop");
JSONObject result = removeService.getObjectValue(req);
System.out.println(result.getString("userName"));
}
}
2.1.2server(辅助测试)
2.1.2.1后台服务接口
package com.kikop.demo;
import com.alibaba.fastjson.JSONObject;
import com.kikop.mytech.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RequestMapping("/myfeign")
@RestController
@SpringBootApplication
public class DemoApplication {
@Autowired
private HelloService helloService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RequestMapping("/say")
public String say() {
return helloService.say();
}
// http://localhost:8085/myspringbootdemo/myfeign/getStrValue?userName=kikop
@RequestMapping(value = "/getStrValue", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String getStrValue(@RequestParam String userName) {
System.out.println("begin getStrValue...");
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.execute(new MyTaskHandler());
System.out.println("end getStrValue!");
return userName.toUpperCase();
}
@RequestMapping(value = "/getObjectValue", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public JSONObject getObjectValue(@RequestBody JSONObject user) throws InterruptedException {
System.out.println("getObjectValue...");
JSONObject result = new JSONObject();
result.put("success", true);
result.put("userName", user.getString("userName").toUpperCase());
System.out.println("getObjectValue!");
return result;
}
}
2.1.2.2服务端配置
server.port=8085
server.servlet.context-path=/myspringbootdemo
mytech.demo.name=kikop
mytech.demo.hobby=swim
3总结
总到来说,Feign的源码实现的过程如下:
- 首先通过@EnableFeignCleints注解开启FeignCleint
- 根据Feign的规则实现接口,并加@FeignCleint注解
- 程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中
- 当接口的方法被调用,通过jdk的动态代理,来生成具体的RequesTemplate
- RequesTemplate在生成Request
- Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
- 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。
网友评论