RabbitMQ 笔记一

概览

MQ基本概念

RabbitMQ入门

基本工作模

1.MQ是什么?

MQ:Message Queue, 存储消息的中间件,是消息发送过程中的暂存容器,主要用于解决分布式系统进程间的通信。

分布式系统通信的两种方式:直接远程调用、借助第三方间接通信

为什么要使用消息中间件?

如有一个电商交易的场景,用户下单之后调用库存系统减库存,然后调用物流系统进行发货,如果刚开始交易,库存,物流都是属于一个系统,那么他们之间就是接口调用。但是随着系统的发展,各个模块业务越来越庞大、业务逻辑越来越复杂,这个时候就必然要做服务化和业务拆分。这个时候就需要考虑这些系统之间是如何交互的。首先想到的就是RPC(Remote Procedure Call),但是随着系统的发展,可能一笔交易后序需要调用几十个接口位于不同系统的接口,比如短信服务、邮件服务等等,这个时候就需要消息中间件来解决问题了。消息中间件最突出的特点就是提供数据传输的可靠性和高效性,主要解决分布式的系统数据传输需求

摘自:为什么使用消息中间件_为什么用消息中间件-CSDN博客

使用MQ的优势:

(1)应用解耦:提高系统容错性和可维护性

        整个系统耦合,会导致系统容错性低、可扩展性低、可维护性低。解耦之后,一个系统挂了,其它系统不会有问题。容错性高、可扩展性高、可维护性高。

(2)异步提速:提升用户体验和系统吞吐量.

        如果一个订单系统同步执行:则有: 订单进数据库20ms+调用子系统一300ms+调用子系统二300ms+调用子系统三300ms=920ms,用户等待920ms

        如果采用异步模式:则有 订单进数据库20ms+消息发到MQ5ms=25ms,用户等待25ms

(3)削峰填谷:提高系统稳定性

        请求瞬间增多每秒5000个,但是子系统每秒只能处理1000请求。

        削峰指的是:提高子系统的稳定性。加入中间件后,所有请求进入消息队列。帮子系统处理高并发的请求量。

        填谷指的是:大量的消息积压在MQ里。子系统每秒从MQ拉取1000个进行处理,直到处理完所有积压的消息。

使用MQ的劣势

(1)系统可用性降低

        引入的第三方插件|依赖越多,系统稳定性越差。

        如果MQ宕机,业务功能就会收到影响。需要额外的工作来确保MQ高可用性。

(2)系统复杂性提高

        没有MQ时:系统间同步远程调用

        引入MQ时:通过MQ异步调用

        引发的问题:如何保证消息不被重复消费?如何处理消息丢失?如何保证消息传递的顺序性。

(3)一致性问题

        消息:A——>MQ——>B|C|D

        如果BC处理成功,D失败了,如何保证数据一致性。

使用MQ应满足什么条件?

  1. 生产者不需要消费者的反馈。(消费者消费消息后返回值为空,这才能使异步调用成为可能)
  2. 容许短暂的不一致
  3. 用了有效果:解耦、提速、削峰等,超过引入MQ的管理成本

常见的MQ产品

2.RabbitMQ概述

RabbitMQ简介:

2007年,Rabbit技术公司 基于AMQP标准开发 Rabbit MQ1.0

使用Erlang语言(一种专门为高并发、分布式系统开发的语言|电信领域使用广泛)

AMQP协议

  1. Advanced Message Queuing Protocol高级消息队列协议
  2. 网络协议,应用层协议的一个开放标准,为面向消息的中间件设计。
  3. 2006年,AMQP规范发布(类比HTTP)
  4. 基于此协议的客户端与消息中间件之间传递消息,不受客户端/中间件产品、开发语言等限制。

RabbitMQ的组成:

Broker中间件,用于接收发信息的用用,如:RabbitMQ Server
Virtual host

虚拟机,处于多租户和安全因素设计的,把AMQP基本组件划分到一个虚拟的分组中,类似于网络中的namespace 。

