新秀网关 Apache-APISIX 初探

2,589 阅读6分钟

一、概述

最近有工作需求需要搞一个网关来给我们的应用做路由、限流等等功能。以前使用的Spring Cloud Zuul做网关,但目前的技术体系如果要用它就比较繁琐点了,所以又看了看其他的,发现Apisix 这货很不错啊。主要是KONG忒复杂了,Apisix很轻量很简单的样子,看起来不错,下面看下和KONG的官方对比。

功能 Apache APISIX KONG
项目归属 Apache 软件基金会 Kong Inc.
技术架构 Nginx + etcd Nginx + postgres
单核 QPS (开启限流和prometheus插件) 18000 1700
平均延迟 0.2 毫秒 2 毫秒
支持 Dubbo 代理
配置回滚
支持生命周期的路由
插件热更新
用户自定义:负载均衡算法、路由
resty <--> gRPC 转码
支持 Tengine 作为运行时
MQTT 协议支持
配置生效时间 事件通知,低于1毫秒更新 定期轮询,5 秒
自带控制台
对接外部身份认证服务
配置中心高可用(HA)
指定时间窗口的限速
支持任何 Nginx 变量做路由条件

嗯~ 看了上面的介绍,感觉这玩意真是溜的飞起来啊,赶紧来试一波。

二、安装

官方那安装文档就不吐槽了,反正真使用起来也是一脸懵逼,下面就根据源码安装来说下。因为是Mac,所以举例也是Mac的。

1、安装openresty 和 etcd

# install OpenResty, etcd and some compilation tools

brew install openresty/brew/openresty etcd luarocks curl git

# start etcd server with v2 protocol
etcd --enable-v2=true &

2、下载APISIX 源码并编译


#下载1.4源码
wget http://www.apache.org/dist/incubator/apisix/1.4/apache-apisix-1.4-incubating-src.tar.gz

tar zxvf apache-apisix-1.4-incubating-src.tar.gz

#进行lua 依赖下载,注意下载的os文件是mac的,如果你部署在linux 最好在linux 执行这个命令
cd apache-apisix-1.4-incubating
make deps

# 上面源码包就打完了,但里面不带dashboard.所以我们还得下载个dashboard进行打包(注意,如果你使用git clone的话,就可以使用git submodule update --init --recursive命令进行dashboard下载了)
这里我直接下载1.4对应的dashboard
wget https://github.com/apache/incubator-apisix-dashboard/archive/329b092dcaa7a505dcdec86c667b6803f5863d94.zip

unzip 329b092dcaa7a505dcdec86c667b6803f5863d94.zip

cd incubator-apisix-dashboard-329b092dcaa7a505dcdec86c667b6803f5863d94/

#然后下载完毕,我们就开始对dashboard进行打包了,这里注意下,你的 Node.js 版本必须在8.12.0 或更高,具体初始化请查看 https://yarnpkg.com/en/docs/install

#现在开始初始化依赖了
yarn install

#在给它编译下
yarn run build:prod

#编译完成后,文件在dist目录下,我们将打好的 dist目录下的内容全部拷贝到apache-apisix-1.4-incubating/dashboard目录下

如图:

3、编辑apisix的配置文件 apisix的配置文件在conf/config.yaml,一般测试不需要改什么,默认的就可以,下面说下需要注意的几个属性

  • allow_admin 代表允许访问dashboard的地址
  • node_listen APISIX监听地址,默认9080,我喜欢给他设置80
  • admin_key 你登录和REST接口需要的
  • etcd host 配置你etcd地址,默认我们本机,上面已经启动了
  • etcd prefix 可以简单理解为哪个库
  • plugins 所有的插件,在你配置时候可以选择

4、启动APISIX

#初始化配置
make init

#出现以下信息表示完成
#./bin/apisix init
#./bin/apisix init_etcd

#这时候会在conf目录下出现nginx.conf 这就是真正的配置

#然后就可以启动了
make run

#出现以下信息代表启动成功
#mkdir -p logs
#mkdir -p /tmp/apisix_cores/
#/usr/local/bin/openresty -p $PWD/ -c $PWD/conf/nginx.conf

现在我们就可以访问配置页面了,http://127.0.0.1:9080/apisix/dashboard

注意:如果你使用http://localhost:9080/apisix/dashboard访问,不好意思,会403,这个就是我们allow_admin的配置,当然你也可以注释掉。 正常访问后,我们就会看到如下的登录页面了

三、使用

1、安装完了,就来测试下吧,登录进来就可以看到整个配置页面
2、我们这里只关心Upstream 和Routes两个菜单
  • Routes 用于配置路由策略
  • Upstream 用于配置转发策略
3、配置Upstream

点击添加,我们来添加一个应用(这个配置是一个基础配置,没有人引用的话没什么意义)

4、配置Routes

点击Routes -》 添加,我们来添加一个路由规则,如图

5、测试

好了,大功告成,下面我们就写个简单的接口测试下。

//自己去搞个工程,下面是controller
@RestController
@RequestMapping("/check")
@CrossOrigin
public class CheckController {

