Eureka レジストリと OpenFeign 呼び出しインターフェイス

必要

アプリケーションは、インターフェイスを介して別のアプリケーションのインターフェイスを呼び出します。OpenFeign を使用してインターフェイス呼び出しを実装します。

説明する

OpenFeign (以降、この記事では Feign と呼びます) を介してリモート インターフェイスを呼び出すには、Eureka レジストリのサポートが必要です。

OpenFeign 呼び出しインターフェイスのロジックは次のとおりです。

  1. インターフェースを提供するアプリケーション (A) は、自身を Eureka サーバー (登録センター) に登録します。アプリケーション A は、それ自体にアプリケーション名を付ける必要があります。
  2. インターフェイスを呼び出すアプリケーション (B) は、登録されているすべてのサービスの情報を Eureka から読み取ります。
  3. アプリケーション B の Feign クライアントは、登録されたサービスの情報からサービスのアプリケーション名を通じてアプリケーション A (対応する IP アドレスとポート番号) を見つけ、A のインターフェイスを呼び出します。

この記事の主な内容

この記事では主に、登録センター (Eureka) の構成方法、Feign の構成方法、および Feign を使用してインターフェイスを呼び出す方法について説明します。
主に次の 3 つの部分で構成されます。

  1. Eureka レジストリを構成します (単一、非クラスター)。
  2. インターフェイスを提供するアプリケーションを構成し、Eureka に登録します。呼び出されるインターフェイスを提供します。
  3. 呼び出しインターフェースのアプリケーションを設定し、Eureka: 呼び出しインターフェースから呼び出し先アドレスを取得します。

エウレカサーバー

1.依存性

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

2. 設定 (application.properties)

この構成はシングルサーバー構成であり、クラスター構成ではありません。

server.port=8761

# 主机名,不配置的时候将根据操作系统的主机名获取。
eureka.instance.hostname=localhost

# 不将自身注册到注册中心。是否将自己注册到注册中心,默认为true。单个Eureka服务器,不需要注册自身,配置为false;如果是Eureka集群,则需要注册自身,即配置为true。
eureka.client.registerWithEureka=false
# 是否从注册中心获取服务注册信息,默认为true。
eureka.client.fetchRegistry=false
# 注册中心对外暴露的注册地址
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/


3. Eurekaサーバーを起動します

アプリケーション起動クラスにアノテーションを追加します@EnableEurekaServer

サンプルコード:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerDemoApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(EurekaServerDemoApplication.class, args);
	}

}

FeignServer

インターフェイスを提供するアプリケーションは、Feign を通じてインターフェイスを呼び出すことができます。

1.依存性

  • エウレカ ディスカバリー クライアント
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2. 設定 (application.properties)

server.port=8081

# 应用名称
spring.application.name=feign-server
# 使用 ip地址:端口号 注册
eureka.instance.prefer-ip-address=true
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
# 注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

3. インターフェースの提供

package com.example.feign.server.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("feign_server_path")
public class DataController {
    
    

	@GetMapping("hello")
	public String hello() {
    
    
		return "hello feign server!";
	}

	@GetMapping("data")
	public String getData() {
    
    
		return "来自FeignServer的数据!";
	}

	@GetMapping("result")
	public String getData(String account) {
    
    
		return "从FeignServer查询的数据!入参为:" + account;
	}

	@GetMapping("two_params")
	public String getDataByTwoParam(String account, String name) {
    
    
		return "从FeignServer查询的数据!account=" + account + ",name=" + name;
	}

}


偽のクライアント

Feign を通じて、FeignServer アプリケーションのインターフェースを呼び出します。

1.依存性

次の 2 つの依存関係を導入する必要があります。

  • エウレカ ディスカバリー クライアント
  • オープンフェイン
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

注: Spring Cloud バージョンは<dependencyManagement>とを通じて管理する必要があります<properties>すでにプロジェクトに追加されている場合は、追加の変更は必要ありません。

<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>
<properties>
	<spring-cloud.version>2021.0.8</spring-cloud.version>
</properties>

2. 設定 (application.properties)

server.port=8082

# 不将自身注册到Eureka注册中心。本配置为是否将自己注册到注册中心,默认为true。
eureka.client.registerWithEureka=false
# 注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

3. Feign クライアントを開きます

アプリケーション起動クラスにアノテーションを追加します@EnableFeignClients

サンプルコード:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@SpringBootApplication
public class FeignClientDemoApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(FeignClientDemoApplication.class, args);
	}

}

