Content-Type请求示例

Content-Type请求示例 1.application/json 请求 python代码示例 import requests import json data = { 'a': 123, 'b': 456 } ## headers中添加上content-type这个参数,指定为json格式 headers = {'Content-Type': 'application/json'} ## post的时候,将data字典形式的参数用json包转换成json格式。 response = requests.post(url='http://127.0.0.1:9000/api/ttt', headers=headers, data=json.dumps(data)) 实际发出的请求体 POST /api/ttt HTTP/1.1 Host: 127.0.0.1:9000 User-Agent: python-requests/2.31.0 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Content-Type: application/json Content-Length: 20 {"a": 123, "b": 456} 2. application/x-www-form-urlencoded 请求 python代码示例 import requests data = { 'a': 123, 'b': 456 } ## headers中添加上content-type这个参数,指定为x-www-form-urlencoded格式 headers = {'Content-Type': 'application/x-www-form-urlencoded'} ## post的时候,默认按x-www-form-urlencoded格式处理 response = requests.post(url='http://127.0.0.1:9000/api/ttt', headers=headers, data=data) 实际发出的请求体 POST /api/ttt HTTP/1.1 Host: 127.0.0.1:9000 User-Agent: python-requests/2.31.0 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 11 a=123&b=456

November 12, 2024 · 1 min

玩转Arduino(3)-感应式垃圾桶

思路 arduino接红外线传感器, 舵机、灯,红外线传感器被触发后,红灯亮起,舵机转动一定的角度,垃圾桶盖被打开,等待3秒后,舵机转回初始位置,垃圾桶盖关闭。 代码 #include <Servo.h> int infraredPin = 2; int lightPin = 6; int steerPin = 9; int closeAngle = 0; int openAngle = 80; Servo servo; void setup() { pinMode(infraredPin, INPUT); pinMode(steerPin, OUTPUT); pinMode(lightPin, OUTPUT); servo.attach(steerPin); // 初始状态: 垃圾桶盖子是关闭的 servo.write(closeAngle); } void loop() { if (digitalRead(infraredPin) == 0) { //检测到障碍物,输出低电平 digitalWrite(lightPin, HIGH); //亮灯 // 垃圾桶开盖 servo.write(openAngle); delay(3000); // 等待3秒 // clear digitalWrite(lightPin, LOW); // 灭灯 servo.write(closeAngle); } delay(200); } 演示视频 B站 ...

November 1, 2024 · 1 min

玩转Arduino(2)-按钮控制小灯

思路 按钮按下输出为低电平,按钮抬起时为高电平。 萌叔希望达到的效果是按一下按钮,小灯亮起,再按一次,小灯熄灭 需要捕获电位从 低电位 -> 高电位 的这个变化 代码 #define legPinIn 10 #define legPinOut 5 bool isLightOn = false; int preMode = HIGH; int currMode = HIGH; void setup() { Serial.begin(9600); pinMode(legPinIn, INPUT); pinMode(legPinOut, OUTPUT); } void loop() { isLightOn = getLightState(); if (isLightOn) { digitalWrite(legPinOut, HIGH); } else { digitalWrite(legPinOut, LOW); } delay(100); } bool getLightState() { if (digitalRead(legPinIn) != currMode) { preMode = currMode; currMode = digitalRead(legPinIn); if (preMode == LOW && currMode == HIGH) { isLightOn = !isLightOn; Serial.print("isLightOn: "); Serial.println(isLightOn); } } return isLightOn; } 演示视频 B站 ...

October 20, 2024 · 1 min

玩转Arduino(1)-自制温度湿度计

思路 从温湿度传感器读取温度和湿度信息,然后在LCD显示器上展示 代码 #include <DHT11.h> #include <Bonezegei_LCD1602_I2C.h> DHT11 dht11(2); Bonezegei_LCD1602_I2C lcd(0x27); void setup() { // Initialize serial communication to allow debugging and data readout. // Using a baud rate of 9600 bps. Serial.begin(9600); lcd.begin(); } void loop() { int temperature = 0; int humidity = 0; // Attempt to read the temperature and humidity values from the DHT11 sensor. int result = dht11.readTemperatureHumidity(temperature, humidity); // Check the results of the readings. // If the reading is successful, print the temperature and humidity values. // If there are errors, print the appropriate error messages. if (result == 0) { Serial.print("Temperature: "); Serial.print(temperature); Serial.print(" °C\tHumidity: "); Serial.print(humidity); Serial.println(" %"); String str = String("Temperat: ") + temperature + String(" C"); lcd.setPosition(0, 0); lcd.print(str.c_str()); lcd.setPosition(0, 1); str = str = String("Humidity: ") + humidity + String(" %"); lcd.print(str.c_str()); } else { // Print error message based on the error code. Serial.println(DHT11::getErrorString(result)); } delay(10000); } 参考资料 1.DHT11驱动 2.LCD1602驱动 ...

