萬物互聯之~RPC專欄

3.RPC引入

上篇回顧:萬物互聯之~深入篇

Code:https://github.com/lotapp/BaseCode/tree/master/python/6.net/6.rpc/

其他專欄最新篇:協程加強之~兼容答疑篇 | 聊聊數據庫~SQL環境篇

3.1.概念

RPC(Remote Procedure Call):分布式系統常見的一種通信方法(遠程過程調用),通俗講:可以一臺計算機的程序調用另一臺計算機的子程序(可以把它看成之前我們說的進程間通信,只不過這一次的進程不在同一臺PC上了)

PS:RPC的設計思想是力圖使遠程調用中的通訊細節對于使用者透明,調用雙方無需關心網絡通訊的具體實現

引用一張網上的圖:
1.rpc.png

HTTP有點相似,你可以這樣理解:

  1. 老版本的HTTP/1.0是短鏈接,而RPC是長連接進行通信
    • HTTP協議(header、body),RPC可以采取HTTP協議,也可以自定義二進制格式
  2. 后來HTTP/1.1支持了長連接(Connection:keep-alive),基本上和RPC差不多了
    • keep-alive一般都限制有最長時間,或者最多處理的請求數,而RPC是基于長連接的,基本上沒有這個限制
  3. 后來谷歌直接基于HTTP/2.0建立了gRPC,它們之間的基本上也就差不多了
    • 如果硬是要區分就是:HTTP-普通話RPC-方言的區別了
    • RPC高效而小眾,HTTP效率沒RPC高,但更通用
  4. PS:RPCHTTP調用不用經過中間件,而是端到端的直接數據交互
    • 網絡交互可以理解為基于Socket實現的(RPCHTTP都是Socket的讀寫操作)

