#922 IP 地址的其他写法
计算机网络 2023-05-20There’s more than one way to write an IP address 介绍了 IP 地址的另外几种不常用表示方法。
coding in a complicated world
There’s more than one way to write an IP address 介绍了 IP 地址的另外几种不常用表示方法。
Closing a stale SSH connection(关闭过时的 SSH 连接)中介绍了利用 SSH 转义序列来关闭失去响应的 SSH 连接。
也就是说在 SSH 终端输入 ~. 会直接中断 SSH 连接。
经过试验,确实有效(使用 SSH 代理建立的连接就没效)。
所有 SSH 转义序列:
[root@dell ~]# ~?
Supported escape sequences:
~. - terminate connection (and any multiplexed sessions)
~B - send a BREAK to the remote system
~C - open a command line
~R - request rekey
~V/v - decrease/increase verbosity (LogLevel)
~^Z - suspend ssh
~# - list forwarded connections
~& - background ssh (when waiting for connections to terminate)
~? - this message
~~ - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)
获得 ChatGPT 4 的资格(购买 Pro)之后,就可以看到左边页面多了一个 Model 选项,选中了 GPT 4
如果 Model 选择 Plugin 那一项,右边又会多出来一个 Plugins 选项


右边的 Plugins 选项一直往下拖,最下面一栏是 Plugin store,点击进入。

上面的插件可以选中体验体验。
可以看到下方有 Install an unverified plugin,Develop your own plugin 两项。
我们开发的插件就是服务器端 API + 相关声明文件,如果就只是放在自己的服务器上,那就算 unverified plugin。
第一选项就是用来安装这样未经验证的插件,可以在 https://www.gptplugins.app/ 找一个试一下。
输入域名,ChatGPT 自动去获取声明文件 https://域名/.well-known/ai-plugin.json。

第二项是用来注册插件到 ChatGPT,也可以用来调试本地插件。
如果是注册插件就填域名好了,如果是调试就输入 localhost:3000 这样的地址。
我用局域网 IP,似乎是不行的,可能只支持 localhost 这个主机名。

现阶段最多能够同时勾选三个插件。
聊天过程中,ChatGPT 自动判断是否需要触发插件的使用。

