公众号支付多人协同开发,请求分发怎么做

1.为什么需要将流量分发

在做微信公众号支付的时候,所有的支付操作都必须在设置的支付授权目录下进行,而且支付授权目录只能设置一个。如果多个开发人员一起做开发的话,不可能每个人都去开通一个公众号去绑定支付授权目录去调试,只能是多个开发人员共用一个公众号,共用一个支付授权目录。但是如果多个人同时开发的话,为了不互相影响,自己调试自己的代码,需要将请求进行分发,根据一些策略分发到对应的开发人员那里。

2.具体的解决思路

把每次的请求做一个处理,添加一个标识,然后到了分发层,根据这个标识去做分发。

3.实现

(1)对每次请求拦截并处理,添加Header

首先,安装Charles进行http抓包,修改http请求,添加Header,Charles的安装与使用说明(参考这篇文章):
https://blog.csdn.net/kaluman/article/details/78320321
安装好以后需要添加一个固定的Header:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
【我们可以添加一个channel:127.0.0.1:808的header,到时候可以直接用channel作为代理的路径】

(2)安装OpenResty,写lua脚本,通过获取Header中的channel来进行流量分发。

安装OpenReaty,参考文章:https://www.cnblogs.com/zdz8207/p/Nginx-Lua-OpenResty.html
安装完后,需要添加http的库:
#添加http的库:
cd /usr/servers/lualib/resty/
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua
#进入lua的配置文件:
vim /www/server/nginx/conf/lua1.conf
修改成下面的代码:


server
{
    listen 80;
    server_name  微信公众号支付的域名;
    access_log /home/lua/access.hongsou.log;
    error_log /home/lua/error.hongsou.log;

   #lua测试
   location /lua {
        default_type 'text/html';
        content_by_lua 'ngx.say("hlo world")';
    }

  

   location / {
        #lua_code_cache off; 
         resolver 8.8.8.8;
         set $backend '';
         rewrite_by_lua_file /usr/servers/lua/hongsou.lua;
         proxy_pass http://$backend;
 }
}

【不加resolver的话可能会报错, 无法解析,加一个8.8.8.8就可以搞定了。
lua_code_cache 是开发环境的配置, 不缓存lua代码, 修改完lua直接生效, 不然每次要重启nginx, 上生产环境要关掉, 严重影响性能。】

#修改lua的文件
mkdir /usr/servers/lua
vim /usr/servers/lua/hongsou.lua

--线上测试服务器的一套代码(云服务器上一套默认的代码,可以供测试人员测试)
--ngx.var.backend = 'ip:port'
--分发到不同开发人员本地的代码
--ngx.var.backend = '127.0.0.1:808'
local basePath='';
local redirectPath=nil;

local headers_tab = ngx.req.get_headers()
for k, v in pairs(headers_tab) do
    print(k..":"..v)
   if("channel"==k)
   then
        redirectPath = v
   end
end

if redirectPath ~=nil
then
        ngx.var.backend = redirectPath
else
        ngx.var.backend = 'ip:port'
end

现在,在云服务器上可以做转发了,但是为了方便测试人员,在本地的电脑上做项目的调试,还需要将云服务器上的请求内网穿透到本地的电脑上,本地的电脑再运行支付的项目。这样就实现了我们的需求。

(3)将云服务器上的请求内网穿透到自己本地的电脑上

我们的添加了Header请求头(channel:127.0.0.1:808),在上面的lua脚本中判断如果有channel的请求头,就会将这个channel请求头的value值(也就是127.0.0.1:808)作为转发的路径,这样请求就会转发到127.0.0.1:808,
这时候可以监听这个端口,再做内网穿透处理。
在码云上找到一个java写的内网穿透项目,地址:
https://gitee.com/wmao/javanet
微信公众支付开发,我们公司是一个页面,但是用这个开源项目始终无法显示页面,发现是在这里:
在这里插入图片描述

