Feignでのリモート呼び出し

📢 この記事は 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 の裏側のクライアント実装にはいくつか種類があるんだ:

  • URLConnection:デフォルト実装。コネクションプールをサポートしていない。

  • Apache HttpClient :コネクションプールをサポート。

  • OKHttp:コネクションプールをサポート。

コネクションプールを使うと Feign のパフォーマンスが上がるよ。

以下は HttpClient に置き換える例だよ。

  1. 依存関係の導入
1
2
3
4
5
<!-- httpClientの依存関係 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
  1. コネクションプールの設定
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つあるよ。

  1. Feign がスキャンすべきパッケージを指定する
1
@EnableFeignClients(basePackages = "net.yexca.feign.clients")
  1. ロードする Client インターフェースを個別に指定する
1
@EnableFeignClients(clients = {UserClient.class})

Visits Since 2025-02-28

Hugo で構築されています。 | テーマ StackJimmy によって設計されています。