Building Microservices with Spring Boot and Apache Thrift. Part 2. Swifty services

http://bsideup.blogspot.com/2015/04/spring-boot-thrift-part2.html

In previous article I showed you how to use pure Apache Thrift to create protocols for your services. This time I will explain how to use Facebook Swift (not the best name for project BTW :)) to get rid of Thrift IDL files for server code.

Few words about Swift. Swift - Java annotation-based generator for Apache Thrift. It‘s priceless when you develop your APIs in Java and want to expose them to the other world using Apache Thrift.

Protocol

Lets recreate our previous example ( https://github.com/bsideup/spring-boot-thrift ) with it. Start with simple build.gradle file:

  apply plugin: ‘java‘
  apply plugin: ‘spring-boot‘
   
  buildscript {
  repositories {
  jcenter()
  }
   
  dependencies {
  classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE")
  }
  }
   
  repositories {
  jcenter()
  }
   
  sourceCompatibility = 1.8
  targetCompatibility = 1.8
   
  dependencies {
   
  compile "org.projectlombok:lombok:1.16.2"
   
  compile("org.springframework.boot:spring-boot-starter-web")
   
  compile ‘com.facebook.swift:swift-annotations:0.14.1‘
  compile ‘com.facebook.swift:swift-service:0.14.1‘
  compile ‘com.facebook.swift:swift-codec:0.14.1‘
   
  compile ‘com.facebook.nifty:nifty-core:0.14.1‘
   
  compile ‘org.apache.thrift:libthrift:0.9.1‘
   
  testCompile "org.springframework.boot:spring-boot-starter-test"
  }

view rawbuild.gradle hosted with ? by GitHub

Nothing special, Spring Boot with few Facebook Swift libraries.

Next we need to add Swift protocol classes:

  package com.example.calculator.protocol;
   
  import com.facebook.swift.service.ThriftMethod;
  import com.facebook.swift.service.ThriftService;
  import com.google.common.util.concurrent.ListenableFuture;
   
  @ThriftService
  public interface TCalculatorService {
   
  @ThriftMethod
  int calculate(int num1, int num2, TOperation op) throws TDivisionByZeroException;
  }

view rawTCalculatorService.java hosted with ? by GitHub

Exception is simple Swift struct which extend Exception (See Ma no TException)

  package com.example.calculator.protocol;
   
  import com.facebook.swift.codec.ThriftStruct;
   
  @ThriftStruct
  public final class TDivisionByZeroException extends Exception {
  }

view rawTDivisionByZeroException.java hosted with ? by GitHub

Enums are handled with Swift, so we don‘t need to annotate them (but we can)

  package com.example.calculator.protocol;
   
  public enum TOperation {
  ADD,
  SUBTRACT,
  MULTIPLY,
  DIVIDE
  }

view rawTOperation.java hosted with ? by GitHub

Next - application main class for Spring Boot:

  package com.example.calculator;
   
  import com.example.calculator.protocol.TCalculatorService;
  import com.facebook.nifty.processor.NiftyProcessorAdapters;
  import com.facebook.swift.codec.ThriftCodecManager;
  import com.facebook.swift.service.ThriftEventHandler;
  import com.facebook.swift.service.ThriftServiceProcessor;
  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;
  import java.util.Arrays;
   
  @Configuration
  @EnableAutoConfiguration
  @ComponentScan
  public class CalculatorApplication {
  public static void main(String[] args) {
  SpringApplication.run(CalculatorApplication.class, args);
  }
   
  @Bean
  TProtocolFactory tProtocolFactory() {
  return new TBinaryProtocol.Factory();
  }
   
  @Bean
  ThriftCodecManager thriftCodecManager() {
  return new ThriftCodecManager();
  }
   
  @Bean
  Servlet thrift(ThriftCodecManager thriftCodecManager, TProtocolFactory protocolFactory, TCalculatorService exampleService) {
  ThriftServiceProcessor processor = new ThriftServiceProcessor(thriftCodecManager, Arrays.<ThriftEventHandler>asList(), exampleService);
   
  return new TServlet(
  NiftyProcessorAdapters.processorToTProcessor(processor),
  protocolFactory,
  protocolFactory
  );
  }
  }

view rawCalculatorApplication.java hosted with ? by GitHub

Test

Now we‘re ready to write some tests:

  package com.example.calculator;
   
  import com.example.calculator.protocol.TCalculatorService;
  import com.example.calculator.protocol.TDivisionByZeroException;
  import com.example.calculator.protocol.TOperation;
  import com.facebook.nifty.client.HttpClientConnector;
  import com.facebook.swift.codec.ThriftCodecManager;
  import com.facebook.swift.service.ThriftClientManager;
  import org.apache.thrift.protocol.TProtocolFactory;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.beans.factory.annotation.Value;
  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 java.net.URI;
   
  import static org.junit.Assert.*;
   
  @RunWith(SpringJUnit4ClassRunner.class)
  @SpringApplicationConfiguration(classes = CalculatorApplication.class)
  @WebAppConfiguration
  @IntegrationTest("server.port:0")
  public class CalculatorApplicationTest {
   
  @Autowired
  TProtocolFactory protocolFactory;
   
  @Autowired
  ThriftCodecManager thriftCodecManager;
   
  @Value("${local.server.port}")
  protected int port;
   
  protected TCalculatorService client;
   
  @Before
  public void setUp() throws Exception {
  HttpClientConnector connector = new HttpClientConnector(URI.create("http://localhost:" + port + "/thrift/"));
   
  ThriftClientManager clientManager = new ThriftClientManager(thriftCodecManager);
  client = clientManager.createClient(connector, TCalculatorService.class).get();
  }
   
  @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);
  }
  }

