使用Spring Boot和Apache Thrift构建微服务
首先
在现代的微服务世界中,为服务提供严格和多语言的客户端非常重要。最好的情况是API能够自我文档化。其中一个最佳工具是Apache Thrift。我想介绍在我最喜欢的微服务平台Spring Boot中使用Apache Thrift的方法。
所有项目的源代码都可以在 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);
}
利用模板文件生成
在这里,我们只使用一个名为”calculate”的方法来定义TCalculatorService。
可以抛出TDivisionByZeroException类型的异常。
请注意,我们只使用了一种可以立即使用的语言。
(注意,这个例子只使用Java作为目标语言)
thrift --gen java calculate.thrift
使用Spring Boot设置计算器应用程序
让我们从一个简单的计算器应用开始吧。这个项目有两个模块,一个是协议,另一个是应用程序。我们从协议开始吧。
在这里,我们依赖于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;
}
}
在这里,我们使用URL映射进行配置,以便通过Servlet调用/calculator/。
之後需要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处理器类的实现以及测试方法。通过这样做,客户端/服务器通信可以以与实际客户端相同的方式执行,从而提高了服务的易用性。
原文来源