源码位置
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.