view rawCalculatorApplicationTest.java hosted with ? by GitHub

As you can see, only difference here (compared to Thrift version) is setUp method.
Diff with Thrift version

Implementation

We still have no Swift service implementation. Implementation of handler looks almost the same asprevious:

  package com.example.calculator.handler;
   
  import com.example.calculator.protocol.TCalculatorService;
  import com.example.calculator.protocol.TDivisionByZeroException;
  import com.example.calculator.protocol.TOperation;
  import org.springframework.stereotype.Component;
   
  import com.example.calculator.service.CalculatorService;
  import org.springframework.beans.factory.annotation.Autowired;
   
  @Component
  public class CalculatorServiceHandler implements TCalculatorService {
   
  @Autowired
  CalculatorService calculatorService;
   
  @Override
  public int calculate(int num1, int num2, TOperation op) throws TDivisionByZeroException {
  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 IllegalArgumentException("Unknown operation " + op);
  }
  }
  }

view rawCalculatorServiceHandler.java hosted with ? by GitHub

Diff with Thrift version

Now if you will run tests you should see all tests green.

Thrift integration

But hey, how about other non-Java consumers of service? Don‘t worry, Swift comes with a tool for generating *.thrift files from annotated Java classes:https://github.com/facebook/swift/tree/master/swift2thrift-generator-cli

Example output for our service will be:

  namespace java.swift com.example.calculator.protocol
  namespace java com.example.calculator.protocol
  namespace py com.example.calculator.protocol
  namespace as3 com.example.calculator.protocol
   
   
  enum TOperation {
  ADD, SUBTRACT, MULTIPLY, DIVIDE
  }
   
  exception TDivisionByZeroException {
  }
   
  service TCalculatorService {
  i32 calculate(1: i32 arg0, 2: i32 arg1, 3: TOperation arg2) throws (1: TDivisionByZeroException ex1);
  }

view rawcalculator.thrift hosted with ? by GitHub

Conclusion

Full source files for this example can be found at GitHub: https://github.com/bsideup/spring-boot-swift

Next time I will show you how to write Async Thrift services using Swift with minimal changes. Stay tuned!

时间: 2024-12-16 08:00:28

Building Microservices with Spring Boot and Apache Thrift. Part 2. Swifty services的相关文章

Building Microservices with Spring Boot and Apache Thrift. Part 1 with servlet

https://dzone.com/articles/building-microservices-spring In the modern world of microservices it's important to provide strict and polyglot clients for your service. It's better if your API is self-documented. One of the best tools for it is Apache T

Spring boot org.apache.catalina.connector.ClientAbortException 错误

1 org.apache.catalina.connector.ClientAbortException: java.io.IOException: 你的主机中的软件中止了一个已建立的连接. 2 at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:393) 3 at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java

Building Microservices with Spring Cloud - Configuration Server

Prepare  Understand "Twelve factor APP" - config Understand Spring boot configuration and Spring Framework Profiles The role of configuration in Microservices Removing "settings" from compiled code Change runtime behaviors Enforce cons

Spring Boot 整合 Apache Solr 异常:Expected mime type application/octet-stream but got text/html 的解决.

解决方法:Spring Data Solr 3.0 以上版本 将@SolrDocument(solrCoreName = "new_core")  中的solrCoreName 字段改为使用collection字段即可 下面是问题解决经过: 暑假期间在做一个高并发的电商平台,需要用到Apache Solr. Solr 版本是7.4 , Spring Boot 版本是 2.0.4 , Spring Data Solr 为 3.0.9 网上大部分的教程用的版本比较旧,在实体类使用的都是  

Building Microservices with Spring Cloud - Intelligent Routing

ref : http://microservices.io/patterns/apigateway.html https://www.nginx.com/blog/building-microservices-using-an-api-gateway https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_router_and_filter_zuul

Building Microservices with Spring Cloud - Service discovery

What is service discovery and why we use it? A way for service to register it self A way for a service to deregister it self A way for a client to find other services A way to check the health of a service and remove unhealthy instances Spring Cloud

Building Microservices with Spring Cloud - Load balancing

ref: https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#spring-cloud-ribbon

spring boot 集成 Apache CXF 调用 .NET 服务端 WebService

1. pom.xml加入 cxf 的依赖 <!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-frontend-jaxws --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>2.7.18&l

Spring Boot 2.2.2.RELEASE 版本中文参考文档

写在前面 在我初次接触MongoDB的时候,是为了做一个监控系统和日志分析系统.当时在用Java操作MongoDB数据里的数据的时候,都是在网上查找Demo示例然后完成的功能,相信大家也同样的体会,网上大都是这种一个入门的小Demo,很少有深入讲解的.功能调通后,自己琢磨效率方面的问题,便开始自己的瞎尝试之路,然后在产品启动阶段初始化多个MongoClient对象,搞了一个类似于连接池一类的池子,完成之后打心眼里觉得自己真是个小机灵鬼. 大概过了一年,为了系统地学习MongoDB的一些高级特性,