应用场景:用户隔离

        多个不同用户使用同一个RabbitMQ时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue等。

Connection链接,publisher/consumer和broker之间建立TCP链接。
Channel

connection内部的逻辑链接。作为一个轻量级Connection,极大减少了操作系统建立TCP链接的开销。

存在原因:        

        如果每次访问MQ都建立链接,消息量大的时候建立TCP链接,开销非常大,效率也低。

        所以使用channel内部逻辑链接,如果应用支持多线程,通常每个thread独占一个channel通信。

AMQP method中

        包含channel id帮助客户端和message broker识别channel

        所以channel之间是完全隔离的

Exchange

交换机,message到达broker的第一站,根据分发规则,匹配查询表中的routing key, 分发消息到queue。

常见的类型

        direct:        point to point

        topic:         publisher-subscribe

        fanout:       multicast

Queue消息队列,消息最终被送到这里等待被消费。
Binding

绑定,exchange 和 queue之间的虚拟连接,binding中包含routing key.

Binding信息被保存在exchange的查询表中,是message分发的依据。

RabbitMQ的工作模式:

        6种,包含:简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing 路由模式、Topics主题模式、RPC远程调用模式。

RabbitMQ官网:RabbitMQ Tutorials — RabbitMQ

补充:

        JMS:java消息服务(Java Message Service)应用程序接口,是java平台关于面向消息的中间件API

        JMS是Java EE 规范的一种,类比JDBC.

        很多消息中间件都实现了JMS规范,如:ActiveMQ。RabbitMQ没有提供JMS的实现,但开源社区有。

3.RabbitMQ的简单实现

RabbitMQ的安装:

1.安装RabbitMQ软件:

windows环境下安装RabbitMQ(超详细)_windows安装rabbitmq-CSDN博客

java依赖:

<dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.20.0</version>
</dependency>

2.开启管理界面及配置:

  • 默认端口号:5672
  • 图形化界面地址: http://127.0.0.1:15672
  • 登陆名: guest    密码:guest   
  • 配置文件:

3.启动服务及基础配置:

RabbitMQ简单模式:

RabbitMQ生产者:

package org.example.producer;import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class SimpleProducer {public void publishMessage() throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory=new ConnectionFactory();//2.设置参数factory.setHost("127.0.0.1");//ip 默认值localhostfactory.setPort(5672);//端口 默认值5672factory.setVirtualHost("/itcast");//虚拟机 默认值/factory.setUsername("heima");factory.setPassword("heima");//3.创建链接 ConnectionConnection connection=factory.newConnection();//4.创建ChannelChannel channel=connection.createChannel();//5.创建队列Queue/*queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)queue: 队列名称durable: 是否持久化,是的话,mq重启后,数据还在。exclusive:是否独占,只能有一个消费者监听队列当connection关闭时,是否删除队列autoDelete: 是否自动删除,当没有consumer时,自动删除。arguments: 参数*/channel.queueDeclare("hello_world",true,false,false,null);//6.发送消息/*basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body) texchange:交换机名称,简单模式下交换机会使用默认的“”routingKey: 路由名称props: 配置消息body:  发送消息数据*/String body="hello rabbitMQ!";channel.basicPublish("","hello_world",null,body.getBytes());//7.释放资源channel.close();connection.close();}
}

生产者生产一个消息:

RabbitMQ消费者:

package org.example.consumer;import com.rabbitmq.client.*;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class SimpleConsumer {public static void main(String[] args) throws IOException, TimeoutException {SimpleConsumer producer=new SimpleConsumer();producer.consumerMessage();}public void consumerMessage() throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory=new ConnectionFactory();//2.设置参数factory.setHost("127.0.0.1");//ip 默认值localhostfactory.setPort(5672);//端口 默认值5672factory.setVirtualHost("/");//虚拟机 默认值/factory.setUsername("guest");factory.setPassword("guest");//3.创建链接 ConnectionConnection connection=factory.newConnection();//4.创建ChannelChannel channel=connection.createChannel();//5.创建队列Queue/*queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)queue: 队列名称durable: 是否持久化,是的话,mq重启后,数据还在。exclusive:是否独占,只能有一个消费者监听队列当connection关闭时,是否删除队列autoDelete: 是否自动删除,当没有consumer时,自动删除。arguments: 参数*/channel.queueDeclare("hello_world",true,false,false,null);//6.接收消息/*basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body) texchange:交换机名称,简单模式下交换机会使用默认的“”routingKey: 路由名称props: 配置消息body:  发送消息数据*/Consumer consumer = new DefaultConsumer(channel){/*此处重写该方法是为了打印回调结果回调方法,收到回调方法后,会自动执行该方法consumerTag: 标识envelope: 获取一些信息,交换机,路由keyproperties: 配置信息body: 数据*/@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println("consumerTag:"+consumerTag);System.out.println("Exchange:"+envelope.getExchange());System.out.println("RoutingKey:"+envelope.getRoutingKey());System.out.println("properties:"+properties);System.out.println("body:"+new String(body));}};channel.basicConsume("hello_world",true,consumer);//7.不需要释放资源,因为要监听队列}
}

总结:

P:生产者,也就是要发送消息的程序。

C:   消费者,消息的接收者,监听队列等待消息到来。

Queue: 消息队列,类似邮箱,可以缓存消息,生产者向其中投递消息,消费者从中取出消息。

4.5种基本工作模式

(1)简单模式 Hello World:

一个生产者,一个队列,一个消费者,不需要设置交换机(使用默认交换机)

(2)工作队列模式Work Queue:

特点: 一个生产者,一个队列,多个消费者(竞争关系),不需要设置交换机(使用默认交换机),同一个消息,对消费者来说是竞争关系,只有一个消费者能消费。

应用场景:对于任务过重或任务较多的情况使用工作队列可以提高任务处理速度。

例如:短信服务部署多个,只需要有一个节点发送成功即可。

发送端:发布多条消息

//创建新的队列channel.queueDeclare("work_queues",true,false,false,null); 
//发多条消息String body="hello rabbitMQ!";for(int i=0;i<10;i++){channel.basicPublish("","hello_world",null,(i+"-----"+body).getBytes());}

接收端:多个消费者竞争消费

创建两个消费者consumer1和consumer2,先启动两个消费者,再启动生产者生产消息,观察到两个消费者的消费过程。

消费者需要修改的地方:监听新的队列

channel.basicConsume("work_queues",true,consumer);

consumer1:

consumer2:

(3)发布Publish/subscribe:

        需要设置类型为fanout的交换机,并且交换机和队列进行绑定,当消息发送到交换机后,交换机将消息发送到绑定队列。

订阅模式多了一个交换机概念Exchange,且过程略有变化:

P: 消息生产者,消息发送给交换机

C:消息的接收者,监听消息队列,等待消息到来

Queue: 消息队列,接收消息,缓存消息。

Exchange: 交换机,只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失。

发送端:引入交换机,并对交换机和队列进行绑定

创建交换机、两个队列、绑定交换机和队列;发送消息并释放资源

其中交换机类型:BuiltinExchangeType枚举

关键代码

