多进程的操作和案例

文章目录

  • 高效编程
    • 一、多任务原理
    • 二、进程
      • 1、概念
      • 2、使用进程
      • 3、全局变量在多个子进程中不能共享
      • 4、启动大量子进程
      • 5、map方法
      • 6、单进程与多进程复制文件对比
      • 7、进程间通信
      • 8、进程实现生产者消费者
      • 9、案例(抓取斗图)

高效编程

一、多任务原理

  • 概念

    现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统

  • 什么叫多任务?

    就是操作系统可以同时运行多个任务

  • 单核CPU实现多任务原理

    操作系统轮流让各个任务交替执行,QQ执行2us(微秒),切换到微信,在执行2us,再切换到陌陌,执行2us……。表面是看,每个任务反复执行下去,但是CPU调度执行速度太快了,导致我们感觉就像所有任务都在同时执行一样

    在这里插入图片描述

  • 多核CPU实现多任务原理

    ​ 真正的秉性执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行

    在这里插入图片描述

  • 并发与并行

    • 并发

      CPU调度执行速度太快了,看上去一起执行,任务数多于CPU核心数

    • 并行

      真正一起执行,任务数小于等于CPU核心数

    • 并发是逻辑上的同时发生,并行更多是侧重于物理上的同时发生。

  • 实现多任务的方式

    • 多进程模式

      启动多个进程,每个进程虽然只有一个线程,但是多个进程可以一起执行多个任务

    • 多线程模式

      启动一个进程,在一个进程的内部启动多个线程,这样多个线程也可以一起执行多个任务

    • 多进程+多线程

      启动多个进程,每个进程再启动多个线程

    • 协程

    • 多进程+协程

二、进程

1、概念

  • 什么是进程?

    是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

  • 对于操作系统

    一个任务就是一个进程。比方说打开浏览器就是启动一个浏览器的进程,在打开一个记事本就启动一个记事本进程,如果打开两个记事本就启动两个记事本进程

2、使用进程

  • 单进程现象

    需要等待代码执行完后再执行下一段代码

    import timedef run1():while 1:print("lucky is a good man")time.sleep(1)def run2():while 1:print("lucky is a nice man")time.sleep(1)if __name__ == "__main__":run1()# 不会执行run2()函数,只有上面的run1()结束才能执行run2()run2()
    
  • 启动进程实现多任务

    • multiprocessing模块

      跨平台的多进程模块,提供了一个Process类用来示例化一个进程对象

    • Process类

      作用:创建进程(子进程)

    • __name__

      这是 Windows 上多进程的实现问题。在 Windows 上,子进程会自动 import 启动它的这个文件,而在 import 的时候是会执行这些语句的。如果你这么写的话就会无限递归创建子进程报错。所以必须把创建子进程的部分用那个 if 判断保护起来,import 的时候 __name__ 不是 __main__ ,就不会递归运行了。

      参数说明
      target指定进程执行的任务
      args给进程函数传递的参数,是一个元组

      注意:此时进程被创建,但是不会启动进程执行

    • 启动进程实现多任务

      from multiprocessing import Process

      创建子进程

      P = Process(target=run,args=(“nice”,),name=‘当前进程名称’)

      • target指定 子进程运行的函数

      • args 指定传递的参数 , 是元组类型

      • 启动进程:Process对象.start()

      获取进程信息

      • os.getpid() 获取当前进程id号
      • os.getppid() 获取当前进程的父进程id号
      • multiprocessing.current_process().name 获取当前进程名称

      父子进程的先后顺序

      • 默认 父进程的结束不能影响子进程 让父进程等待子进程结束再执行父进程

      • p.join() 阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程。

      • 全局变量在过个进程中不能共享

        注意: 在子线程中修改全局变量时对父进程中的全局变量没有影响

    • 示例代码

      import timefrom multiprocessing import Processdef run1(name):while 1:print("%s is a good man"%name)time.sleep(1)def run2():while 1:print("lucky is a nice man")time.sleep(1)if __name__ == "__main__":# 程序启动时的进程称为主进程(父进程)# 创建进程并启动p = Process(target=run1, args=("lucky",))p.start()# 主进程执行run2()函数run2()
      
  • 主进程负责调度

    主进程主要做的是调度相关的工作,一般不负责具体业务逻辑

    import time
    from multiprocessing import Processdef run1():for i in range(7):print("lucky is a good man")time.sleep(1)def run2(name, word):for i in range(5):print("%s is a %s man"%(name, word))time.sleep(1)if __name__ == "__main__":t1 = time.time()# 创建两个进程分别执行run1、run2p1 = Process(target=run1)p2 = Process(target=run2, args=("lucky", "cool"))# 启动两个进程p1.start()p2.start()# 查看耗时t2 = time.time()print("耗时:%.2f"%(t2-t1))
    
  • 父子进程的先后顺序

    主进程的结束不能影响子进程,所以可以等待子进程的结束再结束主进程,等待子进程结束,才能继续运行主进程

    p.join() 阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程。

    import time
    from multiprocessing import Processdef run1():for i in range(7):print("lucky is a good man")time.sleep(1)def run2(name, word):for i in range(5):print("%s is a %s man"%(name, word))time.sleep(1)if __name__ == "__main__":t1 = time.time()p1 = Process(target=run1)p2 = Process(target=run2, args=("lucky", "cool"))p1.start()p2.start()# 主进程的结束不能影响子进程,所以可以等待子进程的结束再结束主进程# 等待子进程结束,才能继续运行主进程p1.join()p2.join()t2 = time.time()print("耗时:%.2f"%(t2-t1))
    

