Feign根据动态ip调用不同主机的微服务
最近遇到这样一个需求,允许在办公内网的每个客户端安装微服务,以便能够调用客户端操作系统的命令。
显然技术点在:根据客户端ip,springcloud调用部署在相应ip上的微服务。springcloud feign的动态url技术,能够提供这一功能的实现。
一、代码实现
1,服务端的Feign接口
1 @FeignClient(name = "BOOK-COMMAND-ENGINE", url = "http://localhost:8015")
2 public interface CommandEngineApi {
3
4 @RequestMapping(value="startEdit")
5 JSONObject startEdit(URI uri, @RequestParam("filePath")String filePath);
6
7 }
2,客户端feign实现接口
1 @RestController
2 public class CommandController {
3
4 private static Logger log = LoggerFactory.getLogger(CommandController.class);
5
6 @RequestMapping(value="startEdit")
7 public JSONObject startEdit(String filePath) {
8 JSONObject result = new JSONObject();
9 String cmd = String.format(CommandParam.START_EDIT_CMD, filePath);
10 try {
11 Runtime.getRuntime().exec(cmd);
12 log.info(cmd);
13 } catch (Exception e) {
14 e.printStackTrace();
15 log.error(e.getMessage());
16 }
17 log.info("execute:" + cmd);
18 result.put("code", 0);
19 return result;
20 }
21 }
3,服务端controller
1 @RequestMapping(value = "/startAdjust")
2 public void startAdjust(@RequestParam(value = "fileId") String fileId) {
3 FileInfo fileInfo = fileService.queryFileByFileId(fileId);
4 File file = new File(fileInfo.getFilePath());
5 String clientIP = IpUtil.getIpAddress();
6 String filePath = file.getAbsolutePath();
7 URI uri;
8 try {
9 uri = new URI(String.format("http://%s:8015", clientIP));
10 logger.info("invoke:" + uri.toString() + " " + filePath);
11 commandEngineApi.startEdit(uri, filePath);
12 } catch (URISyntaxException e) {
13 e.printStackTrace();
14 logger.error(e.getMessage());
15 }
16 }
ps:获取客户端ip
1 public static String getIpAddress() {
2 final String UNKNOWN = "unknown";
3 final String[] matchOptions = {"x-forwarded-for", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
4 final int size = matchOptions.length;
5 try {
6 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
7 HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
8 String ip = UNKNOWN;
9 int index = 0;
10 while(index < size && (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip))){
11 ip = request.getHeader(matchOptions[index]);
12 index++;
13 }
14 if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
15 ip = request.getRemoteAddr();
16 }
17 if (!StringUtils.isEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip) && ip.length() > 15) {
18 String[] ips = ip.split(",");
19 int len = ips.length;
20 for (int i = 0; i < len; i++) {
21 String strIp = ips[index];
22 if (!(UNKNOWN.equalsIgnoreCase(strIp))) {
23 ip = strIp;
24 break;
25 }
26 }
27 }
28 return ip;
29 } catch (Exception e) {
30 return UNKNOWN;
31 }
32 }
二、代码分析
1,Springcloud OpenFeign自动构建实现类
在使用方式上,OpenFeign需要手动构建代理对象,Spring Cloud OpenFeign 不同于 OpenFeign, Spring Cloud OpenFeign 帮我们自动生成了接口的代理对象(即实现类),并且注册到Spring中,我们可以很方便的使用 @Autowired 注入代理对象然后使用。
其默认的代理对象是 LoadBalancerFeignClient。还有一个代理对象是 feign.Client.Default。
两者区别在于:LoadBalancerFeignClient 通过服务名(下文提到)从Eureka查找相关的节点地址url,发起调用。feign.Client.Default 仅是简单的直接调用。
open-feign底层库是使用HttpURLConnection。feign会基于配置,生成URI。当我们不配置url的时候,URI是这样的http://service-name/path。使用FeignClien是LoadBalancerFeignClient,该类在处理该URI的时候会对服务名进行解析,也就是从注册中心查询该服务名下已经注册的服务器信息,包括IP和端口。然后将服务名替换成真实的链接。而当我们不配置url的时候,使用的FeignClient是Client的默认实现Default,该类就没有解析的这一步而是直接通过HttpURLConnection进行请求。
2,.Feign接口中的name与url一定要指定
name属性,是@FeignClient 注解必要的,不定义时会报错其默认指代Eureka上的服务名。
url属性,一定要指定,指定什么值其实不重要,因为最终都会被方法的URI参数值替换掉,它在这里另一个重要的作用,就是将接口的代理对象变成feign.Client.Default(默认是LoadBalancerFeignClient),这样就绕过了从Eureka取节点地址这一步,毕竟第三方的地址不可能注册到我们的Eureka上。
3,Feign接口参数中会比实现类多一个参数URL
feign实现类不能去实现feign接口,因为feign接口要比实现类多一个参数URL,动态调用时接口会根据这个参数分发到不同微服务,其他参数使用@RequestMapping、@RequestParam等正常使用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2018-08-30 巨坑:jqgrid竟然取不到编辑模式下input的值
2018-08-30 JS变量比较陷阱
2018-08-30 JS动态事件绑定问题