Spring Cloud整合Thrift RPC(二) - 应用案例

1,507 阅读11分钟

前言

上一篇简单的阐述了 spring-cloud-thrift-starter 这个插件的配置和使用,并引入了一个calculator的项目。本文将基于一个银行存款、取款的业务场景,给出一套thrift在生产环境的应用案例。

首先设计如下几张简单的数据库表:银行(bank)、分支(branch)、银行卡(deposit_card)、客户(customer)、存款历史纪录(deposit_history)、取款历史纪录(withdraw_history)。

正文

项目结构如下,依然是由三个模块组成:

  • deposit
    • deposit-client
    • deposit-iface
    • deposit-server

Thrift IDL编写

关于 thrift更复杂的用法可以参考Apache Thrift基础学习系列,根据数据库表的设计编写 deposit.thrift

deposit.thrift定义了以下四个部分:命名空间 (namespace)、枚举类型 (enum)、结构类型 (struct)和服务类型 (service)。

(a). 命名空间 (namespace)

// 指定编译生成的源代码的包路径名称
namespace java com.icekredit.rpc.thrift.examples.thrift

(b). 枚举类型 (enum)

// 通过枚举定义银行分支所属区域
enum ThriftRegion {
   NORTH = 1,
   CENTRAL = 2,
   SOUTH = 3,
   EAST = 4,
   SOUTHWEST = 5,
   NORTHWEST = 6,
   NORTHEAST = 7
}

// 存款完成状态
enum ThriftDepositStatus {
   FINISHED = 1,
   PROCCEDING = 2,
   FAILED = 3
}

// 取款完成状态
enum ThriftWithdrawStatus {
   FINISHED = 1,
   PROCCEDING = 2,
   FAILED = 3
}

(c). 结构类型 (struct)

// 银行
struct ThriftBank {
   1: required i64 id,
   2: required string code,
   3: required string name,
   4: optional string description,
   5: optional map<ThriftRegion, list<ThriftBranch>> branches
}

// 银行分支
struct ThriftBranch {
   1: required i64 id,
   2: required string code,
   3: required string name,
   4: required string address,
   5: optional i32 staffs,
   6: optional ThriftBank bank,
   7: optional ThriftRegion region
}

// 客户
struct ThriftCustomer {
   1: required string IDNumber,
   2: required string name,
   3: required string birthday,
   4: required i32 sex = 0,
   5: required i32 age,
   6: optional list<string> address,
   7: optional set<ThriftDepositCard> depositCards
}

// 银行卡
struct ThriftDepositCard {
   1: required string id,
   2: required bool isVip,
   3: required string openingTime,
   4: required double accountBalance,
   5: optional double accountFlow,
   6: optional ThriftBranch branch,
   7: optional ThriftCustomer customer,
   8: optional list<ThriftDeposit> depositHistory,
   9: optional list<ThriftWithdraw> WithdrawHistory
}

// 存款历史纪录
struct ThriftDeposit {
   1: required string serialNumber,
   2: required double transactionAmount,
   3: required string submittedTime,
   4: optional string finishedTime,
   5: optional ThriftDepositStatus status,
   6: optional ThriftDepositCard depositCard
}

// 取款历史纪录
struct ThriftWithdraw {
   1: required string serialNumber,
   2: required double transactionAmount,
   3: required string submittedTime,
   4: optional string finishedTime,
   5: optional ThriftWithdrawStatus status,
   6: optional ThriftDepositCard depositCard
}

(d). 服务类型 (service)

// 银行 - 业务服务定义
service ThriftBankService {
   void registerNewBank(ThriftBank bank);
   list<ThriftBank> queryAllBanks();
   ThriftBank getBankById(i64 bankId);
   map<ThriftRegion, list<ThriftBranch>> queryAllBranchesByRegion(i64 bankId);
}

// 银行分支 - 业务服务定义
service ThriftBranchService {
   void addNewBranch(i64 bankId, ThriftBranch branch);
   list<ThriftBranch> queryAllBranches(i64 bankId);
   ThriftBranch getBranchById(i64 branchId);
}