3、全局变量在多个子进程中不能共享

原因:

​ 在创建子进程时对全局变量做了一个备份,父进程中num变量与子线程中的num不是一个变量

from multiprocessing import Process
#全局变量在进程中 不能共享
num = 10
def run():print("我是子进程的开始")global numnum+=1print(num)print("我是子进程的结束")
if __name__=="__main__":p = Process(target=run)p.start()p.join()print(num)

尝试列表是否能共享

from multiprocessing import Process
#全局变量在进程中 不能共享
mylist = []
def run():print("我是子进程的开始")global mylistmylist.append(1)mylist.append(2)mylist.append(3)print("我是子进程的结束")if __name__=="__main__":p = Process(target=run)p.start()p.join()print(mylist)

4、启动大量子进程

  • 获取CPU核心数

    print(‘CPU number:’ + str(multiprocessing.cpu_count()))

  • 导入

    from multiprocesssing import Pool

  • 开启并发数

    pp = Pool([参数]) #开启并发数 默认是你的核心数

  • 创建子进程,并放入进程池管理

    apply_async为非阻塞模式(并发执行)

    pp.apply_async(run,args=(i,)) #args参数 可以为元组 或者是列表[]

  • 关闭进程池

    pp.close()关闭进程池

  • join()

    在调用join之前必须先调用close,调用close之后就不能再继续添加新的进程了

    pp.join()

    进程池对象调用join,会等待进程池中所有的子进程结束完毕再去执行父进程

  • 实例

    # Pool类:进程池类
    from multiprocessing import Pool
    import time
    import random
    import multiprocessingdef run(index):print('CPU number:' + str(multiprocessing.cpu_count()))print("子进程 %d 启动"%(index))t1 = time.time()time.sleep(random.random()* 5+2)t2 = time.time()print("子进程 %d 结束,耗时:%.2f" % (index, t2-t1))if __name__ == "__main__":print("启动主进程……")# 创建进程池对象# 由于pool的默认值为CPU的核心数,假设有4核心,至少需要5个子进程才能看到效果# Pool()中的值表示可以同时执行进程的数量pool = Pool(2)for i in range(1, 7):# 创建子进程,并将子进程放到进程池中统一管理pool.apply_async(run, args=(i,))# 等待子进程结束# 关闭进程池:在关闭后就不能再向进程池中添加进程了# 进程池对象在调用join之前必须先关闭进程池pool.close()#pool对象调用join,主进程会等待进程池中的所有子进程结束才会继续执行主进程pool.join()print("结束主进程……")
    

    get方法:获取进程的返回值

    from multiprocessing import Lock, Pool
    import timedef function(index):print('Start process: ', index)time.sleep(2)print('End process', index)return indexif __name__ == '__main__':pool = Pool(processes=3)for i in range(4):result = pool.apply_async(function, (i,))print(result.get()) #获取每个 子进程的返回值print("Started processes")pool.close()pool.join()print("Subprocess done.")
    

    注意:这样来获取每个进程的返回值 那么就会变成单进程

