1. 程式人生 > >公眾號支付多人協同開發,請求分發怎麼做

公眾號支付多人協同開發,請求分發怎麼做

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來進行流量分發。


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的原生代碼裡。也就是他們自己開發自己的程式碼,自己除錯自己的程式碼,但是是用的同一個支付域名。這也等同於灰度釋出系統的理念吧。