Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

服务内部调用如何环境隔离 #177

Open
FengChueng opened this issue Nov 30, 2021 · 6 comments
Open

服务内部调用如何环境隔离 #177

FengChueng opened this issue Nov 30, 2021 · 6 comments

Comments

@FengChueng
Copy link

作者你好,感谢你的开源作品。我在使用该开源产品时,遇到以下疑惑,麻烦解答。

服务实例:
A(元数据:env=canary)
B-1(元数据:env=canary)
B-2(元数据:env=prod)
C-1(元数据:env=canary)
C-2(元数据:env=prod)
调用链路:A ->B -> C

例如有一个基于spring schedule的定时服务A,通过FeignClient调用时,即使指定n-d-env=canary,也没有按路由策略调用B-1(元数据:env=canary),而是使用轮询策略调用B-1,B-2。但是当调用至B-1时,则只会调用C-1。当调用B-2时,只会调用C-2。
即可能调用链路为:
A -> B1 -> C1
A -> B2 -> C2

比较疑惑为什么A不会根据元数据 env=canary来调用B1呢?而是使用沦陷调用B服务

网上的方案可以在服务A中通过 DiscoveryClient,获取服务列表,然后根据A的元数据过滤,再使用RestTemplate/Okhttp发起对B的请求。
请问还有其他更优雅的方式实现吗?

@HaojunRen
Copy link
Member

不好意思,迟至今日回复。你的思路没问题,但定时服务需要确认n-d-env是否通过Header传递过去了?

@FengChueng
Copy link
Author

通过以下两种方式,在调用时n-d-env是通过header传递过去了的。被调用方B能接收到该Header。

方式一:
通过实现feign.RequestInterceptor来添加header
`
public class FeignConfiguration implements RequestInterceptor {

private static final String STR = "-";
public static final String TAG_NAME = "TAG_NAME";
public static final String N_D_ENV = "n-d-env";

private ApplicationContext applicationContext;

public FeignConfiguration(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
}

@Override
public void apply(RequestTemplate template) {
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
            .getRequestAttributes();
    if(attributes == null){
        log.warn("Request attributes is null.");
        return;
    }
    HttpServletRequest request = attributes.getRequest();
    Enumeration<String> headerNames = request.getHeaderNames();
    if (headerNames != null) {
    	
        boolean ndEnvHeader = false;
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            String values = request.getHeader(name);
            // 跳过 content-length
            if ("content-length".equals(name)) {
                continue;
            }
            if ("n-d-env".equals(name)) {
                ndEnvHeader = true;
            }
            template.header(name, values);
        }
        if (!ndEnvHeader) {
	        template.header(N_D_ENV, "canary");
        }
    } else {
        log.info("feign interceptor error header:{}", template);
    }
}

}
`
方式二:在feign调用时,传入header
RestBaseResponse createHttpJobWithEnv(@RequestHeader("n-d-env") String env);
feignClient.createHttpJobWithEnv("canary")

通过restful调用A服务,且在请求中header中加入n-d-env,然后通过feign调用B服务,是可以实现灰度的。
通过restful调用A服务,不在请求中header加入n-d-env,然后在feign调用B服务时,header加入n-d-env,是无法实现灰度的。

@HaojunRen
Copy link
Member

一般来说,第一种方式居多

@FengChueng
Copy link
Author

FengChueng commented Mar 11, 2022

通过定时任务触发,采用第一种方式添加header,但是在selectServer的时候,没有根据header中的n-d-env去选择。
nacos中注册的B服务有:2个n-d-env=prod,1个n-d-env=canary。

但是通过restful调用A服务,且在请求中header中加入n-d-env,然后通过feign调用B服务,是根据n-d-env去选择了的,所以比较疑惑,本框架在选择feign client的时候的原理是根据什么呢?我debug跟踪了源码,但是没看懂 -_-
希望作者能解答一下

@HaojunRen
Copy link
Member

HaojunRen commented Mar 12, 2022

需要在负载均衡前,前置进去Header,否则无效。参考

public abstract class ServiceStrategyFilter extends OncePerRequestFilter

@HaojunRen
Copy link
Member

RequestInterceptor是发生在负载均衡结束(即已经完成选择了服务实例)后,进入Feign调用,而蓝绿灰度必须发生在负载均衡时候,那么,必须将你自己定义的Header在负载均衡之前塞入,即通过OncePerRequestFilter,Spring Boot也是通过这个类实现上下文Context的传递

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants