主頁 > 後端開發 > 設計一個基于flask的高并發高可用的查詢ip的http服務

設計一個基于flask的高并發高可用的查詢ip的http服務

2020-11-11 13:05:21 後端開發

結構設計

基礎架構為flask+gunicorn+負載均衡,負載均衡分為阿里云硬體負載均衡服務和軟負載nginx,gunicorn使用supervisor進行管理,

使用nginx軟體負載結構圖

使用阿里云硬體負載均衡服務結構圖

因為flask app需要在記憶體中保存ip樹以及國家、省份、城市相關的字典,因此占用記憶體較高,gunicorn的1個worker需要占用300M記憶體,nginx的4個worker記憶體占用較小(不到100M),因此占用1.3G的記憶體(即需要一個2G記憶體的服務器),當gunicorn任意一個節點掛斷或者升級時,另外一個節點仍然在使用,不會影響整體服務

ip資料庫

IP庫(也叫IP地址資料庫),是由專業技術人員經過長時間通過多種技術手段收集而來的,并且長期有專業人員進行更新、維護、補充,

ip資料庫決議查詢代碼

基于二叉查找樹實作

import struct
from socket import inet_aton, inet_ntoa
import os
import sys

sys.setrecursionlimit(1000000)

_unpack_V = lambda b: struct.unpack("<L", b)
_unpack_N = lambda b: struct.unpack(">L", b)
_unpack_C = lambda b: struct.unpack("B", b)


class IpTree:
    def __init__(self):
        self.ip_dict = {}
        self.country_codes = {}
        self.china_province_codes = {}
        self.china_city_codes = {}

    def load_country_codes(self, file_name):
        try:
            path = os.path.abspath(file_name)
            with open(path, "rb") as f:
                for line in f.readlines():
                    data = https://www.cnblogs.com/CHLL55/p/line.split('\t')
                    self.country_codes[data[0]] = data[1]
                    # print self.country_codes
        except Exception as ex:
            print "cannot open file %s: %s" % (file, ex)
            print ex.message
            exit(0)

    def load_china_province_codes(self, file_name):
        try:
            path = os.path.abspath(file_name)
            with open(path, "rb") as f:
                for line in f.readlines():
                    data = https://www.cnblogs.com/CHLL55/p/line.split('\t')
                    provinces = data[2].split('\r')
                    self.china_province_codes[provinces[0]] = data[0]
                    # print self.china_province_codes
        except Exception as ex:
            print "cannot open file %s: %s" % (file, ex)
            print ex.message
            exit(0)

    def load_china_city_codes(self, file_name):
        try:
            path = os.path.abspath(file_name)
            with open(path, "rb") as f:
                for line in f.readlines():
                    data = https://www.cnblogs.com/CHLL55/p/line.split('\t')
                    cities = data[3].split('\r')
                    self.china_city_codes[cities[0]] = data[0]
        except Exception as ex:
            print "cannot open file %s: %s" % (file, ex)
            print ex.message
            exit(0)

    def loadfile(self, file_name):
        try:
            ipdot0 = 254
            path = os.path.abspath(file_name)
            with open(path, "rb") as f:
                local_binary0 = f.read()
                local_offset, = _unpack_N(local_binary0[:4])
                local_binary = local_binary0[4:local_offset]
                # 256 nodes
                while ipdot0 >= 0:
                    middle_ip = None
                    middle_content = None
                    lis = []
                    # offset
                    begin_offset = ipdot0 * 4
                    end_offset = (ipdot0 + 1) * 4
                    # index
                    start_index, = _unpack_V(local_binary[begin_offset:begin_offset + 4])
                    start_index = start_index * 8 + 1024
                    end_index, = _unpack_V(local_binary[end_offset:end_offset + 4])
                    end_index = end_index * 8 + 1024
                    while start_index < end_index:
                        content_offset, = _unpack_V(local_binary[start_index + 4: start_index + 7] +
                                                    chr(0).encode('utf-8'))
                        content_length, = _unpack_C(local_binary[start_index + 7])
                        content_offset = local_offset + content_offset - 1024
                        content = local_binary0[content_offset:content_offset + content_length]
                        if middle_content != content and middle_content is not None:
                            contents = middle_content.split('\t')
                            lis.append((middle_ip, (contents[0], self.lookup_country_code(contents[0]),
                                                    contents[1], self.lookup_china_province_code(contents[1]),
                                                    contents[2], self.lookup_china_city_code(contents[2]),
                                                    contents[3], contents[4])))
                        middle_content, = content,
                        middle_ip = inet_ntoa(local_binary[start_index:start_index + 4])
                        start_index += 8
                    self.ip_dict[ipdot0] = self.generate_tree(lis)
                    ipdot0 -= 1
        except Exception as ex:
            print "cannot open file %s: %s" % (file, ex)
            print ex.message
            exit(0)

    def lookup_country(self, country_code):
        try:
            for item_country, item_country_code in self.country_codes.items():
                if country_code == item_country_code:
                    return item_country, item_country_code
            return 'None', 'None'
        except KeyError:
            return 'None', 'None'

    def lookup_country_code(self, country):
        try:
            return self.country_codes[country]
        except KeyError:
            return 'None'

    def lookup_china_province(self, province_code):
        try:
            for item_province, item_province_code, in self.china_province_codes.items():
                if province_code == item_province_code:
                    return item_province, item_province_code
            return 'None', 'None'
        except KeyError:
            return 'None', 'None'

    def lookup_china_province_code(self, province):
        try:
            return self.china_province_codes[province.encode('utf-8')]
        except KeyError:
            return 'None'

    def lookup_china_city(self, city_code):
        try:
            for item_city, item_city_code in self.china_city_codes.items():
                if city_code == item_city_code:
                    return item_city, item_city_code
            return 'None', 'None'
        except KeyError:
            return 'None', 'None'

    def lookup_china_city_code(self, city):
        try:
            return self.china_city_codes[city]
        except KeyError:
            return 'None'

    def lookup(self, ip):
        ipdot = ip.split('.')
        ipdot0 = int(ipdot[0])
        if ipdot0 < 0 or ipdot0 > 255 or len(ipdot) != 4:
            return None
        try:
            d = self.ip_dict[int(ipdot[0])]
        except KeyError:
            return None
        if d is not None:
            return self.lookup1(inet_aton(ip), d)
        else:
            return None

    def lookup1(self, net_ip, (net_ip1, content, lefts, rights)):
        if net_ip < net_ip1:
            if lefts is None:
                return content
            else:
                return self.lookup1(net_ip, lefts)
        elif net_ip > net_ip1:
            if rights is None:
                return content
            else:
                return self.lookup1(net_ip, rights)
        else:
            return content

    def generate_tree(self, ip_list):
        length = len(ip_list)
        if length > 1:
            lefts = ip_list[:length / 2]
            rights = ip_list[length / 2:]
            (ip, content) = lefts[length / 2 - 1]
            return inet_aton(ip), content, self.generate_tree(lefts), self.generate_tree(rights)
        elif length == 1:
            (ip, content) = ip_list[0]
            return inet_aton(ip), content, None, None
        else:
            return