// 客户 - 业务服务定义
service ThriftCustomerService {
   ThriftCustomer getCustomerById(string customerId);
   list<ThriftCustomer> queryAllCustomers();
   void addNewUser(ThriftCustomer customer);
   void modifyUserById(string customerId, ThriftCustomer customer);
   i32 getTotalDepositCard(string customerId);

}

// 银行卡 - 业务服务定义
service ThriftDepositCardService {
   set<ThriftDepositCard> queryAllDepositCards(string customerId);
   void addNewDepositCard(string customerId, ThriftDepositCard depositCard);
   ThriftDepositStatus depositMoney(string depositCardId, double money);
   ThriftWithdrawStatus withdrawMoney(string depositCardId, double money);
   list<ThriftDeposit> queryDepositHistorys(string depositCardId);
   list<ThriftWithdraw> queryWithdrawHistorys(string depositCardId);
}

进入src/main/thrift目录,编译生成所需的枚举类结构类业务服务类的源文件。

thrift -gen java ./deposit.thrift

所有生成的源文件都位于同一个**命名空间(包)**下面:com.icekredit.rpc.thrift.examples.thrift

中间契约(deposit-iface)

上述源文件拷贝到 deposit-iface 模块中。

通过Mybatis逆向工程插件生成SQLMapperXML接口文件以及实体类

友情提示Mybatis逆向工程生成的实体类 (entity),需要和Thrift编译生成器生成的结构类 (struct) 区分开来。而Thrift生成器生成的所有源文件,都一定程度封装了底层的通信方式相关协议,开发人员是不应该动手脚的。

为了在Thrift中通过Mybatis完成数据持久化,必须在实体类 (entity)包装一层与结构类 (struct)相互转换的方法。 在每个实体类中,根据业务添加以下两个方法,以DepositCard为例:

  • toThrift():将实体类对象转换为结构类对象
public ThriftDepositCard toThrift() {
    ThriftDepositCard thriftDepositCard = new ThriftDepositCard();
    thriftDepositCard.setId(this.getId());
    thriftDepositCard.setAccountBalance(this.getAccountBalance());
    thriftDepositCard.setAccountFlow(this.getAccountFlow());
    thriftDepositCard.setIsVip(this.getIsVip());
    thriftDepositCard.setOpeningTime(this.getOpeningTime());

    ThriftBranch thriftBranch = new ThriftBranch();
    thriftBranch.setId(this.getBranchId());
    thriftDepositCard.setBranch(thriftBranch);

    ThriftCustomer thriftCustomer = new ThriftCustomer();
    thriftCustomer.setIDNumber(this.getCustomerId());
    thriftDepositCard.setCustomer(thriftCustomer);
    return thriftDepositCard;
}
  • fromThrift()静态方法,将结构类对象转换为实体类对象
public static DepositCard fromThrift(ThriftDepositCard thriftDepositCard) {
    DepositCard depositCard = new DepositCard();
    depositCard.setId(thriftDepositCard.getId());
    depositCard.setAccountBalance(thriftDepositCard.getAccountBalance());
    depositCard.setAccountFlow(thriftDepositCard.getAccountFlow());
    depositCard.setIsVip(thriftDepositCard.isIsVip());

    ThriftCustomer thriftCustomer = thriftDepositCard.getCustomer();
    if (thriftCustomer != null) {
        String customerIDNumber = thriftCustomer.getIDNumber();
        depositCard.setCustomerId(customerIDNumber);
    }

    ThriftBranch thriftBranch = thriftDepositCard.getBranch();
    if (thriftBranch != null) {
        Long branchId = thriftBranch.getId();
        depositCard.setBranchId(branchId);
    }

    depositCard.setOpeningTime(thriftDepositCard.getOpeningTime());
    return depositCard;
}

服务端(deposit-server)

