`
zsxxsz
  • 浏览: 454529 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

使用 acl 库编写高效的 C++ redis 客户端应用

阅读更多

 一、概述

      (可以直接略过此段)redis 最近做为 nosql 数据服务应用越来越广泛,其相对于 memcached 的最大优点是提供了更加丰富的数据结构,所以应用场景就更为广泛。redis 的出现可谓是广大网络应用开发者的福音,同时有大量的开源人员贡献了客户端代码,象针对 java 语言的 jedis,php 语言的 phpredis/predis 等,这些语言的 redis 库既丰富又好用,而对 C/C++ 程序员似乎就没那么幸运了,官方提供了 C 版的 hiredis 作为客户端库,很多爱好者都是基于 hiredis 进行二次封装和开发形成了 C++ 客户端库,但这些库(包括官方的 hiredis)大都使用麻烦,给使用者造成了许多出错的机会。一直想开发一个更易用的接口型的 C++ 版 redis 客户端库(注:官方提供的库基本属于协议型的,这意味着使用者需要花费很多精力去填充各个协议字段同时还得要分析服务器可能返回的不同的结果类型),但每当看到 redis 那 150 多个客户端命令时便心生退缩,因为要给每个命令提供一个方便易用的 C++ 函数接口,则意味着非常巨大的开发工作量。

      在后来的多次项目开发中被官方的 hiredis 库屡次摧残后,终于忍受不了,决定重新开发一套全新的 redis 客户端 API,该库不仅要实现这 150 多个客户端命令,同时需要提供方便灵活的连接池及连接池集群管理功能(幸运的是在 acl 库中已经具备了通用的网络连接池及连接池集群管理模块),另外,根据之前的实践,有可能提供的函数接口要远大于这 150 多个,原因是针对同一个命令可能会因为不同的参数类型场景提供多个函数接口(最终的结果是提供了3,4百个函数 API,7000+行源码,2000+行头文件);在仔细研究了 redis 的通信协议后便着手开始进行设计开发了(redis 的协议设计还是非常简单实用的,即能支持二进制,同时又便于手工调试)。在开发过程中大量参考了 http://redisdoc.com 网站上的中文在线翻译版(非常感谢 黄键宏 同学的辛勤工作)。

 

二、acl redis 库分类

      根据 redis 的数据结构类型,分成 12 个大类,每个大类提供不同的函数接口,这 12 个 C++ 类展示如下:

      1、redis_key:redis 所有数据类型的统一键操作类;因为 redis 的数据结构类型都是基本的 KEY-VALUE 类型,其中 VALUE 分为不同的数据结构类型;

      2、redis_connectioin:与 redis-server 连接相关的类;

      3、redis_server:与 redis-server 服务管理相关的类;

      4、redis_string:redis 中用来表示字符串的数据类型;

      5、redis_hash:redis 中用来表示哈希表的数据类型;每一个数据对象由 “KEY-域值对集合” 组成,即一个 KEY 对应多个“域值对”,每个“域值对”由一个字段名与字段值组成;

      6、redis_list:redis 中用来表示列表的数据类型;

      7、redis_set:redis 中用来表示集合的数据类型;

      8、redis_zset:redis 中用来表示有序集合的数据类型;

      9、redis_pubsub:redis 中用来表示“发布-订阅”的数据类型;

      10、redis_hyperloglog:redis 中用来表示 hyperloglog 基数估值算法的数据类型;

      11、redis_script:redis 中用来与 lua 脚本进行转换交互的数据类型;

      12、redis_transaction:redis 中用以事务方式执行多条 redis 命令的数据类型(注:该事务处理方式与数据库的事务有很大不同,redis 中的事务处理过程没有数据库中的事务回滚机制,仅能保证其中的多条命令都被执行或都不被执行);

       除了以上对应于官方 redis 命令的 12 个类别外,在 acl 库中还提供了另外几个类:

       13、redis_command:以上 12 个类的基类;

       14、redis_client:redis 客户端网络连接类;

       15、redis_result:redis 命令结果类;

       16、redis_pool:针对以上所有命令支持连接池方式;

       17、redis_manager:针对以上所有命令允许与多个 redis-server 服务建立连接池集群(即与每个 redis-server 建立一个连接池);

       18、redis_cluster:支持 redis3.0 集群模式的类。

 

三、acl redis 使用举例

       1)、下面是一个使用 acl 框架中 redis 客户端库的简单例子:

/**
 * @param conn {acl::redis_client&} redis 连接对象
 * @return {bool} 操作过程是否成功
 */
bool test_redis_string(acl::redis_client& conn, const char* key)
{
	// 创建 redis string 类型的命令操作类对象,同时将连接类对象与操作类
	// 对象进行绑定
	acl::redis_string string_operation(&conn);
	const char* value = "test_value";

	// 添加 K-V 值至 redis-server 中
	if (string_operation.set(key, value) == false)
	{
		const acl::redis_result* res = string_operation.get_result();
		printf("set key: %s error: %s\r\n",
			key, res ? res->get_error() : "unknown error");
		return false;
	}
	printf("set key: %s ok!\r\n", key);

	// 需要重置连接对象的状态
	string_operation.clear();

	// 从 redis-server 中取得对应 key 的值
	acl::string buf;
	if (string_operation.get(key, buf) == false)
	{
		const acl::redis_result* res = string_operation.get_result();
		printf("get key: %s error: %s\r\n",
			key, res ? res->get_error() : "unknown error");
		return false;
	}
	printf("get key: %s ok, value: %s\r\n", key, buf.c_str());

	// 探测给定 key 是否存在于 redis-server 中,需要创建 redis 的 key
	// 类对象,同时将 redis 连接对象与之绑定
	acl::redis_key key_operation;
	key_operation.set_client(conn);  // 将连接对象与操作对象进行绑定
	if (key_operation.exists(key) == false)
	{
		if (conn.eof())
		{
			printf("disconnected from redis-server\r\n");
			return false;
		}

		printf("key: %s not exists\r\n", key);
	}
	else
		printf("key: %s exists\r\n", key);

	// 删除指定 key 的字符串类对象
	if (key_operation.del(key, NULL) < 0)
	{
		printf("del key: %s error\r\n", key);
		return false;
	}
	else
		printf("del key: %s ok\r\n", key);

	return true;
}

/**
 * @param redis_addr {const char*} redis-server 服务器地址,
 *  格式为:ip:port,如:127.0.0.1:6379
 * @param conn_timeout {int} 连接 redis-server 的超时时间(秒)
 * @param rw_timeout {int} 与 redis-server 进行通信的 IO 超时时间(秒)
 */
bool test_redis(const char* redis_addr, int conn_timeout, int rw_timeout)
{
	// 创建 redis 客户端网络连接类对象
	acl::redis_client conn(redis_addr, conn_timeout, rw_timeout);
	const char* key = "test_key";
	return test_redis_string(conn, key);
}

       上面的简单例子的操作过程是:在 redis-server 中添加字符串类型数据 --> 从 redis-server 中获取指定的字符串数据 --> 判断指定指定 key 的对象在 redis-server 上是否存在 ---> 从 redis-server 中删除指定 key 的数据对象(即该例中的字符串对象)。通过以上简单示例,使用者需要注意以下几点:

      a)、acl 中的 redis 库的设计中 redis 连接类对象与命令操作类对象是分离的,12 个 redis 命令操作类对应 acl  redis 库中相应的 12 个命令操作类;

      b)、在使用 redis 命令操作类时需要先将 redis 连接类对象与命令操作类对象进行绑定(以便于操作类内部可以利连接类中的网络连接、协议组包以及协议解析等方法);

      c)、在重复使用一个 redis 命令类对象时,需要首先重置该命令类对象的状态(即调用:clear()),这样主要是为了释放上一次命令操作过程的中间内存资源;

      d)、一个 redis 连接类对象可以被多个命令类操作类对象使用(使用前需先绑定一次);

      e)、将 redis 连接对象与命令操作对象绑定有两种方式:可以在构造函数中传入非空 redis 连接对象,或调用操作对象的 set_client 方法进行绑定。

 

      2)、对上面的例子稍加修改,使之能够支持连接池方式,示例代码如下:

/**
 * @param conn {acl::redis_client&} redis 连接对象
 * @return {bool} 操作过程是否成功
 */
bool test_redis_string(acl::redis_client& conn, const char* key)
{
	...... // 代码与上述代码相同,省略

	return true;
}

// 子线程处理类
class test_thread : public acl::thread
{
public:
	test_thread(acl::redis_pool& pool) : pool_(pool) {}

	~test_thread() {}

protected:
	// 基类(acl::thread)纯虚函数
	virtual void* run()
	{
		acl::string key;
		// 给每个线程一个自己的 key,以便以测试,其中 thread_id()
		// 函数是基类 acl::thread 的方法,用来获取线程唯一 ID 号
		key.format("test_key: %lu", thread_id());

		acl::redis_client* conn;

		for (int i = 0; i < 1000; i++)
		{
			// 从 redis 客户端连接池中获取一个 redis 连接对象
			conn = (acl::redis_client*) pool_.peek();
			if (conn == NULL)
			{
				printf("peek redis connection error\r\n");
				break;
			}

			// 进行 redis 客户端命令操作过程
			if (test_redis_string(*conn) == false)
			{
				printf("redis operation error\r\n");
				break;
			}

			// 回收连接对象
			pool_.put(conn, true);
		}

		return NULL;
	}

private:
	acl::redis_pool& pool_;
};

void test_redis_pool(const char* redis_addr, int max_threads,
	int conn_timeout, int rw_timeout)
{
	// 创建 redis 连接池对象
	acl::redis_client_pool pool(redis_addr, max_threads);
	// 设置连接 redis 的超时时间及 IO 超时时间,单位都是秒
	pool.set_timeout(conn_timeout, rw_timeout);

	// 创建一组子线程
	std::vector<test_thread*> threads;
	for (int i = 0; i < max_threads; i++)
	{
		test_thread* thread = new test_thread(pool);
		threads.push_back(thread);
		thread->set_detachable(false);
		thread->start();
	}

	// 等待所有子线程正常退出
	std::vector<test_thread*>::iterator it = threads.begin();
	for (; it != threads.end(); ++it)
	{
		(*it)->wait();
		delete (*it);
	}
}

      除了创建线程及 redis 连接池外,上面的例子与示例 1) 的代码与功能无异。

 

      3)、下面对上面的示例2)稍作修改,使之可以支持 redis 集群连接池的方式,示例代码如下:

/**
 * @param conn {acl::redis_client&} redis 连接对象
 * @return {bool} 操作过程是否成功
 */
bool test_redis_string(acl::redis_client& conn, const char* key)
{
	......  // 与上面示例代码相同,略去
	return true;
}

// 子线程处理类
class test_thread : public acl::thread
{
public:
	test_thread(acl::redis_cluster& cluster) : cluster_(cluster) {}

	~test_thread() {}

protected:
	// 基类(acl::thread)纯虚函数
	virtual void* run()
	{
		acl::string key;
		acl::redis_client_pool* pool;
		acl::redis_client* conn;

		for (int i = 0; i < 1000; i++)
		{
			// 从连接池集群管理器中获得一个 redis-server 的连接池对象
			pool = (acl::redis_client_pool*) cluster_.peek();
			if (pool == NULL)
			{
				printf("peek connection pool failed\r\n");
				break;
			}

			// 从 redis 客户端连接池中获取一个 redis 连接对象
			conn = (acl::redis_client*) pool_.peek();
			if (conn == NULL)
			{
				printf("peek redis connection error\r\n");
				break;
			}

			// 给每个线程一个自己的 key,以便以测试,其中 thread_id()
			// 函数是基类 acl::thread 的方法,用来获取线程唯一 ID 号
			key.format("test_key: %lu_%d", thread_id(), i);
			// 进行 redis 客户端命令操作过程
			if (test_redis_string(*conn, key.c_str()) == false)
			{
				printf("redis operation error\r\n");
				break;
			}

			// 回收连接对象至连接池中
			pool_.put(conn, true);
		}

		return NULL;
	}

private:
	acl::redis_cluster& cluster_;
};

void test_redis_manager(const char* redis_addr, int max_threads,
	int conn_timeout, int rw_timeout)
{
	// 创建 redis 集群连接池对象
	acl::redis_client_cluster cluster;

	// 添加多个 redis-server 的服务器实例地址
	cluster.set("127.0.0.1:6379", max_threads, conn_timeout, rw_timeout);
	cluster.set("127.0.0.1:6380", max_threads, conn_timeout, rw_timeout);
	cluster.set("127.0.0.1:6381", max_threads, conn_timeout, rw_timeout);

	// 创建一组子线程
	std::vector<test_thread*> threads;
	for (int i = 0; i < max_threads; i++)
	{
		test_thread* thread = new test_thread(cluster);
		threads.push_back(thread);
		thread->set_detachable(false);
		thread->start();
	}

	// 等待所有子线程正常退出
	std::vector<test_thread*>::iterator it = threads.begin();
	for (; it != threads.end(); ++it)
	{
		(*it)->wait();
		delete (*it);
	}
}

      该示例只修改了几处代码便支持了集群 redis 连接池方式,其处理过程是:创建集群连接池对象(可以添加多个 redis-server 服务地址) --> 从集群连接池对象中取得一个连接池对象 ---> 从该连接池对象中取得一个连接 ---> 该连接对象与 redis 操作类对象绑定后进行操作。

       上述示例的集群模式并非是 redis3.0 的集群模式,这种集群中的 redis-server 之间是不互联的, 集群的建立是由客户端来维护的,由客户决定数据存储在哪个 redis-server 实例上;而 redis3.0 的集群方式则与之大不同,在 redis3.0 中,redis-server 之间是互联互通的,而且支持数据的冗余备份,数据的存储位置是由服务端决定的,下面的例子是支持 redis3.0 集群模式的客户端例子:

       4)、支持 redis3.0 集群模式的示例代码如下:

// 统一的键值前缀
static acl::string __keypre("test_key_cluster");

// 测试 redis 字符串添加功能
static bool test_redis_string(acl::redis_string& cmd, int i)
{
	acl::string key;
	key.format("%s_%d", __keypre.c_str(), i);

	acl::string value;
	value.format("value_%s", key.c_str());
	
	bool ret = cmd.set(key.c_str(), value.c_str());
	return ret;
	if (i < 10)
		printf("set key: %s, value: %s %s\r\n", key.c_str(),
			value.c_str(), ret ? "ok" : "error");
	return ret;
}

// 测试 redis 键是否存在功能
static bool test_redis_exists(acl::redis_key& cmd, int i)
{
	acl::string key;

	key.format("%s_%d", __keypre.c_str(), i);

	if (cmd.exists(key.c_str()) == false)
	{
		if (i < 10)
			printf("no exists key: %s\r\n", key.c_str());
	}
	else
	{
		if (i < 10)
			printf("exists key: %s\r\n", key.c_str());
	}
	return true;
}

// 子线程处理类
class test_thread : public acl::thread
{
public:
	test_thread(acl::redis_cluster& cluster, int max_conns)
	: cluster_(cluster), max_conns_(max_conns) {}

	~test_thread() {}

protected:
	// 基类(acl::thread)纯虚函数
	virtual void* run()
	{
		acl::redis_string cmd_string;
		acl::redis_key  cmd_key;
		
		// 设置 redis 客户端命令的集群操作模式
		cmd_key.set_cluster(&cluster_, max_conns_);
		cmd_string.set_cluster(&cluster_, max_conns_);
		for (int i = 0; i < 1000; i++)
		{
			// 进行 redis 客户端命令操作过程
			if (test_redis_string(cmd_string, i) == false)
				break;
	
			if (test_redis_exists(cmd_key, i) == false)
				break;

			// 重置客户端命令状态

			cmd_string.clear();
			cmd_key.clear();
		}

		return NULL;
	}

private:
	acl::redis_cluster& cluster_;
	int max_conns_;
};

void test_redis_cluster(int max_threads int conn_timeout, int rw_timeout)
{
	// 创建 redis 集群连接池对象
	acl::redis_client_cluster cluster;

	// 添加一个或多个 redis-server 的服务器实例地址,不必一次加载所有
	// 的 redis-server 服务器地址,redis_cluster 及相关类具有自动发现
	// 及动态添加 redis-server 服务实例的功能

	cluster.set("127.0.0.1:6379", max_threads, conn_timeout, rw_timeout);
	// cluster.set("127.0.0.1:6380", max_threads, conn_timeout, rw_timeout);
	// cluster.set("127.0.0.1:6381", max_threads, conn_timeout, rw_timeout);

	// 创建一组子线程
	std::vector<test_thread*> threads;
	for (int i = 0; i < max_threads; i++)
	{
		test_thread* thread = new test_thread(cluster, max_threads);
		threads.push_back(thread);
		thread->set_detachable(false);
		thread->start();
	}

	// 等待所有子线程正常退出
	std::vector<test_thread*>::iterator it = threads.begin();
	for (; it != threads.end(); ++it)
	{
		(*it)->wait();
		delete (*it);
	}
}

 

        从上面例子可以看出,使用 acl redis 客户端库操作 redis3.0 集群时,只需要将集群的句柄注入到每个 redis 客户端命令即(如上面黄色部分所示);至于如何与 redis 集群交互将由 redis_cluster 及 redis 客户端命令类的基类 redis_command 进行处理;此外,还需要注意示例 4)与示例 3)所支持的集群方式的不同点如下:

        a、示例3 的集群模式下实际上是由客户端根据所连接的所有 redis 服务器的集合来决定数据存储结点,而实际上 redis 服务器之间并不互联;而示例 4 则是真正意义的 redis 服务端的集群模式,所有 redis 服务结点之间是互联互通的(可以配置数据结点存储的冗余数量);

        b、示例3 的客户端必须在开始初始化时添加所有的 redis 服务结点,以便于采用轮循或者哈希访问模式;而示例4 在初始化时只需添加至少一个集群中的服务结点即可,随着访问次数的增加,会根据需要动态添加 redis 服务结点( redis3.0 采用的重定向机制,即当访问某个 redis 结点时,若 key 值不存在于该结点上,则其会返回给客户端一个重定向指令,告诉客户端存储该 key 的 redis 服务结点,因此,根据此特性,acl redis 集群会根据重定向信息动态添加 redis 集群中的服务结点);

        c、此外,示例4 是兼容示例3 的。

 

四、小结

      以上介绍了 acl 框架中新增加的 redis 库的使用方法及处理过程,该库将复杂的协议及网络处理过程隐藏在实现内部,使用户使用起来感觉象是在调用本的函数。在示例 2)、3) 中提到了 acl 线程的使用,有关 acl 库中更为详细地使用线程的文章参见:《使用 acl_cpp 库编写多线程程序》

 

下载:http://sourceforge.net/projects/acl/

svn:svn://svn.code.sf.net/p/acl/code/trunk

github:https://github.com/acl-dev/acl

国内镜像:http://git.oschina.net/acl-dev/acl/tree/master

微博:http://weibo.com/zsxxsz/

QQ 群:242722074

 

3
0
分享到:
评论
2 楼 zsxxsz 2015-09-14  
eryueniaobp 写道
关于reset的使用,查看了  github上的代码 ,发现在

class dbuf_pool  #destroy()  

destroy() {
   delete this ;
}

但是 dbuf_pool 的析构函数是空的 

这样的话,dbuf_pool在析构的时候就造成了 内存泄漏吧?


这个类为一个内存池管理类,重载了 new/delete 函数,所以当 delete this 被调用时,重载的析构函数:dbuf_pool::operator delete 便会被调用,里面通过 acl_dbuf_pool_destroy(dbuf->pool_) 统一释放了内存,另外,dbuf_pool 类对象也是在 pool_ 上分配的(参见重载的 new 方法:dbuf_pool::operator new),所以在此一同被释放。之所以采用这种方法,主要是为了提升内存分配/释放的效率,基本上可以做到非常少的内存分配与释放操作。
1 楼 eryueniaobp 2015-09-09  
关于reset的使用,查看了  github上的代码 ,发现在

class dbuf_pool  #destroy()  

destroy() {
   delete this ;
}

但是 dbuf_pool 的析构函数是空的 

这样的话,dbuf_pool在析构的时候就造成了 内存泄漏吧?

相关推荐

    基于libacl的redis客户端,c++

    在这个项目中,我们将探讨如何使用C++编写一个基于libacl(Access Control Lists,访问控制列表)的Redis客户端,以连接并操作Redis集群。 首先,让我们了解libacl。libacl是一个库,用于处理文件系统的访问控制...

    acl C++跨平台库.rar

    主要包括:lib_acl(用 C 语言写的基础库)、lib_protocol(用 C 语言写的一些网络应用协议库)、lib_acl_cpp(用 C++ 语言编写,封装了 lib_acl/lib_protocol 两个库,同时增加更多实用的功能库)、 lib_fiber(用 ...

    Redis基础笔记总结

    - **语言**: 使用ANSI C语言编写,遵循BSD许可协议。 - **特性**: - **内存存储**: 数据主要存储于内存中,以提高访问速度。 - **数据结构丰富**: 提供了多种数据结构的支持,包括字符串(Strings)、哈希(Hashes)、...

    基于SSM+JSP+HTML的东风锻造有限公司重大停管理系统(Java毕业设计,附源码,数据库,教程).zip

    Java 项目, Java 毕业设计,Java 课程设计,基于 ssm 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:jsp 后台框架:SSM 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4

    数据库系统课程设计报告-体育项目比赛管理系统设计与开发

    一、系统需求分析 1 (一)需求概述 1 (二)业务流分析 1 从运动员角度分析 1 (三)数据流分析 4 (四)数据字典 5 二、数据库概念结构设计 6 (一)实体分析 6 (二)属性分析 6 (三)联系分析 8 (四)概念模型分析(.PDM图) 9 三、数据库逻辑结构设计 9 (一)概念模型转化为逻辑模型 9 1.一对一关系的转化 9 2.一对多关系的转化 9 3.多对多关系的转化 10 (二)逻辑模型设计(.PDM图) 10 四、 数据库物理实现(一)表设计 10 (一)表设计 10 (二)创建表和完整性约束代码设计 11 五、数据库功能调试 15 (一)运动员管理模块 15 (二)负责人管理模块 16 (三)系统管理员管理模块 17 六、设计系统前台软件 21 (一)开发软件选择 21 (二)软件功能要求与设计 22 (三)软件功能实现 22 (四)系统测试 24 七、设计总结 27

    基于SSM+JSP的文物管理系统+数据库(Java毕业设计,包括源码,教程).zip

    Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:jsp 后台框架:SSM 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4

    智慧园区整体解决方案-37PPT(46页).pptx

    智慧园区,作为现代化城市发展的新兴模式,正逐步改变着传统园区的运营与管理方式。它并非简单的信息化升级,而是跨越了行业壁垒,实现了数据共享与业务协同的复杂运行系统。在智慧园区的构建中,人们常常陷入一些误区,如认为智慧园区可以速成、与本部门无关或等同于传统信息化。然而,智慧园区的建设需要长期规划与多方参与,它不仅关乎技术层面的革新,更涉及到管理理念的转变。通过打破信息孤岛,智慧园区实现了各系统间的无缝对接,为园区的科学决策提供了有力支持。 智慧园区的核心价值在于其提供的全方位服务与管理能力。从基础设施的智能化改造,如全面光纤接入、4G/5G网络覆盖、Wi-Fi网络及物联网技术的运用,到园区综合管理平台的建设,智慧园区打造了一个高效、便捷、安全的运营环境。在这个平台上,园区管理方可以实时掌握运营动态,包括道路状况、游客数量、设施状态及自然环境等信息,从而实现事件的提前预警与自动调配。同时,智慧园区还为园区企业提供了丰富的服务,如项目申报、资质认定、入园车辆管理及统计分析等,极大地提升了企业的运营效率。此外,智慧园区还注重用户体验,通过信息发布系统、服务门户系统及各类智慧应用,如掌上营销、智慧停车、智能安防等,为园区员工、企业及访客提供了便捷、舒适的生活与工作体验。值得一提的是,智慧园区还充分利用大数据、云计算等先进技术,对园区的能耗数据进行采集、分析与管理,实现了绿色、节能的运营目标。 在智慧园区的建设过程中,还涌现出了许多创新的应用场景。例如,在环境监测方面,智慧园区通过集成各类传感器与监控系统,实现了对园区水质、空气质量的实时监测与预警;在交通管理方面,智慧园区利用物联网技术,对园区观光车、救援车辆等进行实时定位与调度,提高了交通效率与安全性;在公共服务方面,智慧园区通过构建统一的公共服务平台,为园区居民提供了包括平安社区、便民社区、智能家居在内的多元化服务。这些创新应用不仅提升了园区的智能化水平,还为园区的可持续发展奠定了坚实基础。同时,智慧园区的建设也促进了产业链的聚合与发展,通过搭建聚合产业链平台,实现了园区内企业间的资源共享与合作共赢。总的来说,智慧园区的建设不仅提升了园区的综合竞争力,还为城市的智慧化发展树立了典范。它以用户需求为导向,以技术创新为驱动,不断推动着园区向更加智慧、高效、绿色的方向发展。对于写方案的读者而言,智慧园区的成功案例与创新应用无疑提供了宝贵的借鉴与启示,值得深入探索与学习。

    Java毕业设计-SpringBoot+Vue的基于SpringBoot的冬奥会科普平台(附源码、数据库、教程).zip

    Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:html、javascript、Vue 后台框架:SpringBoot 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4 后台路径地址:localhost:8080/项目名称/admin/dist/index.html 前台路径地址:localhost:8080/项目名称/front/index.html (无前台不需要输入)

    MATLAB设计的芯片字符识别(GUI界面设计).zip

    MATLAB设计的芯片字符识别(GUI界面设计)

    【工程项目】MATLAB口罩识别[自动定位颜色,多人检测,未戴预警 ].zip

    【工程项目】MATLAB口罩识别[自动定位颜色,多人检测,未戴预警 ]

    基于SSM+JSP的雅博书城在线系统+数据库(Java毕业设计,包括源码,教程).zip

    Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:jsp 后台框架:SSM 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4

    基于SpringBoot+Vue的校园疫情防控系统 (2)(Java毕业设计,包括源码、数据库、教程).zip

    Java 项目,仅供学习参考。 Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:html、javascript、Vue 后台框架:SpringBoot 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4 后台路径地址:localhost:8080/项目名称/admin/dist/index.html 前台路径地址:localhost:8080/项目名称/front/index.html (无前台不需要输入)

    2007-2023年 省级-工业机器人安装密度.rar

    工业机器人安装密度通常指的是单位面积或单位人口所拥有的工业机器人数量。这一指标能够直观地反映出一个地区或国家工业机器人的普及程度和使用情况。 本数据参照康茜(2021)和芦婷婷(2021)的方法,根据IRF联盟公布的中国各行业工业机器人安装量(IFR公布的14个大类,对应国民经济行业分类与代码(GB/4754-2011)中13-43的细分行业代码),然后从《中国劳动统计年鉴》里面收集细分行业各个省份的就业人数占全国总就业人数的百分比,工业机器人安装密度=各个省份的就业人数占全国总就业人数的百分比×全国各行业机器人安装数量。 数据 年份、省份、省份代码、所属地域、工业机器人安装密度(台) 参考文献:[1]康茜,林光华.工业机器人与农民工就业:替代抑或促进[J].山西财经大学学报,2021,43(02):43-56. [2]芦婷婷,祝志勇.人工智能是否会降低劳动收入份额——基于固定效应模型和面板分位数模型的检验[J].山西财经大学学报,2021,43(11):29-41. [3]闫雪凌,朱博楷,马超.工业机器人使用与制造业就业:来自中国的证据[J].统计研究,2020,37(01):74-87. [4]王永钦,董雯.机器人的兴起如何影响中国劳动力市场?——来自制造业上市公司的证据[J].经济研究,2020,55(10):159-175.

    2006-2023年 地级市-绿色全要素生产率.rar

    绿色全要素生产率(Green Total Factor Productivity,GTFP)是一个综合考虑环境因素的生产效率指标,旨在更全面地评估经济发展的可持续性和环境友好性。通过提升绿色全要素生产率,可以促进资源的有效利用、环境保护和可持续发展。 本数据通过将劳动、资本等生产要素的投入与地区生产总值(GDP)作为合意产出相联系,同时考虑工业二氧化硫、烟粉尘和废水等非合意的环境排放,采用如SBM-Malmquist-Luenberger指数法等计量手段进行测算。这种方法不仅评估了生产效率的改善情况,还反映了技术进步和环境管理在促进绿色增长方面的作用。 参考文献:排污权交易制度与能源利用效率 -对地级及以上城市的测度与实证 数据 年份、城市、超效率SBM、超效率CCR

    航空航天领域翼型振动与颤振分析的MATLAB仿真程序实现及应用

    内容概要:本文档详细记录了一段用于进行航空器机翼加装挂载(如导弹或其他装备)后的结构动力响应分析,特别是对颤振现象研究的 MATLAB 代码片段。主要内容涵盖初始化几何参数、物性参数以及质量特性等基本信息设定,通过定义多个矩阵(弯曲模式、扭转模式)用以描述系统运动方程的形式表达;采用Theodorsen函数表征气动力特性对于系统稳定性的影响;最终利用模态分析确定临界速度并给出最小颤振速率发生位置的相关讨论与实验数据对比验证。 适合人群:航空航天专业研究人员,工程物理学者及高等院校飞行器设计方向研究生及以上水平的技术爱好者。 使用场景及目标:①理解机翼与附加载体之间的动态交互机制;②掌握利用数学工具进行复杂机械系统的稳定性判断方法;③为实际产品研发提供理论依据和技术支持。 其他说明:文档中的部分内容已被省略以保护原创版权,同时确保敏感算法细节不在未经授权的情况下传播。由于文中涉及到大量的矩阵运算以及高级工程力学概念,请在使用前确认自己拥有足够的前置知识。

    MATLAB 界面指纹识别MATLAB(GUI界面,比对两幅指纹)(构架).zip

    MATLAB 界面指纹识别MATLAB(GUI界面,比对两幅指纹)(构架)

    MATLAB车牌出入库识别(GUI界面,计时计费,停车位计算,倾斜矫正)【工程项目】.zip

    MATLAB车牌出入库识别(GUI界面,计时计费,停车位计算,倾斜矫正)【工程项目】

    基于SSM+JSP+HTML的电气与信息类书籍网上书店(Java毕业设计,附源码,数据库,教程).zip

    Java 项目, Java 毕业设计,Java 课程设计,基于 ssm 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:jsp 后台框架:SSM 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4

    红色博物馆省市县.xlsx

    ## 一、红色博物馆数据的介绍 本分享数据根据高德地图,汇总了全国范围内具有代表性的红色博物馆的基本信息,包括其地理位置、特色及教育意义。可便于对红色博物馆的区域分布和文化传承情况进行快速了解,并深入了解中国革命的历史背景和文化内涵 ## 二、数据指标 经度 纬度 地球表面上一点与本初子午线相距的度数 地球表面上某一点与赤道面之间的夹角 ## 三、数据概览 下图展示了全国范围内红色博物馆的基础数据

    springboot书籍学习平台 LW PPT.zip

    ava项目springboot基于springboot的课程设计,包含源码+数据库+毕业论文

Global site tag (gtag.js) - Google Analytics