簡單概括一下RPC的優缺點就是:

  1. 優點:
    1. 效率更高(可以自定義二進制格式)
    2. 發起RPC調用的一方,在編寫代碼時可忽略RPC的具體實現(跟編寫本地函數調用一般
  2. 缺點:
    • 通用性不如HTTP(方言普及程度肯定不如普通話),如果傳輸協議不是HTTP協議格式,調用雙方就需要專門實現通信庫

PS:HTTP更多是ClientServer的通訊;RPC更多是內部服務器間的通訊

3.2.引入

上面說這么多,可能還沒有來個案例實在,我們看個案例:

本地調用sum()

def sum(a, b):"""return a+b"""return a + bdef main():result = sum(1, 2)print(f"1+2={result}")if __name__ == "__main__":main()

輸出:(這個大家都知道)

1+2=3

1.xmlrpc案例

官方文檔:

https://docs.python.org/3/library/xmlrpc.client.html
https://docs.python.org/3/library/xmlrpc.server.html

都說RPC用起來就像本地調用一樣,那么用起來啥樣呢?看個案例:

服務端:(CentOS7:192.168.36.123:50051)

from xmlrpc.server import SimpleXMLRPCServerdef sum(a, b):"""return a+b"""return a + b# PS:50051是gRPC默認端口
server = SimpleXMLRPCServer(('', 50051))
# 把函數注冊到RPC服務器中
server.register_function(sum)
print("Server啟動ing,Port:50051")
server.serve_forever()

客戶端:(Win10:192.168.36.144

from xmlrpc.client import ServerProxystub = ServerProxy("http://192.168.36.123:50051")
result = stub.sum(1, 2)
print(f"1+2={result}")

輸出:(Client用起來是不是和本地差不多?就是通過代理訪問了下RPCServer而已)

1+2=3

2.server.png

PS:CentOS服務器不是你綁定個端口就一定能訪問的,如果不能記讓防火墻開放對應的端口

這個之前在說MariaDB環境的時候有詳細說:https://www.cnblogs.com/dotnetcrazy/p/9887708.html#_map4

# 添加 --permanent永久生效(沒有此參數重啟后失效)
firewall-cmd --zone=public --add-port=80/tcp --permanent

2.ZeroRPC案例:

zeroRPC用起來和這個差不多,也簡單舉個例子吧:

把服務的某個方法注冊到RPCServer中,供外部服務調用

import zerorpcclass Test(object):def say_hi(self, name):return f"Hi,My Name is{name}"# 注冊一個Test的實例
server = zerorpc.Server(Test())
server.bind("tcp://0.0.0.0:50051")
server.run()

調用服務端代碼

import zerorpcclient = zerorpc.Client("tcp://192.168.36.123:50051")
result = client.say_hi("RPC")
print(result)

3.3.簡單版自定義RPC

看了上面的引入案例,是不是感覺RPC不過如此?NoNoNo,要是真這么簡單也就談不上RPC架構了,上面兩個是最簡單的RPC服務了,可以這么說:生產環境基本上用不到,只能當案例練習罷了,對Python來說,最常用的RPC就兩個gRPC and Thrift

PS:國產最出名的是Dubbo and Tars,Net最常用的是gRPCThriftSurging

1.RPC服務的流程

要自己實現一個RPC Server那么就得了解整個流程了:

  1. Client(調用者)以本地調用的方式發起調用
  2. 通過RPC服務進行遠程過程調用(RPC的目標就是要把這些步驟都封裝起來,讓使用者感覺不到這個過程)
    1. 客戶端的RPC Proxy組件收到調用后,負責將被調用的方法名、參數等打包編碼成自定義的協議
    2. 客戶端的RPC Proxy組件在打包完成后通過網絡把數據包發送給RPC Server
    3. 服務端的RPC Proxy組件把通過網絡接收到的數據包按照相應格式進行拆包解碼,獲取方法名和參數
    4. 服務端的RPC Proxy組件根據方法名和參數進行本地調用
    5. RPC Server(被調用者)本地執行后將結果返回給服務端的RPC Proxy
    6. 服務端的RPC Proxy組件將返回值打包編碼成自定義的協議數據包,并通過網絡發送給客戶端的RPC Proxy組件
    7. 客戶端的RPC Proxy組件收到數據包后,進行拆包解碼,把數據返回給Client
  3. Client(調用者)得到本次RPC調用的返回結果

用一張時序圖來描述下整個過程:
4.時序圖.png

PS:RPC Proxy有時候也叫Stub(存根):(Client Stub,Server Stub)

為屏蔽客戶調用遠程主機上的對象,必須提供某種方式來模擬本地對象,這種本地對象稱為存根(stub),存根負責接收本地方法調用,并將它們委派給各自的具體實現對象

PRC服務實現的過程中其實就兩核心點:

  1. 消息協議:客戶端調用的參數和服務端的返回值這些在網絡上傳輸的數據以何種方式打包編碼和拆包解碼
    • 經典代表:Protocol Buffers
  2. 傳輸控制:在網絡中數據的收發傳輸控制具體如何實現(TCP/UDP/HTTP

2.手寫RPC

下面我們就根據上面的流程來手寫一個簡單的RPC:

1.Client調用:

# client.py
from client_stub import ClientStubdef main():stub = ClientStub(("192.168.36.144", 50051))result = stub.get("sum", (1, 2))print(f"1+2={result}")result = stub.get("sum", (1.1, 2))print(f"1.1+2={result}")time_str = stub.get("get_time")print(time_str)if __name__ == "__main__":main()

輸出:

1+2=3
1.1+2=3.1
Wed Jan 16 22

2.Client Stub,客戶端存根:(主要有打包解包、和RPC服務器通信的方法)

# client_stub.py
import socketclass ClientStub(object):def __init__(self, address):"""address ==> (ip,port)"""self.socket = socket.socket()self.socket.connect(address)def convert(self, obj):"""根據類型轉換成對應的類型編號"""if isinstance(obj, int):return 1if isinstance(obj, float):return 2if isinstance(obj, str):return 3def pack(self, func, args):"""打包:把方法和參數拼接成自定義的協議格式:func:函數名@params:類型-參數,類型2-參數2..."""result = f"func:{func}"if args:params = ""# params:類型-參數,類型2-參數2...for item in args:params += f"{self.convert(item)}-{item},"# 去除最后一個,result += f"@params:{params[:-1]}"# print(result)  # log 輸出return result.encode("utf-8")def unpack(self, data):"""解包:獲取返回結果"""msg = data.decode("utf-8")# 格式應該是"data:xxxx"params = msg.split(":")if len(params) > 1:return params[1]return Nonedef get(self, func, args=None):"""1.客戶端的RPC Proxy組件收到調用后,負責將被調用的方法名、參數等打包編碼成自定義的協議"""data = self.pack(func, args)# 2.客戶端的RPC Proxy組件在打包完成后通過網絡把數據包發送給RPC Serverself.socket.send(data)# 等待服務端返回結果data = self.socket.recv(2048)if data:return self.unpack(data)return None

簡要說明下:(我根據流程在Code里面標注了,看起來應該很輕松)

之前有說到核心其實就是消息協議and傳輸控制,我客戶端存根的消息協議是自定義的格式(后面會說簡化方案):func:函數名@params:類型-參數,類型2-參數2...,傳輸我是基于TCP進行了簡單的封裝


3.Server端:(實現很簡單)

# server.py
import socket
from server_stub import ServerStubclass RPCServer(object):def __init__(self, address, mycode):self.mycode = mycode# 服務端存根(RPC Proxy)self.server_stub = ServerStub(mycode)# TCP Socketself.socket = socket.socket()# 端口復用self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 綁定端口self.socket.bind(address)def run(self):self.socket.listen()while True:# 等待客戶端連接client_socket, client_addr = self.socket.accept()print(f"來自{client_addr}的請求:\n")# 交給服務端存根(Server Proxy)處理self.server_stub.handle(client_socket, client_addr)if __name__ == "__main__":from server_code import MyCodeserver = RPCServer(('', 50051), MyCode())print("Server啟動ing,Port:50051")server.run()

為了簡潔,服務端代碼我單獨放在了server_code.py中:

# 5.RPC Server(被調用者)本地執行后將結果返回給服務端的RPC Proxy
class MyCode(object):def sum(self, a, b):return a + bdef get_time(self):import timereturn time.ctime()

4.然后再看看重頭戲Server Stub:

# server_stub.py
import socketclass ServerStub(object):def __init__(self, mycode):self.mycode = mycodedef convert(self, num, obj):"""根據類型編號轉換類型"""if num == "1":obj = int(obj)if num == "2":obj = float(obj)if num == "3":obj = str(obj)return objdef unpack(self, data):"""3.服務端的RPC Proxy組件把通過網絡接收到的數據包按照相應格式進行拆包解碼,獲取方法名和參數"""msg = data.decode("utf-8")# 格式應該是"格式:func:函數名@params:類型編號-參數,類型編號2-參數2..."array = msg.split("@")func = array[0].split(":")[1]if len(array) > 1:args = list()for item in array[1].split(":")[1].split(","):temps = item.split("-")# 類型轉換args.append(self.convert(temps[0], temps[1]))return (func, tuple(args))  # (func,args)return (func, )def pack(self, result):"""打包:把方法和參數拼接成自定義的協議"""# 格式:"data:返回值"return f"data:{result}".encode("utf-8")def exec(self, func, args=None):"""4.服務端的RPC Proxy組件根據方法名和參數進行本地調用"""# 如果沒有這個方法則返回Nonefunc = getattr(self.mycode, func, None)if args:return func(*args)  # 解包else:return func()  # 無參函數def handle(self, client_socket, client_addr):while True:# 獲取客戶端發送的數據包data = client_socket.recv(2048)if data:try:data = self.unpack(data)  # 解包if len(data) == 1:data = self.exec(data[0])  # 執行無參函數elif len(data) > 1:data = self.exec(data[0], data[1])  # 執行帶參函數else:data = "RPC Server Error Code:500"except Exception as ex:data = "RPC Server Function Error"print(ex)# 6.服務端的RPC Proxy組件將返回值打包編碼成自定義的協議數據包,并通過網絡發送給客戶端的RPC Proxy組件data = self.pack(data)  # 把函數執行結果按指定協議打包# 把處理過的數據發送給客戶端client_socket.send(data)else:print(f"客戶端:{client_addr}已斷開\n")break

再簡要說明一下:里面方法其實主要就是解包執行函數返回值打包

輸出圖示:
3.div.png

再貼一下上面的時序圖:
4.時序圖.png

課外拓展:

HTTP1.0、HTTP1.1 和 HTTP2.0 的區別
https://www.cnblogs.com/heluan/p/8620312.html簡述分布式RPC框架
https://blog.csdn.net/jamebing/article/details/79610994分布式基礎—RPC
http://www.dataguru.cn/article-14244-1.html

4.RPC簡化與提煉

上篇回顧:萬物互聯之~RPC專欄 https://www.cnblogs.com/dunitian/p/10279946.html

上節課解答

之前有網友問,很多開源的RPC中都是使用路由表,這個怎么實現?

其實路由表實現起來也簡單,代碼基本上不變化,就修改一下server_stub.py__init__exe兩個方法就可以了:

class ServerStub(object):def __init__(self, mycode):self.func_dict = dict()# 初始化一個方法名和方法的字典({func_name:func})for item in mycode.__dir__():if not item.startswith("_"):self.func_dict[item] = getattr(mycode, item)def exec(self, func, args=None):"""4.服務端的RPC Proxy組件根據方法名和參數進行本地調用"""# 如果沒有這個方法則返回None# func = getattr(self.mycode, func, None)func = self.func_dict[func]if args:return func(*args)  # 解包else:return func()  # 無參函數

4.1.Json序列化

Python比較6的同志對上節課的Code肯定嗤之以鼻,上次自定義協議是同的通用方法,這節課我們先來簡化下代碼:

再貼一下上節課的時序圖:
4.時序圖.png

1.Json知識點

官方文檔:https://docs.python.org/3/library/json.html

# 把字典對象轉換為Json字符串
json_str = json.dumps({"func": func, "args": args})# 把Json字符串重新變成字典對象
data = json.loads(data)
func, args = data["func"], data["args"]

需要注意的就是類型轉換了(eg:python tuple ==> json array

PythonJSON
dictobject
list, tuplearray
strstring
int, floatnumber
Truetrue
Falsefalse
Nonenull

PS:序列化:json.dumps(obj),反序列化:json.loads(json_str)

2.消息協議采用Json格式

在原有基礎上只需要修改下Stubpackunpack方法即可

Client_Stub(類型轉換都省掉了)

import json
import socketclass ClientStub(object):def pack(self, func, args):"""打包:把方法和參數拼接成自定義的協議格式:{"func": "sum", "args": [1, 2]}"""json_str = json.dumps({"func": func, "args": args})# print(json_str)  # log 輸出return json_str.encode("utf-8")def unpack(self, data):"""解包:獲取返回結果"""data = data.decode("utf-8")# 格式應該是"{data:xxxx}"data = json.loads(data)# 獲取不到就返回Nonereturn data.get("data", None)# 其他Code我沒有改變

Server Stub()

import json
import socketclass ServerStub(object):def unpack(self, data):"""3.服務端的RPC Proxy組件把通過網絡接收到的數據包按照相應格式進行拆包解碼,獲取方法名和參數"""data = data.decode("utf-8")# 格式應該是"格式:{"func": "sum", "args": [1, 2]}"data = json.loads(data)func, args = data["func"], data["args"]if args:return (func, tuple(args))  # (func,args)return (func, )def pack(self, result):"""打包:把方法和參數拼接成自定義的協議"""# 格式:"data:返回值"json_str = json.dumps({"data": result})return json_str.encode("utf-8")# 其他Code我沒有改變

輸出圖示:
3.div.png

4.2.Buffer序列化

RPC其實更多的是二進制的序列化方式,這邊簡單介紹下

1.pickle知識點

官方文檔:https://docs.python.org/3/library/pickle.html

用法和Json類似,PS:序列化:pickle.dumps(obj),反序列化:pickle.loads(buffer)

2.簡單案例

和Json案例類似,也只是改了packunpack,我這邊就貼一下完整代碼(防止被吐槽)

1.Client

# 和上一節一樣
from client_stub import ClientStubdef main():stub = ClientStub(("192.168.36.144", 50051))result = stub.get("sum", (1, 2))print(f"1+2={result}")result = stub.get("sum", (1.1, 2))print(f"1.1+2={result}")time_str = stub.get("get_time")print(time_str)if __name__ == "__main__":main()

2.ClientStub

import socket
import pickleclass ClientStub(object):def __init__(self, address):"""address ==> (ip,port)"""self.socket = socket.socket()self.socket.connect(address)def pack(self, func, args):"""打包:把方法和參數拼接成自定義的協議"""return pickle.dumps((func, args))def unpack(self, data):"""解包:獲取返回結果"""return pickle.loads(data)def get(self, func, args=None):"""1.客戶端的RPC Proxy組件收到調用后,負責將被調用的方法名、參數等打包編碼成自定義的協議"""data = self.pack(func, args)# 2.客戶端的RPC Proxy組件在打包完成后通過網絡把數據包發送給RPC Serverself.socket.send(data)# 等待服務端返回結果data = self.socket.recv(2048)if data:return self.unpack(data)return None

3.Server

# 和上一節一樣
import socket
from server_stub import ServerStubclass RPCServer(object):def __init__(self, address, mycode):self.mycode = mycode# 服務端存根(RPC Proxy)self.server_stub = ServerStub(mycode)# TCP Socketself.socket = socket.socket()# 端口復用self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 綁定端口self.socket.bind(address)def run(self):self.socket.listen()while True:# 等待客戶端連接client_socket, client_addr = self.socket.accept()print(f"來自{client_addr}的請求:\n")try:# 交給服務端存根(Server Proxy)處理self.server_stub.handle(client_socket, client_addr)except Exception as ex:print(ex)if __name__ == "__main__":from server_code import MyCodeserver = RPCServer(('', 50051), MyCode())print("Server啟動ing,Port:50051")server.run()

4.ServerCode

# 和上一節一樣
# 5.RPC Server(被調用者)本地執行后將結果返回給服務端的RPC Proxy
class MyCode(object):def sum(self, a, b):return a + bdef get_time(self):import timereturn time.ctime()

5.ServerStub

import socket
import pickleclass ServerStub(object):def __init__(self, mycode):self.mycode = mycodedef unpack(self, data):"""3.服務端的RPC Proxy組件把通過網絡接收到的數據包按照相應格式進行拆包解碼,獲取方法名和參數"""func, args = pickle.loads(data)if args:return (func, args)  # (func,args)return (func, )def pack(self, result):"""打包:把方法和參數拼接成自定義的協議"""return pickle.dumps(result)def exec(self, func, args=None):"""4.服務端的RPC Proxy組件根據方法名和參數進行本地調用"""# 如果沒有這個方法則返回Nonefunc = getattr(self.mycode, func)if args:return func(*args)  # 解包else:return func()  # 無參函數def handle(self, client_socket, client_addr):while True:# 獲取客戶端發送的數據包data = client_socket.recv(2048)if data:try:data = self.unpack(data)  # 解包if len(data) == 1:data = self.exec(data[0])  # 執行無參函數elif len(data) > 1:data = self.exec(data[0], data[1])  # 執行帶參函數else:data = "RPC Server Error Code:500"except Exception as ex:data = "RPC Server Function Error"print(ex)# 6.服務端的RPC Proxy組件將返回值打包編碼成自定義的協議數據包,并通過網絡發送給客戶端的RPC Proxy組件data = self.pack(data)  # 把函數執行結果按指定協議打包# 把處理過的數據發送給客戶端client_socket.send(data)else:print(f"客戶端:{client_addr}已斷開\n")break

輸出圖示:
3.div.png

然后關于RPC高級的內容(會涉及到注冊中心),咱們后面說架構的時候繼續,網絡這邊就說到這

轉載于:https://www.cnblogs.com/dunitian/p/10279946.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276138.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276138.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276138.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python判斷字符大小寫轉換_Python 字符串大小寫轉換的簡單實例

①所有字母都轉換為大寫# -*- coding:utf-8 -*-if __name__ "__main__":a hello, world!print(a.upper())輸出:HELLO, WORLD!②所有字母都轉換為小寫# -*- coding:utf-8 -*-if __name__ "__main__":a HELLO, WORLD!print(a.lower())輸出&am…

正則表達式如何匹配正反斜杠

轉載鏈接:http://wiki.ubuntu.org.cn/Python%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97#.E5.8F.8D.E6.96.9C.E6.9D.A0.E7.9A.84.E9.BA.BB.E7.83.A6 反斜杠的麻煩 在早期規定中,正則表達式用反斜杠字符 ("…

前端進階必備Node.js,你得了解一下

作為前端開發,工作中肯定離不開 JavaScript ,而 Node.js 是基于 JavaScript 語言和 V8 引擎的 Web 服務器項目,讓你可以直接使用 JavaScript 來搭架服務器。而且在 Node 環境下,通過模塊化的 JavaScript 代碼,加上函數…

Google推出“Google實驗室” Ad Innovations

4月1日消息,據國外媒體報道,Google近日推出了Ad Innovations功能,類似于“Google實驗室”,但專門用于展示最新的廣告技術、方案等,并征求廣告主的反饋。 目前Ad Innovations已有數款新功能,Google將按照反饋…

JS中utf8和GBK的字符編碼轉換

1、PHP中的 json_encode 函數只限編碼UTF-8的數據,當轉換GBK或者GB2312等編碼的數據時,會將漢字轉為NULL。 2、JavaScript 中json 的使用: ① 將對象轉為json字符串:JSON.stringify(obj)② 將json轉為對象:dataObj ev…

tcptracerte參數_TCP/IP詳解學習筆記(4)-ICMP協議,ping和Traceroute【轉】

1.IMCP協議介紹前面講到了,IP協議并不是一個可靠的協議,它不保證數據被送達,那么,自然的,保證數據送達的工作應該由其他的模塊來完成。其中一個重要的模塊就是ICMP(網絡控制報文)協議。當傳送IP數據包發生錯誤&#xf…

[實踐系列]Promises/A+規范

前言 [實踐系列] 主要是讓我們通過實踐去加深對一些原理的理解。 實踐系列-前端路由 實踐系列-Babel原理 有興趣的同學可以關注 實踐系列 。 求star求follow~ 什么是Promise ? Promise是JS異步編程中的重要概念,異步抽象處理對象,是目前比較流行Javas…

Web Components 上手指南

現在的前端開發基本離不開 React、Vue 這兩個框架的支撐,而這兩個框架下面又衍生出了許多的自定義組件庫:Element(Vue)Ant Design(React)這些組件庫的出現,讓我們可以直接使用已經封裝好的組件&…

隱藏網頁文件的后綴(IIS測試通過)!

網上很多網站會看到如這樣的地址: /content?actadd&id1 /column?actedit&id2 原本是 /content.asp?actadd&id1 /column.asp?actedit&id2 這樣的效果就是在iis上做了下手腳,使用了rewrite重寫組件,就可以實現。 rewrite.rar轉載于:ht…

Linux下查看文件內容的ASCII碼以檢查內容的編碼一致

轉載鏈接:http://blog.csdn.net/tiantang46800/article/details/6460567 ascii查詢方式,查看文件以ascii顯示,od命令 隨著計算機飛速的發展,很多人開始學習Linux,怎樣才能學好Linux,一定要學好Linux的命令…

乘基取整法是什么_十進制小數轉二進制小數乘2取整法的直觀理解

乘2取整法介紹舉例:0.35轉換成二進制0.3520.7 取0(d1)0.721.4 取1(d2)0.420.8 取0(d3)0.821.6 取1(d4)0.621.2 取1(d5)0.220.4 取0(d6)直到滿足規定的位數為止所以(0.35)10(0.d1d2d3d4d5d6)2(0.010110)2這個方法不難掌握,就是有點不好理解&#xf…

如何遠程連接Windows和linux服務器

linux的方法在下面 Windows服務器遠程連接 登錄控制臺查看服務器系統是什么系統例如阿里云的ECS服務器 Windows系統可以使用微軟自帶的遠程工具進行連接,可以連接的系統有Windows server 和Windows 7-10 等等系列;Windows系統,例如Windows10系…

URL是什么

URL是什么意思? 懸賞分:0 - 提問時間2006-3-12 08:14我在玩QQ空間的時候,在添加音樂時會有一個添加URL的地方‘~我是想問那是什么意思???提問者: caoyiwang1107 - 魔法學徒 一級 其他…

手把手教你接入前端熱門抓包神器 - whistle

大家好,我是若川,今天推薦騰訊前端團隊的這篇好文。whistle 是一個基于 Node.js 的跨平臺網絡調試工具。最近隨著 Nohost 的開源,有不少同學問了 whistle 相關的問題,本篇文章將結合幾個常見的業務場景介紹如何在本地前端項目開發…

Linux命令之hexdump - ”十六“進制查看器

轉載鏈接:http://codingstandards.iteye.com/blog/805778 用途說明 hexdump命令一般用來查看”二進制“文件的十六進制編碼,但實際上它的用途不止如此,手冊頁上的說法是“ascii, decimal, hexadecimal, octal dump“,這也就是本文…

使用數據增強技術提升模型泛化能力

在《提高模型性能,你可以嘗試這幾招...》一文中,我們給出了幾種提高模型性能的方法,但這篇文章是在訓練數據集不變的前提下提出的優化方案。其實對于深度學習而言,數據量的多寡通常對模型性能的影響更大,所以擴充數據規…

關于不同用戶進入系統報錯的請求

我自己搞了個系統,用超級用戶進入系統正常,用普通用戶進入系統就報錯,Microsoft JET Database Engine (0x80040E07) 標準表達式中數據類型不匹配。 /xs/huiyuan/huiyuan_bf.asp, 第 203 行 代碼如下請各位高手幫忙 <% if request.Cookies("shiwei_username")"…

React 與 Vue 框架的設計思路大 PK

大家好&#xff0c;我是若川。今天分享一篇框架設計思路的好文。關于我 大家好我是花果山的大圣&#xff0c;今天很榮幸&#xff0c;有機會跟大家分享一下很多年輕人感興趣的話題《 Vue 和 React 設計思想 PK》,個人水平有限&#xff0c;如果有理解不到位的請傾盆&#xff0c;大…

php foreach id是否存在數組_請糾正這 5 個 PHP 編碼小陋習

在做過大量的代碼審查后&#xff0c;我經常看到一些重復的錯誤&#xff0c;以下是糾正這些錯誤的方法。在循環之前測試數組是否為空$items [];// ...if (count($items) > 0) {foreach ($items as $item) {// process on $item ...}}foreach以及數組函數 (array_*) 可以處理…

1161轉進制(C語言)

一&#xff1a;題目 二&#xff1a;思路分析 1.首先該題目讓我們使用遞歸求十進制轉其他進制 2.其次&#xff0c;我們要知道十進制轉換為其他進制怎么轉換&#xff0c;以例題所給的數據為例 由此圖可以看出&#xff0c;十進制轉換為其他進制&#xff0c;是輾轉相除法&#xf…