if __name__ == "__main__":
    import sys

    reload(sys)
    sys.setdefaultencoding('utf-8')
    ip_tree = IpTree()
    ip_tree.load_country_codes("doc/country_list.txt")
    ip_tree.load_china_province_codes("doc/china_province_code.txt")
    ip_tree.load_china_city_codes("doc/china_city_code.txt")
    ip_tree.loadfile("doc/mydata4vipday2.dat")
    print ip_tree.lookup('123.12.23.45')

http請求

提供ip查詢服務的GET請求和POST請求

@ip_app.route('/api/ip_query', methods=['POST'])
def ip_query():
    try:
        ip = request.json['ip']
    except KeyError as e:
        raise InvalidUsage('bad request: no key ip in your request json body. {}'.format(e), status_code=400)
    if not is_ip(ip):
        raise InvalidUsage('{} is not a ip'.format(ip), status_code=400)
    try:
        res = ip_tree.lookup(ip)
    except Exception as e:
        raise InvalidUsage('internal error: {}'.format(e), status_code=500)
    if res is not None:
        return jsonify(res)
    else:
        raise InvalidUsage('no ip info in ip db for ip: {}'.format(ip), status_code=501)


@ip_app.route('/api/ip_query', methods=['GET'])
def ip_query_get():
    try:
        ip = request.values.get('ip')
    except ValueError as e:
        raise InvalidUsage('bad request: no param ip in your request. {}'.format(e), status_code=400)
    if not is_ip(ip):
        raise InvalidUsage('{} is not a ip'.format(ip), status_code=400)
    try:
        res = ip_tree.lookup(ip)
    except Exception as e:
        raise InvalidUsage('internal error: {}'.format(e), status_code=500)
    if res is not None:
        return jsonify(res)
    else:
        raise InvalidUsage('no ip info in ip db for ip: {}'.format(ip), status_code=501)

