feign 实现签名、服务地址动态切换
文章目录
- 什么是feign
- 快速启动
- 1. 客户端
- 2. 服务端
- 实现签名验证
- 1. 客户端
- 2. 服务端
- 服务端自动生成feign
- 通过继承实现feign客户端和服务提供方api共用
- 1. 提取公共api
- 2. 服务端实现公共api
- 3. 客户端继承公共api
- 客户端实现动态连接服务端url
- 背景
- 方案(基于[服务端自动生成feign api](#服务端自动生成feign))
- 步骤
- 代码
- 客户端(manager)
什么是feign
Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了http调用流程。
快速启动
1. 客户端
- maven依赖:
<dependency><groupId>com.netflix.feign</groupId><artifactId>feign-core</artifactId><version>8.18.0</version></dependency>
- feign声明服务方api
public interface HelloControllerApi {@RequestLine("GET /api/hello?name={name}")String hello(@Param(value = "name") String name);
}
- 调用服务提供方api
public class HelloControllerApiTest {private HelloControllerApi service;@Beforepublic 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");}@Testpublic void hello(){// 调用http://127.0.0.1:8080/api/hello?name=world 的http接口System.out.println(service.hello("world"));}}
2. 服务端
服务端是spring-web服务,当然可以基于其他形式web服务。
- 服务代码:
@Controller
@RequestMapping(value = "api")
public class HelloController {@RequestMapping(value = "/hello", method = {RequestMethod.GET})@ResponseBodypublic 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);}
}
实现签名验证
1. 客户端
- 添加feign拦截器,并在请求heard中添加token
service = Feign.builder().options(new Request.Options(1000, 3500)).retryer(new Retryer.Default(5000, 5000, 3)).requestInterceptor(new AuthRequestInterceptor(authKey)).target(HelloControllerApi.class, "http://127.0.0.1:8080");
public class AuthRequestInterceptor implements RequestInterceptor {private String token;public AuthRequestInterceptor(String token) {this.token = token;}@Overridepublic void apply(RequestTemplate template) {template.header("token", token);}}
2. 服务端
- 服务端添加过滤器,对token进行验证
@Component
public class AuthFilter implements Filter {@Value("${token:test_token}")private String token;@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {String remoteToken = ((HttpServletRequest) servletRequest).getHeader("token");Assert.isTrue(token.equals(remoteToken), "签名验证失败");filterChain.doFilter(servletRequest, servletResponse);}
}
服务端自动生成feign
通过继承实现feign客户端和服务提供方api共用
spring-cloud-start-openfeign
由于有支持spring-mvc controller主机,因此本例使用spring-cloud-start-openfeign
依赖。实现原理为:1. 提取controller rest接口抽象接口,2. 在客户端由Feign接口继承,3. 在服务端由controller类实现处理逻辑。
1. 提取公共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);
}
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
@Controller
@RequestMapping
public class UserController implements IUserController {@Override@ResponseBodypublic List<String> listAll(String name) {ArrayList<String> list = new ArrayList<>();list.add("达菲");list.add("olu");list.add(name);return list;}
}
- token验证拦截器
@Component
public class AuthFilter implements Filter {@Value("${token:test_token}")private String token;@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {String remoteToken = ((HttpServletRequest) servletRequest).getHeader("token");if(!token.equals(remoteToken)){((HttpServletResponse)servletResponse).setStatus(401);servletResponse.getWriter().write("签名验证失败");return;}filterChain.doFilter(servletRequest, servletResponse);}
}
- 启动类
@SpringBootApplication(scanBasePackages = {"com.vhicool.manager"})
public class ManagerApplication {public static void main(String[] args) {SpringApplication.run(ManagerApplication.class, args);}
}
3. 客户端继承公共api
- mavne依赖
<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>
- 客户端调用类
@FeignClient(value = "user", url = "http://localhost:8080")
public interface UserApi extends IUserController {}
- 启动类
@SpringBootApplication
@EnableFeignClients
public class ManagerApplication {public static void main(String[] args) {SpringApplication.run(ManagerApplication.class, args);}
}
- feign公共配置
@Configuration
public class FeignConfiguration {/*** 请求超时时间* @return*/@Beanpublic Request.Options options() {return new Request.Options(2000, 3500);}/*** 拦截器* @return*/@Beanpublic RequestInterceptor interceptor() {return new AuthRequestInterceptor("test_token");}
}
public class AuthRequestInterceptor implements RequestInterceptor {private String token;public AuthRequestInterceptor(String token) {this.token = token;}@Overridepublic void apply(RequestTemplate template) {template.header("token", token);}}
- 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {@Autowiredprivate UserApi userApi;@Testpublic void listAll() {System.out.println(userApi.listAll("饼饼"));}
}
客户端实现动态连接服务端url
背景
现在客户端调用服务端还是1对1的场景,在实际使用场景中,我们需要客户端可以连接不同的服务,例如:一个Manager 同时管理多个woker,每个worker对应一个业务环境(生产、测试、开发等)。用户使用Manager进行环境切换,后端也需要对应访问不同环境的worker服务。
方案(基于服务端自动生成feign api)
步骤
1. 在manager处理请求时,获取当前业务环境,并设置为当前线程上下文环境(基于`ThreadLocal`的`com.alibaba.transmittable-thread-local`)
2. 在触发feign调用是,根据当前线程上下文环境,动态设置服务提供方url(feign拦截器`RequestInterceptor`)
代码
客户端(manager)
- 新增maven依赖
<dependency><groupId>com.alibaba</groupId><artifactId>transmittable-thread-local</artifactId><version>2.10.2</version></dependency>
- 设置当前线程所在环境
public class ZBaseContextManager {private static final TransmittableThreadLocal<String> ZBASE_CONTEXT = new TransmittableThreadLocal();/*** 设置当前线程环境* @param env*/public static void setEnv(String env) {ZBASE_CONTEXT.set(env);}/*** 获取当前线程环境* @return*/public static String currentEnv() {return ZBASE_CONTEXT.get();}
}
- feign请求获取当前环境,并将url替换成对应环境worker的url
public class EnvRouterInterceptor implements RequestInterceptor {private static final Logger LOG = LoggerFactory.getLogger(EnvRouterInterceptor.class);private Map<String, String> envUrlMap;public EnvRouterInterceptor(Map<String, String> envUrlMap) {this.envUrlMap = envUrlMap;}@Overridepublic void apply(RequestTemplate template) {String env = ZBaseContextManager.currentEnv();String url = this.envUrlMap.get(env);template.target(url);LOG.info("env : {} ,target url : {}", env, template.url());}
}
- 设置feign全局拦截器
@Beanpublic RequestInterceptor envRouterInterceptor() {Map<String, String> envMap = new HashMap<>();envMap.put("dev", "http://localhost:8080");envMap.put("fat", "http://10.10.134.196:8080");return new EnvRouterInterceptor(envMap);}
- 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class DynamicUserControllerTest {@Autowiredprivate UserApi userApi;@Testpublic void listAllFat() {ZBaseContextManager.setEnv("fat");System.out.println(userApi.listAll("meimei"));}@Testpublic void listAllDev() {ZBaseContextManager.setEnv("dev");System.out.println(userApi.listAll("meimei2"));}
}
输出日志
env : fat ,target url : http://10.10.134.196:8080/user/list-all?name=meimeienv : dev ,target url : http://localhost:8080/user/list-all?name=meimei2
- 推荐:
Feign异常传递
feign 实现签名、服务地址动态切换相关推荐
- SpringBoot+AOP实现多数据源动态切换
SpringBoot+AOP实现多数据源动态切换 背景 设计总体思路 步骤 背景 系统后端需要访问多个数据库,现有的数据库连接配置写入配置文件中.后端需要从一个数据库的配置表里动态的读取其它mysql ...
- Java实现动态切换数据源
在一般的Java项目中,如果使用Spring去管理数据库连接信息,一般只能连接一个数据库,可是会有部分情况我们需要连接多个数据库,甚至还会存在不同的请求需要根据配置信息连接不同的数据库,比如: 在很多 ...
- 在vue中用openlayers调取天地图服务并动态选择各个省份的中心,及行政边界
vue这块我就不说了,直接讲openlayers. 1.openlayers是什么? Openlayers是一个专为Web GIS客户端开发提供的JavaScript类库包,用于实现标准格式发布的地图 ...
- flex4实现图片的动态切换
用flex4实现图片的动态切换,动态创建linkbutton按钮,代码如下: <?xml version="1.0" encoding="utf-8"?& ...
- spring boot 切换 oracle 和 mysql_spring-boot多数据源动态切换
作者:码农教程 出自:码农教程 原文:manongjc.com/detail/14-ucsldoihyzmwcui.html 当项目中存在多数据源时,就涉及到数据源的动态切换,通过研究,特此记录一下. ...
- android log丢失(三)动态切换logd机制和kernel机制
之前我们分析了如何修改版本使log机制变成kernel的log机制.这篇博客我们继续修改可以动态切换,这样方便平时log丢失时调试. 我们先来看liblog库的编译mk文件,这个文件中主要修改了对使用 ...
- 多库操作:多个数据库的动态切换(一)
▼ 更多精彩推荐,上午11点到达 ▼ 在平时的开发中,受到传统模式的影响,我们都是习惯了单一的数据库表操作,把数据都建到一个库里边,然后进行增删改查,这个是很经典的开发模式. 但是随着项目开发,总会出 ...
- EFCore动态切换Schema
最近做个分库分表项目,用到schema的切换感觉还是有些坑的,在此分享下. 先简要说下我们的分库分表 分库分表规则 我定的规则是,订单号(数字)除以16,得出的结果为这个订单所在的数据库,然后他的余数 ...
- MacOS Apple M1 安装ARM架构的JDK及动态切换版本
JDK下载安装: 咱就是说,ARM版本的JDK就是一个字,真特么快,想变快吗,赶紧下载叭!! 1.下载地址:https://www.azul.com/downloads/?package=jdk 筛选 ...
- java 动态数据源切换,Java+Spring+MyBatis实现多数据源的动态切换
在实际的项目开发过程中我们经常会遇到一个项目需要使用多个数据源的情况,而多数据源又可分为固定多数据源和动态多数据源两种情况. 固定多数据源是指在项目中需要使用多个数据源,但数据源的个数是确定的,不会改 ...
最新文章
- python安装在哪个盘_python要安装在哪个盘
- IQueryable和IEnumerable的区别
- 精选30道Java笔试题解答
- 用户体验中巧妙的过场动画
- 计算机学报格式_如何快速写好一篇格式正确的参考文献?
- 中继代理 server2008R2
- 详解 QT 主要类 QWidget
- mysql 51cto 数据类型_Mysql数据类型
- web前端是什么?如何能成为一名合格的前端开发工程师?
- SAP License:SAP 清帐函数示例
- 粒子群PSO算法实验及其代码解释
- selenium启动chrome时Proxy server需要验证用户
- python 删除文件到回收站 SHFileOperation
- Deep Voice 论文
- 第五章 站在巨人的肩膀上
- 来吧 兄弟萌 我们一起学做粥
- api系列聚美优品的知识点
- 如何辨识“真假”敏捷?
- [计算机毕业设计]大数据的B站数据分析与可视化
- 《左耳听风》-ARTS-打卡记录-第十四周
热门文章
- 新版微信支持鸿蒙os,微信突然宣布!“新功能”全面爆发,鸿蒙OS措手不及
- 伊利诺伊香槟分校计算机排名,伊利诺伊大学香槟分校计算机工程排行业界内最整体分析...
- MindSpore布道师招募计划,开启AI的信仰之跃
- 大数据杀熟!我被美团会员割了韭菜
- 利用ansys计算机械结构最小安全系数教程,安全系数
- 飞秋教程 2013 阳阳整理完整版
- CF1694B Paranoid String 构造/子串计数
- BZOJ 2037: [Sdoi2008]Sue的小球(DP)
- python安装reporter
- 针对三层别墅的两种无线组网方案