欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Cloud开发人员如何解决服务冲突和实例乱窜?(IP实现方案)

程序员文章站 2022-06-29 09:45:29
一、背景 在我上一篇文章《 "Spring Cloud开发人员如何解决服务冲突和实例乱窜?" 》中提到使用服务的 来实现隔离和路由,有朋友问到能不能直接通过 来实现?本文就和大家一起来讨论一下这个问题   二、可行性分析 要实现通过 来隔离和路由的话有一个非常关键的点需要解决,就是怎样实现 ......

Spring Cloud开发人员如何解决服务冲突和实例乱窜?(IP实现方案)

一、背景

在我上一篇文章《spring cloud开发人员如何解决服务冲突和实例乱窜?》中提到使用服务的元数据来实现隔离和路由,有朋友问到能不能直接通过ip来实现?本文就和大家一起来讨论一下这个问题

 

二、可行性分析

要实现通过ip来隔离和路由的话有一个非常关键的点需要解决,就是怎样实现ip可辨识,意思就是如何区分那个ip服务器上的,那个ip开发人员本机

Spring Cloud开发人员如何解决服务冲突和实例乱窜?(IP实现方案)

如上图所示其实我们还是能找到规律可以辨识的,所以这个是可以行的!

  • 开发人员本机ip - 其实就是客户端ip,也就是原始请求方的ip172.16.20.2
  • 服务器ip - 可以理解为服务器上的服务所在机器的ip(有点绕):172.16.20.1

 

三、路由规则逻辑

主要实现以下目标:

  1. 普通用户访问服务器上的页面时,请求的所有路由只调用服务器上的实例
  2. 开发a访问时,请求的所有路由优先调用开发a本机启动的实例,如果没有则调用服务器上的实例
  3. 开发b访问时同上,请求的所有路由优先调用开发b本机启动的实例,如果没有则调用服务器上的实例

在找到ip的辨识规律后,推导出下面3个路由规则来实现上面的目标

  1. 优先匹配原始请求方的ip的服务实例
  2. 再者匹配上游服务所在机器ip的服务实例
  3. 上面2个逻辑都匹配不到的话使用轮询的方式找一个实例

 
具体的自定义负载均衡的对象怎么写我这里就不详细描述了,可以参考我上一篇文章《spring cloud开发人员如何解决服务冲突和实例乱窜?

 

四、获取原始请求方的ip

获取原ip的代码片段如下,只需要在网关上增加一个过滤器获取ip,然后添加到header里面一直传递下去就可以了

/**
 * 获取ip地址
 */
private string getipaddr(httpservletrequest request){
    string ip = request.getheader("x-forwarded-for");
    if (isemptyip(ip)) {
        ip = request.getheader("proxy-client-ip");
        if (isemptyip(ip)) {
            ip = request.getheader("wl-proxy-client-ip");
            if (isemptyip(ip)) {
                ip = request.getheader("http_client_ip");
                if (isemptyip(ip)) {
                        ip = request.getheader("http_x_forwarded_for");
                        if (isemptyip(ip)) {
                            ip = request.getremoteaddr();
                            if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
                                // 根据网卡取本机配置的ip
                                try {
                                    ip = inetaddress.getlocalhost().gethostaddress();
                                } catch (unknownhostexception e) {
                                    log.error("inetaddress.getlocalhost()-error", e);
                                }
                            }
                        }
                }
            }
        }
    } else if (ip.length() > 15) {
        string[] ips = ip.split(",");
        for (int index = 0; index < ips.length; index++) {
            string strip = ips[index];
            if (!isemptyip(ip)) {
                ip = strip;
                break;
            }
        }
    }
    return ip;
}

private boolean isemptyip(string ip) {
    if (strutil.isempty(ip) || unknown_str.equalsignorecase(ip)) {
        return true;
    }
    return false;
}

把原ip添加到headerhttp_x_forwarded_for里面传递给下游服务

requestcontext ctx = requestcontext.getcurrentcontext();
httpservletrequest request = ctx.getrequest();
string sourceip = getipaddr(request);
ctx.getzuulrequestheaders().put("http_x_forwarded_for", sourceip);

 

五、获取服务器所在机器的ip

直接使用jdk自带的inetaddress就可以了

string localip = inetaddress.getlocalhost().gethostaddress()

 

六、总结

通过ip的方案来实现开发环境服务实例隔离和策略路由后,可以实现到开发完全无感知,既不需要配置元数据,也不需要自己去传version之类的参数了。
但是这个方案其实也是有局限性

  1. 开发服务器必须是只用一台来部署所有的服务,因为如果上游服务和下游服务不在同一个ip上就失去了辨识能力了
  2. 因为网络环境比较复杂,不一定能获取到客户端的真实原ip
  3. 开发人员启动客户端/前端的机器与启动后台服务必须是同一台电脑上才行;例如如果是前端开发人员a启动的客户端,去调试后台开发人员b启动的服务就不行了,因为原ip与注册上去的服务实例ip匹配不上

 
最后可能大家会问原ip怎样全链路传递下去?链路传递可以参考一下我的另外一篇文章《日志排查问题困难?分布式日志链路跟踪来帮你

 

推荐阅读

 
扫码关注有惊喜!

Spring Cloud开发人员如何解决服务冲突和实例乱窜?(IP实现方案)