在服务端模块引入:

  • spring-cloud-starter-thrift-serverthrift服务端的 starter程序。
  • calculator-iface:中间契约模块,这里作为服务端骨架(Skeleton)程序。

pom.xml

<parent>
    <groupId>com.icekredit.rpc.thrift.examples</groupId>
    <artifactId>deposit</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>deposit-server</artifactId>
<packaging>jar</packaging>

<dependencies>
    <!-- Thrift相关依赖 -->
    <dependency>
        <groupId>com.icekredit.rpc.thrift</groupId>
        <artifactId>spring-cloud-starter-thrift-server</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.icekredit.rpc.thrift.examples</groupId>
        <artifactId>deposit-iface</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- SpringBoot依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- 数据库相关依赖 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.5</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>
    <!-- Swagger依赖 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.6.1</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.6.1</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

application.yml中配置thrift服务端的运行参数数据源连接池参数Mybatis相关属性:

application.yml

server:
  port: 8080

endpoints:
  actuator:
    sensitive: false
    enabled: true
management:
  security:
    enabled: false

spring:
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/deposit?useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password: root
  thrift:
    server:
      service-id: deposit-server-rpc
      service-model: hsHa
      port: 25000
      worker-queue-capacity: 1000
      hs-ha:
        min-worker-threads: 5
        max-worker-threads: 20
        keep-alived-time: 3

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.icekredit.rpc.thrift.examples.http.entities

logging:
  level:
    root: INFO
    com:
      icekredit:
        rpc:
          thrift:
            examples:
              mapper: DEBUG

服务端程序启动入口类,设置 Swagger API所在的包路径名称。

Application.java

@SpringBootApplication
@EnableSwagger2
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public Docket createRestfulApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.icekredit.rpc.thrift.examples.service.http.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Deposit Server")
                .description("Deposit Server")
                .version("1.0")
                .build();
    }
}

编写服务端的Thrift的实现,以ThriftDepositCardService为例,由实现类ThriftDepositCardServiceImpl实现ThriftDepositCardService.Iface接口的方法:

ThriftDepositCardServiceImpl.java

@ThriftService(name = "thriftDepositCardService")
public class ThriftDepositCardServiceImpl implements ThriftDepositCardService.Iface {
    private final BranchMapper branchMapper;
    private final DepositCardMapper depositCardMapper;
    private final CustomerMapper customerMapper;
    private final DepositHistoryMapper depositHistoryMapper;
    private final WithdrawHistoryMapper withdrawHistoryMapper;

    @Autowired
    public ThriftDepositCardServiceImpl(BranchMapper branchMapper, DepositCardMapper depositCardMapper, CustomerMapper customerMapper, DepositHistoryMapper depositHistoryMapper, WithdrawHistoryMapper withdrawHistoryMapper) {
        this.branchMapper = branchMapper;
        this.depositCardMapper = depositCardMapper;
        this.customerMapper = customerMapper;
        this.depositHistoryMapper = depositHistoryMapper;
        this.withdrawHistoryMapper = withdrawHistoryMapper;
    }

    @Override
    public Set<ThriftDepositCard> queryAllDepositCards(String customerId) throws TException {
        List<DepositCard> depositCardList = depositCardMapper.queryAllDepositCards(customerId);
        // 查询客户持有的银行卡
        return depositCardList.stream().map(depositCard -> {
            ThriftDepositCard thriftDepositCard = depositCard.toThrift();
            Long branchId = depositCard.getBranchId();
            if (Objects.nonNull(branchId) && branchId > 0L) {
                Branch branch = branchMapper.findById(branchId);
                ThriftBranch thriftBranch = branch.toThrift();
                ThriftBank thriftBank = new ThriftBank();
                thriftBank.setId(branch.getBankId());
                thriftBranch.setBank(thriftBank);
                thriftDepositCard.setBranch(thriftBranch);
            }

            Customer customer = customerMapper.findById(customerId);
            ThriftCustomer thriftCustomer = customer.toThrift();
            thriftDepositCard.setCustomer(thriftCustomer);
            return thriftDepositCard;
        }).collect(Collectors.toSet());
    }