经过我的体验,开发非常简单,除了原本的服务之外,需要的额外工作就两项:清单文件,OpenAPI(如果原本没有的话)。
清单文件:
{
"schema_version": "v1",
"name_for_model": "todo",
"name_for_human": "Todo Plugin",
"description_for_model": "Simple task management, task description, task date, task completion. Supports adding, deleting, and querying.",
"description_for_human": "Simple task management.",
"auth": {
// 本地测试 Auth Type 必须是 none
"type": "none"
},
"api": {
"url": "http://localhost:8080/.well-known/openapi.yaml",
"has_user_authentication": true,
"type": "openapi"
},
"logo_url": "http://localhost:8080/.well-known/logo.png",
"contact_email": "hello@contact.com",
"legal_info_url": "hello@legal.com"
}
我一两年前设计的一个通过 Redis ZSet 做事件广播的方案,刚用 Python 写了一个示例代码贴出来。
import logging
import threading
import time
import redis
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s %(message)s')
redis_host = '127.0.0.1'
redis_port = 6379
redis_db = 1
redis_password = None
redis_prefix = 'broadcast:'
redis_conn = redis.StrictRedis(host=redis_host, port=redis_port, db=redis_db, password=redis_password)
def handle_broadcast(data):
# 这里是处理收到的广播请求数据的函数
# 你需要根据具体需求来实现这个函数
logging.info(f'处理广播请求数据:{data} ===== ===== ===== =====')
def event_broadcast(data):
now = time.time()
now_ms = int(now * 1000)
now_10s = int(now) // 10
key = redis_prefix + str(now_10s)
score = now_ms
pipeline = redis_conn.pipeline()
pipeline.zadd(key, {data: score})
pipeline.expire(key, 300)
pipeline.execute()
# function event_broadcast(data) {
# const now = Date.now();
# const now_ms = now;
# const now_10s = Math.floor(now / 10000);
#
# const key = redis_prefix + now_10s;
# const score = now_ms;
#
# const pipeline = redis_conn.pipeline();
# pipeline.zadd(key, score, data);
# pipeline.expire(key, 300);
# pipeline.exec();
# }
last_score = 0
def event_fetch():
global last_score
now = time.time()
now_ms = int(now * 1000)
now_10s = int(now) // 10
keys = [
redis_prefix + str(now_10s - 2),
redis_prefix + str(now_10s - 1),
redis_prefix + str(now_10s),
]
pipeline = redis_conn.pipeline()
for key in keys:
logging.info('%s %20s %20s', key, last_score, now_ms)
pipeline.zrangebyscore(key, last_score, now_ms, withscores=True)
results = pipeline.execute()
for data_list in results:
for data, _ in data_list:
handle_broadcast(data.decode('utf-8'))
last_score = now_ms
def broadcast_loop():
i = 0
while True:
i += 1
data = f'广播请求数据 {i}'
event_broadcast(data)
logging.info(f'广播请求:{data}')
time.sleep(0.5)
def main():
broadcast_thread = threading.Thread(target=broadcast_loop, daemon=True)
broadcast_thread.start()
while True:
event_fetch()
time.sleep(5)
main()
看到微信公众号 Go语言教程 的文章《golang 实现简单网关》才知道还有 httputil.ReverseProxy 这么个东西。
PS:这玩意儿有什么必要放在标准库?
还挺有趣,在作者示例的基础之上完善了一下,实现多服务,多后端节点的一个负载均衡(还可以再补上权重)。
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
)
func main() {
addr := "127.0.0.1:2002"
backends := map[string][]string{
"service1": {"http://127.0.0.1:2003", "http://127.0.0.1:2004"},
}
reversePorxy := NewReverseProxy(backends)
log.Println("Starting httpserver at " + addr)
log.Fatal(http.ListenAndServe(addr, reversePorxy))
}
func reqConvert(req *http.Request, target *url.URL) {
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if target.RawQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = target.RawQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = target.RawQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
req.Header.Set("User-Agent", "")
}
req.Header.Set("X-Real-Ip", strings.Split(req.RemoteAddr, ":")[0])
}
func NewReverseProxy(backends map[string][]string) *httputil.ReverseProxy {
var targets = make(map[string][]*url.URL)
for srv, nodes := range backends {
for _, nodeUrl := range nodes {
target, _ := url.Parse(nodeUrl)
targets[srv] = append(targets[srv], target)
}
}
director := func(req *http.Request) {
segments := strings.SplitN(req.URL.Path, "/", 3)
if len(segments) != 3 {
return
}
srv := segments[1]
req.URL.Path = segments[2]
if _, ok := targets[srv]; !ok {
log.Printf("unknown path: %s", req.URL.Path)
return
}
rand.Seed(time.Now().UnixNano())
randomIndex := rand.Intn(len(targets[srv]))
target := targets[srv][randomIndex]
reqConvert(req, target)
}
modifyFunc := func(res *http.Response) error {
if res.StatusCode != http.StatusOK {
oldPayLoad, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
newPayLoad := []byte("hello " + string(oldPayLoad))
res.Body = ioutil.NopCloser(bytes.NewBuffer(newPayLoad))
res.ContentLength = int64(len(newPayLoad))
res.Header.Set("Content-Length", fmt.Sprint(len(newPayLoad)))
}
return nil
}
return &httputil.ReverseProxy{Director: director, ModifyResponse: modifyFunc}
}
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}
开源中国文章 紧跟 AI 步伐, Gitee 已支持 AI 模型托管 中介绍了 Git LFS 的使用。
结合 git lfs help,记录如下:
$ git lfs install
Updated Git hooks.
Git LFS initialized.
git lfs trace path/to/largefile
# 会记录在 .gitattributes 文件中
# path/to/largefile filter=lfs diff=lfs merge=lfs -text
# 正常提交
git add .gitattributes path/to/largefile
git commit -m "..."
# 推送
git lfs push --all
git lfs push --all origin
git lfs pull --all
# 指定文件
git lfs pull -I <filepath>
git lfs ls-files 列出大文件做开发有时会需要系统支持在多个地区提供支持,主要是语言,其实还应该包括时区,货币单位等其他信息。
这一套动作叫做国际化和本地化。
国际化是指不要把一些东西写死,是指能够简单灵活改造适应不同地区的需求。
而本地化是指,将这套国际化的产品,针对不同区域做一个适配。
也就是说国际化和本地化是相辅相成的,是一套组合拳。
国际化的英语单词是 internationalization,缩写成 i18n(i 和 n 之间有 18 个字母)。
本地话的英语单词是 localization,缩写成 l10n(l 和 n 之间有 10 个字母)。
明明是一件事,有些地方叫国际化,有些地方写本地化,混乱!我更喜欢全球化 Globalization 这个词。
在如微软及IBM等企业中,则会使用全球化(globalization)来表示此两者的合称。
在英文中,也会使用g11n做为简称。
也有使用缩写 GILT (globalization、internationalization、localization和translation),即“全球化、国际化、本地化和翻译”。
相关工作内容(继续参考维基百科):
只属于本地化的主题有:
我们绝大多数场景应该只能关注到上面标粗的三个点:翻译,货币,合规。
2021/05/09,VPN 与 NAT 讲了我使用 Windows route 命令和 Linux iptables 命令做 NAT,实现 Windows 机器的 VPN 流量走 Linux 的指定网络设备。
现在情况可能会发生一些新的变化,所以这里记录一下我的几点思路。
看了 How to split JavaScript strings into sentences, words or graphemes with "Intl.Segmenter" 了解到,现在 Web 已经支持分词了:
const text = `我爱北京天安门,天安门上太阳升。伟大领袖毛主席,指引我们向前进。`;
const granularities = ["sentence", "word", "grapheme"];
granularities.forEach(function (granularity) {
// console.log([granularity, index, self])
let segmenter = new Intl.Segmenter("zh", { granularity: granularity });
let seg = segmenter.segment(text);
// console.log(seg) // Segments{}
let result = Array.from(seg, (s) => s.segment);
console.log(result);
});
// ['我爱北京天安门,天安门上太阳升。', '伟大领袖毛主席,指引我们向前进。']
// ['我', '爱', '北京', '天安门', ',', '天安门', '上', '太阳', '升', '。', '伟大', '领袖', '毛主席', ',', '指引', '我们', '向', '前进', '。']
// ['我', '爱', '北', '京', '天', '安', '门', ',', '天', '安', '门', '上', '太', '阳', '升', '。', '伟', '大', '领', '袖', '毛', '主', '席', ',', '指', '引', '我', '们', '向', '前', '进', '。']
package main
import (
"fmt"
"github.com/Shopify/go-lua"
)
func main() {
state := lua.NewState()
defer state.Close()
// 加载 Lua 代码
lua.DoString(state, `
function add(a, b)
return a + b
end
`)
// 调用 Lua 函数
lua.GetGlobal(state, "add")
lua.PushInteger(state, 1)
lua.PushInteger(state, 2)
lua.Call(state, 2, 1)
// 获取 Lua 函数返回值
result := lua.ToInteger(state, -1)
lua.Pop(state, 1)
fmt.Println(result)
}