📢 この記事は gemini-3-flash-preview によって翻訳されました
Feignは宣言的な Http クライアントだよ。Githubはこちら:
https://github.com/OpenFeign/feign
簡単な使い方
依存関係を導入するよ。
1
2
3
4
| <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
|
起動クラスにアノテーションを追加して、Feign の機能を有効にするよ。
1
2
| @EnableFeignClients
@SpringBootApplication
|
Feign クライアントを書くよ。
1
2
3
4
5
| @FeignClient("userService")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
|
RestTemplate を置き換えるよ。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| @Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
// @Autowired
// private RestTemplate restTemplate;
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
// 1.注文を検索
Order order = orderMapper.findById(orderId);
// 2.ユーザーを検索
// String url = "http://localhost:8081/user/" + order.getUserId();
// String url = "http://userService/user/" + order.getUserId();
// User user = restTemplate.getForObject(url, User.class);
// Feign を使う
User user = userClient.findById(order.getUserId());
// 3.ユーザー情報をセット
order.setUser(user);
// 4.返す
return order;
}
}
|
カスタム設定
Feign はデフォルト設定を上書きするためにカスタム設定ができるんだ。変更できる設定はこんな感じ:
| タイプ | 役割 | 説明 |
|---|
| feign.Logger.Level | ログレベルの変更 | NONE、BASIC、HEADERS、FULL の4つのレベルがあるよ |
| feign.codec.Decoder | レスポンス解析器 | HTTP呼び出しの結果を解析する。例えばJSON文字列をJavaオブジェクトにするなど |
| feign.codec.Encoder | リクエストパラメータ符号化 | リクエストパラメータを符号化して、HTTPリクエストで送りやすくする |
| feign. Contract | サポートするアノテーション形式 | デフォルトは SpringMVC のアノテーション |
| feign. Retryer | 失敗時のリトライメカニズム | リクエスト失敗時のリトライ。デフォルトはないけど、Ribbonのリトライが使われるよ |
普通はログレベルだけ設定すれば十分だよ。ログレベルの種類はこれ:
- NONE:ログを一切記録しない(デフォルト)
- BASIC:リクエストメソッド、URL、レスポンスステータスコード、実行時間のみ記録
- HEADERS:BASICの内容に加えて、リクエストとレスポンスのヘッダー情報も記録
- FULL:リクエストとレスポンスの全ての詳細(ヘッダー、ボディ、メタデータ)を記録
設定方法は、設定ファイルを使う方法と Bean を作る方法の2つがあるよ。
設定ファイル
特定のサービスに対して設定する場合:
1
2
3
4
5
| feign:
client:
config:
userservice: # 特定のマイクロサービス向けの設定
loggerLevel: FULL # ログレベル
|
全てのサービスに対して設定する場合:
1
2
3
4
5
| feign:
client:
config:
default: # defaultにするとグローバル設定になるよ
loggerLevel: FULL # ログレベル
|
Bean
まずクラスを定義して、Logger.Level のオブジェクトを宣言するよ。
1
2
3
4
5
6
| public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC; // ログレベルをBASICに設定
}
}
|
グローバルに適用したいなら、起動クラスの @EnableFeignClients アノテーションに含めるよ。
1
| @EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
|
特定のサービスだけに適用したいなら、@FeignClient アノテーションに含めるよ。
1
| @FeignClient(value = "userService", configuration = DefaultFeignConfiguration .class)
|
Feign の使用の最適化
Feign の裏側のクライアント実装にはいくつか種類があるんだ:
コネクションプールを使うと Feign のパフォーマンスが上がるよ。
以下は HttpClient に置き換える例だよ。
- 依存関係の導入
1
2
3
4
5
| <!-- httpClientの依存関係 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
|
- コネクションプールの設定
1
2
3
4
5
6
7
8
9
| feign:
client:
config:
default: # グローバル設定
loggerLevel: BASIC # ログレベル
httpclient:
enabled: true # HttpClientのサポートを有効化
max-connections: 200 # 最大接続数
max-connections-per-route: 50 # パスごとの最大接続数
|
ベストプラクティス
ベストプラクティスっていうのは、使っていく中でまとめられた「一番良い使い方の経験則」のことだね。
Feign のクライアントとサービス提供側の Controller のコードはすごく似ているんだ。
1
2
3
4
5
6
7
8
9
10
11
12
| // Feign クライアント
@FeignClient("userService")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
// Controller
@GetMapping("/user/{id}")
public User queryById(@PathVariable("id") Long id) {
return userService.queryById(id);
}
|
重複コードを減らすための、2つの簡略化メソッドを紹介するね。
方法1:継承
消費側の FeignClient と提供側の Controller で共通の親インターフェースを定義して標準化する方法だよ。
インターフェースを定義する:
1
2
3
4
| public interface UserAPI{
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
|
Feign クライアントの実装:
1
2
| @FeignClient(value="userService")
public interface UserClient extends UserAPI{}
|
Controller クラス:
1
2
3
4
5
6
| @RestController
public class UserController implements UserAPI{
public User findById(@PathVariable("id") Long id){
// 処理コード
}
}
|
メリット:シンプルで、コードを共有できる。
デメリット:
- サービス提供側と消費側が密結合になる。
- 引数リストのアノテーションマッピングは継承されないから、Controller でメソッド、引数リスト、アノテーションをもう一度宣言しないといけない。
方法2:切り出し方式
FeignClient を独立したモジュールとして切り出して、インターフェースに関連する POJO やデフォルトの Feign 設定もそのモジュールに入れて、全ての消費者が使えるようにする方法だよ。
新しいプロジェクトを作成して、Feign のスターター依存関係を導入するよ。
1
2
3
4
| <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
|
消費側で feign-api の依存関係を導入する。
定義した FeignClient が SpringBootApplication のスキャン対象外にある場合、そのままでは使えないんだ。解決策は2つあるよ。
- Feign がスキャンすべきパッケージを指定する
1
| @EnableFeignClients(basePackages = "net.yexca.feign.clients")
|
- ロードする Client インターフェースを個別に指定する
1
| @EnableFeignClients(clients = {UserClient.class})
|