14.OpenResty实现灰度发布

概述


OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。
OpenResty 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。

http://openresty.org/cn/

lua 脚本:https://www.runoob.com/lua/lua-tutorial.html

openresty 与nginx 的区别 openresty 底层基于 nginx 实现扩展功能 封装 能够整合lua脚本 实现更加高级灵活的功能

场景

1.对nginx做额外扩展功能 例如 nginx连接 redis、MySQL 、es;

2.动态路由、限流 等高级灵活配置

3.nginx+lua 实现灰度发布

快速入门

content_by_lua

1.安装成功后,我们就可以使用 openresty 直接输出 html 页面。

content_by_lua是内容处理器,接受请求并输出响应,适用于location、location if。

    server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        location / {
			      default_type text/html;
            content_by_lua '
                ngx.say("<p>mayikt Hello, World!</p>")
            ';
        }

–输出响应内容体;(内容体结束后没有换行符;)

ngx.print(“mayikt”)

–输出响应内容体;(内容体结束后,输出一个换行符;)

ngx.say(“mayikt”)

cotent_by_lua_file

2.cotent_by_lua_file 适应于复杂的 lua 脚本,专门放入一个文件中

    server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
		
        location / {
			      default_type text/html;
            content_by_lua_file lua/mayikt/mayikt01.lua;
        }
}

img

img

img

整合lua常见案例

连接mysql

1.连接mysql 数据库插入一条数据

local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
        ngx.say("failed to instantiate mysql: ", err)
        return
end
db:set_timeout(5000)
local ok, err, errno, sqlstate = db:connect{
        host = "127.0.0.1",
        port = 3306,
        database = "mayikt",
        user = "root",
        password="root",
        max_packet_size = 1024 * 1024
}
if not ok then
        ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate)
        return
end
ngx.say("connected to mysql.");
local  res, err, errno, sqlstate = 
db:query("INSERT INTO `mayikt`.`mayikt_member` (`id`, `name`, `age`) VALUES (null, 'm', '11');")
if not res then
        ngx.say("bad request: ", err, ": ", errno, ": ", sqlstate, ".")
        return
end
local cjson = require "cjson"
ngx.say("result: ", cjson.encode(res))

2.获取请求参数插入到数据库中

local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
        ngx.say("failed to instantiate mysql: ", err)
        return
end
db:set_timeout(5000)
local ok, err, errno, sqlstate = db:connect{
        host = "127.0.0.1",
        port = 3306,
        database = "mayikt",
        user = "root",
        password="root",
        max_packet_size = 1024 * 1024
}
if not ok then
        ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate)
        return
end
ngx.say("connected to mysql.");
local uri_args=ngx.req.get_uri_args()
local name = uri_args["name"]
local age = uri_args["age"]
local insertSql="INSERT INTO `mayikt`.`mayikt_member` (`id`, `name`, `age`) VALUES (null,'"..name.."',"..age..");";
ngx.say(insertSql);
local  res, err, errno, sqlstate = 
db:query(insertSql)
if not res then
        ngx.say("bad request: ", err, ": ", errno, ": ", sqlstate, ".")
        return
end
local cjson = require "cjson"
ngx.say("result: ", cjson.encode(res))

“INSERT INTO mayikt.mayikt_member (id, name, age) VALUES (null,'”…name…“',”…age…“);”;

多级缓存

nginx →接口→Redis→查询

--设置响应头类型
ngx.header.content_type="application/json;charset=utf8"
--获取请求中的参数ID
local uri_args = ngx.req.get_uri_args();
local id = uri_args["id"];

--获取本地缓存 
--加载nginx缓存模块(需要在nginx.conf中定义)
--在nginx.conf中配置lua_shared_dict dis_cache 128m; 缓存模块
local cache_ngx = ngx.shared.dis_cache;
--根据ID 获取本地缓存数据
local adCache = cache_ngx:get('ad_cache_'..id);
ngx.say("adCache: ", adCache)	
if adCache == "" or adCache == nil then
	--引入mysql库
	local mysql = require("resty.mysql");
	--创建mysql对象
	local db, err = mysql:new()
	--连接
	local ok, err, errno, sqlstate = db:connect{
			host = "127.0.0.1",
			port = 3306,
			database = "mayikt",
			user = "root",
			password="root",
			max_packet_size = 1024 * 1024
	}
	--查询db
	local select_sql = "select * from mayikt_member where id="..id  
	local res, err, errno, sqlstate = db:query(select_sql)  
	if not res then  
	   ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
	   return close_db(db)  
	end 
	--输出到返回响应中
	local cjson = require "cjson"
    local responsejson = cjson.encode(res);	
	ngx.say("result: ", responsejson)	
	--关闭连接
	mysql:close()
	--将redis中获取到的数据存入nginx本地缓存
	cache_ngx:set('ad_cache_'..id, responsejson, 10*60); --设置缓存时间10分钟
	local adCache1 = cache_ngx:get('ad_cache_'..id);
	ngx.say(adCache1);	
