• 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等正常使用。

  • 相关阅读:
    htb系列-Web Challenges-Console
    htb系列-Web Challenges-FreeLancer
    离散数学1复习要点
    样本均值和总体均值的区别
    chapter7.参数估计
    计算机系统基础复习指北
    C语言学生信息管理系统
    数学的意义
    计算机系统基础第一章
    C语言文件读写的操作
  • 原文地址:https://www.cnblogs.com/guanghe/p/16639147.html
Copyright © 2020-2023  润新知