    @RequestMapping("/getIp")
    public String getIp(HttpServletRequest request){
        Map<String,Object> datas = new HashMap<>(16);
        datas.put("OsName", OSUtil.getOsName());
        datas.put("ProcessNo", OSUtil.getProcessNo());
        datas.put("RealIp", OSUtil.getRealIp(request));
        datas.put("HostName", OSUtil.getHostName());

        return JSON.toJSONString(datas);
    }
}

辅助工具类

public class OSUtil {

    private static volatile String OS_NAME;
    private static volatile String HOST_NAME;
    private static volatile List<String> IPV4_LIST;
    private static volatile int PROCESS_NO = 0;

    public static String getOsName() {
        if (OS_NAME == null) {
            OS_NAME = System.getProperty("os.name");
        }
        return OS_NAME;
    }

    public static String getHostName() {
        if (HOST_NAME == null) {
            try {
                InetAddress host = InetAddress.getLocalHost();
                HOST_NAME = host.getHostName();
            } catch (UnknownHostException e) {
                HOST_NAME = "unknown";
            }
        }
        return HOST_NAME;
    }

    /**
     * 获取系统环境变量分隔符
     *
     * @return
     */
    public static String getPathSeparator() {
        return System.getProperty("path.separator");
    }

    public static List<String> getAllIPV4() {
        if (IPV4_LIST == null) {
            IPV4_LIST = new LinkedList<String>();
            try {
                Enumeration<NetworkInterface> interfs = NetworkInterface.getNetworkInterfaces();
                while (interfs.hasMoreElements()) {
                    NetworkInterface networkInterface = interfs.nextElement();
                    Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
                    while (inetAddresses.hasMoreElements()) {
                        InetAddress address = inetAddresses.nextElement();
                        if (address instanceof Inet4Address) {
                            String addressStr = address.getHostAddress();
                            if ("127.0.0.1".equals(addressStr)) {
                                continue;
                            }
                            IPV4_LIST.add(addressStr);
                        }
                    }
                }
            } catch (SocketException e) {

            }
        }
        return IPV4_LIST;
    }

    public static String getRealIp(HttpServletRequest request){
        // 获取客户端ip地址
        String clientIp = request.getHeader("x-forwarded-for");

        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getHeader("Proxy-Client-IP");
        }
        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getHeader("WL-Proxy-Client-IP");
        }
        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getRemoteAddr();
        }
        /*
         * 对于获取到多ip的情况下,找到公网ip.
         */
        String sIP = null;
        if (clientIp != null && !clientIp.contains("unknown") && clientIp.indexOf(",") > 0) {
            String[] ipsz = clientIp.split(",");
            for (String anIpsz : ipsz) {
                if (!isInnerIP(anIpsz.trim())) {
                    sIP = anIpsz.trim();
                    break;
                }
            }
            /*
             * 如果多ip都是内网ip,则取第一个ip.
             */
            if (null == sIP) {
                sIP = ipsz[0].trim();
            }
            clientIp = sIP;
        }
        if (clientIp != null && clientIp.contains("unknown")){
            clientIp =clientIp.replaceAll("unknown,", "");
            clientIp = clientIp.trim();
        }
        if ("".equals(clientIp) || null == clientIp){
            clientIp = "127.0.0.1";
        }
        return clientIp;

    }

    /**
     * 判断IP是否是内网地址
     * @param ipAddress ip地址
     * @return 是否是内网地址
     */
    public static boolean isInnerIP(String ipAddress) {
        boolean isInnerIp;
        long ipNum = getIpNum(ipAddress);
        /**
         私有IP:A类  10.0.0.0-10.255.255.255
         B类  172.16.0.0-172.31.255.255
         C类  192.168.0.0-192.168.255.255
         **/
        long aBegin = getIpNum("10.0.0.0");
        long aEnd = getIpNum("10.255.255.255");

        long bBegin = getIpNum("172.16.0.0");
        long bEnd = getIpNum("172.31.255.255");

        long cBegin = getIpNum("192.168.0.0");
        long cEnd = getIpNum("192.168.255.255");
        isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
                || ipAddress.equals("127.0.0.1");
        return isInnerIp;
    }

    private static boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }

    private static long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Integer.parseInt(ip[0]);
        long b = Integer.parseInt(ip[1]);
        long c = Integer.parseInt(ip[2]);
        long d = Integer.parseInt(ip[3]);

        return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
    }


    public static int getProcessNo() {
        if (PROCESS_NO == 0) {
            try {
                PROCESS_NO = Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
            } catch (Exception e) {
                PROCESS_NO = -1;
            }
        }
        return PROCESS_NO;
    }

}

启动我们的工程,我们工程配置的启动端口是6080,这时我们使用我们的网关去进行转发访问, http://127.0.0.1:9080/check/getIp,返回如下信息:

{"ProcessNo":18944,"OsName":"Mac OS X","HostName":"luqiangdeMacBook-Pro.local","RealIp":"127.0.0.1"}

上面通过访问apisix 进行了请求转发,将/check/getIp 转发到了localhost 6080。

恭喜你,完成初步体验。

本文使用 mdnice 排版