4. インターフェースの定義(FeignServerに相当)

@FeignClient: Feign インターフェイスを示します。

name: Feign によって呼び出されるアプリケーションのアプリケーション名。

path: FeignClient のすべてのインターフェイスの共通パス。一般的には、Feign によって呼び出されるアプリケーションのコントローラーのパブリック インターフェイス パス、つまりコントローラー上の @RequestMapping 内のインターフェイス パスに対応します。

注: FeignClient では、nameと はvalue相互のエイリアスです。名前は公式 Web サイトの例で使用されており、この例でも名前フィールドが使用されています。しかし、テスト後、値フィールドの効果は名前とまったく同じです。

package com.example.feign.client.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "feign-server", path = "feign_server_path")
public interface DataClient {
    
    

	@GetMapping("data")
	String getData();

	@GetMapping("result")
	String getDataByOneParam(@RequestParam("account") String account);

	@GetMapping("two_params")
	public String getDataByTwoParam(@RequestParam("account") String account, @RequestParam("name") String name);

}

5. Feign インターフェイスを呼び出す

ローカル メソッドを呼び出すのと同じように、Feign インターフェイスを呼び出します。

package com.example.feign.client.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.feign.client.feign.DataClient;

@RestController
@RequestMapping("feign_client")
public class FeignClientDataController {
    
    

	@GetMapping("hello")
	public String hello() {
    
    
		return "hello feign client!";
	}

	@Autowired
	private DataClient client;

	@GetMapping("data")
	public String getData() {
    
    
		return "通过FeignClient调用:" + client.getData();
	}

	@GetMapping("one_param")
	public String getDataByOneParam(String account) {
    
    
		return "通过FeignClient调用:" + client.getDataByOneParam(account);
	}

	@GetMapping("two_params")
	public String getDataByTwoParam(String account, String name) {
    
    
		return "通过FeignClient调用:" + client.getDataByTwoParam(account, name);
	}

}


電話の例

ユーレカ

ここに画像の説明を挿入

FeignServerのインターフェースを直接呼び出す

ここに画像の説明を挿入

FeignClient は Feign を通じて FeignServer のインターフェースを呼び出します。

ここに画像の説明を挿入

プロジェクトの作成時に Eureka と Feign の依存関係を追加する

新しい SpringBoot プロジェクトを作成するときは、SpringBoot クリエーターを通じて依存関係を追加できます。この時点で、左下の依存関係検索ボックスで、Eureka と OpenFeign の依存関係を直接検索できます。必要な依存関係を確認すると、対応する依存関係が作成時にプロジェクトに直接追加されます。

Eureka と OpenFeign の 3 つの依存関係と、それらの対応する意味は次のとおりです。

Eureka Server: エウレカサーバー;
Eureka Discovery Client: エウレカクライアント;
OpenFeign: 偽クライアント;

ここに画像の説明を挿入

アプリケーションで着信側のアプリケーション名を設定します

Feign クライアントは構成 (プレースホルダー) を使用して、呼び出し先のアプリケーション名を設定します。
Feign では、name 属性と url 属性がプレースホルダーをサポートします。

公式サイトの例

ここに画像の説明を挿入

コード例

ここに画像の説明を挿入

FeignClient の構成

package com.example.feign.client.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "${feign.name}", path = "feign_server_path")
public interface DataClient {
    
    

	@GetMapping("data")
	String getData();

	@GetMapping("one_param")
	String getDataByOneParam(@RequestParam("account") String account);

	@GetMapping("two_params")
	public String getDataByTwoParam(@RequestParam("account") String account, @RequestParam("name") String name);

}

アプリケーション構成

# 被调用的Feign服务的应用名
feign.name=feign-server

contextId: 同じアプリケーションに対応する複数の FeignClient を区別します。

呼び出されたアプリケーションによって提供されるインターフェイスは、ビジネス ロジックに従っていくつかの異なるモジュールに分割される場合があります。Feign クライアントでは、各モジュールが独立した FeignClient に対応します。
呼び出しは同じアプリケーションであるため、複数の FeignClient のアプリケーション名 (名前フィールド) は同じです。フィールドを通じてcontextIdさまざまな FeignClient を破棄する必要があります。そうしないと、競合が発生し、エラーが報告されます。

コアコード

2 つの FeignClient は、それぞれ異なるものを使用しますcontextId

package com.example.feign.client.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "${feign.name}", path = "feign_server_path", contextId = "DataClient")
public interface DataClient {
    
    
	// 接口定义代码,省略...
}

