From fc4dfc3302d4ee002e51a76745f85320bb07c8dc Mon Sep 17 00:00:00 2001 From: spacewander Date: Tue, 12 Jan 2021 18:31:23 +0800 Subject: [PATCH] feat: support dubbo Fix #89 Signed-off-by: spacewander --- .github/workflows/build.yml | 9 + .gitignore | 2 + .travis/ASF-Release.cfg | 5 +- .travis/apisix_cli_test/test_dubbo.sh | 53 +++ .travis/linux_openresty_1_17_runner.sh | 2 +- .travis/linux_openresty_common_runner.sh | 167 ++++++++++ .travis/linux_openresty_runner.sh | 149 +-------- Makefile | 2 +- README.md | 1 + apisix/cli/ngx_tpl.lua | 43 +++ apisix/cli/ops.lua | 9 + apisix/init.lua | 12 + apisix/plugins/dubbo-proxy.lua | 69 ++++ conf/config-default.yaml | 3 + doc/plugins/dubbo-proxy.md | 131 ++++++++ rockspec/apisix-master-0.rockspec | 1 + t/APISIX.pm | 53 +++ .../dubbo-backend-interface/pom.xml | 45 +++ .../org/apache/dubbo/backend/DemoService.java | 43 +++ .../dubbo-backend-provider/pom.xml | 96 ++++++ .../backend/provider/DemoServiceImpl.java | 61 ++++ .../dubbo/backend/provider/Provider.java | 40 +++ .../META-INF/spring/dubbo-demo-provider.xml | 39 +++ .../src/main/resources/dubbo.properties | 17 + .../src/main/resources/log4j.properties | 23 ++ t/lib/dubbo-backend/pom.xml | 97 ++++++ t/plugin/dubbo-proxy/route.t | 314 ++++++++++++++++++ t/plugin/dubbo-proxy/upstream.t | 137 ++++++++ utils/linux-install-openresty.sh | 52 +++ 29 files changed, 1524 insertions(+), 151 deletions(-) create mode 100755 .travis/apisix_cli_test/test_dubbo.sh create mode 100755 .travis/linux_openresty_common_runner.sh create mode 100644 apisix/plugins/dubbo-proxy.lua create mode 100644 doc/plugins/dubbo-proxy.md create mode 100644 t/lib/dubbo-backend/dubbo-backend-interface/pom.xml create mode 100644 t/lib/dubbo-backend/dubbo-backend-interface/src/main/java/org/apache/dubbo/backend/DemoService.java create mode 100644 t/lib/dubbo-backend/dubbo-backend-provider/pom.xml create mode 100644 t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/DemoServiceImpl.java create mode 100644 t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/Provider.java create mode 100644 t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml create mode 100644 t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties create mode 100644 t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties create mode 100644 t/lib/dubbo-backend/pom.xml create mode 100644 t/plugin/dubbo-proxy/route.t create mode 100644 t/plugin/dubbo-proxy/upstream.t diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f812d20585cb9..7192316ce951b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,6 +58,15 @@ jobs: - name: Linux Before install run: sudo ./.travis/${{ matrix.os_name }}_runner.sh before_install + - name: Start Dubbo Backend + if: matrix.os_name == 'linux_openresty' + run: | + sudo apt install -y maven + cd t/lib/dubbo-backend + mvn package + cd dubbo-backend-provider/target + java -Djava.net.preferIPv4Stack=true -jar dubbo-demo-provider.one-jar.jar > /tmp/java.log & + - name: Install Redis Cluster if: startsWith(matrix.os_name, 'linux_openresty') run: | diff --git a/.gitignore b/.gitignore index 665f66d6e7c70..ce1002a5c8db9 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,8 @@ fastcgi_temp client_body_temp utils/lj-releng default.etcd/ +t/lib/dubbo-backend/dubbo-backend-interface/target/ +t/lib/dubbo-backend/dubbo-backend-provider/target/ .idea/ *.iml \.* diff --git a/.travis/ASF-Release.cfg b/.travis/ASF-Release.cfg index b08604509ae8e..b4ba843fb90b0 100644 --- a/.travis/ASF-Release.cfg +++ b/.travis/ASF-Release.cfg @@ -79,14 +79,15 @@ DISCLAIMER.txt LICENSE*.txt NOTICE.txt -# Exclude travis CI files +# Exclude CI files build-cache deps test-nginx grpc_server_example -.travis.yml grpcurl t/servroot +t/lib/dubbo-backend/dubbo-backend-provider/target +t/lib/dubbo-backend/dubbo-backend-interface/target conf .travis/openwhisk-utilities diff --git a/.travis/apisix_cli_test/test_dubbo.sh b/.travis/apisix_cli_test/test_dubbo.sh new file mode 100755 index 0000000000000..49814a1133024 --- /dev/null +++ b/.travis/apisix_cli_test/test_dubbo.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +. ./.travis/apisix_cli_test/common.sh + +# enable dubbo +echo ' +plugins: + - dubbo-proxy +' > conf/config.yaml + +make init + +if ! grep "location @dubbo_pass " conf/nginx.conf > /dev/null; then + echo "failed: dubbo location not found in nginx.conf" + exit 1 +fi + +echo "passed: found dubbo location in nginx.conf" + +# dubbo multiplex configuration +echo ' +plugins: + - dubbo-proxy +plugin_attr: + dubbo-proxy: + upstream_multiplex_count: 16 +' > conf/config.yaml + +make init + +if ! grep "multi 16;" conf/nginx.conf > /dev/null; then + echo "failed: dubbo multiplex configuration not found in nginx.conf" + exit 1 +fi + +echo "passed: found dubbo multiplex configuration in nginx.conf" diff --git a/.travis/linux_openresty_1_17_runner.sh b/.travis/linux_openresty_1_17_runner.sh index e36dd8336ee98..39d83336f28ce 100755 --- a/.travis/linux_openresty_1_17_runner.sh +++ b/.travis/linux_openresty_1_17_runner.sh @@ -18,4 +18,4 @@ export OPENRESTY_VERSION=1.17.8.2 -. ./.travis/linux_openresty_runner.sh +. ./.travis/linux_openresty_common_runner.sh diff --git a/.travis/linux_openresty_common_runner.sh b/.travis/linux_openresty_common_runner.sh new file mode 100755 index 0000000000000..2229d6a1869e3 --- /dev/null +++ b/.travis/linux_openresty_common_runner.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +. ./.travis/common.sh + +before_install() { + sudo cpanm --notest Test::Nginx >build.log 2>&1 || (cat build.log && exit 1) + docker pull redis:3.0-alpine + docker run --rm -itd -p 6379:6379 --name apisix_redis redis:3.0-alpine + docker run --rm -itd -e HTTP_PORT=8888 -e HTTPS_PORT=9999 -p 8888:8888 -p 9999:9999 mendhak/http-https-echo + # Runs Keycloak version 10.0.2 with inbuilt policies for unit tests + docker run --rm -itd -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=123456 -p 8090:8080 -p 8443:8443 sshniro/keycloak-apisix + # spin up kafka cluster for tests (1 zookeper and 1 kafka instance) + docker pull bitnami/zookeeper:3.6.0 + docker pull bitnami/kafka:latest + docker network create kafka-net --driver bridge + docker run --name zookeeper-server -d -p 2181:2181 --network kafka-net -e ALLOW_ANONYMOUS_LOGIN=yes bitnami/zookeeper:3.6.0 + docker run --name kafka-server1 -d --network kafka-net -e ALLOW_PLAINTEXT_LISTENER=yes -e KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper-server:2181 -e KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092 -p 9092:9092 -e KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true bitnami/kafka:latest + docker pull bitinit/eureka + docker run --name eureka -d -p 8761:8761 --env ENVIRONMENT=apisix --env spring.application.name=apisix-eureka --env server.port=8761 --env eureka.instance.ip-address=127.0.0.1 --env eureka.client.registerWithEureka=true --env eureka.client.fetchRegistry=false --env eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/ bitinit/eureka + sleep 5 + docker exec -i kafka-server1 /opt/bitnami/kafka/bin/kafka-topics.sh --create --zookeeper zookeeper-server:2181 --replication-factor 1 --partitions 1 --topic test2 + + # start skywalking + docker run --rm --name skywalking -d -p 1234:1234 -p 11800:11800 -p 12800:12800 apache/skywalking-oap-server +} + +do_install() { + export_or_prefix + + ./utils/linux-install-openresty.sh + + ./utils/linux-install-luarocks.sh + sudo luarocks install luacheck > build.log 2>&1 || (cat build.log && exit 1) + + ./utils/linux-install-etcd-client.sh + + if [ ! -f "build-cache/apisix-master-0.rockspec" ]; then + create_lua_deps + + else + src=`md5sum rockspec/apisix-master-0.rockspec | awk '{print $1}'` + src_cp=`md5sum build-cache/apisix-master-0.rockspec | awk '{print $1}'` + if [ "$src" = "$src_cp" ]; then + echo "Use lua deps cache" + sudo cp -r build-cache/deps ./ + else + create_lua_deps + fi + fi + + # sudo apt-get install tree -y + # tree deps + + git clone https://github.com/iresty/test-nginx.git test-nginx + make utils + + git clone https://github.com/apache/openwhisk-utilities.git .travis/openwhisk-utilities + cp .travis/ASF* .travis/openwhisk-utilities/scancode/ + + ls -l ./ + if [ ! -f "build-cache/grpc_server_example" ]; then + wget https://github.com/iresty/grpc_server_example/releases/download/20200901/grpc_server_example-amd64.tar.gz + tar -xvf grpc_server_example-amd64.tar.gz + mv grpc_server_example build-cache/ + fi + + if [ ! -f "build-cache/proto/helloworld.proto" ]; then + if [ ! -f "grpc_server_example/main.go" ]; then + git clone https://github.com/iresty/grpc_server_example.git grpc_server_example + fi + + cd grpc_server_example/ + mv proto/ ../build-cache/ + cd .. + fi + + if [ ! -f "build-cache/grpcurl" ]; then + wget https://github.com/api7/grpcurl/releases/download/20200314/grpcurl-amd64.tar.gz + tar -xvf grpcurl-amd64.tar.gz + mv grpcurl build-cache/ + fi +} + +script() { + export_or_prefix + openresty -V + + ./build-cache/grpc_server_example & + + ./bin/apisix help + ./bin/apisix init + ./bin/apisix init_etcd + ./bin/apisix start + + #start again --> fial + res=`./bin/apisix start` + if [ "$res" != "APISIX is running..." ]; then + echo "failed: APISIX runs repeatedly" + exit 1 + fi + + #kill apisix + sudo kill -9 `ps aux | grep apisix | grep nginx | awk '{print $2}'` + + #start -> ok + res=`./bin/apisix start` + if [ "$res" == "APISIX is running..." ]; then + echo "failed: shouldn't stop APISIX running after kill the old process." + exit 1 + fi + + sleep 1 + cat logs/error.log + + sudo sh ./t/grpc-proxy-test.sh + sleep 1 + + ./bin/apisix stop + sleep 1 + + sudo bash ./utils/check-plugins-code.sh + + make lint && make license-check || exit 1 + + # APISIX_ENABLE_LUACOV=1 PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t + PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t +} + +after_success() { + # cat luacov.stats.out + # luacov-coveralls + echo "done" +} + +case_opt=$1 +shift + +case ${case_opt} in +before_install) + before_install "$@" + ;; +do_install) + do_install "$@" + ;; +script) + script "$@" + ;; +after_success) + after_success "$@" + ;; +esac diff --git a/.travis/linux_openresty_runner.sh b/.travis/linux_openresty_runner.sh index f451bbe053f09..eb1e9858899d6 100755 --- a/.travis/linux_openresty_runner.sh +++ b/.travis/linux_openresty_runner.sh @@ -16,151 +16,6 @@ # limitations under the License. # -. ./.travis/common.sh -before_install() { - sudo cpanm --notest Test::Nginx >build.log 2>&1 || (cat build.log && exit 1) - docker pull redis:3.0-alpine - docker run --rm -itd -p 6379:6379 --name apisix_redis redis:3.0-alpine - docker run --rm -itd -e HTTP_PORT=8888 -e HTTPS_PORT=9999 -p 8888:8888 -p 9999:9999 mendhak/http-https-echo - # Runs Keycloak version 10.0.2 with inbuilt policies for unit tests - docker run --rm -itd -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=123456 -p 8090:8080 -p 8443:8443 sshniro/keycloak-apisix - # spin up kafka cluster for tests (1 zookeper and 1 kafka instance) - docker pull bitnami/zookeeper:3.6.0 - docker pull bitnami/kafka:latest - docker network create kafka-net --driver bridge - docker run --name zookeeper-server -d -p 2181:2181 --network kafka-net -e ALLOW_ANONYMOUS_LOGIN=yes bitnami/zookeeper:3.6.0 - docker run --name kafka-server1 -d --network kafka-net -e ALLOW_PLAINTEXT_LISTENER=yes -e KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper-server:2181 -e KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092 -p 9092:9092 -e KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true bitnami/kafka:latest - docker pull bitinit/eureka - docker run --name eureka -d -p 8761:8761 --env ENVIRONMENT=apisix --env spring.application.name=apisix-eureka --env server.port=8761 --env eureka.instance.ip-address=127.0.0.1 --env eureka.client.registerWithEureka=true --env eureka.client.fetchRegistry=false --env eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/ bitinit/eureka - sleep 5 - docker exec -i kafka-server1 /opt/bitnami/kafka/bin/kafka-topics.sh --create --zookeeper zookeeper-server:2181 --replication-factor 1 --partitions 1 --topic test2 - - # start skywalking - docker run --rm --name skywalking -d -p 1234:1234 -p 11800:11800 -p 12800:12800 apache/skywalking-oap-server -} - -do_install() { - export_or_prefix - - ./utils/linux-install-openresty.sh - - ./utils/linux-install-luarocks.sh - sudo luarocks install luacheck > build.log 2>&1 || (cat build.log && exit 1) - - ./utils/linux-install-etcd-client.sh - - if [ ! -f "build-cache/apisix-master-0.rockspec" ]; then - create_lua_deps - - else - src=`md5sum rockspec/apisix-master-0.rockspec | awk '{print $1}'` - src_cp=`md5sum build-cache/apisix-master-0.rockspec | awk '{print $1}'` - if [ "$src" = "$src_cp" ]; then - echo "Use lua deps cache" - sudo cp -r build-cache/deps ./ - else - create_lua_deps - fi - fi - - # sudo apt-get install tree -y - # tree deps - - git clone https://github.com/iresty/test-nginx.git test-nginx - make utils - - git clone https://github.com/apache/openwhisk-utilities.git .travis/openwhisk-utilities - cp .travis/ASF* .travis/openwhisk-utilities/scancode/ - - ls -l ./ - if [ ! -f "build-cache/grpc_server_example" ]; then - wget https://github.com/iresty/grpc_server_example/releases/download/20200901/grpc_server_example-amd64.tar.gz - tar -xvf grpc_server_example-amd64.tar.gz - mv grpc_server_example build-cache/ - fi - - if [ ! -f "build-cache/proto/helloworld.proto" ]; then - if [ ! -f "grpc_server_example/main.go" ]; then - git clone https://github.com/iresty/grpc_server_example.git grpc_server_example - fi - - cd grpc_server_example/ - mv proto/ ../build-cache/ - cd .. - fi - - if [ ! -f "build-cache/grpcurl" ]; then - wget https://github.com/api7/grpcurl/releases/download/20200314/grpcurl-amd64.tar.gz - tar -xvf grpcurl-amd64.tar.gz - mv grpcurl build-cache/ - fi -} - -script() { - export_or_prefix - openresty -V - - ./build-cache/grpc_server_example & - - ./bin/apisix help - ./bin/apisix init - ./bin/apisix init_etcd - ./bin/apisix start - - #start again --> fial - res=`./bin/apisix start` - if [ "$res" != "APISIX is running..." ]; then - echo "failed: APISIX runs repeatedly" - exit 1 - fi - - #kill apisix - sudo kill -9 `ps aux | grep apisix | grep nginx | awk '{print $2}'` - - #start -> ok - res=`./bin/apisix start` - if [ "$res" == "APISIX is running..." ]; then - echo "failed: shouldn't stop APISIX running after kill the old process." - exit 1 - fi - - sleep 1 - cat logs/error.log - - sudo sh ./t/grpc-proxy-test.sh - sleep 1 - - ./bin/apisix stop - sleep 1 - - sudo bash ./utils/check-plugins-code.sh - - make lint && make license-check || exit 1 - # APISIX_ENABLE_LUACOV=1 PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t - PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t -} - -after_success() { - # cat luacov.stats.out - # luacov-coveralls - echo "done" -} - -case_opt=$1 -shift - -case ${case_opt} in -before_install) - before_install "$@" - ;; -do_install) - do_install "$@" - ;; -script) - script "$@" - ;; -after_success) - after_success "$@" - ;; -esac +export OPENRESTY_VERSION=source +. ./.travis/linux_openresty_common_runner.sh diff --git a/Makefile b/Makefile index 026d5405338f3..e8e8887ebef7c 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ INST_LUADIR ?= $(INST_PREFIX)/share/lua/5.1 INST_BINDIR ?= /usr/bin INSTALL ?= install UNAME ?= $(shell uname) -OR_EXEC ?= $(shell which openresty) +OR_EXEC ?= $(shell which openresty || which nginx) LUAROCKS_VER ?= $(shell luarocks --version | grep -E -o "luarocks [0-9]+.") SHELL := /bin/bash -o pipefail diff --git a/README.md b/README.md index f5c921c6845e3..79ef5b2e6e9f5 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ A/B testing, canary release, blue-green deployment, limit rate, defense against - **Multi protocols** - [TCP/UDP Proxy](doc/stream-proxy.md): Dynamic TCP/UDP proxy. + - [dubbo Proxy](doc/plugins/dubbo-proxy.md): Dynamic HTTP to dubbo proxy. - [Dynamic MQTT Proxy](doc/plugins/mqtt-proxy.md): Supports to load balance MQTT by `client_id`, both support MQTT [3.1.\*](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html), [5.0](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html). - [gRPC proxy](doc/grpc-proxy.md): Proxying gRPC traffic. - [gRPC transcoding](doc/plugins/grpc-transcode.md): Supports protocol transcoding so that clients can access your gRPC API by using HTTP/JSON. diff --git a/apisix/cli/ngx_tpl.lua b/apisix/cli/ngx_tpl.lua index 9f496d22ca831..40a7a7932567d 100644 --- a/apisix/cli/ngx_tpl.lua +++ b/apisix/cli/ngx_tpl.lua @@ -240,6 +240,18 @@ http { keepalive 320; } + {% if enabled_plugins["dubbo-proxy"] then %} + upstream apisix_dubbo_backend { + server 0.0.0.1; + balancer_by_lua_block { + apisix.http_balancer_phase() + } + + multi {* dubbo_upstream_multiplex_count *}; + keepalive 320; + } + {% end %} + init_by_lua_block { require "resty.core" apisix = require("apisix") @@ -398,6 +410,13 @@ http { set $upstream_scheme 'http'; set $upstream_host $host; set $upstream_uri ''; + set $ctx_ref ''; + + {% if enabled_plugins["dubbo-proxy"] then %} + set $dubbo_service_name ''; + set $dubbo_service_version ''; + set $dubbo_method ''; + {% end %} {% if with_module_status then %} location = /apisix/nginx_status { @@ -556,6 +575,30 @@ http { } } + {% if enabled_plugins["dubbo-proxy"] then %} + location @dubbo_pass { + access_by_lua_block { + apisix.dubbo_access_phase() + } + + dubbo_pass_all_headers on; + dubbo_pass_body on; + dubbo_pass $dubbo_service_name $dubbo_service_version $dubbo_method apisix_dubbo_backend; + + header_filter_by_lua_block { + apisix.http_header_filter_phase() + } + + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } + + log_by_lua_block { + apisix.http_log_phase() + } + } + {% end %} + {% if enabled_plugins["proxy-mirror"] then %} location = /proxy_mirror { internal; diff --git a/apisix/cli/ops.lua b/apisix/cli/ops.lua index a0852e5eedb71..09efc6d153764 100644 --- a/apisix/cli/ops.lua +++ b/apisix/cli/ops.lua @@ -235,6 +235,14 @@ Please modify "admin_key" in conf/config.yaml . yaml_conf.apisix.ssl.ssl_cert = "cert/ssl_PLACE_HOLDER.crt" yaml_conf.apisix.ssl.ssl_cert_key = "cert/ssl_PLACE_HOLDER.key" + local dubbo_upstream_multiplex_count = 32 + if yaml_conf.plugin_attr and yaml_conf.plugin_attr["dubbo-proxy"] then + local dubbo_conf = yaml_conf.plugin_attr["dubbo-proxy"] + if tonumber(dubbo_conf.upstream_multiplex_count) >= 1 then + dubbo_upstream_multiplex_count = dubbo_conf.upstream_multiplex_count + end + end + -- Using template.render local sys_conf = { lua_path = env.pkg_path_org, @@ -244,6 +252,7 @@ Please modify "admin_key" in conf/config.yaml . with_module_status = with_module_status, error_log = {level = "warn"}, enabled_plugins = enabled_plugins, + dubbo_upstream_multiplex_count = dubbo_upstream_multiplex_count, } if not yaml_conf.apisix then diff --git a/apisix/init.lua b/apisix/init.lua index bdbde96aef0de..57c891e54db48 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -24,6 +24,7 @@ local admin_init = require("apisix.admin.init") local get_var = require("resty.ngxvar").fetch local router = require("apisix.router") local set_upstream = require("apisix.upstream").set_by_route +local ctxdump = require("resty.ctxdump") local ipmatcher = require("resty.ipmatcher") local ngx = ngx local get_method = ngx.req.get_method @@ -34,6 +35,7 @@ local ipairs = ipairs local tostring = tostring local type = type local ngx_now = ngx.now +local ngx_var = ngx.var local str_byte = string.byte local str_sub = string.sub local tonumber = tonumber @@ -543,6 +545,16 @@ function _M.http_access_phase() end set_upstream_host(api_ctx) + + if api_ctx.dubbo_proxy_enabled then + ngx_var.ctx_ref = ctxdump.stash_ngx_ctx() + return ngx.exec("@dubbo_pass") + end +end + + +function _M.dubbo_access_phase() + ngx.ctx = ctxdump.apply_ngx_ctx(ngx_var.ctx_ref) end diff --git a/apisix/plugins/dubbo-proxy.lua b/apisix/plugins/dubbo-proxy.lua new file mode 100644 index 0000000000000..57a093f062a30 --- /dev/null +++ b/apisix/plugins/dubbo-proxy.lua @@ -0,0 +1,69 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local core = require("apisix.core") +local ngx_var = ngx.var + + +local plugin_name = "dubbo-proxy" + +local schema = { + type = "object", + properties = { + service_name = { + type = "string", + minLength = 1, + }, + service_version = { + type = "string", + pattern = [[^\d+\.\d+\.\d+]], + }, + method = { + type = "string", + minLength = 1, + }, + }, + required = { "service_name", "service_version"}, +} + +local _M = { + version = 0.1, + priority = 507, + name = plugin_name, + schema = schema, +} + + +function _M.check_schema(conf) + return core.schema.check(schema, conf) +end + + +function _M.access(conf, ctx) + ctx.dubbo_proxy_enabled = true + + ngx_var.dubbo_service_name = conf.service_name + ngx_var.dubbo_service_version = conf.service_version + if not conf.method then + -- remove the prefix '/' from $uri + ngx_var.dubbo_method = core.string.sub(ngx_var.uri, 2) + else + ngx_var.dubbo_method = conf.method + end +end + + +return _M diff --git a/conf/config-default.yaml b/conf/config-default.yaml index a4cd24e3b48b9..ed8ec4c074021 100644 --- a/conf/config-default.yaml +++ b/conf/config-default.yaml @@ -215,6 +215,7 @@ plugins: # plugin list (sorted in alphabetical order) - batch-requests - consumer-restriction - cors + # - dubbo-proxy - echo # - error-log-logger # - example-plugin @@ -270,3 +271,5 @@ plugin_attr: server-info: report_interval: 60 # server info report interval (unit: second) report_ttl: 3600 # live time for server info in etcd (unit: second) + dubbo-proxy: + upstream_multiplex_count: 32 diff --git a/doc/plugins/dubbo-proxy.md b/doc/plugins/dubbo-proxy.md new file mode 100644 index 0000000000000..175f3c9b28e6a --- /dev/null +++ b/doc/plugins/dubbo-proxy.md @@ -0,0 +1,131 @@ + + +# Summary + +- [**Name**](#Name) +- [**Requirement**](#Requirement) +- [**Runtime Attributes**](#Runtime-Attributes) +- [**Static Attributes**](#Static-Attributes) +- [**How To Enable**](#How-To-Enable) +- [**Test Plugin**](#Test-Plugin) +- [**Disable Plugin**](#Disable-Plugin) + +## Name + +dubbo-proxy plugin allows you proxy HTTP request to [**dubbo**](http://dubbo.apache.org). + +## Requirement + +If you are using OpenResty, you need to build it with dubbo support, see [How to build](https://github.com/api7/mod_dubbo#how-to-build). + +To make http2dubbo work in APISIX, we enhance the dubbo module based on Tengine's `mod_dubbo`. The modifications are contributed back to Tengine, but they are not included in the latest release version (Tengine-2.3.2) yet. So Tengine itself is unsupported. + +## Runtime Attributes + +| Name | Type | Requirement | Default | Valid | Description | +| ------------ | ------ | ----------- | -------- | ------------ | -------------------------------------------------------------------- | +| service_name | string | required | | | dubbo provider service name| +| service_version | string | required | | | dubbo provider service version| +| method | string | optional | the path of uri | | dubbo provider service method| + +## Static Attributes + +| Name | Type | Requirement | Default | Valid | Description | +| ------------ | ------ | ----------- | -------- | ------------ | -------------------------------------------------------------------- | +| upstream_multiplex_count | number | required | 32 | >= 1 | the maximum number of multiplex requests in an upstream connection | + +## How To Enable + +First of all, enable the dubbo-proxy plugin in the `config.yaml`: + +``` +# Add this in config.yaml +plugins: + - ... # plugin you need + - dubbo-proxy +``` + +Then reload APISIX. + +Here's an example, enable the dubbo-proxy plugin on the specified route: + +```shell +curl http://127.0.0.1:9080/apisix/admin/upstream/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "nodes": { + "127.0.0.1:20880": 1 + }, + "type": "roundrobin" +}' + +curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "methods": ["GET"], + "uris": [ + "/hello" + ], + "plugins": { + "dubbo-proxy": { + "service_name": "org.apache.dubbo.sample.tengine.DemoService", + "service_version": "0.0.0", + "method": "tengineDubbo" + } + }, + "upstream_id": 1 +}' +``` + +## Test Plugin + +You can follow the [Quick Start](https://github.com/alibaba/tengine/tree/master/modules/mod_dubbo#quick-start) example in Tengine and use the configuration above to test it. +They should provide the same result. + +## Disable Plugin + +When you want to disable the dubbo-proxy plugin on a route/service, it is very simple, + you can delete the corresponding json configuration in the plugin configuration, + no need to restart the service, it will take effect immediately: + +```shell +$ curl http://127.0.0.1:2379/v2/keys/apisix/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d value=' +{ + "methods": ["GET"], + "uris": [ + "/hello" + ], + "plugins": { + }, + "upstream_id": 1 + } +}' +``` + +The dubbo-proxy plugin has been disabled now. It works for other plugins. + +If you want to disable dubbo-proxy plugin totally, +you need to comment out in the `config.yaml`: + +```yaml +plugins: + - ... # plugin you need + #- dubbo-proxy +``` + +And then reload APISIX. diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec index 1396c88123e59..ef1a6fc60b3ea 100644 --- a/rockspec/apisix-master-0.rockspec +++ b/rockspec/apisix-master-0.rockspec @@ -31,6 +31,7 @@ description = { } dependencies = { + "lua-resty-ctxdump = 0.1-0", "lua-resty-template = 1.9", "lua-resty-etcd = 1.4.3", "lua-resty-balancer = 0.02rc5", diff --git a/t/APISIX.pm b/t/APISIX.pm index 2634eea45b2af..220745e778ded 100644 --- a/t/APISIX.pm +++ b/t/APISIX.pm @@ -28,6 +28,7 @@ no_root_location(); # avoid generated duplicate 'location /' worker_connections(128); my $apisix_home = $ENV{APISIX_HOME} || cwd(); +my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; sub read_file($) { my $infile = shift; @@ -131,6 +132,50 @@ if ($profile) { } +my $dubbo_upstream = ""; +my $dubbo_location = ""; +my $version = eval { `$nginx_binary -V 2>&1` }; +if ($version =~ m/\/mod_dubbo/) { + $dubbo_upstream = <<_EOC_; + upstream apisix_dubbo_backend { + server 0.0.0.1; + balancer_by_lua_block { + apisix.http_balancer_phase() + } + + multi 1; + keepalive 320; + } + +_EOC_ + + $dubbo_location = <<_EOC_; + location \@dubbo_pass { + access_by_lua_block { + apisix.dubbo_access_phase() + } + + dubbo_pass_all_headers on; + dubbo_pass_body on; + dubbo_pass \$dubbo_service_name \$dubbo_service_version \$dubbo_method apisix_dubbo_backend; + + header_filter_by_lua_block { + apisix.http_header_filter_phase() + } + + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } + + log_by_lua_block { + apisix.http_log_phase() + } + } + +_EOC_ +} + + add_block_preprocessor(sub { my ($block) = @_; my $wait_etcd_sync = $block->wait_etcd_sync // 0.1; @@ -269,6 +314,8 @@ _EOC_ keepalive 32; } + $dubbo_upstream + init_by_lua_block { $init_by_lua_block } @@ -362,6 +409,10 @@ _EOC_ set \$upstream_scheme 'http'; set \$upstream_host \$host; set \$upstream_uri ''; + set \$ctx_ref ''; + set \$dubbo_service_name ''; + set \$dubbo_service_version ''; + set \$dubbo_method ''; location = /apisix/nginx_status { allow 127.0.0.0/24; @@ -459,6 +510,8 @@ _EOC_ } } + $dubbo_location + location = /proxy_mirror { internal; diff --git a/t/lib/dubbo-backend/dubbo-backend-interface/pom.xml b/t/lib/dubbo-backend/dubbo-backend-interface/pom.xml new file mode 100644 index 0000000000000..3087def77ca1c --- /dev/null +++ b/t/lib/dubbo-backend/dubbo-backend-interface/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + org.apache.dubbo.backend + dubbo-backend + 1.0.0-SNAPSHOT + + org.apache.dubbo.backend + dubbo-backend-interface + 1.0.0-SNAPSHOT + jar + ${project.artifactId} + + + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + diff --git a/t/lib/dubbo-backend/dubbo-backend-interface/src/main/java/org/apache/dubbo/backend/DemoService.java b/t/lib/dubbo-backend/dubbo-backend-interface/src/main/java/org/apache/dubbo/backend/DemoService.java new file mode 100644 index 0000000000000..5e319f5559588 --- /dev/null +++ b/t/lib/dubbo-backend/dubbo-backend-interface/src/main/java/org/apache/dubbo/backend/DemoService.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.backend; + +import java.util.Map; + +public interface DemoService {; + + /** + * standard samples tengine dubbo infterace demo + * @param context pass http infos + * @return Map pass to response http + **/ + Map hello(Map context); + + /** + * test for dubbo non-200 response + * @param context pass http infos + * @return Map pass to response http + **/ + Map fail(Map context); + + /** + * test for dubbo response timeout + * @param context pass http infos + * @return Map pass to response http + **/ + Map timeout(Map context); +} diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/pom.xml b/t/lib/dubbo-backend/dubbo-backend-provider/pom.xml new file mode 100644 index 0000000000000..7ceb3a18e338a --- /dev/null +++ b/t/lib/dubbo-backend/dubbo-backend-provider/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + org.apache.dubbo.backend + dubbo-backend + 1.0.0-SNAPSHOT + + org.apache.dubbo.backend + dubbo-backend-provider + 1.0.0-SNAPSHOT + jar + ${project.artifactId} + + + true + 1.7.25 + 2.12.0 + 2.7.7 + + + + + org.apache.dubbo.backend + dubbo-backend-interface + 1.0.0-SNAPSHOT + + + org.apache.dubbo + dubbo + ${dubbo.version} + + + org.apache.httpcomponents + httpclient + 4.5.5 + + + + + dubbo-demo-provider + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.apache.dubbo.backend.provider.Provider + + + + + + com.jolira + onejar-maven-plugin + 1.4.4 + + + + one-jar + + + + + + + diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/DemoServiceImpl.java b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/DemoServiceImpl.java new file mode 100644 index 0000000000000..260784cccbcbb --- /dev/null +++ b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/DemoServiceImpl.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.backend.provider; + +import org.apache.dubbo.backend.DemoService; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.lang.InterruptedException; + +public class DemoServiceImpl implements DemoService { + @Override + public Map hello(Map context) { + Map ret = new HashMap(); + ret.put("body", "dubbo success\n"); + ret.put("status", "200"); + + for (Map.Entry entry : context.entrySet()) { + System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); + if (entry.getKey().startsWith("extra-arg")) { + ret.put("Got-" + entry.getKey(), entry.getValue()); + } + } + + return ret; + } + + @Override + public Map fail(Map context) { + Map ret = new HashMap(); + ret.put("body", "dubbo fail\n"); + ret.put("status", "503"); + return ret; + } + + @Override + public Map timeout(Map context) { + Map ret = new HashMap(); + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch (InterruptedException ex) {} + ret.put("body", "dubbo fail\n"); + ret.put("status", "503"); + return ret; + } +} diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/Provider.java b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/Provider.java new file mode 100644 index 0000000000000..28607375b0065 --- /dev/null +++ b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/Provider.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.backend.provider; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import java.util.concurrent.TimeUnit; +import java.lang.InterruptedException; + +public class Provider { + + /** + * To get ipv6 address to work, add + * System.setProperty("java.net.preferIPv6Addresses", "true"); + * before running your application. + */ + public static void main(String[] args) throws Exception { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"}); + context.start(); + while (true) { + try { + TimeUnit.MINUTES.sleep(1); + } catch (InterruptedException ex) {} + } + } +} diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml new file mode 100644 index 0000000000000..14b5afd7613b9 --- /dev/null +++ b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties new file mode 100644 index 0000000000000..258fd3bf2628c --- /dev/null +++ b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +dubbo.application.qos.enable=false diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties new file mode 100644 index 0000000000000..2f4f4addf137f --- /dev/null +++ b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +###set log levels### +log4j.rootLogger=info, stdout +###output to the console### +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n diff --git a/t/lib/dubbo-backend/pom.xml b/t/lib/dubbo-backend/pom.xml new file mode 100644 index 0000000000000..ed4bf0a755cd1 --- /dev/null +++ b/t/lib/dubbo-backend/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + org.apache.dubbo.backend + dubbo-backend + 1.0.0-SNAPSHOT + pom + ${project.artifactId} + A dubbo backend for test based on dubbo-samples-tengine + + true + 2.7.7 + + + dubbo-backend-interface + dubbo-backend-provider + + + + + + + org.springframework.boot + spring-boot-dependencies + 2.1.5.RELEASE + pom + import + + + org.apache.dubbo + dubbo-dependencies-bom + ${dubbo.version} + pom + import + + + org.apache.dubbo + dubbo + ${dubbo.version} + + + org.springframework + spring + + + javax.servlet + servlet-api + + + log4j + log4j + + + + + + + + + org.springframework.boot + spring-boot-starter + 2.1.5.RELEASE + + + org.apache.dubbo + dubbo-spring-boot-starter + 2.7.1 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + diff --git a/t/plugin/dubbo-proxy/route.t b/t/plugin/dubbo-proxy/route.t new file mode 100644 index 0000000000000..8e456104e5bd2 --- /dev/null +++ b/t/plugin/dubbo-proxy/route.t @@ -0,0 +1,314 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX; + +my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $version = eval { `$nginx_binary -V 2>&1` }; + +if ($version !~ m/\/mod_dubbo/) { + plan(skip_all => "mod_dubbo not installed"); +} else { + plan('no_plan'); +} + +repeat_each(1); +log_level('info'); +no_root_location(); +no_shuffle(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!defined $block->disable_dubbo) { + my $extra_yaml_config = <<_EOC_; +plugins: + - dubbo-proxy + - response-rewrite + - proxy-rewrite +_EOC_ + + $block->set_value("extra_yaml_config", $extra_yaml_config); + } + + if (!$block->yaml_config) { + my $yaml_config = <<_EOC_; +apisix: + node_listen: 1984 + config_center: yaml + enable_admin: false +_EOC_ + + $block->set_value("yaml_config", $yaml_config); + } + + if ($block->apisix_yaml) { + my $upstream = <<_EOC_; +upstreams: + - nodes: + "127.0.0.1:20880": 1 + type: roundrobin + id: 1 +#END +_EOC_ + + $block->set_value("apisix_yaml", $block->apisix_yaml . $upstream); + } + + if (!$block->request) { + $block->set_value("request", "GET /hello"); + } + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } +}); + +run_tests(); + +__DATA__ + +=== TEST 1: ignore route's dubbo configuration if dubbo is disable globally +--- disable_dubbo +--- apisix_yaml +routes: + - + uri: /hello + plugins: + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + method: hello + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +--- response_body +hello world + + + +=== TEST 2: check schema +--- apisix_yaml +routes: + - + uri: /hello + plugins: + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + method: hello + upstream_id: 1 +--- error_log +property "service_version" is required +--- error_code: 404 + + + +=== TEST 3: sanity +--- apisix_yaml +routes: + - + uri: /hello + plugins: + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + method: hello + upstream_id: 1 +--- more_headers +Extra-Arg-K: V +--- response_headers +Got-extra-arg-k: V +--- response_body +dubbo success + + + +=== TEST 4: enabled in service +--- apisix_yaml +routes: + - uri: /hello + service_id: 1 + +services: + - + plugins: + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + method: hello + id: 1 + upstream_id: 1 +--- response_body +dubbo success + + + +=== TEST 5: work with comsumer +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: true + admin_key: null +plugins: + - key-auth + - dubbo-proxy +--- config + location /t { + content_by_lua_block { + local json = require("toolkit.json") + local t = require("lib.test_admin").test + + local code, message = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username":"jack", + "plugins": { + "key-auth": { + "key": "jack" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(message) + return + end + + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "upstream":{ + "nodes": { + "127.0.0.1:20880": 1 + }, + "type": "roundrobin" + }, + "plugins": { + "dubbo-proxy": { + "service_name": "org.apache.dubbo.backend.DemoService", + "service_version": "0.0.0", + "method": "hello" + }, + "key-auth": {} + }, + "uris": ["/hello"] + }]] + ) + + if code >= 300 then + ngx.status = code + end + + ngx.say(message) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: blocked +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: true + admin_key: null +plugins: + - key-auth + - dubbo-proxy +--- error_code: 401 + + + +=== TEST 7: passed +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: true + admin_key: null +plugins: + - key-auth + - dubbo-proxy +--- more_headers +apikey: jack +--- response_body +dubbo success + + + +=== TEST 8: rewrite response +--- apisix_yaml +routes: + - + uri: /hello + plugins: + response-rewrite: + headers: + fruit: banana + body: "hello world\n" + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + method: hello + upstream_id: 1 + +--- response_body +hello world +--- response_headers +fruit: banana + + + +=== TEST 9: rewrite request +--- apisix_yaml +routes: + - + uri: /hello + plugins: + proxy-rewrite: + headers: + extra-arg-fruit: banana + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + method: hello + upstream_id: 1 + +--- response_body +dubbo success +--- response_headers +Got-extra-arg-fruit: banana + + + +=== TEST 10: use uri as default method +--- apisix_yaml +routes: + - + uri: /hello + plugins: + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + upstream_id: 1 + +--- response_body +dubbo success diff --git a/t/plugin/dubbo-proxy/upstream.t b/t/plugin/dubbo-proxy/upstream.t new file mode 100644 index 0000000000000..27590a58038f8 --- /dev/null +++ b/t/plugin/dubbo-proxy/upstream.t @@ -0,0 +1,137 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX; + +my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $version = eval { `$nginx_binary -V 2>&1` }; + +if ($version !~ m/\/mod_dubbo/) { + plan(skip_all => "mod_dubbo not installed"); +} else { + plan('no_plan'); +} + +repeat_each(1); +log_level('info'); +no_root_location(); +no_shuffle(); +worker_connections(256); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /hello"); + } + + if (!defined $block->disable_dubbo) { + my $extra_yaml_config = <<_EOC_; +plugins: + - dubbo-proxy +_EOC_ + + $block->set_value("extra_yaml_config", $extra_yaml_config); + } + + my $yaml_config = $block->yaml_config // <<_EOC_; +apisix: + node_listen: 1984 + config_center: yaml + enable_admin: false +_EOC_ + + $block->set_value("yaml_config", $yaml_config); +}); + +run_tests(); + +__DATA__ + +=== TEST 1: retry +--- apisix_yaml +upstreams: + - nodes: + - host: 127.0.0.1 + port: 20881 + weight: 1 + - host: 127.0.0.1 + port: 20880 + weight: 1 + type: roundrobin + id: 1 +routes: + - + uri: /hello + plugins: + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + method: hello + upstream_id: 1 +#END +--- response_body +dubbo success + + + +=== TEST 2: upstream return error +--- apisix_yaml +routes: + - + uri: /hello + plugins: + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + method: fail + upstream_id: 1 +upstreams: + - nodes: + "127.0.0.1:20880": 1 + type: roundrobin + id: 1 +#END +--- response_body +dubbo fail +--- error_code: 503 + + + +=== TEST 3: upstream timeout +--- apisix_yaml +routes: + - + uri: /hello + plugins: + dubbo-proxy: + service_name: org.apache.dubbo.backend.DemoService + service_version: 0.0.0 + method: timeout + upstream_id: 1 +upstreams: + - nodes: + "127.0.0.1:20880": 1 + type: roundrobin + timeout: + connect: 0.1 + read: 0.1 + send: 0.1 + id: 1 +#END +--- error_log +upstream timed out +--- error_code: 504 diff --git a/utils/linux-install-openresty.sh b/utils/linux-install-openresty.sh index 0558efd274905..88c6f9f90346e 100755 --- a/utils/linux-install-openresty.sh +++ b/utils/linux-install-openresty.sh @@ -15,6 +15,58 @@ # See the License for the specific language governing permissions and # limitations under the License. # +set -euo pipefail + +if [ "$OPENRESTY_VERSION" == "source" ]; then + cd .. + + wget https://openresty.org/download/openresty-1.19.3.1.tar.gz + tar -zxvpf openresty-1.19.3.1.tar.gz + + git clone --depth=1 https://github.com/api7/ngx_multi_upstream_module.git + cd ngx_multi_upstream_module || exit 1 + ./patch.sh ../openresty-1.19.3.1 + + git clone --depth=1 https://github.com/api7/mod_dubbo.git ../mod_dubbo + + cd ../openresty-1.19.3.1 || exit 1 + ./configure --prefix="/usr/local/openresty-debug" \ + --add-module=../mod_dubbo --add-module=../ngx_multi_upstream_module \ + --with-debug \ + --with-poll_module \ + --with-pcre-jit \ + --without-http_rds_json_module \ + --without-http_rds_csv_module \ + --without-lua_rds_parser \ + --with-stream \ + --with-stream_ssl_module \ + --with-stream_ssl_preread_module \ + --with-http_v2_module \ + --without-mail_pop3_module \ + --without-mail_imap_module \ + --without-mail_smtp_module \ + --with-http_stub_status_module \ + --with-http_realip_module \ + --with-http_addition_module \ + --with-http_auth_request_module \ + --with-http_secure_link_module \ + --with-http_random_index_module \ + --with-http_gzip_static_module \ + --with-http_sub_module \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_mp4_module \ + --with-http_gunzip_module \ + --with-threads \ + --with-compat \ + --with-luajit-xcflags='-DLUAJIT_NUMMODE=2 -DLUAJIT_ENABLE_LUA52COMPAT' + make + sudo make install + + sudo apt-get install lua5.1 liblua5.1-0-dev + + exit 0 +fi wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add - sudo apt-get -y update --fix-missing