5、map方法

  • 概述

    如果你现在有一堆数据要处理,每一项都需要经过一个方法来处理,那么map非常适合

    比如现在你有一个数组,包含了所有的URL,而现在已经有了一个方法用来抓取每个URL内容并解析,那么可以直接在map的第一个参数传入方法名,第二个参数传入URL数组。

  • 概述

    from multiprocessing import Pool
    import requests
    from requests.exceptions import ConnectionErrordef scrape(url):try:print(requests.get(url))except ConnectionError:print('Error Occured ', url)finally:print('URL', url, ' Scraped')if __name__ == '__main__':pool = Pool(processes=3)urls = ['https://www.baidu.com','http://www.meituan.com/','http://blog.csdn.net/','http://xxxyxxx.net']pool.map(scrape, urls)
    

    在这里初始化一个Pool,指定进程数为3,如果不指定,那么会自动根据CPU内核来分配进程数。

    然后有一个链接列表,map函数可以遍历每个URL,然后对其分别执行scrape方法。

6、单进程与多进程复制文件对比

  • 单进程复制文件

    import timedef copy_file(path, toPath):with open(path, "rb") as fp1:with open(toPath, "wb") as fp2:while 1:info = fp1.read(1024)if not info:breakelse:fp2.write(info)fp2.flush()if __name__ == "__main__":t1 = time.time()for i in range(1, 5):path = r"/Users/lucky/Desktop/file/%d.mp4"%itoPath = r"/Users/lucky/Desktop/file2/%d.mp4"%icopy_file(path, toPath)t2 = time.time()print("单进程耗时:%.2f"%(t2-t1))
    
  • 多进程复制文件

    import time
    from multiprocessing import Pool
    import osdef copy_file(path, toPath):with open(path, "rb") as fp1:with open(toPath, "wb") as fp2:while 1:info = fp1.read(1024)if not info:breakelse:fp2.write(info)fp2.flush()if __name__ == "__main__":t1 = time.time()path = r"/Users/xialigang/Desktop/视频"dstPath = r"/Users/xialigang/Desktop/1视频"fileList = os.listdir(path)pool = Pool()for i in fileList:newPath1 = os.path.join(path, i)newPath2 = os.path.join(dstPath, i)pool.apply_async(copy_file, args=(newPath1, newPath2))pool.close()pool.join()t2 = time.time()print("耗时:%.2f"%(t2-t1))
    