    @Override
    @Transactional
    public void addNewDepositCard(String customerId, ThriftDepositCard depositCard) throws TException {
        DepositCard newDepositCard = DepositCard.fromThrift(depositCard);
        // 新增银行卡信息
        depositCardMapper.save(newDepositCard);
    }

    @Override
    @Transactional
    public ThriftDepositStatus depositMoney(String depositCardId, double money) throws TException {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        try {
            DepositHistory depositHistory = new DepositHistory();
            depositHistory.setSubmittedTime(sf.format(new Date()));
            depositCardMapper.incrementMoney(depositCardId, money);
            depositHistory.setFinishedTime(sf.format(new Date()));
            depositHistory.setSerialNumber(UUID.randomUUID().toString().replace("-", ""));
            depositHistory.setTransactionAmount(money);
            depositHistory.setDepositCardId(depositCardId);
            depositHistory.setStatus(1);
            // 新增存款历史记录
            depositHistoryMapper.save(depositHistory);
            return ThriftDepositStatus.FINISHED;
        } catch (Exception e) {
            e.printStackTrace();
            return ThriftDepositStatus.FAILED;
        }
    }

    @Override
    @Transactional
    public ThriftWithdrawStatus withdrawMoney(String depositCardId, double money) throws TException {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        try {
            WithdrawHistory withdrawHistory = new WithdrawHistory();
            withdrawHistory.setSubmittedTime(sf.format(new Date()));
            depositCardMapper.decrementMoney(depositCardId, money);
            withdrawHistory.setFinishedTime(sf.format(new Date()));
            withdrawHistory.setSerialNumber(UUID.randomUUID().toString().replace("-", ""));
            withdrawHistory.setTransactionAmount(money);
            withdrawHistory.setDepositCardId(depositCardId);
            withdrawHistory.setStatus(1);
            // 新增取款历史记录
            withdrawHistoryMapper.save(withdrawHistory);
            return ThriftWithdrawStatus.FINISHED;
        } catch (Exception e) {
            e.printStackTrace();
            return ThriftWithdrawStatus.FAILED;
        }
    }

    @Override
    public List<ThriftDeposit> queryDepositHistorys(String depositCardId) throws TException {
        List<DepositHistory> depositHistory = depositHistoryMapper.queryDepositHistoryList(depositCardId);
        // 查询存款历史纪录
        return depositHistory.stream().map(DepositHistory::toThrift).collect(Collectors.toList());
    }

    @Override
    public List<ThriftWithdraw> queryWithdrawHistorys(String depositCardId) throws TException {
        List<WithdrawHistory> withdrawHistory = withdrawHistoryMapper.queryWithdrawHistoryList(depositCardId);
        // 查询取款历史纪录
        return withdrawHistory.stream().map(WithdrawHistory::toThrift).collect(Collectors.toList());
    }
}

Mybatis持久层,还是以DepositCardMapper为例:

DepositCardMapper.java

@Repository
@Mapper
public interface DepositCardMapper {
    int save(DepositCard record);
    List<DepositCard> queryAllDepositCards(@Param("customerId") String customerId);
    void decrementMoney(@Param("depositCardId") String depositCardId, @Param("money") Double money);
    void incrementMoney(@Param("depositCardId") String depositCardId, @Param("money") Double money);
    Long countRowsByCustomerId(@Param("customerId") String customerId);
}

DepositCardMapper.xml