//4.创建ChannelChannel channel=connection.createChannel();//定义交换机/*exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments) throws IOException {exchange: 交换机名称type: 交换机类型DIRECT("direct"),:定向FANOUT("fanout"),:扇形(广播),发送消息到每一个消费者TOPIC("topic"),:通配符的方式HEADERS("headers");:参数匹配durable: 是否持久化autoDelete: 自动删除internal: 内部使用,一般falsearguments: 参数*/String exchangeName="test_fanout";channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT,true,false,false,null);//5.创建队列QueueString queueName1="test_fanout_queue1";String queueName2="test_fanout_queue2";channel.queueDeclare(queueName1,true,false,false,null);channel.queueDeclare(queueName2,true,false,false,null);//绑定交换机/*queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments)queue:队列名称exchange:交换机名称routingKey:路由键,绑定规则如果交换机的类型为fanout,routingKey设置为“”arguments:参数*/channel.queueBind(queueName1,exchangeName,"");channel.queueBind(queueName2,exchangeName,"");//6.发送消息String body="日志信息: 张三调用了FindAll方法...日志级别:info...";channel.basicPublish(exchangeName,"",null,body.getBytes());//7.释放资源channel.close();connection.close();

 接收端:创建两个消费者,分别监听两个队列

consumer1监听queue1,consumer2监听queue2

//Consumer1:
channel.basicConsume("test_fanout_queue1",true,consumer);//Consumer2:
channel.basicConsume("test_fanout_queue2",true,consumer);

启动生产者,消息生产正常,启动消费者1,启动消费者2,两个消费者都收到了消息。

consumer1:

consumer2: 

(4)路由模式Routing:

        需要设置类型为direct的交换机,交换机和队列进行绑定,并且指定routing key, 当发送消息到交换机后,交换机会根据routing key 将消息发送到对应的队列。

队列与交换机的绑定,不能是任意绑定,而是根据routing key绑定队列,消息根据绑定来决定分发到哪个队列中。

发送端:交换机绑定队列时,指定路由模式

注意:交换者的类型为:BuiltinExchangeType.TOPIC

关键代码:

 //4.创建ChannelChannel channel=connection.createChannel();String exchangeName="test_topic";channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC,true,false,false,null);//5.创建队列QueueString queueName1="test_routing_queue1";String queueName2="test_routing_queue2";channel.queueDeclare(queueName1,true,false,false,null);channel.queueDeclare(queueName2,true,false,false,null);//绑定交换机/*queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments)queue:队列名称exchange:交换机名称routingKey:路由键,绑定规则如果交换机的类型为fanout,routingKey设置为“”arguments:参数*/channel.queueBind(queueName1,exchangeName,"error");channel.queueBind(queueName2,exchangeName,"info");channel.queueBind(queueName2,exchangeName,"error");channel.queueBind(queueName2,exchangeName,"warning");//6.发送消息String body="日志信息: 张三调用了FindAll方法...日志级别:info...";channel.basicPublish(exchangeName,"info",null,("info:\t"+body).getBytes());channel.basicPublish(exchangeName,"error",null,("error:\t"+body).getBytes());channel.basicPublish(exchangeName,"warning",null,("warning:\t"+body).getBytes());

接收端:创建Consumer1类,Consumer2类。

consumer1 监听队列1, consumer2监听队列2

//consumer1 
channel.basicConsume("test_routing_queue1",true,consumer);//consumer2
channel.basicConsume("test_routing_queue2",true,consumer);

启动生产者生产正常,启动消费者1,启动消费者2

consumer1:

consumer2:

(5)通配符模式Topic:

        需要设置类型为topic的交换机,交换机和队列进行绑定,并且指定通配符方式的routing key, 当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列。

应用场景:如像根据日志级别监听某个子系统 系统名.error 消息,并入库

发送者

指定路由方式,但是路由方式以通配符匹配的形式存在:  #匹配一个  *匹配多个

 channel.queueBind(queueName1,exchangeName,"#.error");channel.queueBind(queueName2,exchangeName,"order.*");channel.queueBind(queueName2,exchangeName,"*.*");//6.发送消息String body="日志信息: 张三调用了FindAll方法...日志级别:info...";channel.basicPublish(exchangeName,"order.info",null,("order.info:\t"+body).getBytes());channel.basicPublish(exchangeName,"order.error",null,("order.error:\t"+body).getBytes());channel.basicPublish(exchangeName,"A.error",null,("A.error:\t"+body).getBytes());