POST請求需要在請求體中包含類似下面的json欄位

{
	"ip": "165.118.213.9"
}

GET請求的形式如:http://127.0.0.1:5000/api/ip_query?ip=165.118.213.9

服務部署

安裝依賴庫

依賴的庫requirements.txt如下:

certifi==2017.7.27.1
chardet==3.0.4
click==6.7
Flask==0.12.2
gevent==1.1.1
greenlet==0.4.12
gunicorn==19.7.1
idna==2.5
itsdangerous==0.24
Jinja2==2.9.6
locustio==0.7.5
MarkupSafe==1.0
meld3==1.0.2
msgpack-python==0.4.8
requests==2.18.3
supervisor==3.3.3
urllib3==1.22
Werkzeug==0.12.2

安裝方法:pip install -r requirements.txt

配置supervisor

vim /etc/supervisor/conf.d/ip_query_http_service.conf,內容如下

[program:ip_query_http_service]
directory = /root/qk_python/ip_query
command = gunicorn -w10 -b0.0.0.0:8080 ip_query_app:ip_app --worker-class gevent
autostart = true
startsecs = 5
autorestart = true
startretries = 3
user = root
stdout_logfile=/root/qk_python/ip_query/log/gunicorn.log
stderr_logfile=/root/qk_python/ip_query/log/gunicorn.err

內容添加完成之后,需要創建stdout_logfile和stderr_logfile這兩個目錄,否則supervisor啟動會報錯,然后更新supervisor啟動ip_query_http_service行程,

# 啟動supervisor
supervisord -c /etc/supervisor/supervisord.conf	

# 更新supervisor服務
supervisorctl update

關于supervisor的常用操作參見最后面的參考資料,

安裝nginx

如果是軟負載的形式需要安裝nginx,編譯安裝nginx的方法參見最后面的參考資料,

配置nginx

vim /usr/local/nginx/nginx.conf,修改組態檔內容如下:

#user  nobody;
#nginx行程數,建議設定為等于CPU總核心數,
worker_processes  4;
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#全域錯誤日志定義型別,[ debug | info | notice | warn | error | crit ]
error_log  logs/error.log  info;
#行程檔案
pid        logs/nginx.pid;
#一個nginx行程打開的最多檔案描述符數目,理論值應該是最多打開檔案數(系統的值ulimit -n)與nginx行程數相除,但是nginx分配請求并不均勻,所以建議與ulimit -n的值保持一致,
worker_rlimit_nofile 65535;
events {
    #參考事件模型 linux 下使用epoll
    use epoll;
    #單個行程最大連接數(最大連接數=連接數*行程數)
    worker_connections  65535;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;
    sendfile        on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nopush on; #防止網路阻塞
    tcp_nodelay on; #防止網路阻塞
    #gzip  on;
    server {
		#這里配置銜接服務提供的代理埠.
        listen       9000;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        location / {
            #            root   html;
            #            index  index.html index.htm;
            proxy_pass http://127.0.0.1:8000;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            #后端的Web服務器可以通過X-Forwarded-For獲取用戶真實IP
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            client_max_body_size 10m; #允許客戶端請求的最大單檔案位元組數
            client_body_buffer_size 128k; #緩沖區代理緩沖用戶端請求的最大位元組數,
            proxy_buffer_size 4k; #設定代理服務器(nginx)保存用戶頭資訊的緩沖區大小
            proxy_temp_file_write_size 64k;       #設定快取檔案夾大小,大于這個值,將從upstream服務器傳
        }

        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }       
    }
}

壓力測驗

做壓力測驗,選擇正確的工具是前提,以下工具中,jmeter運行在windows機器較多,其他工具建議都運行在*nix機器上,

壓力測驗工具選擇

工具名稱 優缺點 建議
ApacheBench(ab) 命令使用簡單,效率高,統計資訊完善,施壓機器記憶體壓力小 推薦
locust python撰寫,效率低,受限于GIL,需要撰寫python測驗腳本 不推薦
wrk 命令使用簡單,效率高,統計資訊精煉,坑少,少報錯 最推薦
jmeter 基于java,Apache開源,圖形化界面,操作簡便 推薦
webbench 使用簡單,但是不支持POST請求 一般
tsung erlang撰寫,配置模板較多,較復雜 不推薦

上述六種工具全部親身使用過,下面選擇ab、wrk、jmeter三種工具簡單說明安裝使用方法,其他工具的使用方法如有需要,自行google

ab

安裝

apt-get install apache2-utils 

常見options

