最近在看apisix的时候, 发现它的upstream写法很是固定, 比如只能指定一组ip或者域名的节点, 或者从Nacos等注册中心拉取节点, 对于动态上游的支持很有限, 比如这位老哥提出的Issue:
人家就想简简单单的根据paramater或者url去决定转发到哪个upstream都不支持, 开发者给出的建议是你可以根据traffic-split这种插件自己写个去~ 然后潇洒 close issue
然后我在看它的Plugin插件开发说明的时候, 发现了 External Plugin 这个东西, 简单来说就它支持其他语言写的插件, 跑在别的语言的runner上面, 然后apisix通过RPC远程调用去调用插件, 工作原理就是这张图
这就激起了我的好奇心, 然后拉下他的代码来看了一下
代码仓库地址: https://github.com/apache/apisix-java-plugin-runner
他的大概原理就是通过监听同一个Unix套接字来进行RPC调用, java runner是一个springboot项目, 内部使用netty来实现RPC通信
有两种方式, 最新的方式是用jar包的方式引入他的runner, 他给出的例子是这个仓库:https://github.com/tzssangglass/java-plugin-runner-demo-1, 但是测试之后发现maven中央仓库中好像没有他的jar包, 所以先用另一种方式, 就是直接在runner项目中进行插件开发
从https://github.com/apache/apisix-java-plugin-runner拉取代码
在runner-starter文件夹中的pom文件中增加依赖(如果有则忽略):
<dependency>
<groupId>org.apache.apisix</groupId>
<artifactId>apisix-runner-plugin</artifactId>
<version>0.3.0-SNAPSHOT</version>
</dependency>
然后在runner-plugin文件夹下的 src\main\java\org\apache\apisix\plugin\runner\filter\ 目录下新增插件文件(这边给了一个简单的例子, 就是根据传入的cityid去查询某个城市的天气)
package org.apache.apisix.plugin.runner.filter;
import com.google.gson.Gson;
import org.apache.apisix.plugin.runner.HttpRequest;
import org.apache.apisix.plugin.runner.HttpResponse;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.net.http.HttpClient;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class ProxyPassFilter implements PluginFilter {
@Override
public String name() {
return "ProxyPassFilter";
}
@Override
public void filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) {
/*
* If the conf you configured is of type json, you can convert it to Map or json.
*/
String configStr = request.getConfig(this);
Gson gson = new Gson();
Map<String, Object> conf = new HashMap<>();
conf = gson.fromJson(configStr, conf.getClass());
/*
* You can use the parameters in the configuration.
*/
String cityid = request.getArg("cityid");
java.net.http.HttpRequest httpRequest = java.net.http.HttpRequest.newBuilder()
.GET()
.uri(URI.create("<https://v0.yiketianqi.com/api?unescape=1&version=v91&appid=43656176&appsecret=I42og6Lm&ext=&cityid=>" + cityid))
.build();
java.net.http.HttpResponse<String> httpResponse;
try {
httpResponse = HttpClient.newHttpClient()
.send(httpRequest, java.net.http.HttpResponse.BodyHandlers.ofString());
response.setStatusCode(httpResponse.statusCode());
response.setBody(httpResponse.body());
} catch (Exception e) {
// write log
response.setStatusCode(500);
response.setBody("{\\"code\\":\\"500\\",\\"message\\":\\"internal error\\"}");
}
/* note: The body is currently a string type.
If you need the json type, you need to escape the json content here.
For example, if the body is set as below
"{\\"key1\\":\\"value1\\",\\"key2\\":2}"
The body received by the client will be as below
{"key1":"value1","key2":2}
*/
/* Using the above code, the client side receives the following
header:
HTTP/1.1 401 Unauthorized
Content-Type: text/plain; charset=utf-8
Connection: keep-alive
new-header: header_by_runner
Server: APISIX/2.6
body:
{"key1":"value1","key2":2}
*/
chain.filter(request, response);
}
@Override
public List<String> requiredVars() {
return null;
}
@Override
public Boolean requiredBody() {
return null;
}
}
在设置响应code和body的时候会把actionType设置为stop, 所以这个例子中, 如果配置了这个插件, 则永远不会调用到上游的upstream