• 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;
      }

    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 {
      }

    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
      );
      }
      }

    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);
      }
      }

    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);
      }
      }
      }

    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!
  • 相关阅读:
    Hbase性能调优(一)
    文章标题
    JDBC的PreparedStatement启动事务使用批处理executeBatch()
    java.lang.OutOfMemoryError: PermGen space及其解决方法
    linux 关机命令总结
    oracle 启动关闭以及监听启动关闭命令
    bash: sqlplus: command not found 解决方法
    VMware 虚拟机 linux执行 ifconfig 命令 eth0没有IP地址(intet addr、Bcast、Mask) UP BROADCAST MULTICAST 问题
    Linux的文本编辑和文本内容查看命令
    RHEL6服务器网络配置 修改linux RHEL6系统下的ip方法
  • 原文地址:https://www.cnblogs.com/bigben0123/p/7483815.html
Copyright © 2020-2023  润新知