option 含義
-r 當接收到socket錯誤的時候ab不退出
-t 發送請求的最長時間
-c 并發數,一次構造的請求數量
-n 發送的請求數量
-p postfile,指定包含post資料的檔案
-T content-type,指定post和put發送請求時請求體的型別

使用

測驗GET請求

ab -r -t 120 -c 5000 http://127.0.0.1:8080/api/ip_query?ip=165.118.213.9

測驗POST請求

ab -r -t 120 -c 5000 -p /tmp/post_data.txt -T 'application/json' http://127.0.0.1:8080/api/ip_query

其中/tmp/post_data.txt檔案的內容為待發送的-T指定格式的資料,在此處為json格式

{"ip": "125.118.213.9"}

wrk

http://www.restran.net/2016/09/27/wrk-http-benchmark/

安裝

apt-get install libssl-dev
git clone https://github.com/wg/wrk.git
cd wrk
make
cp wrk /usr/sbin

常見options

option 含義
-c 打開的連接數,即并發數
-d 壓力測驗時間:發送請求的最長時間
-t 施壓機器使用的執行緒數量
-s 指定要加載的lua腳本
--latency 列印延遲統計資訊

使用

測驗GET請求

wrk -t10 -c5000 -d120s --latency http://127.0.0.1:8080/api/ip_query?ip=165.118.213.9

測驗POST請求

wrk -t50 -c5000 -d120s --latency -s /tmp/wrk_post.lua http://127.0.0.1:8080

其中/tmp/wrk_post.lua檔案的內容為待加載的lua腳本,指定post的path,header,body

request = function()
  path = "/api/ip_query"
  wrk.headers["Content-Type"] = "application/json"
  wrk.body = "{\"ip\":\"125.118.213.9\"}"
  return wrk.format("POST", path)
end

jmeter

安裝

安裝jmeter前需要先安裝jdk1.8,然后在Apache官網可以下載jmeter,點此下載

使用

xmind-jmeter

以上圖片來自一個測驗大牛,非常詳細,完整的xmind檔案下載見:jmeter-張蓓.xmind

jmeter的入門級使用也可以參考最后面的參考資料部分:使用Apache Jmeter進行并發壓力測驗

壓力測驗結果分析

wrk GET請求壓測結果

root@ubuntu:/tmp# wrk -t10 -c5000 -d60s --latency http://127.0.0.1:8080/api/ip_query?ip=165.118.213.9
Running 1m test @ http://127.0.0.1:8080/api/ip_query?ip=165.118.213.9
  10 threads and 5000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   897.19ms  322.83ms   1.99s    70.52%
    Req/Sec   318.80    206.03     2.14k    68.84%
  Latency Distribution
     50%  915.29ms
     75%    1.11s 
     90%    1.29s 
     99%    1.57s 
  187029 requests in 1.00m, 51.01MB read
  Socket errors: connect 0, read 0, write 0, timeout 38
Requests/sec:   3113.27
Transfer/sec:    869.53KB

ab GET請求壓測結果

root@ubuntu:/tmp# ab -r -t 60 -c 5000 http://127.0.0.1:8080/api/ip_query?ip=165.118.213.9
This is ApacheBench, Version 2.3 <$Revision: 1796539 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, https://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Completed 50000 requests
Finished 50000 requests


Server Software:        gunicorn/19.7.1
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /api/ip_query?ip=165.118.213.9
Document Length:        128 bytes

Concurrency Level:      5000
Time taken for tests:   19.617 seconds
Complete requests:      50000
Failed requests:        2
   (Connect: 0, Receive: 0, Length: 1, Exceptions: 1)
