Spring Boot 和 API 网关 Kong 微服务初探

最近接到一个需求,将API网关Kong引入Spring Boot微服务体系中。为此我做了一个小Demo,放到了Github(Service A、Service B)上。这个示例非常简单,服务A调用服务B,也就是简化的微服务模型,但是在实际部署的时候会有区别。

Kong API网关在这里有两个作用,一个是用于外部访问内部接口(南北向),另外一个是内部接口之间的访问(东西向)。在实际生产部署中,是需要部署两套Kong集群的,但是在开发演示的时候,可以用一个Kong节点替代。

一、Kong简介

Kong是一款开源API网关,基于OpenResty技术栈,其核心是Nginx。API网关作为微服务技术体系中的一个核心组件,在这里我就不过多阐述。这里简单提到Kong的几个优点:

1、高性能、低延迟(Nginx);

2、采用插件结构,社区版和企业版提供大量成熟的插件,也支持自定义插件;

3、RESTful接口管理,非常易于使用;

4、既可以用于南北向流量,也可以用于东西向流量——统一技术栈。

Spring Boot 和 API 网关 Kong 微服务初探_第1张图片
image

二、非容器化

非容器化的方案非常简单,只需要在Service A的Controller的REST请求地址调整为Kong配置的Service B地址即可。

export HOST_IP_ADDR=127.0.0.1

curl -i -X POST http://localhost:8001/services/ \
  -d"name=kong-springboot2" \
  -d"protocol=http" \
  -d"host=${HOST_IP_ADDR}" \
  -d"port=8090"

curl -i -X POST http://localhost:8001/services/kong-springboot2/routes/ \
  -d"protocols[]=http" \
  -d"paths[]=/api/order" \
  -d"strip_path=false"

这里我通过Kong的REST接口配置了Service B的Service、Route,这个时候我就可以通过curl http://localhost:8000/api/order访问Service B,也就是Service A的请求Service B的路径。

在配置Service A时,我使用了Kong内置的权限认证插件Basic Auth,通过这个插件,我们可以使用账号密码来保护某些服务接口。

PRIVATE_ROUTE_ID=$(curl -s -X POST http://localhost:8001/services/kong-springboot/routes/ \
  -H'Content-Type: application/json' \
  -d'{ "protocols": ["http"], "hosts": ["kong-springboot"], "paths": ["/api/private"], "strip_path": false }' | jq -r .id)

curl -X POST http://localhost:8001/routes/${PRIVATE_ROUTE_ID}/plugins \
  -d"name=basic-auth" \
  -d"config.hide_credentials=true"

curl -X POST http://localhost:8001/consumers \
  -d"username=user_polaristech"

curl -X POST http://localhost:8001/consumers/user_polaristech/basic-auth \
  -d"username=polaristech" \
  -d"password=123"

curl -i -u polaristech:123 http://localhost:8000/api/private -H'Host: kong-springboot'

通过Basic Auth插件,未受保护的/api/public可以直接访问,但是/api/private无法访问,返回Unauthorized错误。

三、容器化

将Service A和Service B打包成Docker镜像的具体操作在这里不再累述,可以参考项目中的Dockerfile,直接运行build_docker.sh脚本即可,我已经提前生成镜像传到Docker Hub:Service A Docker Image和Service B Docker Image,直接拉取到本地即可部署。

在容器化方案中,我们将Service A和Service B打包成Docker镜像,准备部署到Kubernetes。在这里,我们知道Kubernetes的services和pods的ip都是只用于内部的网络请求,因此外部请求需要一个Ingress Controller。在这里,我踩了一个坑:如果通过Ingress访问Kong的REST管理接口,更新的信息是会被清除的,也就是在这种情况下,目前需要通过Kubernetes的yaml配置文件去写Kong的配置信息。

在Service A请求Service B的地址中,我使用了Kubernetes内置的服务发现DNS,相关代码如下:

@RestController
@RequestMapping("/api")
public class ApplicationController {

    @GetMapping("/public")
    public ResponseEntity getPublicString() {
        return ResponseEntity.ok("It is public.\n");
    }

    @GetMapping("/private")
    public ResponseEntity getPrivateString(HttpServletRequest request) {
        String username = request.getHeader("X-Credential-Username");

        RestTemplate rest = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();

        System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
        headers.add("Host", "kong.springboot2");

        return rest.exchange("http://kong-proxy.kong-api.svc.cluster.local:80/api/order", HttpMethod.GET, new HttpEntity(headers), Order.class);
    }
}

部署到Kubernetes/OpenShift的相关配置文件在Service A项目下的config目录下,具体操作步骤如下(基于OpenShift):

oc cluster up
oc login -u system:admin
oc new-project kong-api
oc create —namespace kong-api -f postgres-openshift.yaml
oc create —namespace kong-api -f kong-resources-openshift.yaml
kubectl create -f kongspringboot-application-openshift.yaml
kubectl create -f kongspringboot-ingress-openshift.yaml
kubectl create -f kongspringboot2-application-openshift.yaml
kubectl create -f kongspringboot2-ingress-openshift.yaml
kubectl create -f kongadminapi-ingress-openshift.yaml

请求服务接口的命令如下:

oc get svc # 获取kong-proxy的IP和端口
http ${PROXY_IP}:${HTTP_PORT} Host:kong-admin.api # 通过Ingress访问Kong Admin API
http ${PROXY_IP}:${HTTP_PORT}/api/public Host:kong.springboot # 未调用服务B路径
http ${PROXY_IP}:${HTTP_PORT}/api/private Host:kong.springboot # 调用服务B路径

你可能感兴趣的:(Spring Boot 和 API 网关 Kong 微服务初探)