<insert id="save" parameterType="com.icekredit.rpc.thrift.examples.http.entities.DepositCard">
    INSERT INTO deposit_card (id, is_vip, opening_time,
                              account_balance, account_flow, branch_id,
                              customer_id)
    VALUES (#{id,jdbcType=VARCHAR}, #{isVip,jdbcType=BIT}, #{openingTime,jdbcType=VARCHAR},
            #{accountBalance,jdbcType=DOUBLE}, #{accountFlow,jdbcType=DOUBLE}, #{branchId,jdbcType=BIGINT},
            #{customerId,jdbcType=VARCHAR})
</insert>

<select id="queryAllDepositCards" resultMap="BaseResultMap" parameterType="java.lang.String">
    SELECT
    <include refid="Base_Column_List"/>
    FROM deposit_card
    WHERE customer_id = #{customerId}
</select>

<select id="countRowsByCustomerId" resultType="java.lang.Long" parameterType="java.lang.String">
    SELECT COUNT(id)
    FROM deposit_card
    WHERE customer_id = #{customerId}
</select>

<update id="decrementMoney">
    UPDATE deposit_card
    <set>
        <if test="money != null">
            account_balance = account_balance - #{money},
        </if>
    </set>
    WHERE id = #{depositCardId}
</update>

<update id="incrementMoney">
    UPDATE deposit_card
    <set>
        <if test="money != null">
            account_balance = account_balance + #{money},
        </if>
    </set>
    WHERE id = #{depositCardId}
</update>

客户端(deposit-client)

同样,在客户端模块引入:

  • spring-cloud-starter-thrift-clientthrift客户端的 starter程序。
  • deposit-iface:中间契约模块,这里作为客户端桩(Stub)程序。

pom.xml

<parent>
    <groupId>com.icekredit.rpc.thrift.examples</groupId>
    <artifactId>deposit</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>deposit-client</artifactId>

<dependencies>
    <!-- Thrift相关依赖 -->
    <dependency>
        <groupId>com.icekredit.rpc.thrift</groupId>
        <artifactId>spring-cloud-starter-thrift-client</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.icekredit.rpc.thrift.examples</groupId>
        <artifactId>deposit-iface</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- SpringBoot依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Cloud Consul服务注册与发现 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
    <!-- Spring Cloud声明式Restful客户端 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
    </dependency>
    <!-- Swagger依赖 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.6.1</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.6.1</version>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

application.yml中配置thrift的客户端的的运行参数和 Consul 的服务注册与发现的参数:

application.yml

server:
  port: 8080

endpoints:
  actuator:
    sensitive: false
    enabled: true
management:
  security:
    enabled: false

spring:
  cloud:
    consul:
      host: 192.168.91.128
      port: 8500
      discovery:
        register: false
        register-health-check: true
        health-check-interval: 30s
      retry:
        max-attempts: 3
        max-interval: 2000
  thrift:
    client:
      package-to-scan: com.icekredit.rpc.thrift.examples.thrift.client
      service-model: hsHa
      pool:
        retry-times: 3
        pool-max-total-per-key: 200
        pool-min-idle-per-key: 10
        pool-max-idle-per-key: 40
        pool-max-wait: 10000
        connect-timeout: 5000

客户端程序启动入口类,设置 Swagger API所在的包路径名称,同时允许自身作为注册程序注册到注册中心

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableSwagger2
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public Docket createRestfulApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.icekredit.rpc.thrift.examples"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Deposit Client")
                .description("Deposit Client")
                .version("1.0")
                .build();
    }
}

客户端使用@ThriftClient注解标识服务端thrift服务代理接口代理服务IDdeposit-server-rpc代理的目标类ThriftDepositCardService

DepositCardThriftClient.java

@ThriftClient(serviceId = "deposit-server-rpc", refer = ThriftDepositCardService.class)
public interface DepositCardThriftClient extends ThriftClientAware<ThriftDepositCardService.Client> {
}

BankThriftClient.java

@ThriftClient(serviceId = "deposit-server-rpc", refer = ThriftBankService.class)
public interface BankThriftClient extends ThriftClientAware<ThriftBankService.Client> {
}

在客户端控制器中通过ThriftReferer注入需要使用的服务代理接口,通过 thriftClient.client()即可获取Thrift客户端桩对象,然后实现远程服务的调用。

DepositCardRpcController.java