接收端:创建Consumer1类,Consumer2类。

consumer1监听队列1,consumer2监听队列2

//Consumer1
channel.basicConsume("test_topic_queue1",true,consumer);//Consumer2
channel.basicConsume("test_topic_queue2",true,consumer);

consumer1:

consumer2:

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Idea上操作Git回退本地版本,怎么样保留已修改的文件,回退本地版本的四种方式代表什么?

Git的基本概念:Git是一个版本控制系统,用于管理代码的变更历史记录。核心概念包括仓库、分支、提交和合并。 1、可以帮助开发者合并开发的代码 2、如果出现冲突代码的合并,会提示后提交合并代码的开发者,让其解决冲突 3、代码文件版本管理 问题描述 当我们使用git提交代码…

Linux本地部署SVN服务结合内网穿透实现远程访问

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

php下curl发送cookie

目录 一&#xff1a;使用 CURLOPT_COOKIE 选项 二&#xff1a;CURLOPT_COOKIEFILE 三&#xff1a;CURLOPT_HTTPHEADER php curl发送cookie的几种方式,下面来介绍下 一&#xff1a;使用 CURLOPT_COOKIE 选项 通过设置 CURLOPT_COOKIE 选项&#xff0c;你可以将 cookie 字符…

el-table 动态渲染多级表头;一级表头根据数据动态生成,二级表头固定

一、表格需求&#xff1a; 实现一个动态表头&#xff0c;一级表头&#xff0c;根据数据动态生成&#xff0c;二级表头固定&#xff0c;每列的数据不一样&#xff0c;难点在于数据的处理。做这种表头需要两组数据&#xff0c;一组数据是实现表头的&#xff0c;另一组数据是内容…

《WebKit技术内幕》学习之十三(3):移动WebKit

3 其他机制 3.1 新渲染机制 为了移动领域更好的用户体验&#xff0c;渲染机制所做的改进主要是提升渲染性能来增加响应的速度&#xff0c;甚至不惜牺牲一些跟规范定义的行为不一致的地方。在这一小节中主要介绍三个方面的技术&#xff0c;其一是Tiled Backing Store&#x…

81.网游逆向分析与插件开发-背包的获取-装备栏数据结构的逆向分析

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;自动化助手显示物品数据-CSDN博客 然后游戏中有弓箭&#xff0c;弓箭有数量&#xff0c;可以作为突破口&#xff0c;也可以使用物品id 获取弓的方式 获取弓箭的方式 然后搜索250 然后搜索出一个 然后…

C语言-算法-线性dp

[USACO1.5] [IOI1994]数字三角形 Number Triangles 题目描述 观察下面的数字金字塔。 写一个程序来查找从最高点到底部任意处结束的路径&#xff0c;使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。 在上面的样例中&#xff0c;从 7 → 3 → 8 →…

k8s-基础知识(Pod,Deployment,ReplicaSet)

k8s职责 自动化容器部署和复制随时扩展或收缩容器容器分组group&#xff0c;并且提供容器间的负载均衡实时监控&#xff0c;即时故障发现&#xff0c;自动替换 k8s概念及架构 pod pod是容器的容器&#xff0c;可以包含多个container pod是k8s最小可部署单元&#xff0c;容器…

从物联网看智慧文旅的未来:技术与实践的完美结合,重塑旅游体验的新篇章

一、物联网技术&#xff1a;智慧文旅的基石 随着科技的飞速发展&#xff0c;物联网技术已经深入到我们生活的方方面面&#xff0c;尤其在智慧文旅领域&#xff0c;物联网技术更是起到了不可或缺的作用。它如同智慧文旅的基石&#xff0c;为旅游行业带来了前所未有的创新和变革…

Elasticsearch内核解析 - 数据模型篇

