我正在尝试为一个使用 SSL 的外部 HTTP API 创建一个 FeignClient。斗争是 - 如何用我的逻辑修改默认的 Spring FeignClient,在这种情况下是 SSL 连接工厂。所以基本上我想保留 Spring 自动为 FeignClients 做的所有好事,比如 Hystrix、Sleuth 跟踪等,并让它与我的 SSL 工厂一起工作。
将不胜感激任何建议。
这是我尝试做的:
我尝试在 ComponentScan 之外提供自定义 @Configuration:
@Configuration
public class CustomFeignConfiguration
{
@Bean
public Feign.Builder feignBuilder()
{
Client trustSSLSockets = new Client.Default(
TrustingSSLSocketFactory.get("server1"),
new NoopHostnameVerifier());
log.info("feignBuilder called");
return Feign.builder().client(trustSSLSockets);
}
...
}
Run Code Online (Sandbox Code Playgroud)
通过注解让 FeignClient 使用
@FeignClient(name = "sslClient", configuration = CustomFeignConfiguration.class, url = "https://...")
Run Code Online (Sandbox Code Playgroud)
其中“TrustingSSLSocketFactory”的实现与此类似。
现在,如果我在 Spring 应用程序中注入我的客户端,我可以看到“feignBuilder”被调用并且它成功加载了我的密钥。问题是创建的客户端实际上并未将指定的 SSLFactory 用于 createSocket 调用。所以我得到:
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: …Run Code Online (Sandbox Code Playgroud) 弹簧启动版本1.3.8.RELEASE和云版本'Brixton.SR'相同的代码工作正常.但是'1.5.2.RELEASE'和'Dalston.RC1'不起作用.
例外
HTTP状态500 - 请求处理失败; 嵌套异常是java.lang.RuntimeException:com.netflix.client.ClientException:负载均衡器没有客户端的可用服务器:math-service
这是我的契约
@RequestLine("GET /products/{id}")
@Headers({"Content-Type: application/json"})
ApiResponse getProduct(@Param("id") String productId) throws Exception;
Run Code Online (Sandbox Code Playgroud)
我想获取 id = "a/b" 的产品,
如果我将此作为参数发送给 getProduct("a/b")
然后形成的 URL 是http://api/products/a/b404,而不是 URL 应该是http://api/products/a%2Fb
有没有解决的办法?
我已经有一个 Eureka 服务器正在运行,并且在那里注册了一个微服务(account-server -> Service B)。问题是当尝试使用Service A中的 Feign 时。假设我在服务 A 上了一堂课。
@FeignClient("account-server")
public interface AccountFooProxy {
@RequestMapping(
value = {"/deviceToken/{deviceToken}"},
method = {RequestMethod.GET}
)
String getAccountByDeviceToken(@PathVariable("deviceToken") String var1);
}
Run Code Online (Sandbox Code Playgroud)
我已经知道“帐户服务器”已在尤里卡注册。服务A中的SpringBoot是:
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
}
Run Code Online (Sandbox Code Playgroud)
然后是同一个服务 A 中的 RestController,我在其中创建了这个虚拟类以 @Autowired 一个 FeignClient 来调用服务 B
@RestController
@RequestMapping("/foo")
class FooTest {
@Autowired
private AccountFooProxy accountProxy;
private static final String DEVICE_TOKEN = "xxx";
@RequestMapping(path = "/foo", method = …Run Code Online (Sandbox Code Playgroud) 到目前为止,我们有一个 feign 客户端,如果出现异常,我们曾经重试如下
Retryer<ClientResponse> retryer = RetryerBuilder.<ClientResponse>newBuilder()
.retryIfExceptionOfType(FeignException.class)
.withStopStrategy(StopStrategies.stopAfterAttempt(retryCount))
.withWaitStrategy(WaitStrategies.exponentialWait(maxWaitSeconds, TimeUnit.SECONDS))
.build();
retryer.call(() -> {
return client.doStuffs(someInput); }
);
Run Code Online (Sandbox Code Playgroud)
最近我尝试从这个自定义重试器移动到一个内置的假装重试器,如下所示:
Feign client = Feign.builder()
.decoder(jacksonDecoder)
.encoder(jacksonEncoder)
.logger(slf4jLogger)
.client(okHttpClient)
.retryer(new Retryer.Default(
SECONDS.toMillis(minWaitSeconds),
SECONDS.toMillis(maxWaitSeconds),
retryCount
))
.requestInterceptor(new BasicAuthRequestInterceptor(clientConfig.getUser(), clientConfig.getPassword()))
.target(target);
client.doStuffs(someInput);
Run Code Online (Sandbox Code Playgroud)
理解是假装客户端本身会处理异常,但显然,情况并非如此,当客户端抛出 a 时5xx,我得到一个异常而没有重试。实现重试是否还需要其他东西?
这项服务在 dropwizard 中,git 和 SO 线程主要围绕 spring/ribbon 而我不是这种情况。
深度
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>${feign.version}</version>
</dependency>
Run Code Online (Sandbox Code Playgroud) 我们的一个外部 API 使用带有特殊字符的查询参数名称(不要问我为什么,我不知道)。我的这个 API 的假客户端方法声明如下:
@GetMapping("/v1/user/{userId}/orders")
List<Order> getOrders(
@PathVariable("userId") String userId,
@RequestParam("page[number]") Integer pageNumber,
@RequestParam("page[size]") Integer pageSize);
Run Code Online (Sandbox Code Playgroud)
正如我提到的,请求参数包含特殊字符[和]. 我正在使用 Spock 进行测试,我想像这样设置 Wiremock 存根:
wiremock.stubFor(
get(urlPathMatching('/v1/users/' + userId + '/orders'))
.withQueryParam("page[number]", new EqualToPattern("1"))
.withQueryParam("page[size]", new AnythingPattern())
.willReturn(
status(200)
.withHeader("Content-type", "application/json")
.withBody("""[]""")
))
Run Code Online (Sandbox Code Playgroud)
但我得到:
--------------------------------------------------------------------------------------------------
| Closest stub | Request |
--------------------------------------------------------------------------------------------------
|
GET | GET
/v1/users/123/orders | /v1/users/123/orders?page%5Bnumber%5D=%7Bpage%5Bnumber%5
| D%7D&page%5Bsize%5D=%7Bpage%5Bsize%5D%7D
|
Query: page[number] = 1 | <<<<< Query is not present
Query: page[size] [anything] (always) | <<<<< …Run Code Online (Sandbox Code Playgroud) 我有一个 spring boot (version 2.2.6.RELEASE) web 项目。
从这个 Web 应用程序(我称之为“APP1”)我想使用来自另一个 Web 应用程序的 PATCH 方法调用另一个 URI(我们称之为“APP2”)。在我的 pom.xml 中,我有以下依赖项:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)
下面是我如何调用其他 Web 应用程序的 PATCH 方法。
@FeignClient(name = "clientName", url = "base-uri")
public interface MyInterface{
@PatchMapping(value = "/target-uri")
void callClientMethod(Map<String, Object> args);
Run Code Online (Sandbox Code Playgroud)
我在互联网上寻找解决方案,并在我的 pom.xml 中添加了以下 snipet
<dependency>
<groupId>com.netflix.feign</groupId> <!-- Also tried io.github.openfeign -->
<artifactId>feign-httpclient</artifactId>
<version>8.18.0</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
之后,APP2 的 PATCH 方法仍然被正确调用,但在 APP1 中我收到以下错误: java.lang.NoSuchMethodError: feign.Response.create (ILjava/lang/String;Ljava/util/Map;Lfeign/Response$Body;)假/响应;
我有一个 API,它将以自定义响应消息的形式返回失败,同时仍然发送 Http 200 响应。例子:
Status: 200
Body: {
"code":404,
"message":"Data not found",
"data":{},
"status":"ERROR"
}
Run Code Online (Sandbox Code Playgroud)
我当前的 ErrorDecoder 实现:
public class ApiFeignErrorDecoder implements ErrorDecoder {
private final Gson gson;
public ApiFeignErrorDecoder(Gson gson) {
this.gson = gson;
}
@Override
public Exception decode(String methodKey, Response response) {
try {
ApiResponse apiResponse = gson.fromJson(response.body().asReader(), BaseApiResponse.class);
ResponseStatus status = apiResponse.getStatus();
switch (status) {
case SUCCESS:
return null;
case FAIL:
return new Exception("Failure message recorded");
case ERROR:
return new Exception("Error message recorded");
default:
return new …Run Code Online (Sandbox Code Playgroud) 我有 2 项服务,ServiceA并且有一个名为 的 API 。现在我想从我正在使用的API 中调用这个 API ,并且这两个服务都使用 Spring Boot。ServiceBServiceBcreateDocument(@RequestParam("file") MultipartFile file)ServiceAFeignClient
我的 FeignClient 看起来像这样
@FeignClient(
name = "serviceB",
url = "com.serviceb.url",
configuration = {ServiceBErrorDecoder.class},
fallbackFactory = ServiceBFallbackFactory.class)
@Service
public interface ServiceB{
@PostMapping(value = "/documents")
ResponseEntity createDocument(@RequestParam("file") MultipartFile file);
}
Run Code Online (Sandbox Code Playgroud)
现在,在 ServiceA 中,我想调用这个createDocumentAPI。我可以做到这一点,但问题是如何创建MultipartFile对象并传递它?我有InputStream包含一些内容的对象,但我不想创建物理文件。
服务A代码
public void processObjects(Entity entity) throws JsonProcessingException {
SomeObject object = new SomeObject();
//setters
....
InputStream inputStream = new ByteArrayInputStream(
Objects.requireNonNull(SerializationUtils.serialize(object)));
InputStreamResource inputStreamResource …Run Code Online (Sandbox Code Playgroud) 我有一大堆使用共享配置的 Feign 客户端(MyFeignConfiguration.class):
@FeignClient(name = "clientA", url = "http://serviceA.com", fallbackFactory = ServiceAFallbackFactory.class, configuration = MyFeignConfiguration.class)
@FeignClient(name = "clientB", url = "http://serviceB.com", fallbackFactory = ServiceBFallbackFactory.class, configuration = MyFeignConfiguration.class)
@FeignClient(name = "clientC", url = "http://serviceC.com", fallbackFactory = ServiceCFallbackFactory.class, configuration = MyFeignConfiguration.class)
Run Code Online (Sandbox Code Playgroud)
但是,对于新客户端,我想将用于 OkHttp 的底层 Http 客户端更改为 OkHttp。在MyFeignConfiguration课堂上,我可以添加以下内容:
@Configuration
class MyFeignConfiguration {
@Bean
public Client getClient() {
return OkHttpClient() // use the OkHttp client
}
@Bean
public ErrorDecoder getErrorDecoder() {
//... existing configs
}
Run Code Online (Sandbox Code Playgroud)
然而,现在所有的客户端都在使用这个OkHttp客户端。如何配置新的 feign 客户端以便只有它使用 OkHttp 客户端?另外,我仍然需要使用 …
feign ×10
java ×6
spring-boot ×5
spring ×3
spring-cloud ×3
rest ×2
dropwizard ×1
http-patch ×1
hystrix ×1
openfeign ×1
ssl ×1
wiremock ×1