Kong提供了Http的管理API,可以实现对Kong的管理。我们利用Kong的Amin API,实现一套JAVA的管理API。这里以添加一个Service和Route为示例:
使用retrofit2实现
添加Maven依赖
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>${retrofit.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>${retrofit.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.1</version>
</dependency>
实现kong客户端的架子
添加Kong客户端
@Data
public class KongClient {
private ServiceService serviceService;
private RouteService routeService;
public KongClient(String adminUrl) {
if (adminUrl == null || adminUrl.isEmpty()) {
throw new IllegalArgumentException("The adminUrl cannot be null or empty!");
}
RetrofitServiceCreator retrofitServiceCreatorForAdminUrl = new RetrofitServiceCreator(adminUrl);
{
serviceService = retrofitServiceCreatorForAdminUrl.create(ServiceService.class,RetrofitServiceService.class);
routeService = retrofitServiceCreatorForAdminUrl.create(RouteService.class,RetrofitRouteService.class);
}
}
}
添加Retrofit处理类
public class RetrofitServiceCreator {
private Retrofit retrofit;
// -------------------------------------------------------------------
public RetrofitServiceCreator(String baseUrl) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(initOkHttpClient(baseUrl.toLowerCase().startsWith("https"))) // support https
.addConverterFactory(CustomGsonConverterFactory.create()) // replace GsonConverterFactory
}
// -------------------------------------------------------------------
@SuppressWarnings("unchecked")
public <T> T create(Class<T> serviceInterface, Class<?> retrofitServiceInterface) {
Object proxied = retrofit.create(retrofitServiceInterface);
return (T) Proxy.newProxyInstance(
RetrofitServiceCreator.class.getClassLoader(),
new Class[] { serviceInterface },
new RetrofitBodyExtractorInvocationHandler(proxied));
}
public <T> T createRetrofitService(Class<T> retrofitServiceInterface) {
return retrofit.create(retrofitServiceInterface);
}
// -------------------------------------------------------------------
private OkHttpClient initOkHttpClient(boolean supportHttps) {
if(supportHttps) {
HttpsUtil.SSLParams sslParams = HttpsUtil.getSslSocketFactory(null, null, null);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
.build();
return okHttpClient;
}
return new OkHttpClient.Builder().build();
}
}
添加动态代理处理类
@Slf4j
public class RetrofitBodyExtractorInvocationHandler implements InvocationHandler {
private Object proxied;
public RetrofitBodyExtractorInvocationHandler(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
Method method1 = proxied.getClass().getMethod(methodName, parameterTypes);
Call call = (Call) method1.invoke(proxied, args);
Response response = call.execute();
log.debug("Http Request: " + response.raw().request());
log.debug("Http Response: " + response.raw().toString());
if(!response.isSuccessful()) {
throw new KongClientException(response.errorBody() != null ? response.errorBody().string() : String.valueOf(response.code()));
}
return response.body();
}
}
添加自定义JSON转换工厂
class CustomGsonConverterFactory extends Converter.Factory {
private final Gson gson;
private CustomGsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
public static CustomGsonConverterFactory create() {
return create(new Gson());
}
public static CustomGsonConverterFactory create(Gson gson) {
return new CustomGsonConverterFactory(gson);
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonRequestBodyConverter<>(gson, adapter);
}
}
实现Service的Java Admin API
添加Service实体
@Data @Builder
public class Service {
private String id;
private String name;
private String protocol;
private String host;
private Integer port;
private String path;
private String url;
private Integer retries;
@SerializedName("connect_timeout")
private Long connectTimeout;
@SerializedName("read_timeout")
private Long readTimeout;
@SerializedName("write_timeout")
private Long writeTimeout;
@SerializedName("created_at")
private Long createdAt;
@SerializedName("updated_at")
private Long updatedAt;
}
添加Service接口,这里只要删除和新增
public interface ServiceService {
Service addService(Service service);
void deleteService(String nameOrId);
}
添加Retrofit的处理接口
public interface RetrofitServiceService {
@POST("services/")
Call<Service> addService(@Body Service request);
@DELETE("services/{nameOrId}")
Call<Void> deleteService(@Path("nameOrId") String nameOrId);
}
实现Route的Java Admin API
添加Route实体
@Data @Builder
public class Route {
private String id;
private List<String> protocols;
private List<String> methods;
private List<String> hosts;
private List<String> paths;
@SerializedName("strip_path")
private Boolean stripPath;
@SerializedName("preserve_host")
private Boolean preserveHost;
private Service service;
@SerializedName("created_at")
private Long createdAt;
@SerializedName("updated_at")
private Long updatedAt;
}
添加Route接口,这里只要删除和新增
public interface RouteService {
Route addRoute(Route route);
void DeleteRoute(String id);
}
添加Retrofit的处理接口
public interface RetrofitRouteService {
@POST("routes/")
Call<Route> addRoute(@Body Route route);
@DELETE("routes/{id}")
Call<Void> DeleteRoute(@Path("id") String id);
}
单元测试一下
新建一个名字为example-service
,地址为http://mockbin.org
的Service。并为Service添加host为example.com
的Route路由。
public class ServiceRouteTest extends BaseTest {
public static final String EXAMPLE_SERVICE = "example-service";
@Test
public void createServiceAndRouteTest(){
// 删除Route和Service
CommonList<Route> commonList = kongClient.getRouteService().listRoutesByService(EXAMPLE_SERVICE);
List<Route> routeList = commonList.getData();
if(null!=routeList && routeList.size()>0 ){
for (Route route : routeList) {
kongClient.getRouteService().DeleteRoute(route.getId());
}
}
kongClient.getServiceService().deleteService(EXAMPLE_SERVICE);
// 新建Service和Route
Service service = Service.builder().url("http://mockbin.org").name(EXAMPLE_SERVICE).build();
Service response4service = kongClient.getServiceService().addService(service);
printJson(response4service);
List<String> hostList = new ArrayList<>();
hostList.add("example.com");
Route route = Route.builder().hosts(hostList).service(Service.builder().id(response4service.getId()).build()).build();
Route response4route = kongClient.getRouteService().addRoute(route);
printJson(response4route);
}
}
最后,检查下效果
使用GET
方法,访问地址http://192.168.56.112:8000
,并添加在头部添加host[]=example.com
,结果如下:
写在最后
- 利用架子可以自定义其他Kong的JAVA客户端。
- 很多时候,我们的API地址是通过程序扫描出来的,或者管理系统进行配置的。这个时候,我们就可以利用Kong的JAVA客户端快速的实现Kong的接口管理,轮训等。
- 应用降级,流控,金丝雀,灰度等等,都可以通过Kong的JAVA客户端轻松实现。