7、进程间通信

  • 队列共享

    • 导入

      from multiprocessing import Queue

    • 使用

      que = Queue() #创建队列

      que.put(数据) #压入数据

      que.get() #获取数据

    • 队列常用函数

      Queue.empty() 如果队列为空,返回True, 反之False

      Queue.full() 如果队列满了,返回True,反之False

      Queue.get([block[, timeout]]) 获取队列,timeout等待时间

      Queue.get_nowait() 相当Queue.get(False)

      Queue.put(item) 阻塞式写入队列,timeout等待时间

      Queue.put_nowait(item) 相当Queue.put(item, False)

    • 特点:先进先出

    • 注意:

      get方法有两个参数,blocked和timeout,意思为阻塞和超时时间。默认blocked是true,即阻塞式。

      当一个队列为空的时候如果再用get取则会阻塞,所以这时候就需要吧blocked设置为false,即非阻塞式,实际上它就会调用get_nowait()方法,此时还需要设置一个超时时间,在这么长的时间内还没有取到队列元素,那就抛出Queue.Empty异常。

      当一个队列为满的时候如果再用put放则会阻塞,所以这时候就需要吧blocked设置为false,即非阻塞式,实际上它就会调用put_nowait()方法,此时还需要设置一个超时时间,在这么长的时间内还没有放进去元素,那就抛出Queue.Full异常。

      另外队列中常用的方法

    • 队列的大小

      Queue.qsize() 返回队列的大小 ,不过在 Mac OS 上没法运行。

    实例

    import multiprocessing
    queque = multiprocessing.Queue() #创建 队列
    #如果在子进程 和主进程 之间 都压入了数据 那么在主进程 和 子进程 获取的就是 对方的数据
    def fun(myque):# print(id(myque)) #获取当前的队列的存储地址  依然是拷贝了一份myque.put(['a','b','c']) #在子进程里面压入数据# print("子进程获取",myque.get())#获取队列里面的值if __name__=='__main__':# print(id(queque))queque.put([1,2,3,4,5]) #将列表压入队列  如果主进程也压入了数据 那么在主进程取的就是在主进程压入的数据 而不是子进程的p = multiprocessing.Process(target=fun,args=(queque,))p.start()p.join()print("主进程获取",queque.get())#在主进程进行获取print("主进程获取",queque.get())#在主进程进行获取# print("主进程获取",queque.get(block=True, timeout=1))#在主进程进行获取
  • 字典共享

    • 导入

      import multiprocess

    • 概述

      Manager是一个进程间高级通信的方法 支持Python的字典和列表的数据类型

    • 创建字典

      myDict = multiprocess.Manager().dict()

    实例

    import multiprocessingdef fun(mydict):# print(mylist)mydict['x'] = 'x'mydict['y'] = 'y'mydict['z'] = 'z'if __name__=='__main__':# Manager是一种较为高级的多进程通信方式,它能支持Python支持的的任何数据结构。mydict = multiprocessing.Manager().dict()p = multiprocessing.Process(target=fun,args=(mydict,))p.start()p.join()print(mydict)
    
  • 列表共享

    • 导入

      import multiprocess

    • 创建列表

      myDict = multiprocess.Manager().list()

    实例(字典与列表共享)

    import multiprocessingdef fun(List):# print(mylist)List.append('x')List.append('y')List.append('z')if __name__=='__main__':# Manager是一种较为高级的多进程通信方式,它能支持Python支持的的任何数据结构。List = multiprocessing.Manager().list()p = multiprocessing.Process(target=fun,args=(List,))p.start()p.join()print(List)
    
  • 注意

    进程名.terminate() 强行终止子进程

  • deamon

    在这里介绍一个属性,叫做deamon。每个进程程都可以单独设置它的属性,如果设置为True,当父进程结束后,子进程会自动被终止。

    进程.daemon = True

    设置在start()方法之前

    import multiprocessing
    import time
    def fun():time.sleep(100)
    if __name__=='__main__':p = multiprocessing.Process(target=fun)p.daemon = Truep.start()print('over')
    
  • 进程名.terminate() 强行终止子进程

    import multiprocessing
    import time
    def fun():time.sleep(100)
    if __name__=='__main__':p = multiprocessing.Process(target=fun)p.start()p.terminate()p.join()print('over')
    

8、进程实现生产者消费者

生产者消费者模型描述:

生产者是指生产数据的任务,消费者是指消费数据的任务。

当生产者的生产能力远大于消费者的消费能力,生产者就需要等消费者消费完才能继续生产新的数据,同理,如果消费者的消费能力远大于生产者的生产能力,消费者就需要等生产者生产完数据才能继续消费,这种等待会造成效率的低下,为了解决这种问题就引入了生产者消费者模型。

生产者/消费者问题可以描述为:两个或者更多的进程(线程)共享同一个缓冲区,其中一个或多个进程(线程)作为“生产者”会不断地向缓冲区中添加数据,另一个或者多个进程(线程)作为“消费者”从缓冲区中取走数据。

  • 代码

    from multiprocessing import Process
    from multiprocessing import Queue
    import timedef product(q):print("启动生产子进程……")for data in ["good", "nice", "cool", "handsome"]:time.sleep(2)print("生产出:%s"%data)# 将生产的数据写入队列q.put(data)print("结束生产子进程……")def t(q):print("启动消费子进程……")while 1:print("等待生产者生产数据")# 获取生产者生产的数据,如果队列中没有数据会阻塞,等待队列中有数据再获取value = q.get()print("消费者消费了%s数据"%(value))print("结束消费子进程……")if __name__ == "__main__":q = Queue()p1 = Process(target=product, args=(q,))p2 = Process(target=customer, args=(q,))p1.start()p2.start()p1.join()# p2子进程里面是死循环,无法等待它的结束# p2.join()# 强制结束子进程p2.terminate()print("主进程结束")
    