package net.bcxuexi.server.stream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import net.bcxuexi.config.Config;
import net.bcxuexi.server.model.SocketModel;
import net.bcxuexi.tools.MyLog;
/**
 * socket输出流
 */
public class WriteStream extends Thread {
	private SocketModel socketModel;
	private boolean stopWrite = false;
	// 数据阻塞队列,使用LinkedBlockingQueue 大小默认为Integer.MAX_VALUE
	private BlockingQueue<StreamData> blockingQueue;

	public WriteStream(SocketModel socketModel) {
		this.socketModel = socketModel;
		blockingQueue = new LinkedBlockingQueue<StreamData>();
	}

	@Override
	public void run() {
		try {
			Socket socket = socketModel.getSocket();
			while (!stopWrite) {
				if (socket == null || socket.isClosed()) {
					stopWrite = true;
				}
				String socketType = socketModel.getType();
				StreamData streamData = null;// 待发送数据

				try {
					streamData = take();// 从阻塞队列中取数据,可能会被阻塞
				} catch (InterruptedException e) {
					// 没有取到数据
				}

				if (streamData != null) {
					// 数据发送
					DataOutputStream out = new DataOutputStream(
							socket.getOutputStream());
					if(Config.debug){
						String msg = new String(streamData.data, 0, streamData.total);
						MyLog.info("向客户端发送数据connId="+socketModel.getConnId()+";数据:"+msg);
					}
					out.write(streamData.data, 0, streamData.total);
				}
				try {
					sleep(80);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭socket
			socketModel.closeSocket("writestream结束,关闭socket服务."+socketModel.toString());
		}
	}

	/**
	 * 添加数据。可能发生阻断。 如果队列已满则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续
	 * 
	 * @param data
	 * @throws InterruptedException
	 *             中断异常
	 */
	public void addData(StreamData data) throws InterruptedException {
		blockingQueue.put(data);
	}

	/**
	 * 取走BlockingQueue里排在首位的对象。
	 * 若BlockingQueue为空,阻断调用此方法的线程,直到BlockingQueue有新的数据被加入
	 * 
	 * @return
	 * @throws InterruptedException
	 *             中断异常
	 */
	protected StreamData take() throws InterruptedException {
		return blockingQueue.take();
	}

	public void removeData(StreamData data) {
		blockingQueue.remove(data);
	}
	/**
	 * 是否在处理proxyConnId的数据
	 * @param proxyConnId
	 */
	public boolean hasProxyConnId(String proxyConnId){
		for (Iterator iterator = blockingQueue.iterator(); iterator.hasNext();) {
			StreamData streamData = (StreamData) iterator.next();
			String pConnId = streamData.getProxyConnId();
			if(proxyConnId.equals(pConnId)){
				return true;
			}
		}
		return false;
	}
	
	public int getSize(){
		return blockingQueue.size();
	}

	public void stopWrite() {
		this.stopWrite = true;
	}
}

问题就在这个类里,我要想显示页面,这个socket必须关闭,不然数据写不出去。
在这里插入图片描述
所以,我就改造了一下,因为数据都是http请求数据,数据的开头肯定是“HTTP” ,可以根据这个来跳出while循环,然后关闭socket。然后页面就神奇的显示了。下面是修改的代码:

package net.bcxuexi.server.stream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import net.bcxuexi.config.Config;
import net.bcxuexi.server.model.SocketModel;
import net.bcxuexi.tools.MyLog;
/**
 * socket输出流
 */
public class WriteStream extends Thread {
	private SocketModel socketModel;
	private boolean stopWrite = false;
	// 数据阻塞队列,使用LinkedBlockingQueue 大小默认为Integer.MAX_VALUE
	private BlockingQueue<StreamData> blockingQueue;

	public WriteStream(SocketModel socketModel) {
		this.socketModel = socketModel;
		blockingQueue = new LinkedBlockingQueue<StreamData>();
	}
	@Override
	public void run() {
		Socket socket=null;
		DataOutputStream out=null;
		try {
			 socket = socketModel.getSocket();
			while (!stopWrite) {
				if (socket == null || socket.isClosed()) {
					stopWrite = true;
				}
				String socketType = socketModel.getType();
				//数据类型,是控制型数据还是通讯型数据   control   data
				System.out.println("数据类型:"+socketType);				
				StreamData streamData = null;// 待发送数据
				try {
					streamData = take();// 从阻塞队列中取数据,可能会被阻塞
				} catch (InterruptedException e) {
					// 没有取到数据
				}
				if (streamData != null) {
					// 数据发送
					 out = new DataOutputStream(
							socket.getOutputStream());
					if(Config.debug){
						String msg = new String(streamData.data, 0, streamData.total);
						MyLog.info("向客户端发送数据connId="+socketModel.getConnId()+";数据:"+msg);
					}					
//==============================梁臣		 begin=================================================
					String liangChenData=null;
					try {
						liangChenData=new String(streamData.data,"UTF-8");
					} catch (UnsupportedEncodingException e1) {
						e1.printStackTrace();
					}
					System.out.println("------将要发送给浏览器的数据-------:"+liangChenData);					
							
					out.write(streamData.data, 0, streamData.total);
					if(liangChenData.startsWith("HTTP")){
						stopWrite=true;
					}
//==============================梁臣		end==================================================	
				}
				
				try {
					sleep(80);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}											
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {			
			try {
				out = new DataOutputStream(
						socket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
			// 关闭socket
			socketModel.closeSocket1("writestream结束,关闭socket服务."+socketModel.toString(),out,socket);
		}
	}
	/**
	 * 添加数据。可能发生阻断。 如果队列已满则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续
	 * 
	 * @param data
	 * @throws InterruptedException
	 *             中断异常
	 */
	public void addData(StreamData data) throws InterruptedException {
		blockingQueue.put(data);
	}
	/**
	 * 取走BlockingQueue里排在首位的对象。
	 * 若BlockingQueue为空,阻断调用此方法的线程,直到BlockingQueue有新的数据被加入
	 * 
	 * @return
	 * @throws InterruptedException
	 *             中断异常
	 */
	protected StreamData take() throws InterruptedException {
		return blockingQueue.take();
	}

	public void removeData(StreamData data) {
		blockingQueue.remove(data);
	}
	/**
	 * 是否在处理proxyConnId的数据
	 * @param proxyConnId
	 */
	public boolean hasProxyConnId(String proxyConnId){
		for (Iterator iterator = blockingQueue.iterator(); iterator.hasNext();) {
			StreamData streamData = (StreamData) iterator.next();
			String pConnId = streamData.getProxyConnId();
			if(proxyConnId.equals(pConnId)){
				return true;
			}
		}
		return false;
	}
	
	public int getSize(){
		return blockingQueue.size();
	}

	public void stopWrite() {
		this.stopWrite = true;
	}
}

这个也算是这个开源项目的bug。修复了这个bug,这个内网穿透的项目就能用了。
然后,监听808端口,把请求转发到本地电脑里运行的电脑里了。
这个内网穿透的工具,我已经把它改造成springBoot版本的了,百度网盘地址:
链接:https://pan.baidu.com/s/1wHQ-szg-mZgWnYcMSaBckQ
提取码:mn2g

至此,微信公众号支付的多人协同开发就可以实现了,程序员A可以在抓包工具里添加Header(channel:127.0.0.1:808) ,lua的流量分发脚本会将程序员A的发起的请求转发到支付域名绑定的云服务器的808端口,内网穿透监听808端口,然后把请求转发到程序员A的本地代码里。同理,程序员B发起的请求会访问到程序员B的本地代码里。也就是他们自己开发自己的代码,自己调试自己的代码,但是是用的同一个支付域名。这也等同于灰度发布系统的理念吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值