Elasticsearch内核解析 - 数据模型篇 - 知乎 Elasticsearch是一个实时的分布式搜索和分析引擎&#xff0c;它可以帮助我们用很快的速度去处理大规模数据&#xff0c;可以用于全文检索、结构化检索、推荐、分析以及统计聚合等多种场景。 Elasticsearch是一个建立在全文搜索引擎…

8.6跳跃游戏②(LC45-M)

算法&#xff1a; 与上一题一样&#xff0c;还是看最大覆盖范围 要从覆盖范围出发&#xff0c;不管怎么跳&#xff0c;覆盖范围内一定是可以跳到的&#xff0c;以最小的步数增加覆盖范围&#xff0c;覆盖范围一旦覆盖了终点&#xff0c;得到的就是最少步数&#xff01; 这里…

go 语言中 json.Unmarshal([]byte(jsonbuff), j) 字节切片得使用场景

struct_tag的使用 在上面的例子看到&#xff0c;我们根据结构体生成的json的key都是大写的&#xff0c;因为结构体名字在go语言中不大写的话&#xff0c;又没有访问权限&#xff0c;这种问题会影响到我们对json的key的名字&#xff0c;所以go官方给出了struct_tag的方法去修改…

unity刷新grid,列表

获取UIGrid 组件&#xff0c;更新列表 listParent.GetComponent().repositionNow true;

Ubuntu 申请 SSL证书并搭建邮件服务器

文章目录 Log 一、域名连接到泰坦&#xff08;Titan&#xff09;电子邮件二、NameSilo Hosting 避坑三、Ubuntu 搭建邮件服务器1. 环境准备2. 域名配置3. 配置 Postfix 和 Dovecot① 安装 Nginx② 安装 Tomcat③ 申请 SSL 证书&#xff08;Lets Encrypt&#xff09;④ 配置 pos…

【并发编程】 synchronized的普通方法,静态方法,锁对象,锁升级过程,可重入锁,非公平锁

目录 1.普通方法 2.静态方法 3.锁对象 4.锁升级过程 5.可重入的锁 6.不公平锁 非公平锁的 lock 方法&#xff1a; 1.普通方法 将synchronized修饰在普通同步方法&#xff0c;那么该锁的作用域是在当前实例对象范围内,也就是说对于 SyncDemosdnewSyncDemo();这一个实例对象…

ORBSLAM3安装

1. 依赖 在该目录下打开终端,安装下面所有依赖。 1. 1 编译软件 sudo apt-get install gccsudo apt-get install g++sudo apt-get install build-essentialsudo apt-get install cmakesudo apt-get install openssl sudo apt-get install libssl-dev1. 2 Pangolin git cl…

蓝桥杯备战——5.动态数码管扫描

1.分析原理图 经查阅说明书得知数码管为共阳极&#xff0c;共阳端口接到了U8,而段码接到了U7。 如果需要选中U8,我们只需要将P250;P261;P271; 如果需要选中U7,我们只需要将P251;P261;P271; 2.代码示例 void Delay1ms() //12.000MHz {unsigned char data i, j;i 12;j 169;…

java以SSL方式连ES

先做准备工作&#xff0c;浏览器方式访问 ES7.X url https://127.0.0.1:8027 弹出用户名和密码 输入后在浏览器得到 { “name” : “DTCNPEMS04”, “cluster_name” : “cnp-es-cluster”, “cluster_uuid” : “wb0So_FqQBOKqtXnsqofTg”, “version” : { “number” : “7.…

java web 校园健康管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web校园健康管理系统是一套完善的java web信息管理系统 &#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysq…

第一篇【传奇开心果短博文系列】Python的库OpenCV技术点案例示例:cv2常用功能和方法

传奇开心果短博文系列 短博文系列目录Python的库OpenCV技术点案例示例系列 短博文目录一、前言二、常用功能和方法示例三、归纳总结 短博文系列目录 Python的库OpenCV技术点案例示例系列 短博文目录 一、前言 cv2是Python中常用的第三方库&#xff0c;也称为OpenCV库&#…