SpringBoot Dropwizard等框架,@Service注解默认单例,工作中遇到的问题

项目背景:
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;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值