有时我们需要与遗留的应用程序一起工作。遗留的应用程序,很难重写,也很难改变。想象一下,例如,这个应用程序正在发送原始的TCP套接字来与另一个进程进行通信。原始TCP套接字的速度很快,但它们有各种问题,例如,所有的数据都是通过网络以纯文本发送的,而且没有认证(如果我们不实现一个协议的话)。
一个解决方案是使用https连接来代替。我们也可以用一个验证承载器来验证这些请求。例如,我已经用Python和Flask创建了一个简单的http服务器。
import logging
import os
from functools import wraps
from flask import Flask, request, abort
from flask import jsonify
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
app = Flask(__name__)
def authorize_bearer(bearer):
def authorize(f):
@wraps(f)
def decorated_function(*args, **kws):
if 'Authorization' not in request.headers:
abort(401)
data = request.headers['Authorization']
if str.replace(str(data), 'Bearer ', '') != bearer:
abort(401)
return f(*args, **kws)
return decorated_function
return authorize
@app.route('/register', methods=['POST'])
@authorize_bearer(bearer=os.getenv('TOKEN'))
def hello_world():
req_data = request.get_json()
logger.info(req_data)
return jsonify({"status": "OK", "request_data": req_data})
现在我们只需要改变我们的传统应用,使用一个http客户端而不是原始的TCP套接字。但有时这是不可能的。想象一下,例如,如果这个应用程序运行在一个没有https支持的旧操作系统上,或者我们无法在传统的应用程序中找到并编译一个http客户端。
一个可能的解决方案是隔离该应用程序,只改变TCP套接字的目标。我们可以用localhost代替原来的ip地址,并在localhost创建一个代理,监听TCP套接字并将信息发送到HTTP服务器。
我们将在Go中建立这个代理。我们可以用任何语言(Python, C#, Javascript, ...)来做。我在Go中的功夫不是很好(我更喜欢Python),但这并不难,我们可以在Windows、Linux和Mac上建立一个二进制的代理,没有任何问题。然后我们只需要把二进制文件复制到目标主机上就可以了(不需要安装,不需要SDK,什么都不需要。 只需要复制和运行)。
package main
import (
"bufio"
"encoding/json"
"flag"
"log"
"net"
"net/http"
"os"
"strings"
)
func main() {
port, closeConnection, url := parseFlags()
openSocket(*port, *closeConnection, *url, onMessage)
}
func onMessage(url string, buffer string) {
bearer := os.Getenv("TOKEN")
client := &http.Client{}
req, _ := http.NewRequest("POST", url, strings.NewReader(buffer))
req.Header.Add("Authorization", "Bearer "+bearer)
req.Header.Add("content-type", "application/json")
resp, err := client.Do(req)
if err != nil {
log.Println(err)
} else {
if resp.Status == "200" {
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
log.Println(result["status"])
} else {
log.Println("Response status: " + resp.Status)
}
defer resp.Body.Close()
}
}
func parseFlags() (*string, *bool, *string) {
port := flag.String("port", "7777", "port number")
closeConnection := flag.Bool("close", true, "Close connection")
url := flag.String("url", "http://localhost:5000/register", "Destination endpoint")
flag.Parse()
return port, closeConnection, url
}
func openSocket(port string, closeConnection bool, url string, onMessage func(url string, buffer string)) {
PORT := "localhost:" + port
l, err := net.Listen("tcp4", PORT)
log.Printf("Serving %s\n", l.Addr().String())
if err != nil {
log.Fatalln(err)
}
defer l.Close()
for {
c, err := l.Accept()
if err != nil {
log.Fatalln(err)
}
go handleConnection(c, closeConnection, url, onMessage)
}
}
func handleConnection(c net.Conn, closeConnection bool, url string, onMessage func(url string, buffer string)) {
log.Printf("Accepted connection from %s\n", c.RemoteAddr().String())
for {
ip, port, err := net.SplitHostPort(c.RemoteAddr().String())
netData, err := bufio.NewReader(c).ReadString('\n')
if err != nil {
log.Println(err)
}
message := map[string]interface{}{
"body": strings.TrimSpace(netData),
"ipFrom": ip,
"port": port,
}
log.Printf("Making request with %s\n", message)
bytesRepresentation, err := json.Marshal(message)
if err != nil {
log.Println(err)
} else {
//buffer := bytes.NewBuffer(bytesRepresentation)
onMessage(url, string(bytesRepresentation))
}
if closeConnection {
c.Close()
return
}
}
c.Close()
这就是全部。我们可以在几乎不改变代码的情况下升级我们的传统应用程序。
源代码可在我的github上找到