October 20, 2024 · 1 min

玩转高性能日志库ZAP (7)-标识

1.前言 在Java中我们常常会这样使用日志 import org.slf4j.Logger; import org.slf4j.LoggerFactory; class Main { final static Logger log = LoggerFactory.getLogger(Main.class); public static void main(String[] args) { log.info("info, hello {}", "slf4j"); log.error("error"); } } 输出 6:30:03.350 [main] INFO Main - info, hello slf4j 16:30:03.356 [main] ERROR Main - error 这里在每条日志中都有类名,可以很容易从文件中过滤出这个类相关的日志。 2.zap中使用name标识 zap中的玩法 package main import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "time" ) func main() { config := zap.NewProductionConfig() config.Encoding = "console" config.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05") logger, _ := config.Build() defer logger.Sync() // 输出该条日志在代码文件中的行号 logger = logger.WithOptions(zap.AddCaller()) logger.Info("failed to fetch URL", zap.Int("attempt", 3), zap.Duration("backoff", time.Second), ) go func() { // worker1 // 携带name标识 logger1 := logger.Named("worker1") for { time.Sleep(time.Millisecond * 200) logger1.Info("hello1") } }() go func() { // worker2 logger2 := logger.Named("worker2") for { time.Sleep(time.Millisecond * 200) logger2.Info("hello2") } }() time.Sleep(3 * time.Second) } 输出 ...

October 9, 2024 · 2 min

一次Redis内存排查记录

1. 起因 萌叔手上有一个测试用的Redis集群,集群是Master-slave模式, 平常也不怎么使用。 于是打算清理释放一下内存,打算降低一下配置。 使用FLUSHALL FLUSHALL 可以看到所有的key都已经被清空 > info Keyspace # Keyspace 查看内存开销情况 > info memory # Memory used_memory:537921192 used_memory_human:513.00M used_memory_rss:544067584 used_memory_rss_human:518.86M used_memory_peak:650406864 实例的内存开销超过500MB,内存让狗吃了? 2. 排查 # Memory used_memory:537921192 used_memory_human:513.00M used_memory_rss:544067584 used_memory_rss_human:518.86M used_memory_peak:650406864 used_memory_peak_human:620.28M used_memory_peak_perc:82.71% used_memory_overhead:537863648 used_memory_startup:791600 used_memory_dataset:57544 used_memory_dataset_perc:0.01% allocator_allocated:537984728 allocator_active:538705920 allocator_resident:548462592 total_system_memory:201219264512 total_system_memory_human:187.40G used_memory_lua:40960 used_memory_lua_human:40.00K used_memory_scripts:216 used_memory_scripts_human:216B number_of_cached_scripts:1 maxmemory:1073741824 maxmemory_human:1.00G maxmemory_policy:allkeys-lru allocator_frag_ratio:1.00 allocator_frag_bytes:721192 allocator_rss_ratio:1.02 allocator_rss_bytes:9756672 rss_overhead_ratio:0.99 rss_overhead_bytes:-4395008 mem_fragmentation_ratio:1.01 mem_fragmentation_bytes:6229608 mem_not_counted_for_evict:0 mem_replication_backlog:536870912 // 约为512MB mem_clients_slaves:16922 mem_clients_normal:183998 mem_aof_buffer:0 mem_allocator:jemalloc-5.1.0 active_defrag_running:0 lazyfree_pending_objects:0 # Replication role:master connected_slaves:1 slave0:ip=192.168.88.29,port=9981,state=online,offset=23482674189,lag=0 master_replid:b58677283938996fdae00f7692d24d11d88ff488 master_replid2:effdd3cd4ab2030a2d49273e0cf2ed74df4bccb9 master_repl_offset:23482674347 second_repl_offset:17046600529 repl_backlog_active:1 repl_backlog_size:536870912 // 约为512MB repl_backlog_first_byte_offset:22945803436 repl_backlog_histlen:536870912 # Cluster cluster_enabled:0 # Keyspace 将INFO命令返回的所有内容都交给chatGPT分析,笔者找到了答案,主要原因是复制积压缓冲区设置的过大。 ...

September 11, 2024 · 1 min

RocketMQ架构设计中的”暴力美学”(2)-故障处理

本文基于rocketmq-all-4.8.0 1.引言 人们在潜意识里,总会觉得复杂且精巧的东西是好东西。但是这个复杂这个词在软件架构设计中,却不一定是好事情。 因为过于精巧和复杂的系统往往意味着系统更难以维护,出现问题后,故障更难排查。 萌叔在阅读和分享RocketMQ的过程中, 发现它有很多设计非常的简单粗暴,堪称”暴力美学”的典范, 同时又给人眼前一亮的感觉(还能这么玩)。 2.故障处理 在介绍故障处理机制时,我们假定一个场景 2.1 架构 假定broker有2组 group1 Master1和Slave1 group2 Master2和Slave2 2.2 故障发生 故障发生前,萌叔创建了1个topic,指定分片数为4 mqadmin updateTopic -n <namesrvAddr> -c <clusterName> -t myTopic -q 4 这里需要强调一下,这里指定的4个分片,并不是全局4个分片,而是每个broker有4个分片,情况如下图。 此时 Topic路由信息形如: { "OrderTopicConf": "", "queueDatas": [{ "brokerName": "group1", "readQueueNums": 4, "writeQueueNums": 4, "perm": 6, // 可读可写 "topicSynFlag": 0 }, { "brokerName": "group2", "readQueueNums": 4, "writeQueueNums": 4, "perm": 6, // 可读可写 "topicSynFlag": 0 }], "brokerDatas": [{ "cluster": "Default_Cluster", "brokerName": "group1", "brokerAddrs": { "0": "192.168.12.123:10911", // master "1": "192.168.12.127:10911" // slave } }, { "cluster": "Default_Cluster", "brokerName": "group2", "brokerAddrs": { "0": "192.168.12.220:10911", "1": "192.168.12.12:10911" } }] } 生产者消息路由策略:Round Robin 消费者消息路由策略:AllocateByAveragely ...

August 7, 2024 · 2 min

heavykeeper算法的一些设计思想

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.引言 topk问题是十分经典的算法问题,它的核心是从大量数据中找出前k个最大或者最小的元素。 它对于提高缓存的命中率,以及发现新增热点的发现有很大的帮助。 对于topk问题而言, 如果庞大的数据集本身是固定的,萌叔觉得topk问题本身还不算复杂, 顶多算是一个大数据计算的问题。 但如果这个庞大的数据集本身就处于变化中,不断在新增元素, topk问题就会变得非常复杂。 2023年萌叔看到一篇B站的文章,介绍了B站使用的heavykeeper算法来计算topk问题,取得了比较好的效果。 本文不是纯粹意义上的介绍heavykeeper算法的,萌叔想探讨一下里面的设计理念。 heavykeeper 对应的参考实现见参考资料4 B站给出的试验结果数据 2.数据结构 heavykeeper的设计目标是解决高流速情况下的topk问题且topk的元素出现的频率必须显著高于其它元素。 互联网数据大体满足二八定律,是符合要求的。 1)heavykeeper使用的d行w列的二维数组, 2)流体中的每个元素在 每一行上都有一个对应的位置,由哈希函数决定,哈希函数从h1()、h2() 到 hd() 这个数据结构与Count-Min Sketch算法是非常像的,唯一的区别是heavykeeper中二维数组的bucket,是一个结构体 type bucket struct { fingerprint uint64 // 比Count-Min Sketch算法多出来的字段 count uint64 } 3.处理逻辑 对应代码 heavy_keeper.go func (topk *TopK) jobAdder() { for item := range topk.items { itemFingerprint := xxhash.Checksum64S(item, uint64(topk.seed)) var maxCount uint64 itemHeapIdx, itemHeapExist := topk.minHeap.Find(item) heapMin := topk.minHeap.Min() // compute d hashes for i := uint32(0); i < topk.depth; i++ { bI := make([]byte, 4) binary.LittleEndian.PutUint32(bI, uint32(i)) bucketNumber := xxhash.Checksum64S(append([]byte(item), bI...), uint64(topk.seed)) % uint64(topk.width) topk.buckets[i][bucketNumber].mu.Lock() fingerprint := topk.buckets[i][bucketNumber].fingerprint count := topk.buckets[i][bucketNumber].count if count == 0 { // Case 1 topk.buckets[i][bucketNumber].fingerprint = itemFingerprint topk.buckets[i][bucketNumber].count = 1 maxCount = max(maxCount, 1) } else if fingerprint == itemFingerprint { // Case 2 if itemHeapExist || count <= heapMin { topk.buckets[i][bucketNumber].count++ maxCount = max(maxCount, topk.buckets[i][bucketNumber].count) } } else { // Case 3 decay := math.Pow(topk.decay, float64(count)) if rand.Float64() < decay { topk.buckets[i][bucketNumber].count-- if topk.buckets[i][bucketNumber].count == 0 { topk.buckets[i][bucketNumber].fingerprint = itemFingerprint topk.buckets[i][bucketNumber].count = 1 maxCount = max(maxCount, 1) } } } topk.buckets[i][bucketNumber].mu.Unlock() } // update heap if itemHeapExist { topk.minHeap.Fix(itemHeapIdx, maxCount) } else { topk.minHeap.Add(minheap.Node{ Count: maxCount, Item: item, }) } } } 3.1 众数问题 看到上面的处理逻辑,很多读者可能会感到懵,为什么这样做就可以找出topk的元素? 为了解释heavykeeper的设计理念,我们先来看一个求众数问题 LeetCode 第169题 ...

July 25, 2024 · 2 min

grafana-使用value mapping将数值转换为文本

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 导读 萌叔的API服务内部使用了localcache服务,这个localcache是有状态的。 我在Grafana中配置的初始图表如下 这里读者可以看出localcache的状态是数值型的,对此业务陌生的情况下,无法理解其含义,能否把数值转换为文本呢? 2. 使用value mapping配置 Grafana提供value mapping,可以根据value值来修改显示内容 改进后,效果如下 状态以中文显示,并且增加了背景颜色 在上图中,inuse表示缓存实际使用量,萌叔希望根据inuse的数值范围,给出文字提示。那么,再来改一下。 改进后,效果如下 3. 总结 value mapping是一个非常有用的功能,感兴趣的朋友可以试试。 参考资料 1.Configure value mappings

June 19, 2024 · 1 min

一个自动化测试框架 vearne/autotest

注意: 本文基于 vearne/autotest v0.0.9 1. 引言 萌叔手上的API服务不少,核心的项目都有CI和单元测试。 但是毕竟在单元测试中,对外部数据库、微服务的调用都是mock的,缺乏对整个链路的自动化测试 这段时间开发了一个自动化测试框架 vearne/autotest 初步实现了对HTTP协议API服务的支持,后期应该还会支持gRPC协议的API服务 2. 框架的优势 无需进行程序开发,只需要编写配置文件 可以指定testcase之间的依赖关系 无依赖关系的testcase可以并发执行,执行速度更快 使用xpath提取变量,书写方便 支持从文件中导入变量,支持从response中提取变量 3. 完整的示例 3.1 启动API服务 使用docker compose启动一个HTTP RESTful API服务 cd docker-compose docker compose up -d 这个服务是book管理服务,它支持对book的增删改查 添加 curl -X POST 'http://localhost:8080/api/books' \ --header 'Content-Type: application/json' \ --data '{"title": "book3_title", "author": "book3_author"}' 接口返回 { "id": 3, "title": "book3_title", "author": "book3_author" } 修改 curl -X PUT 'localhost:8080/api/books/3' \ --header 'Content-Type: application/json' \ --data '{"title": "book3_title", "author": "book3_author-2"}' 接口返回 { "id": 3, "title": "book3_title", "author": "book3_author-2" } 3.2 自动化测试 autotest run -c=./config_files/autotest.yml -e=./config_files/.env.dev 自动化测试中的每一个测试用例都是 ...

May 8, 2024 · 2 min