@RestController
@RequestMapping("/rpc/deposit")
public class DepositCardRpcController {
    @ThriftReferer
    private DepositCardThriftClient thriftClient;

    @GetMapping("/queryAllDepositCards")
    public List<DepositCard> queryAllDepositCards(@RequestParam("customerId") String customerId)
            throws Exception {
        return thriftClient.client().queryAllDepositCards(customerId)
                .stream().map(DepositCard::fromThrift)
                .collect(Collectors.toList());
    }

    @PostMapping("/addNewDepositCard")
    public void addNewDepositCard(DepositCard depositCard) throws Exception {
        thriftClient.client().addNewDepositCard(depositCard.getCustomerId(), depositCard.toThrift());
    }

    @GetMapping("/depositMoney")
    public ThriftDepositStatus depositMoney(@RequestParam("depositCardId") String depositCardId,
                                            @RequestParam("money") double money) throws Exception {
        return thriftClient.client().depositMoney(depositCardId, money);
    }

    @GetMapping("/withdrawMoney")
    public ThriftWithdrawStatus withdrawMoney(@RequestParam("depositCardId") String depositCardId,
                                              @RequestParam("money") double money) throws Exception {
        return thriftClient.client().withdrawMoney(depositCardId, money);
    }

    @GetMapping("/queryDepositHistory")
    public List<DepositHistory> queryDepositHistory(@RequestParam("depositCardId") String depositCardId)
            throws Exception {
        return thriftClient.client().queryDepositHistorys(depositCardId)
                .stream().map(DepositHistory::fromThrift)
                .collect(Collectors.toList());
    }

    @GetMapping("/queryWithdrawHistory")
    public List<WithdrawHistory> queryWithdrawHistory(@RequestParam("depositCardId") String depositCardId)
            throws Exception {
        return thriftClient.client().queryWithdrawHistorys(depositCardId)
                .stream().map(WithdrawHistory::fromThrift)
                .collect(Collectors.toList());
    }
}

BankRpcController.java

@RestController
@RequestMapping("/rpc/bank")
public class BankRpcController {
    @ThriftReferer
    private BankThriftClient thriftClient;

    @PostMapping("/addNewBank")
    public void addNewBank(Bank bank) throws Exception {
        thriftClient.client().registerNewBank(bank.toThrift());
    }

    @GetMapping("/getBankById")
    public Bank getBankById(@RequestParam("bankId") Long bankId) throws Exception {
        return Bank.fromThrift(thriftClient.client().getBankById(bankId));
    }

    @GetMapping("/queryAllBranchesByRegion")
    public Map<Region, List<Branch>> queryAllBranchesByRegion(@RequestParam("bankId") Long bankId) throws Exception {
        Map<ThriftRegion, List<ThriftBranch>> thriftRegionListMap = thriftClient.client()
                .queryAllBranchesByRegion(bankId);
        Map<Region, List<Branch>> regionListMap = new HashMap<>();

        for (Map.Entry<ThriftRegion, List<ThriftBranch>> entry : thriftRegionListMap.entrySet()) {
            ThriftRegion thriftRegion = entry.getKey();
            Region region = Region.findByValue(thriftRegion.getValue());

            List<ThriftBranch> thriftBranches = entry.getValue();
            List<Branch> branchList = thriftBranches.stream().map(Branch::fromThrift).collect(Collectors.toList());
            regionListMap.put(region, branchList);
        }
        return regionListMap;
    }
}

因为服务代理客户端接口使用@ThriftClient标识,通过(服务ID + 客户端桩 + 版本号)唯一标识, 即使同时注入多个服务代理客户端接口@ThriftReferer也可忽略注解属性的配置。

总结

有一点是肯定的,那就是在已有技术框架(比如说:Spring + Mybatis/JPA)内,为了提高服务的性能吞吐量,而引入诸如ThriftRPC框架,编程难度复杂度是会大大提高的。好比一把双刃剑,技术选型时还需要多方面权衡利弊。


欢迎关注技术公众号: 零壹技术栈

零壹技术栈

本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。