else
 	--nginx本地缓存中获取到数据直接输出
 	ngx.say("nginx缓存中查询",adCache)
end

nginx.conf 里面配置

     #定义Nginx缓存模块,模块名字叫dis_cache,容量大小128M(与lua脚本的缓存相对应:local cache_ngx = ngx.shared.dis_cache;)
    lua_shared_dict dis_cache 128m;

img

反向代理

ngx.var.backend = "127.0.0.1:8081/"
    server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        location /agent {	
		    set $backend ''; 
            rewrite_by_lua_file   lua/mayikt/agent.lua;
			proxy_pass http://$backend;
        }			
}

灰度发布

先去让一小部分用户测试下 如果没有问题 让所有用户都可以访问。

测试数据----

httpclient

lua代码

local uri_args=ngx.req.get_uri_args()
local user = uri_args["user"]
local  requestBody ="http://127.0.0.1:9001/serviceAddress?user="..user
local http = require("resty.http")
local httpc = http.new()
local resp, err = httpc:request_uri(requestBody ,{
method = "GET"
})
if not resp then
ngx.say("request error :", err)
return
end
ngx.var.backend =resp.body;

接口代码

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.alibaba.nacos.discovery.NacosDiscoveryClient;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 余胜军
 * @ClassName NacosServiceAddress
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
@RestController
public class NacosServiceAddress {
    @Autowired
    private DiscoveryClient discoveryClient;
    private AtomicInteger atomicInteger = new AtomicInteger(1);
    @Value("${mayikt.grayscaleUserConfig}")
    private String[] grayscaleUserConfig;
    @Value("${mayikt.grayscale.version}")
    private String grayscaleVersion;
    @Value("${mayikt.formal.version}")
    private String formalVersion;

    /**
     * 根据服务名称获取接口地址
     *
     * @return
     */
    @RequestMapping("/serviceAddress")
    public String serviceAddress(HttpServletRequest request) {
        // 判断用户是否是灰度用户
        boolean isGrayscale = false;
        String user = request.getParameter("user");
        if (!StringUtils.isEmpty(user)) {
            for (int i = 0; i < grayscaleUserConfig.length; i++) {
                if (grayscaleUserConfig[i].equals(user)) {
                    isGrayscale = true;
                    break;
                }
            }
        }
        List<ServiceInstance> filterInstances = new ArrayList<>();
        // 根据服务名称获取对应的接口地址
        List<ServiceInstance> instances = discoveryClient.getInstances("mayikt-member");

        boolean finalIsGrayscale = isGrayscale;
        instances.forEach((serviceInstance -> {
            String metadataVersion = serviceInstance.getMetadata().get("version");
            //判断是否是灰度发布
            String parameterVersion = finalIsGrayscale ? grayscaleVersion : formalVersion;
            if (parameterVersion.equals(metadataVersion)) {
                filterInstances.add(serviceInstance);
            }
        }));
        int index = atomicInteger.incrementAndGet() % filterInstances.size();
        ServiceInstance serviceInstance = filterInstances.get(index);
        String resultAddres = serviceInstance.getHost() + ":" + serviceInstance.getPort();
        return resultAddres;
    }
}

报错找不到lua

img

拷贝到

D:\path\cloud\openresty-1.19.9.1-win64\openresty\lua\resty

引入 这个两个:

http://static.mayikt.com/http.lua

http:/static.mayikt.com/http_headers.lua

nginx 中需要配置:

        location /grayscale {	
		    set $backend '';
            rewrite_by_lua_file   lua/mayikt/grayscale.lua;
			proxy_pass http://$backend;			
        }		
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌陌龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值