聊聊Http服务化改造实践

开发 前端
Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求。Feign通过注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了http调用流程。

在微服务架构体系中远程RPC调用主要包括Dubbo与Http调用两个大类,由于Dubbo拥有服务注册中心,并且起服务的命名非常规范,使用包名.类名.方法名进行描述。

而http调用通常都是使用httpclient等相关类库,这些在使用上并没有问题,但API都是分散在整个工程的各个地方,如果HTTP调用也可以使用类似Dubbo服务的表示方法,采用声明式定义就好了。

在开源的世界中只有想不到,没有找不到,为了解决Feign的声明式服务化管理,Feign框架应运而生,本文主要介绍如何使用Feign实现Http服务声明化管理与调用。

1.什么是Feign

Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求。Feign通过注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了http调用流程。

2、快速入门实例

2.1定义客户端

首先要引入Feign的maven依赖,如下图所示:

 <dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.18.0</version>
</dependency>

2.2 定义服务调用API(类似Dubbo API)

服务调用的API声明代码如下所示:

@FeignClient
public interface HelloControllerApi {
@RequestLine("GET /api/hello?name={name}")
String hello(@Param(value = "name") String name);
}

这里的要点是使用@FeignClient进行声明。声明后就可以通过HelloControllerApi进行远程HTTP调用,示例代码如下:

public class HelloControllerApiTest {

private HelloControllerApi service;

@Before
public void before(){
service = Feign.builder()
.options(new Request.Options(1000, 3500))
.retryer(new Retryer.Default(5000, 5000, 3))
.target(HelloControllerApi.class, "http://127.0.0.1:8080");
}
@Test
public void hello(){
// 调用http://127.0.0.1:8080/api/hello?name=world 的http接口
System.out.println(service.hello("world"));
}

}

当然需要在调用方的启动类上增加@EnableFeignClients(defaultConfiguration = FeignConfiguration.class)注解。

2.3定义服务端

服务端与Feign并无关系,主要按照API的方式实现即可,服务端实现代码如下所示:

@Controller
@RequestMapping(value = "api")
public class HelloController {
@RequestMapping(value = "/hello", method = {RequestMethod.GET})
@ResponseBody
public String list(@RequestParam String name) {
return "Hello " + name;
}
}

//启动类
@SpringBootApplication(scanBasePackages = {"com.vhicool.manager"})
public class ManagerApplication {
public static void main(String[] args) {
SpringApplication.run(ManagerApplication.class, args);
}
}

3.实现签名校验

上述只是简单实用Feign,接下来以实现签名校验为例展示Feign的扩展机制。

签名验证是最常见的安全机制,首先在客户端定义一个签名拦截器,用于生成签名信息,示范代码如下图所示:

public class AuthRequestInterceptor implements feign.RequestInterceptor {
private TokenService tokenService;

public AuthRequestInterceptor(TokenService tokenService) {
this.tokenService = tokenService;
}

@Override
public void apply(RequestTemplate template) {
template.header("token", tokenService.getToken());
}

}

并且在Feign的全局配置文件中创建对应的拦截器,示例代码如下:

public class FeignConfiguration {
@Bean
public RequestInterceptor authRequestInterceptor(ResourceIdentity resourceIdentity) {
AuthRequestInterceptor authRequestInterceptor = new AuthRequestInterceptor(resourceIdentity);
authRequestInterceptor.setErrorEncodeType(errorEncodeType);
return authRequestInterceptor;
}
}

同时在服务端获取token并对token进行校验,示例代码如下:

@Component
public class AuthFilter implements Filter {

@Autowired
private TokenService tokeService;

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String remoteToken = ((HttpServletRequest) servletRequest).getHeader("token");
if(!tokeService.valid(token)) {
//异常处理逻辑
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}

4.服务端自动生成Feign

上面的示例虽然实现了服务接口的声明式管理,但调用端、客户端并没有显示的约束关系,接下来展示如何使用客户端、服务端使用继承方式定义服务调用API。

例如要实现如下图的效果:

原生的Feign无法实现该效果,我们需要使用OpenFeign类库,两者之间的对比如下图所示:

接下来详细介绍具体实现方法。

4.1 提取公共API

首先使用一个模块定义公共API,需要引入maven依赖,代码示例如下所示:

 <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

接下来定义公共的服务接口,客户端、服务端都需要实现该接口,公共服务端接口定义如下:

public interface IUserController {
@RequestMapping(value = "user/list-all", method = {RequestMethod.GET})
List<String> listAll(@RequestParam String name);
}

4.2 服务端实现公共API

首先需要添加相应的maven依赖,代码如下:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.vhicool</groupId>
<artifactId>feign-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>

服务端采用继承方式实现,具体代码如下所示:

@Controller
@RequestMapping
public class UserController implements IUserController {
@Override
@ResponseBody
public List<String> listAll(String name) {
ArrayList<String> list = new ArrayList<>();
list.add("达菲");
list.add("olu");
list.add(name);
return list;
}
}

4.3 客户端实现公共API

客户端首先同样需要增加相应的依赖,具体代码如下所示:


<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.vhicool</groupId>
<artifactId>feign-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>

客户端服务调用类需要继承公共API:

@FeignClient(value = "user", url = "http://localhost:8080")
public interface UserApi extends IUserController {
}

同时客户端启动类需要增加@EnableFeignClients注解,具体示例代码如下所示:

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

同样基于Springboot编程方式,可以为Feign配置全局参数,具体如下:

@Configuration
public class FeignConfiguration {
/**
* 请求超时时间
* @return
*/
@Bean
public Request.Options options() {
return new Request.Options(2000, 3500);
}
//拦截器等定义
}

接下来客户端就可以用如下方式进行调用:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {
@Autowired
private UserApi userApi;

@Test
public void listAll() {
System.out.println(userApi.listAll("饼饼"));
}
}

时,当前项目编译的jar包,类也已经被替换成我们自定义的类,目标达成。

作者介绍:李大伟,现任中通快递中间件高级架构师,对消息中间件、Flink有着非常丰富的实践经验。

责任编辑:武晓燕 来源: 中间件兴趣圈
相关推荐

2017-11-14 10:23:20

HTTP服务异步

2023-03-27 08:49:51

2018-05-09 08:18:26

微服务改造架构

2024-07-05 09:24:11

2021-10-26 11:42:51

系统

2023-12-06 19:04:31

多平台消息推送

2024-02-29 18:06:39

HTTP性能优化

2019-02-28 09:22:37

Nacos微服务DNS

2023-09-11 08:50:03

Maven工具关系管理

2024-09-11 20:05:56

2019-01-11 09:41:56

网易考拉服务架构微服务

2021-10-04 19:49:23

HTTP模块No.js

2023-01-04 18:10:26

服务模块化jre

2022-08-01 07:27:36

JavaDocker容器

2016-11-28 09:00:10

浏览器浏览器缓存服务端

2023-04-28 08:06:04

低代码AI智能

2022-02-23 09:03:29

JavaScript开发命名约定

2023-11-28 07:55:05

Calico容器网络

2018-01-28 20:36:13

戴尔

2018-09-10 15:57:52

IstioUCloudIPv6
点赞
收藏

51CTO技术栈公众号