项目背景:
1、大项目基于PaaS平台,采用微服务架构,所以服务会注册在msb上,调用其他服务,通过msb查询到服务所在pod的ip和port
2、服务之间的调用都是通过rest接口调用,代码部分通过Retrofit实现
工作中遇到的问题:
服务A(我们的服务)调用服务B的某个接口,返回“No route to host (Host unreachable)”
分析结论:
我们的服务A部署启动时,Dopwizard(或者SpringBoot)会实例化@Service注解的类,默认是单例。在这个类中有个成员变量:
@Service
public class TestService {
private RestServiceB restServiceB = RetrofitTest.getRestServiceB();
...
}
RestServiceB 是个封装了需要调用服务B的rest接口,类似于:
public interface RestServiceB {
@GET("/test/city")
Call<Response> getCitys(@Query("city") String city);
}
RetrofitTest 类:
public class RetrofitTest {
private static final String HTTP = "http://";
private static final String COLON = ":";
private static final Retrofit RETROFIT;
private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient();
static {
RETROFIT = new Retrofit.Builder()
.client(OK_HTTP_CLIENT)
.baseUrl(getServiceBUrl())
.addConverterFactory(JacksonConverterFactory.create())
.build();
}
//这个方法只在服务启动时,TestService被初始化时,调用一次
public static RestServiceB getRestServiceB() {
return RETROFIT.create(RestServiceB.class);
}
private static String getServiceBUrl() {
//通过注册中心(msb)查询 ServiceB 的 ip:port
...
}
}
getRestServiceB()方法只在服务启动时,TestService被初始化时,调用一次,RETROFIT只实例化一次,内部的baseUrl也只调用一次,也就是说值查询过一次ServiceB的ip:port;
由于是基于PaaS的微服务架构,如果ServiceB在运行过程中,因为某些原因pod被驱逐了(宕机),PaaS架构会重启pod,ServiceB所在pod重启后,ip和port有可能发生变化。如果此时TestService 类中,仍然使用restServiceB.getCitys,就会报错:No route to host (Host unreachable)
解决方法:
1、每次调用服务B的接口时,都重新查询一次它的ip:port
这种方法没有必要,毕竟ServiceB被驱逐也是偶现的
2、调用接口失败时,重新查询一次它的ip:port
RetrofitTest 中增加getNewPaasApi()方法,会重新获取baseUrl
public class RetrofitTest {
...
public static RestServiceB getNewRestServiceB() {
//refresh baeUrl
Retrofit retrofit = new Retrofit.Builder()
.client(OK_HTTP_CLIENT)
.baseUrl(getServiceBUrl())
.addConverterFactory(JacksonConverterFactory.create())
.build();
return retrofit.create(RestServiceB .class);
}
}
先用原有的restServiceB 调用接口,如果抛出异常,catch后,refresh restServiceB 即可:
private RestServiceB restServiceB = RetrofitTest.getRestServiceB();
private Response<Response> tryGetCitys(String city) {
return Optional.ofNullable(doGetCitys(city)).orElse(doGetCitys(city));
}
private Response<Response> doGetCitys(String city) {
Response<Response> response = null;
try {
response = restServiceB.getCitys(city).execute();
} catch (IOException e) {
// No route to host (Host unreachable)
//refrash restServiceB 中baseUrl后,重新调用这个方法
restServiceB = RetrofitTest.getNewRestServiceB();
}
return response;
}