9、案例(抓取斗图)

from multiprocessing import Process,Queue
from concurrent.futures import ThreadPoolExecutor
from lxml import etree
import time
import requestsheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36"
}def get_img_src(url, q):"""进程1: 负责提取页面中所有的img的下载地址将图片的下载地址通过队列. 传输给另一个进程进行下载"""resp = requests.get(url, headers=headers)tree = etree.HTML(resp.text)srcs = tree.xpath("//li[@class='list-group-item']//img[@referrerpolicy='no-referrer']/@data-original")for src in srcs:q.put(src.strip())resp.close()def download_img(q):"""进程2: 将图片的下载地址从队列中提取出来. 进行下载."""with ThreadPoolExecutor(20) as t:while 1:try:s = q.get(timeout=20)t.submit(donwload_one, s)except Exception as e:print(e)breakdef donwload_one(s):# 单纯的下载功能resp = requests.get(s, headers=headers)file_name = s.split("/")[-1]# 请提前创建好img文件夹with open(f"img/{file_name}", mode="wb") as f:f.write(resp.content)print("一张图片下载完毕", file_name)resp.close()if __name__ == '__main__':t1 = time.time()q = Queue()  # 两个进程必须使用同一个队列. 否则数据传输不了p_list = []for i in range(1, 11):url = f"https://www.pkdoutu.com/photo/list/?page={i}"p = Process(target=get_img_src, args=(url, q))p_list.append(p)for p in p_list:p.start()p2 = Process(target=download_img, args=(q,))p2.start()for p in p_list:p.join()p2.join()print((time.time()-t1)/60)
# 0.49572664896647134

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/12677.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【更新中】《硬件架构的艺术》笔记(二):时钟与复位

本章主要针对ASIC设计给出建议,独立于CAD工具以及工艺,主要针对模块设计和存储器接口。 同步设计 这是对时钟域控制最安全的方法,单个主时钟和单个主置位/复位信号驱动设计中所有时序器件。 避免使用行波计数器 行波计数器:用…

Spring Boot编程训练系统:架构设计与技术选型

3系统分析 3.1可行性分析 通过对本编程训练系统实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本编程训练系统采用SSM框架,JAVA作为开发语言&#…

DAY111PHP开发框架THIKNPHP反序列化POP利用链RCE执行文件删除

一、文件删除利用链分析 1、__destruct发现调用$this->removeFiles(); 2、removeFiles();函数方法file_exists,unlink($filename);文件删除功能 3、unserialize(base64_decode($_GET[id])); 有可控变量 4、pop文件删除利用链的使用 只有在这个类中调用 Files可…

如何使用亿图脑图MindMaster大纲功能

亿图脑图MindMaster作为一款出色的思维导图软件,具备丰富的编辑和展示功能。就拿大纲模式而言,用户可以清晰地浏览思维导图上的内容。因为大纲功能可以将思维导图上的内容以文档归纳的形式呈现出来,便于用户分类记忆。 自由切换思维导图模式…

Python的Eval函数执行结果和Lua脚本中LuaFunction的执行结果有何异同

最近在维护一个项目的时候,同时用到了Python和Lua两种脚本语言,发现很多有意思的东西,比如Python的Eval函数和Lua的LuaFunction函数,他们都是返回目标函数的句柄,那么在用法和机制上又有什么不同呢?为了全面…

DQN强化训练agent玩是男人就下xx层小游戏

游戏代码参考Python是男人就下一百层小游戏源代码_是男人就下一百层完整代码python-CSDN博客 在游戏作者代码基础上修改了下使该游戏在失败后能自动重新开始,方便后续能不间断训练 def reset_game(self):self.score 0self.end Falseself.last 6 * SIDEself.dire …

2024最新版JavaScript逆向爬虫教程-------基础篇之面向对象

目录 一、概念二、对象的创建和操作 2.1 JavaScript创建对象的方式2.2 对象属性操作的控制2.3 理解JavaScript创建对象 2.3.1 工厂模式2.3.2 构造函数2.3.3 原型构造函数 三、继承 3.1 通过原型链实现继承3.2 借用构造函数实现继承3.3 寄生组合式继承 3.3.1 对象的原型式继承…

