如何解决如何使用鉴别器和声明性客户端路由请求?
用例
我正在尝试使用 micronaut 声明式客户端调用服务。该服务实际上是许多相同的服务,但针对我们系统中的每个原则托管在不同的主机上。例如
tenetA.example.com/api
tenetB.example.com/api
从 micronaut 开始,我想使用请求标头 X-tenetID
,并根据它调用正确的服务。听起来很简单吧?
第一次尝试:使用过滤器
我尝试的第一件事是在客户端上使用过滤器
@FilterMatcher
@Documented
@Retention(RUNTIME)
@Target({TYPE,PARAMETER})
public @interface MyFilterAnnotation
{
}
@MyFilterAnnotation
@Client("http://replaceme.example.com/")
public interface MyClient
{
@Post(uri = "some/endpoint")
AuthResponse auth(@Body CustomCredentials credentials,@RequestAttribute(name = "tenet-id") String tenetID);
}
@MyFilterAnnotation
@Singleton
public class MyFilter implements HttpClientFilter
{
@Override
public int getOrder()
{
return -10; // Tried playing with the order here to no avail
}
@Override
public Publisher<? extends HttpResponse<?>> doFilter(MutableHttpRequest<?> request,ClientFilterChain chain)
{
String tenetID = request.getAttribute("tenet-id",String.class).orElse(null);
UriBuilder builder = UriBuilder.of(request.getUri());
builder.host(tenetID + ".example.com");
request.uri(builder.build());
return chain.proceed(request);
}
}
我已经确认我的过滤器正在被调用,并且请求 uri 正在设置,但是请求 uri 没有在链中进一步得到尊重。覆盖订单似乎也没有任何影响。它仍在向 replaceme.example.com
发送流量。我在这里做错了吗?
我发现一些论坛帖子说这种方法可能行不通,因为被选中的主机是在流程的早期完成的,以考虑 LoadBalancer 机制。这让我进行了第二次尝试:
第二次尝试:使用客户端负载均衡器
由于似乎无法在过滤器中更改 url 的主机,我尝试编写自己的 DiscoveryClientLoadBalancerFactory
@Replaces(DiscoveryClientLoadBalancerFactory.class)
public class MyLoadBalancer extends DiscoveryClientLoadBalancerFactory
{
/**
* @param discoveryClient The discover client
*/
public MyLoadBalancer(final DiscoveryClient discoveryClient)
{
super(discoveryClient);
}
@Override
public LoadBalancer create(final String serviceID)
{
return discriminator ->
{
// discriminator always seems to be null here.
return Publishers.just(ServiceInstance.of("myService",discriminator + "example.com",8080));
};
}
}
我被困在这里,因为我不知道如何告诉声明性客户端使用我的 tenetID 作为鉴别器。我只见过 @Inject
注释创建一个 DefaultHttpClient
,它只调用 loadBalancer.select(getLoadBalancerDiscriminator())
并且 getLoadBalancerDiscriminator()
总是返回 null
。有没有办法根据请求标头设置鉴别器?
什么是不可行的:应用配置
一些使用 bitbucket 的示例文档表明 url 来自配置。我们的原则来来去去,每个服务都必须使用存储在云保管库中的自己的凭据进行身份验证。因此,在 application.conf 或类似文件中存储某种映射在这里不是很有用,因为我们不想每次添加新原则或轮换密钥时都必须重新启动。
帮助
在这两种方法之间,LoadBalancer 路由看起来更黑,因为我正在做的是请求路由或 url 重写,而不是负载平衡。这些方法之一应该有效吗?有没有更好的方法来做我想做的事?
解决方法
我的最终解决方案是扩展 DefaultHttpClient
,即使它被标记为 @Internal
并覆盖 resolveRequestURI
方法
public interface MyClient
{
@Post(uri = "some/endpoint")
AuthResponse auth(@Body CustomCredentials credentials,@Header("tenet-id") String tenetID);
}
@Singleton
public class MyHttpClient extends DefaultHttpClient
{
// Takes the tenet-id header and looks up the url to send the request to.
@Override
protected <I> Publisher<URI> resolveRequestURI(final HttpRequest<I> request,final boolean includeContextPath)
{
var headers = (MutableHttpHeaders) request.getHeaders();
var customerID = headers.get("tenet-id",String.class).orElseThrow(() -> new IllegalArgumentException("Request must have an tenet-id header"));
var requestURI = request.getUri();
var resolvedURI = secret.getUrl().resolve(includeContextPath ? prependContextPath(requestURI) : requestURI);
return Publishers.just(resolvedURI);
}
}
编辑
我了解到这个解决方案并不理想,因为它提供了一个具体的候选 bean,并阻止了任何类型的配置,就像您在普通 http 服务上所做的那样。租户传播之类的事情不再起作用。我必须解决这个问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。