Total transferred:      14050000 bytes
HTML transferred:       6400000 bytes
Requests per second:    2548.85 [#/sec] (mean)
Time per request:       1961.668 [ms] (mean)
Time per request:       0.392 [ms] (mean, across all concurrent requests)
Transfer rate:          699.44 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0  597 1671.8      4   15500
Processing:     4  224 201.4    173    3013
Waiting:        4  223 200.1    172    2873
Total:          7  821 1694.4    236   15914

Percentage of the requests served within a certain time (ms)
  50%    236
  66%    383
  75%   1049
  80%   1155
  90%   1476
  95%   3295
  98%   7347
  99%   7551
 100%  15914 (longest request)

jmeter GET請求壓測結果

結果分析

以上三個工具的壓測結果大體相同,RPS(Requests per second)大致在3000左右,此時機器配置為4核4G記憶體,并且gunicorn開了10個worker,記憶體占用3.2G,單臺機器只有3000并發,對于此配置的機器來說,需要進一步分析原因,后續再弄一臺機器,負載均衡后能達到5000以上才能滿足使用要求,

壓力測驗注意事項

檔案打開數

壓力測驗時對施壓機器的檔案打開數一般有要求,遠不止1024個open files,需要增加linux系統的檔案打開數,增加方法:

# 檔案打開數
ulimit -a
# 修改檔案打開數
ulimit -n 500000

SYN洪水攻擊保護

linux系統中有一個引數:/etc/sysctl.conf組態檔中的net.ipv4.tcp_syncookies欄位,這個欄位值默認為1,表示系統會檢測SYN洪水攻擊,并開啟保護,因此壓測時,如果發送大量重復性資料的請求,受壓機器SYN佇列溢位之后啟用SYN cookie,導致會有大量請求超時失敗,阿里云的負載均衡是有SYN洪水攻擊檢測和DDos攻擊檢測功能的,因此在做壓力測驗時需要注意兩點:

  • 測驗時適當關閉負載均衡機器的 net.ipv4.tcp_syncookies 欄位
  • 造資料時應該盡量避免大量重復性資料,以免被識別為攻擊,

gunicorn簡介及調優

關于gunicorn的選擇可以參考測驗報告:Python WSGI Server 性能分析

在選定gunicorn作為WSGI server之后,需要根據機器選擇相應的worker數量以及每個worker的worker-class,

worker數量選擇

每一個worker都是作為一個單獨的子行程來運行,都持有一份獨立的記憶體資料,每增加或減少一個worker,系統記憶體明顯的成倍數的改變,最初單臺機器gunicorn開啟3個worker,系統只支持1000RPS的并發,當把worker擴展為9個之后,系統支持3000RPS的并發,因此在記憶體足夠的時候,可以適當增加worker數量,

worker-class選擇

可以參考尾部的參考資料中的gunicorn常用settingsGunicorn 幾種 Worker class 性能測驗比較這兩篇文章,

將gunicorn啟動時的worker-class從默認的sync改成gevent之后,系統RPS直接翻倍,

worker-class worker數量 ab測驗的RPS
sync 3 573.90
gevent 3 1011.84

gevent依賴:gevent >= 0.13,因此需要先使用pip安裝,對應的gunicorn啟動flask應用的命令需要修改為:

gunicorn -w10 -b0.0.0.0:8080 ip_query_app:ip_app --worker-class gevent

改進點

改進ip資料庫準確性

損失效率換取準確性:使用單一ip資料庫會存在一些ip無法查詢出結果的情況,并且國外ip一般只能精確到國家,可以平衡幾家ip資料庫的準確度和覆寫率,當無法查詢出準確的地址資訊時去查詢另外幾個ip資料庫,

提高單臺機器并發量

從發起請求,到WSGI服務器處理,到應用介面,到ip查詢每個程序都需要單獨分析每秒可執行量,進而分析系統瓶頸,從根本上提高單機并發量,


參考資料

  • 全球 IPv4 地址歸屬地資料庫(IPIP.NET 版)
  • 使用flask開發RESTful架構的api服務器端(5)–部署flask應用到nginx
  • python web 部署:nginx + gunicorn + supervisor + flask 部署筆記
  • flowsnow-nginx編譯安裝
  • supervisor推薦教程-使用 supervisor 管理行程
  • 維基-二叉查找樹
  • 簡書-wrk壓力測驗post介面
  • 使用Apache Jmeter進行并發壓力測驗
  • gunicorn常用settings
  • Gunicorn 幾種 Worker class 性能測驗比較

記得幫我點贊哦!

精心整理了計算機各個方向的從入門、進階、實戰的視頻課程和電子書,按照目錄合理分類,總能找到你需要的學習資料,還在等什么?快去關注下載吧!!!

resource-introduce

念念不忘,必有回響,小伙伴們幫我點個贊吧,非常感謝,

我是職場亮哥,YY高級軟體工程師、四年作業經驗,拒絕咸魚爭當龍頭的斜杠程式員,

聽我說,進步多,程式人生一把梭

如果有幸能幫到你,請幫我點個【贊】,給個關注,如果能順帶評論給個鼓勵,將不勝感激,

職場亮哥文章串列:更多文章

wechat-platform-guide-attention

本人所有文章、回答都與著作權保護平臺有合作,著作權歸職場亮哥所有,未經授權,轉載必究!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/210509.html

標籤:Python

上一篇:初入Django框架

下一篇:django 專案啟動

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more