网络编程示例之网络socket程序编程

注意:学习资料可在ElfBoard官方网站“资料”专区获取。 本节用到的源码路径:ELF 1开发板资料包->03-例程源码->03-1 命令行例程源码->05_elf1_cmd_net tcp协议程序 tcp_server.c 服务端仍然是按照如下顺序进行编写: socket()//创…

标准的渠道治理方法

在当今竞争激烈的市场环境中,品牌的渠道管理犹如一座大厦的基石,至关重要。而其中,对渠道价格的治理更是关键环节,直接关系到品牌的生死存亡与长远发展。 当品牌渠道中不幸出现低价、窜货链接时,一场关乎品牌未来走向…

双指针算法的妙用:提高代码效率的秘密(3)

双指针算法的妙用:提高代码效率的秘密(3) 前言: 小编在昨日讲述了关于双指针算法的两个题目,今日继续分享两个题目的解析,我相信,我只要坚持每天啥刷题,算法能力终究会提高的&…

动力商城-03 Idea集成apifox Mybatis-Plus字段策略

1.Idea下载apifox插件 2.新建令牌放入Idea 3.右键上传到对应接口 4.设置前置url 插件能够自动识别swagger注解 Mybatis-Plus字段策略 1、FieldStrategy作用 Mybatis-Plus字段策略FieldStrategy的作用主要是在进行新增、更新时,根据配置的策略判断是否对实体对…

11.11--final关键字和抽象类

一 java 1.final 关键字-----放在 访问修饰符后面 1)防止被继承 2)防止 父类方法 被重写 3)防止 类中的 属性 被修改 4)防止 局部属性 被修改 1.2.细节 1)final 修饰属性 必须赋初值 ------------------------------…

IntelliJ+SpringBoot项目实战(三)---基于源代码直接生成漂亮的接口文档

在SpringBoot中可以集成代码插件自动生成接口文档,而且生成的文档很漂亮,除了接口功能介绍、传入参数、响应参数,还具体类似postman的功能,可调用接口进行测试,另外还可以下单WORD版、.md,html格式的文档。下面我们先看…

TemplatesImpl 在Shiro中的利用链学习1

一、前言 在前面的学习中,我们学习了CC1、CC6链,其中CC1链受限于Java8u71版本,而CC6则是通杀的利用链;后来又将 TemplateImpl 融入到 CommonsCollections 利用链中,绕过了 InvokerTransformer 不能使用的限制&#xf…

中仕公考:2025年省考请注意!

打算参加25年省考的考生们注意啦!如果打算参加2025年公务员省考,从这个时间点开始备考刚刚好,如果还不知道怎么备考的,看这篇就够了! 省考流程: 网上报名——资格审查——确认缴费——查看报名序号——准考证打印——笔试——成…

开发RAG应用,你必须知道的7个Embedding模型

在自然语言处理(NLP)领域,Embedding模型是将文本数据转化为数值向量的核心技术,从而让计算机能够便捷地衡量文本间的语义关联,这种表示法已成为多种基础NLP任务的核心,如文本相似度判定、语义搜索、信息检索…

基于Java+SpringBoot学生成绩管理系统

一、作品包含 源码数据库设计文档全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据库&…

Kong API网关,微服务架构中,你看到就不想错过的选型

今天,很多公司都采用微服务架构来处理复杂业务,但随着服务数量增加,API管理成了一项繁重任务。Kong API网关,作为一款高性能的开源API网关,给开发者带来了极大便利。它不仅可以简化API的调用和管理,还拥有丰…

计算机毕业设计 | springboot+vue汽车修理管理系统 汽修厂系统(附源码)

1,项目背景 在如今这个信息时代,“汽车维修管理系统” 这种维修方式已经为越来越多的人所接受。在这种背景之下,一个安全稳定并且强大的网络预约平台不可或缺,在这种成熟的市场需求的推动下,在先进的信息技术的支持下…

使用京东API接口进行支付结算有哪些注意事项?

用京东API接口进行支付结算时,需要注意以下几个事项: 遵守京东开放平台规定:在使用京东API接口时,必须遵守京东开放平台的相关规定,不得滥用接口或进行非法操作。 保护用户隐私:为了保护用户隐私&#xff…