背景
最近公司的微服务治理做了升级,逐步从全面的dubbo RPC切换到 http+dubbo的多元化微服务架构模式,其原因无外乎dubbo支持语言过于单一,在多语言协作中的乏力,加之跨业务部门之间的安全、权限、各核心平台能力沉淀等需要。在此HTTP迁移过程中尝试过Spring-Cloud-Open-Feign,但是从压测效果来看不是很理想,性能与RestTemplate相差甚远,而RestTemplate用起来开发效率不是很高,要写不少重复代码,那是不是可以像Mybatis封装JDBC、Spring-Cloud-Open-Feign封装Feign一样,给RestTemplate也做下简单有效的封装呢,于是就产生了SpringCloud-RestTemplate-Feign。
原理架构图
即,定义了一套基于Spring Cloud规范的注解:@EnableChtFeignClients、@ChtFeignClient等,解析常用Spring MVC的注解@RequestMapping、@RequestBody、@RequestParam、@RequestHeader…实现对RestTemplate的请求调用封装。
使用方法:
-
下载源码编译安装
地址:https://github.com/muyunisawesome/spring-cloud-resttemplate-feign -
引入依赖
<dependency>
<groupId>com.cht</groupId>
<artifactId>spring-cloud-resttemplate-feign</artifactId>
<version>1.0.15-RELEASE</version>
</dependency>
- 启动注解
@SpringBootApplication
@EnableChtFeignClients
@Slf4j
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
log.info("----------DemoServer 启动成功----------------");
}
}
- 编写业务接口
package com.cht.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 响应dto示例
*/
@Data
public class OrderDto implements Serializable {
private String uuid;
private String name;
private String name2;
private String mobile;
private Date time;
}
package com.cht.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 请求dto示例
*/
@Data
public class DemoDto implements Serializable {
private String name;
private String blackTime;
private Date blackTime2;
private Date testField;
private String testField2;
private String driverUuid;
}
package com.cht.demo;
import com.cht.PageResult;
import com.cht.Response;
import com.cht.demo.dto.DemoDto;
import com.cht.dto.OrderDto;
import com.cht.rst.feign.ChtFeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Your business interface, such as order、pay.etc
*/
@ChtFeignClient(name = "test-api1", url = "http://localhost:8081")
public interface ChtTestFeignClient {
/**
* 业务示例
* @param token 请求头示例,比如token
* @param demoDto 请求体示例
* @param aaa 请求参数,用于url之上
* @param ccc 同上
* @param bbb url中的变量示例
* @return
*/
@PostMapping(value = "/{bbb}/ttt1")
Response<PageResult<OrderDto>> ttt1(@RequestHeader("token") String token,
@RequestBody DemoDto demoDto,
@RequestParam("aaa") String aaa,
@RequestParam("ccc") String ccc,
@PathVariable("bbb") String bbb);
}
注意:此处@ChtFeignClient注解的name属性值必须全局唯一,因为每一个业务接口都是对应的spring子容器。name作为容器 ID 使用。
- 使用业务接口, 像普通Spring Bean一样
package com.cht.demo;
import com.cht.PageResult;
import com.cht.Response;
import com.cht.demo.dto.DemoChildDto;
import com.cht.demo.dto.DemoDto;
import com.cht.dto.OrderDto;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.text.ParseException;
import java.util.Date;
@RestController
public class ConsumerController {
@Resource
private ChtTestFeignClient chtTestFeignClient;
@GetMapping(value = "/demo1")
public Response<PageResult<OrderDto>> ttt1() throws ParseException {
DemoDto demoDto = new DemoDto();
demoDto.setName("foo");
demoDto.setBlackTime(new Date());
DemoChildDto demoChildDto = new DemoChildDto();
demoChildDto.setAge(11);
demoDto.setAgeDto(demoChildDto);
Response<PageResult<OrderDto>> w =
chtTestFeignClient.ttt1("token", demoDto, "aaa", null, "yyy");
return w;
}
}
目前效果和性能都已经过大量的测试、线上验证