package com.example.feign.client.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "${feign.name}", path = "files", contextId = "FileClient")
public interface FileClient {
    
    
	// 接口定义代码,省略...
}

contextId のないエラー レポート

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
[2m2023-08-06 23:27:20.184[0;39m [31mERROR[0;39m [35m14592[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.b.d.LoggingFailureAnalysisReporter  [0;39m [2m:[0;39m 

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean '${feign.name}.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

参考

  1. 公式文書
    ここに画像の説明を挿入

  2. ブログの
    2 つの FeignClient インターフェイスが同じサービス名を使用してエラーを報告します

@RequestParam: メソッドパラメータのアノテーションを取得

Feign の Get メソッドの場合、リクエスト パラメーターに@RequestParam注釈を付ける必要があります。
アノテーションが追加されていない場合、パラメータの数に応じて次の 2 つのエラーが報告されます。

2種類のエラー

本体パラメータ 0 が null でした

偽のクライアントが Get メソッドを呼び出すと、インターフェイスにパラメーターが含まれており、エラーが報告されます。

java.lang.IllegalArgumentException: 本体パラメータ 0 が null でした

メソッドの Body パラメータが多すぎます

偽のクライアントが Get メソッドを呼び出すと、インターフェイスに複数のパラメーターが含まれており、エラーが報告されます。

メソッドの Body パラメータが多すぎます

エラーインターフェイスの元のコード

本体パラメータ 0 が null でした

  • サーバー側インターフェースを偽装する
	@GetMapping("one_param")
	public String getData(String account) {
    
    
		return "从FeignServer查询的数据!入参为:" + account;
	}
  • 偽のクライアント
	@GetMapping("one_param")
	String getData(String account);

メソッドの Body パラメータが多すぎます

  • サーバー側インターフェースを偽装する
	@GetMapping("two_params")
	public String getDataByTwoParam(String account, String name) {
    
    
		return "从FeignServer查询的数据!account=" + account + ",name=" + name;
	}
  • 偽のクライアント
	@GetMapping("two_params")
	public String getDataByTwoParam(String account, String name);

解決策: @RequestParam

Feign インターフェイス パラメーターは@RequestParam注釈を追加します。

偽クライアントの場合、変更されたコードは次のとおりです。

import org.springframework.web.bind.annotation.RequestParam;
	@GetMapping("result")
	String getData(@RequestParam("account") String account);

	@GetMapping("two_params")
	public String getDataByTwoParam(@RequestParam("account") String account, @RequestParam("name") String name);

Feign クライアントの完全なコード例

package com.example.feign.client.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "feign-server", path = "feign_server_path")
public interface FeignInvocationService {
    
    

	@GetMapping("data")
	String getFeignServerData();

	@GetMapping("result")
	String getData(@RequestParam("account") String account);

	@GetMapping("two_params")
	public String getDataByTwoParam(@RequestParam("account") String account, @RequestParam("name") String name);

}

成功した呼び出しのインターフェイスの例

ここに画像の説明を挿入

ここに画像の説明を挿入

@SpringQueryMap

Feign の GET インターフェイス数据类(つまり、POJO) をパラメーターとして使用し、 @SpringQueryMap アノテーションを使用してパラメーターをマークします。
パラメータの前にコメントを追加しないと@SpringQueryMap、Feign はエラーを報告します。

コード例

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.GetMapping;

import com.example.feign.client.feign.query.InputQuery;

@FeignClient(name = "${feign.name}", path = "feign_server_path", contextId = "DataClient")
public interface DataClient {
    
    

	// 其他接口...

	@GetMapping("query_object")
	public String getDataByQueryObject(@SpringQueryMap InputQuery query);

}

エラーを報告する

2023-08-07 23:06:25.570[0;39m [31mERROR[0;39m [35m9368[0;39m [2m---[0;39m [2m[nio-8082-exec-3][0;39m [36mo.a.c.c.C.[.[.[/].[dispatcherServlet]   [0;39m [2m:[0;39m Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.FeignException$MethodNotAllowed: [405] during [GET] to [http://feign-server/feign_server_path/query_object] [DataClient#getDataByQueryObject(InputQuery)]: [{"timestamp":"2023-08-07T15:06:25.532+00:00","status":405,"error":"Method Not Allowed","path":"/feign_server_path/query_object"}]] with root cause

feign.FeignException$MethodNotAllowed: [405] during [GET] to [http://feign-server/feign_server_path/query_object] [DataClient#getDataByQueryObject(InputQuery)]: [{"timestamp":"2023-08-07T15:06:25.532+00:00","status":405,"error":"Method Not Allowed","path":"/feign_server_path/query_object"}]
        at feign.FeignException.clientErrorStatus(FeignException.java:221) ~[feign-core-11.10.jar:na]
        at feign.FeignException.errorStatus(FeignException.java:194) ~[feign-core-11.10.jar:na]
        at feign.FeignException.errorStatus(FeignException.java:185) ~[feign-core-11.10.jar:na]

公式文書

偽りの @QueryMap のサポート

ここに画像の説明を挿入

@SpringQueryMap パラメータがありません

@SpringQueryMap を使用するインターフェイス。パラメーター オブジェクトが只能含まれます。インターフェイスに 2 つのパラメーター オブジェクトがあり、両方に @SpringQueryMap アノテーションが付けられている場合、2 番目のパラメーター オブジェクトはインターフェイス リクエストのパラメーターに解析されますが、まったく解析されません。一个
被丢弃

偽のログを印刷する

Feign はリクエストログ情報を送信します

設定ファイル

# 打印Feign接口调用日志(仅开发测试环境使用)
logging.level.com.kiiik.web=debug
feign.client.config.default.loggerLevel=FULL

参考記事:
Print Feignログ

url: IP アドレスとポート番号を使用して呼び出されたアプリケーションにアクセスします

@FeignClient は、URL フィールドを通じてハウスサーバーの IP アドレスとポート番号を指定できます。呼び出されるアプリケーションが Eureka 登録センターに登録されていない場合は、URL を通じて実際のアドレスを直接構成するだけで十分です。

コード例

FeignClient: URL フィールド

package com.example.feign.client.feign;

import org.springframework.cloud.openfeign.FeignClient;

@FeignClient(name = "${feign.name}", url = "${feign.url}", path = "feign_server_path", contextId = "DataClient")
public interface DataClient {
    
    
    // 接口,省略...
}

構成

# 被调用的Feign服务的IP地址和端口号(用于调用没有注册到Eureka的服务)
feign.url=http://localhost:8081

公式ウェブサイトのドキュメント

ここに画像の説明を挿入

Feign 通話インターフェイスを介してファイルをダウンロードする

実装

ResponseFeign 呼び出しインターフェイスを介してファイルをダウンロードし、Feign インターフェイスに値、フルネームを直接返してからfeign.ResponseResponse入力ストリームを取得する必要があります。その後、入力ストリームを処理したり、出力ストリームに入れたり、ローカルに保存したり、ユーザーに配信したりできます。

コード例

package com.example.feign.client.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import feign.Response;

@FeignClient(name = "${feign.name}", url = "${feign.url}", path = "files", contextId = "FileClient")
public interface FileClient {
    
    

	@GetMapping("download")
	Response download();

	@GetMapping("/download/{filename}")
	Response download(@PathVariable("filename") String filename);

}

package com.example.feign.client.controller;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.feign.client.feign.FileClient;

@RestController
@RequestMapping("files")
public class FileController {
    
    

	@Autowired
	private FileClient client;

	@GetMapping("download")
	public void download(HttpServletResponse response) throws IOException {
    
    
		InputStream inputStream = client.download().body().asInputStream();

		String fileName = URLEncoder.encode("测试文件.txt", "UTF-8");
		response.setHeader("content-disposition", "attachment;fileName=" + fileName);
		ServletOutputStream outputStream = response.getOutputStream();

		IOUtils.copy(inputStream, outputStream);
		IOUtils.closeQuietly(inputStream);
		IOUtils.closeQuietly(outputStream);
	}

	@GetMapping("/download/{filename}")
	public void downloadByPathname(@PathVariable("filename") String filename, HttpServletResponse response)
			throws IOException {
    
    
		InputStream inputStream = client.download(filename).body().asInputStream();

		String fileName = URLEncoder.encode(filename, "UTF-8");
		response.setHeader("content-disposition", "attachment;fileName=" + fileName);
		ServletOutputStream outputStream = response.getOutputStream();

		IOUtils.copy(inputStream, outputStream);
		IOUtils.closeQuietly(inputStream);
		IOUtils.closeQuietly(outputStream);
	}

}

Spring Cloud OpenFeign 公式ドキュメント

Spring Cloud OpenFeign 公式ドキュメント

Spring Cloud OpenFeign 公式ウェブサイト

Spring Cloud OpenFeign 公式ウェブサイト

おすすめ

転載: blog.csdn.net/sgx1825192/article/details/131886977