SpringCloud——Feign例項及原理
一、例項
1、配置feign
新增依賴
在maven的pom中新增feign
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
配置啟用
在Application啟動類中新增@EnableFeignClients註解。
@EnableEurekaClient
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2、新建feign
@FeignClient(name = "SERVICE-NAME", url = "${***}")
public interface TestFeign {
}
其中:
name:微服務的名稱,一定要以eureka後臺配置的保持一致。
url:可以手動指定feign的呼叫地址
fallback:標記容錯後執行的類
在feign中定義介面的方式與正常介面並無差異,需注意引數名稱等保持一致。如:
@RequestMapping(value = "/user/message", method = RequestMethod.POST)
JSONObject sendMessage(@RequestParam("userId") String userId, @RequestParam("content") String content);
3、呼叫feign
在ServiceImpl中注入feign介面,正常使用即可。
@Autowired
TestFeign testFeign;
二、原理
如果不使用springcloud的相關元件,呼叫服務需要要走http,配置請求head、body,然後才能發起請求。獲得響應體後,還需解析等操作,十分繁瑣。
那為什麼feign的使用如此簡單輕量呢?
1、如何啟用
啟動配置上檢查是否有@EnableFeignClients註解,如果有該註解,則開啟包掃描,掃描被@FeignClient註解介面。掃描出該註解後,通過beanDefinition注入到IOC容器中,方便後續被呼叫使用。
在FeignClientsRegistrar中,registerFeignClients()完成了註冊feign的操作。
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
scanner.setResourceLoader(this.resourceLoader);
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
......
//遍歷該專案所需呼叫的服務
Iterator var17 = ((Set)basePackages).iterator();
while(var17.hasNext()) {
String basePackage = (String)var17.next();
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
Iterator var21 = candidateComponents.iterator();
while(var21.hasNext()) {
BeanDefinition candidateComponent = (BeanDefinition)var21.next();
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
//獲取feign中的詳細資訊
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = this.getClientName(attributes);
//註冊配置資訊
this.registerClientConfiguration(registry, name, attributes.get("configuration"));
//註冊feign客戶端
this.registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
註冊feign客戶端,包括使用註解時配置的所有資訊。
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
this.validate(attributes);
definition.addPropertyValue("url", this.getUrl(attributes));
definition.addPropertyValue("path", this.getPath(attributes));
String name = this.getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(2);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = ((Boolean)attributes.get("primary")).booleanValue();
beanDefinition.setPrimary(primary);
String qualifier = this.getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
2、如何發起請求
ReflectiveFeign內部使用了jdk的動態代理為目標介面生成了一個動態代理類,這裡會生成一個InvocationHandler(jdk動態代理原理)統一的方法攔截器,同時為介面的每個方法生成一個SynchronousMethodHandler攔截器,並解析方法上的 後設資料,生成一個http請求模板。
在SynchronousMethodHandler類中生成RequestTemplate發起請求。
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
Object executeAndDecode(RequestTemplate template) throws Throwable {
Request request = targetRequest(template);
Response response;
long start = System.nanoTime();
try {
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
}
傳送http請求
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection).toBuilder().request(request).build();
}
HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
final HttpURLConnection
connection =
(HttpURLConnection) new URL(request.url()).openConnection();
if (connection instanceof HttpsURLConnection) {
HttpsURLConnection sslCon = (HttpsURLConnection) connection;
if (sslContextFactory != null) {
sslCon.setSSLSocketFactory(sslContextFactory);
}
if (hostnameVerifier != null) {
sslCon.setHostnameVerifier(hostnameVerifier);
}
}
connection.setConnectTimeout(options.connectTimeoutMillis());
connection.setReadTimeout(options.readTimeoutMillis());
connection.setAllowUserInteraction(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod(request.method());
Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);
boolean
gzipEncodedRequest =
contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);
boolean
deflateEncodedRequest =
contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);
boolean hasAcceptHeader = false;
Integer contentLength = null;
for (String field : request.headers().keySet()) {
if (field.equalsIgnoreCase("Accept")) {
hasAcceptHeader = true;
}
for (String value : request.headers().get(field)) {
if (field.equals(CONTENT_LENGTH)) {
if (!gzipEncodedRequest && !deflateEncodedRequest) {
contentLength = Integer.valueOf(value);
connection.addRequestProperty(field, value);
}
} else {
connection.addRequestProperty(field, value);
}
}
}
// Some servers choke on the default accept string.
if (!hasAcceptHeader) {
connection.addRequestProperty("Accept", "*/*");
}
if (request.body() != null) {
if (contentLength != null) {
connection.setFixedLengthStreamingMode(contentLength);
} else {
connection.setChunkedStreamingMode(8196);
}
connection.setDoOutput(true);
OutputStream out = connection.getOutputStream();
if (gzipEncodedRequest) {
out = new GZIPOutputStream(out);
} else if (deflateEncodedRequest) {
out = new DeflaterOutputStream(out);
}
try {
out.write(request.body());
} finally {
try {
out.close();
} catch (IOException suppressed) { // NOPMD
}
}
}
return connection;
}
三、小結
1、約定大於配置
2、新瓶裝舊酒
相關文章
- NIO原理及例項
- 【SpringCloud】(八):認識Feign及使用SpringGCCloud
- Exchanger的工作原理及例項
- Springcloud(二) feignSpringGCCloud
- SpringCloud-FeignSpringGCCloud
- Java 下 SSL 通訊原理及例項Java
- 網路測速一原理及例項
- pr劫持的原理及操作例項、pr劫持的原理
- CGroup 介紹、應用例項及原理描述
- SpringCloud Feign的分析SpringGCCloud
- 【SpringCloud】(四):建立Eureka Server例項SpringGCCloudServer
- JVM 垃圾回收器工作原理及使用例項介紹JVM
- AOP的原理和例項
- Feign的工作原理
- 【SpringCloud】(九):Feign的自定義配置SpringGCCloud
- SpringCloud微服務治理二(Robbin,Hystix,Feign)SpringGCCloud微服務
- SpringCloud-使用Feign呼叫服務介面SpringGCCloud
- 【SpringCloud】(十四):Feign對Hystrix的支援 fallbackFactorySpringGCCloud
- ZIP壓縮演算法原理分析及解壓例項程式碼演算法
- Python物件導向多型實現原理及程式碼例項Python物件多型
- ActiveMQ 入門及例項MQ
- TRIZ 機械振動原理 例項
- springcloud中feign檔案上傳、下載SpringGCCloud
- (22)SpringCloud-使用Feign呼叫服務介面SpringGCCloud
- 分享一個 SpringCloud Feign 中所埋藏的坑SpringGCCloud
- SpringCloud 2020.0.4 系列之 FeignSpringGCCloud
- 大神教你C++寫時複製實現原理及例項解析C++
- Vue例項及生命週期Vue
- AWK簡介及使用例項
- Jquery入門及例項一jQuery
- SpringCloud系列之使用Feign進行服務呼叫SpringGCCloud
- [jaeger] 四、微服務之呼叫鏈(Feign+SpringCloud)微服務SpringGCCloud
- SpringCloud之服務提供與呼叫(Ribbon,Feign)SpringGCCloud
- springcloud學習筆記(二)Spring Cloud FeignSpringGCCloud筆記
- SpringCloud之使用Feign跨服務呼叫最佳方式SpringGCCloud
- SpringCloud微服務(基於Eureka+Feign+Hystrix+Zuul)SpringGCCloud微服務Zuul
- SpringCloud學習筆記:宣告式呼叫Feign(4)SpringGCCloud筆記
- SpringCloud解決feign呼叫token丟失問題SpringGCCloud