1. はじめに
現代のマイクロサービスの世界では、サービスに厳密で多言語のクライアントを提供することが重要です。 API が自己文書化されている方がよいでしょう。
そのための最適なツールの 1 つは Apache Thrift です。
私のお気に入りのマイクロサービス プラットフォームである Spring Boot での使用方法を説明したいと思います。
すべてのプロジェクトのソース コードは GitHub で入手できます
https://github.com/bsideup/spring-boot-thrift
2. 電卓アプリのテンプレートファイルを作成する
Apache Thrift に慣れていなくても、そのテンプレートファイルは非常にわかりやすいです。
namespace cpp com.example.calculator
namespace d com.example.calculator
namespace java com.example.calculator
namespace php com.example.calculator
namespace perl com.example.calculator
namespace as3 com.example.calculator
enum TOperation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
}
exception TDivisionByZeroException {
}
service TCalculatorService {
i32 calculate(1:i32 num1, 2:i32 num2, 3:TOperation op) throws (1:TDivisionByZeroException divisionByZero);
}
3. テンプレートファイルからジェネレートする
ここでは、calculate という 1 つのメソッドだけを使用して TCalculatorService を定義します。
タイプ TDivisionByZeroException の例外をスローできます。
すぐに使用できる言語の数に注意してください。
(ただし、この例ではターゲットとして Java のみを使用します)
thrift --gen java calculate.thrift
4. Spring Boot での電卓アプリの設定する
簡単な電卓アプリから始めましょう。プロトコルとアプリの2 つのモジュールがあります。プロトコルから始めます。プロジェクトは次のようになります。
ここでは、Spring Boot Web アプリのプロトコルと典型的なスターターに依存しています。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>${thrift.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
CalculatorApplication はメイン クラスです。 この例では、同じファイルで Spring を構成しますが、アプリでは代わりに別の構成クラスを使用する必要があります。
package com.example.calculator;
import com.example.calculator.handler.CalculatorServiceHandler;
import org.apache.thrift.protocol.*;
import org.apache.thrift.server.TServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.*;
import javax.servlet.Servlet;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class CalculatorApplication {
public static void main(String[] args) {
SpringApplication.run(CalculatorApplication.class, args);
}
@Bean
public TProtocolFactory tProtocolFactory() {
//We will use binary protocol, but it's possible to use JSON and few others as well
return new TBinaryProtocol.Factory();
}
@Bean
public ServletRegistrationBean <HttpServlet> stateServlet(TProtocolFactory protocolFactory, CalculatorServiceHandler handler) {
ServletRegistrationBean <HttpServlet> servRegBean = new ServletRegistrationBean <>();
servRegBean.setServlet(new TServlet(new TCalculatorService.Processor <>(handler), protocolFactory));
servRegBean.addUrlMappings("/calculator/*");
servRegBean.setLoadOnStartup(1);
return servRegBean;
}
}
ここでは、/calculator/ が Servlet で呼び出されるようにURLマッピングを使って設定をしています。
その後、Thrift ハンドラー クラスが必要です。
package com.example.calculator.handler;
import com.example.calculator.*;
import com.example.calculator.service.CalculatorService;
import org.apache.thrift.TException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CalculatorServiceHandler implements TCalculatorService.Iface {
@Autowired
CalculatorService calculatorService;
@Override
public int calculate(int num1, int num2, TOperation op) throws TException {
switch(op) {
case ADD:
return calculatorService.add(num1, num2);
case SUBTRACT:
return calculatorService.subtract(num1, num2);
case MULTIPLY:
return calculatorService.multiply(num1, num2);
case DIVIDE:
try {
return calculatorService.divide(num1, num2);
} catch(IllegalArgumentException e) {
throw new TDivisionByZeroException();
}
default:
throw new TException("Unknown operation " + op);
}
}
}
この例では、Thrift ハンドラーが通常の Spring Bean であり、それに依存関係を注入できることを示したいと思います。
次に、CalculatorService 自体を実装する必要があります。
package com.example.calculator.service;
import org.springframework.stereotype.Component;
@Component
public class CalculatorService {
public int add(int num1, int num2) {
return num1 + num2;
}
public int subtract(int num1, int num2) {
return num1 - num2;
}
public int multiply(int num1, int num2) {
return num1 * num2;
}
public int divide(int num1, int num2) {
if(num2 == 0) {
throw new IllegalArgumentException("num2 must not be zero");
}
return num1 / num2;
}
}
それで最後になります。
サービスを何らかの方法でテストする必要があります。
通常、アプリケーションが JSON REST API を提供している場合でも、そのためのクライアントを実装する必要があります。 倹約はあなたのためにそれを行います。 気にする必要はありません。 また、さまざまなプロトコルをサポートします。 生成されたクライアントをテストで使用してみましょう。
package com.example.calculator;
import org.apache.thrift.protocol.*;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransport;
import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CalculatorApplication.class)
@WebAppConfiguration
@IntegrationTest("server.port:0")
public class CalculatorApplicationTest {
@Autowired
protected TProtocolFactory protocolFactory;
@Value("${local.server.port}")
protected int port;
protected TCalculatorService.Client client;
@Before
public void setUp() throws Exception {
TTransport transport = new THttpClient("http://localhost:" + port + "/calculator/");
TProtocol protocol = protocolFactory.getProtocol(transport);
client = new TCalculatorService.Client(protocol);
}
@Test
public void testAdd() throws Exception {
assertEquals(5, client.calculate(2, 3, TOperation.ADD));
}
@Test
public void testSubtract() throws Exception {
assertEquals(3, client.calculate(5, 2, TOperation.SUBTRACT));
}
@Test
public void testMultiply() throws Exception {
assertEquals(10, client.calculate(5, 2, TOperation.MULTIPLY));
}
@Test
public void testDivide() throws Exception {
assertEquals(2, client.calculate(10, 5, TOperation.DIVIDE));
}
@Test(expected = TDivisionByZeroException.class)
public void testDivisionByZero() throws Exception {
client.calculate(10, 0, TOperation.DIVIDE);
}
}
このテストでは、Spring Boot アプリケーションを実行し、ランダムなポートにバインドしてテストします。 すべてのクライアント/サーバー通信は、実際のクライアントと同じ方法で実行されます。
クライアント側からのサービスの使いやすさに注目してください。 メソッドを呼び出して例外をキャッチしているだけです。
5. 最後に
この記事では、Spring BootとApache Thriftを使用してマイクロサービスを構築する方法を説明しました。Apache Thriftは厳密で多言語のクライアントを提供し、APIが自己文書化される利点があります。簡単な電卓アプリケーションを例に、テンプレートファイルの作成、コード生成、Spring Bootでの設定、Thriftハンドラークラスの実装、およびテスト方法を示しました。これにより、クライアント/サーバー通信が実際のクライアントと同じ方法で実行され、サービスの使いやすさが向上します。
翻訳元サイト