源码位置
mesosphere/marathon-client
Feign is used as the underlying REST library.
也就是以restful api 的方式与 marathon server 交流
String endpoint = "<Marathon's endpoint>"; Marathon marathon = MarathonClient.getInstance(endpoint);
根据操作对象 Marathon 可以操作marathon 提供的所有v2 http 接口,比如
App app = new App();
app.setId("echohisleepbye-app");
app.setCmd("echo hi; sleep 10; echo bye;");
app.setCpus(1.0);
app.setMem(16.0);
app.setInstances(1);
marathon.createApp(app);
主要包结构
mesosphere.marathon.client auth model v2 各类请求和相应的model Marathon MarathonClient MarathonException
我们来看 创建 Marathon 操作对象的代码
public static Marathon getInstance(String endpoint, RequestInterceptor… interceptors) {
Builder b = Feign.builder()
.encoder(new GsonEncoder(ModelUtils.GSON))
.decoder(new GsonDecoder(ModelUtils.GSON))
.errorDecoder(new MarathonErrorDecoder());
if (interceptors != null)
b.requestInterceptors(asList(interceptors));
String debugOutput = System.getenv(DEBUG_JSON_OUTPUT);
if ("System.out".equals(debugOutput)) {
System.setProperty("org.slf4j.simpleLogger.logFile", "System.out");
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");
b.logger(new Slf4jLogger()).logLevel(Logger.Level.FULL);
} else if (debugOutput != null) {
b.logger(new Logger.JavaLogger().appendToFile(debugOutput)).logLevel(Logger.Level.FULL);
}
b.requestInterceptor(new MarathonHeadersInterceptor());
return b.target(Marathon.class, endpoint);
}
比较一栋,再看下Marathon.class
public interface Marathon {
// Apps
@RequestLine("GET /v2/apps")
@Headers(HeaderUtils.MARATHON_API_SOURCE_HEADER)
GetAppsResponse getApps() throws MarathonException;
@RequestLine("GET /v2/apps")
@Headers(HeaderUtils.MARATHON_API_SOURCE_HEADER)
GetAppsResponse getApps(@QueryMap Map<String, String> queryMap) throws MarathonException;
@RequestLine("GET /v2/apps/{id}")
@Headers(HeaderUtils.MARATHON_API_SOURCE_HEADER)
GetAppResponse getApp(@Param("id") String id) throws MarathonException;
...
}
也很易懂,所以重点就在 b.target(Marathon.class, endpoint);
上了,全称是 feign.Builder.target(Class<T> apiType, String url);
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if(Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
这段代码最好倒过来看
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
通过 一个InvocationHandler 可以运行时构造一个 interface Marathon
实例。 我们能用反射做什么 提到: dynamic proxies 总让人跟代理模式扯上关系,但实际上说dynamic interface implementations 更为直观。
spring 自定义一个namespace ,比如 <custom name="",config="",interface="CustomIface">
,然后spring 将其序列化为一个对象,并自动注入到代码中
@Component
Class A {
@Autowire
private CustomIface custom;
}
两者的共同点是:
<custom>
或者是处理成 spring的BeanDefinition,或者处理成一个FactoryBean, 最终需要 Proxy.newProxyInstance
来返回一个代理类。 不同的是:
所以,我来看下 Feign 提供的效果
OpenFeign/feign 将这种方式称为Interface Annotations,Feign annotations define the Contract between the interface and how the underlying client should work.