万物互联之~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.mzph.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…