hillpig的个人博客分享 http://blog.sciencenet.cn/u/hillpig 畅想ing,思考ing,前行ing Email:bluevaley@gmail.com

博文

PG9.1+pgpool-II3.1--之HA (Hot-Standby+Streaming Replication)

已有 18857 次阅读 2012-2-27 20:47 |个人分类:postgresql|系统分类:科研笔记

本教程是PostgreSQL Cluster系列教程的一部分,该系列包括:

  1. PostgreSQL9.1 PITR示例  (该教程主要阐述DBA如何基于WAL日志做备份恢复)

  2. PostgreSQL9.1 Warm-Standby ---之基于拷贝WAL文件的方法        (file-based log shipping)

  3. PostgreSQL9.1 Warm-Standby ---之基于流复制的方法 (streaming        replication)

  4. PostgreSQL9.1 Warm-Standby ---之基于同步复制的方法 (Synchronous Replication)

  5. PostgreSQL9.1 Hot-Standby ---之基于拷贝WAL文件的方法

  6. PostgreSQL9.1 Hot-Standby ---之基于流复制的方法

  7. PostgreSQL9.1 Hot-Standby ---之基于同步复制的方法

  8. PG9.1+pgpool-II3.1--之HA (Hot-Standby+Streaming Replication)

  9. PG9.1+pgpool-II3.1--之Load Balancing (when meeting large amounts of requests)

  10. PG9.1+pgpool-II3.1--之Parallel Query (when meeting large amounts of data)

  11. PostgreSQL9.1 HA --- 之Slony

我们知道若仅仅使用Postgresql的warm-standby方式来做HA有三种机制:

  • file-based log shipping

  • streaming replication

  • synchronous replication

当然你要是使用Hot-Standby的方式也会有相应这三种,本文作者尝试过PostgreSQL9.1+pgpool-II3.1+Warm-Standby+Streaming Replication的方式(具体可参考Online Recovery),发现pgpool-II在做master和warm-standby切换的时有些小问题,即warm-standby在做online recovery恢复后,实际上已经正常运行,但是pgpoolAdmin无法显示其运行状态(大家可以试试,或许是因为warm-standby不能接受客户端连接的原因,这是我的瞎猜,也可能是我配置的不合适)。那我们本文主要说说如何使用hot-standby来做HA(有人说hot-standy不是做load balancing的吗,不错,但是细想想,promote一下hot-standby不也可以做master吗)。
那既然要用hot-standby来做,就涉及到是用file-based log shipping,还是用streaming replication,synchronous replication,基于文件的方式在主从切换(Failover,Switchover,Failback是在pgool里看到的,从字面上看应该是恢复到最开始的主从架构)的时候需要写脚本稍微麻烦一些(例如master->hot-standby需要archieve的位置就与hot-standby->master的地方不同,以及其它麻烦,此处不表),故本文主要是写后两者,而根据前面的文章考虑到synchronous replication仅仅是需要对streaming replication做一些简单的修改就可实现,我们也不再探讨synchronous replication的方式(有兴趣的朋友可以来写写),这样本文主要探讨在pg9.1 + pgpool-II3.1上基于hot-standby + streaming replication来配置HA。

接着说pgpool-II,根据官方manual: It provides the following features:
   * Connection Pooling
   * Replication
     pgpool-II can manage multiple PostgreSQL servers. Activating the replication feature makes it possible to create a realtime backup on 2 or more PostgreSQL clusters, so that the service can continue without interruption if one of those clusters fails.
   * Load Balance
   * Limiting Exceeding Connections
   * Parallel Query

本文我们关注Replication,为什么建HA时要用pgpool-II呢,这是因为我们想让pgpool-II自动的管理master和standby库(当然我们也可以自己写一些脚本,把这些配置都自动化),即自动化管理在教程“PostgreSQL9.1 Warm-Standby ---之基于拷贝WAL文件的方法”中提到的HA里的三个方面:

  • 正常运行,即standby一直处于online recovery状态

  • Failover阶段,即master由于某种原因关闭掉,standby提升为master

  • Switchover阶段,即master的角色设定为新的standy,即一直处于online recovery

  • Failback 阶段(根据pgpool来做的补充,恢复到最初正常运行状态)

当然此处我们是基于hot-standby的方法。先说“A.正常运行”阶段,我们来分析分析,若没有pgpool-II我们怎么自己写shell自动化。先说环境:

  1. /home/postgres/db/master/pgsql 目录是master数据库的目录,端口为6432(之前的教程是5432,我们后面在prallel mode时把5432的库安装在/usr/local/pgsql下,专供pgpool-II存系统库用,即SystemDB使用)

  2. /home/postgres/db/standby/pgsql 目录是一台standy数据库的目录,端口为7432

  3. /home/postgres/base 是master->standby的基础备份库的目录

“正常运行”下,我们是如何基于streaming replication的方法配置hot-standby的呢,这里是简略步骤:

  1. 安装pg9.1在master,standby上

  2. 对master库做配置(在postgresql.conf中:wal_level = hot_standbymax_wal_senders = 1, 在pg_hbal.conf中:host    replication repluser 127.0.0.1/32 password,当然你可以让认证更加简单或复杂)(我们再想想,是不是standby里的postgresql.conf也可以设置同样的参数呢?答案是“是的”。),启动master库

  3. 对master库做基础备份(SELECT pg_start_backup('bak20120228'); tar czvf /home/postgres/base/base_data.tar.gz /home/postgres/db/master/pgsql/data/; SELECT pg_stop_backup();

  4. 对standby库使用基础备份的data目录(tar -xzvf /home/postgres/base/base_data.tar.gz ),修改配置(创建recovery.conf文件,使之含有:standby_mode = on trigger_file = '/home/postgres/trigger/pgsql.recovery.trigger',在postgresql.conf中:hot_standby = on),启动standby库

好,是不是第3,4步可以自动化呢,若我们把这两步合并在一起,即直接把master的data目录用rsync到standby的data目录里,是不是不需要/home/postgres/base 目录了呢。好,我们把第3,4步的自动化shell脚本记成basebackup.sh(脚本参考修改自:Simple Streaming replication setting with pgpool-II)。实际上,可以在pgpool.conf里recovery_1st_stage_command = 'basebackup.sh'设置(pgpool-II会去master的data目录里找该shell文件,当然pgpool-II在该目录下还会寻找pgpool_remote_start脚本文件,以让master远程启动standby),这是后话。

那么在“B.Failover”阶段怎么自动化呢?我们这么设想一下,当master出现故障死掉后,pgpool-II检测不到master的状态信息,就在相应的trigger目录下新建一个相应的trigger文件(我们把这些命令封装成failover.sh文件,在pgpool.conf里failover_command = '/usr/local/etc/failover.sh %d "%h" %p %D %m %M "%H" %P'设置)以promote standby为master角色。就这么简单。有朋友会说了,什么时候把该trigger删除掉呢?好我们就进入“Switchover”阶段。

C.Switchover”阶段主要是master修好后重新进入cluster,pgpool-II检测到后,设置原master为新的hot-standby,这就需要首先删除trigger文件(将被设定为hot-standby的原master也将使用该trigger文件)(在pgpool.conf里failback_command = '/bin/rm -f /home/postgres/trigger/pgsql.recovery.trigger'设置),在原master的data目录下新建一个recovery.conf文件(内容和原hot-standby的一样),是不是就可以了。

在“D.Failback”阶段,是不是pgpool-II确认master和standby一致性后,只需要把master下的pgpool.conf删除掉,把hot-standby下pgpool.conf恢复一下,然后重启二者就可以了?哈,这是俺瞎猜的,pgpool-II可能还会做更多的工作(有哪位朋友看看pgpool-II的源码来写写)。

罗嗦了这么多,我们得上张图,要不然有些朋友会觉得枯燥,该图包括本文主要参考自Simple Streaming replication setting with pgpool-II,建议朋友们先提前学习一下该文,本文稍作修改,让操作更加简单化,以让初学者很容易的上机操作。

上图不言自明,那接下来怎么配呢?先说机器配置,建议你都先创建好这些目录:

  1. /home/postgres/db/master/pgsql 目录是master数据库的目录,端口为6432

  2. /home/postgres/db/standby/pgsql 目录是一台standy数据库的目录,端口为7432

  3. /usr/local/pgsql 目录是pgpool-II使用的SystemDB数据库的目录,端口为5432

  4. /home/postgres/var/run/pgpool 是pgpool-II运行时放pid的目录

  5. /home/postgres/var/log/pgpool/trigger 是主从切换时放trigger文件的目录

  6. 假定pgpool-II-3.1.2.tar.gz,pgpoolAdmin-3.1.1.tar.gz放在/home/postgres/develop下

  7. 本系列教程中使用的都是RHEL 6

在动手配置之前,我们得说说各种用户角色。

  1. PostgreSQL9.1 Hot-Standby ---之基于流复制的方法中我们提到用流复制的话要有个CREATE ROLE repluser REPLICATION LOGIN PASSWORD 'pwd_repluser'账户,此处为了简化我们让系统我们假定原有的postgres用户来通讯,即
    #host   replication      postgres         0.0.0.0/0               trust
    local   replication      postgres                                trust

    所以也就不用建立repluser角色了。

  2. 由于我们要装pgpoolAdmin3.1.1,其必定要与pgpool通讯,这时通讯的用户名密码存储在pgpool的配置文件/usr/local/etc/pcp.conf里,密码用md5生成(具体怎么生成参考:1.3. Configuring PCP Commands),这里我们的用户名密码都为postgres,示例如下:
    # USERID:MD5PASSWD
    postgres:e8a48653851e28c69d0506508fb27fc5

  3. 实际上pgpoolAdmin运行在Tomcat下,而Tomcat运行时用户是apache,pgpoolAdmin还需要使用apache用户来运行/usr/local/bin/pcp_*等等命令,这时需要给这些命令赋予apache用户相应的权限。当然,apache用户还会被pgpool用来和master/standby通讯,故在pgpool.conf里还得设置health_check_user = 'apache',这就需要在master/standby里都创建该用户。


好,下面我们来一步步安装配置。
1. 安装pgpool-II3.1.2和pgpoolAdmin3.1.1
以postgres用户登录,先安装专为pgpool-II使用的PostgreSQL9.1(直接用make world安装包含dblink的扩展模块):
cd /home/postgres/develop/
rm -fr postgresql-9.1.2
tar zxf postgresql-9.1.2.tar.gz
cd postgresql-9.1.2
./configure --prefix=/usr/local/pgsql --with-includes=/usr/local/readline/include --with-libraries=/usr/local/readline/lib
make world
make install-world
/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data
createuser -p 5432 pgpool
createdb -p 5432 -O pgpool pgpool
/usr/local/pgsql/bin/postmaster -D /usr/local/pgsql/data

/usr/local/pgsql/bin/psql -p 5432 -U pgpool pgpool
CREATE EXTENSION dblink
然后由于pgpool将安装在/usr/local/bin, /usr/local/lib, /usr/local/include, /usr/local/share/man/man8等目录下,故先对这些目录设置相应的权限:
su
chmod -R 777 /usr/local/bin  /usr/local/lib /usr/local/etc  /usr/local/include /usr/local/share

安装pgpool-II,并使用默认参数启动:
cd /home/postgres/develop/
tar zxf pgpool-II-3.1.2.tar.gz
cd pgpool-II-3.1.2
./configure
make
make install
cd /usr/local/etc
cp pgpool.conf.sample pgpool.conf
cp pcp.conf.sample pcp.conf
修改pcp.conf,增加一行(即用户名postgres密码pgpoolAdmin):postgres:6b9883ad2fdc7256b95cfafbbe8e455b
mkdir /var/run/pgpool
pgpool -n & #尝试启动pgpool-II
pgpool stop #尝试关闭

/usr/local/pgsql/bin/psql -f /usr/local/share/pgpool-II/system_db.sql -p 5432 -U pgpool pgpool #把systemDB安装到上面的5432端口postgresql中
为避免后面的库找不到,我们设置一些环境变量:
su
gedit /etc/profile
最后增加:
export LIBDIR=/usr/local/lib:/usr/local/pgsql/lib:$LIBDIR
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

然后安装pgpoolAdmin,由于其需要安装在应用服务器中,我们下载并安装apache-tomcat-7.0.16.zip,解压缩到目录/home/postgres/website/。看看是否可以启动Tomcat(当然这需要jdk,此处不再说明如何安装):
chmod -R 777 /home/postgres/website/apache-tomcat-7.0.16/bin
sh /home/postgres/website/apache-tomcat-7.0.16/bin/startup.sh
sh /home/postgres/website/apache-tomcat-7.0.16/bin/shutdown.sh #关闭tomcat

由于pgpoolAdmin需要使用php的库,你从lib_for_php.tar.gz下载,解压缩到/home/postgres/website/apache-tomcat-7.0.16/lib 目录,并配置/home/postgres/website/apache-tomcat-7.0.16/conf/web.xml(参考Running PHP applications in Tomcat 6),你可以在最后的</web-app>标记之前增加:
<web-app xmlns=... >
....
+    <listener><listener-class>php.java.servlet.ContextLoaderListener</listener-class></listener>
+    <servlet><servlet-name>PhpJavaServlet</servlet-name><servlet-class>php.java.servlet.PhpJavaServlet</servlet-class>
+    </servlet>
+    <servlet><servlet-name>PhpCGIServlet</servlet-name><servlet-class>php.java.servlet.fastcgi.FastCGIServlet</servlet-class>
+        <init-param><param-name>prefer_system_php_exec</param-name><param-value>On</param-value></init-param>
+        <init-param><param-name>php_include_java</param-name><param-value>Off</param-value></init-param>
+    </servlet>
+    <servlet-mapping><servlet-name>PhpJavaServlet</servlet-name><url-pattern>*.phpjavabridge</url-pattern> </servlet-mapping>
+    <servlet-mapping><servlet-name>PhpCGIServlet</servlet-name><url-pattern>*.php</url-pattern></servlet-mapping>

</web-app>

并确保/home/postgres/website/apache-tomcat-7.0.16/conf/server.xml中的docBase="webapps“。
好,下载pgpoolAdmin-3.1.1.tar.gz,并解压缩到/home/postgres/website/apache-tomcat-7.0.16/webapps中,然后:
cd /home/postgres/website/apache-tomcat-7.0.16/webapps/pgpooladmin
mkdir templates_c
chmod 777 templates_c
su
chown apache /home/postgres/website/apache-tomcat-7.0.16/webapps/pgpooladmin/conf/pgmgt.conf.php
chmod
777 /home/postgres/website/apache-tomcat-7.0.16/webapps/pgpooladmin/conf/pgmgt.conf.php
然后重新启动Tomcat,通过浏览器打开如下地址:http://localhost:8080/pgpooladmin/install/index.php,是不是如下图正常:

若是上图的模样则表明成功,然后一路点next选择相应的配置,若对某些文件有权限问题,请做相应修改,例如(具体可参考Installing pgpool Administration Tool):
cd /usr/local/etc
chown apache pgpool.conf
chmod 644 pgpool.conf
chown apache pcp.conf
chmod 644 pcp.conf
su
chmod 755 /usr/local/bin/pgpool
chmod 755 /usr/local/bin/pcp_*

好,如果你见到下面这个界面,则表示配置成功:

接下来进入pgpoolAdmin的管理页面http://localhost:8080/pgpooladmin/status.php:默认用户名postgres密码pgpoolAdmin(后面我们将演示如何修改pgpool以允许pgpoolAdmin以不同的用户名密码登录):

点击Start pgpool是不是启动了?再点击Stop pgpool按钮是不是关闭了?如果都奏效,这说明咱安装成功。

2.千辛万苦我们把pgpool和pgpoolAdmin安装上了,接下来我们就得把Master和Standby的数据库安装上,这就很简单了,Master:
cd /home/postgres/develop/
rm -fr postgresql-9.1.2
tar zxf postgresql-9.1.2.tar.gz
cd postgresql-9.1.2
./configure --prefix=/home/postgres/db/master/pgsql --with-includes=/usr/local/readline/include --with-libraries=/usr/local/readline/lib --with-pgport=6432
make
make install
/home/postgres/db/master/pgsql/bin/initdb -D /home/postgres/db/master/pgsql/data

Standby:
cd /home/postgres/develop/
rm -fr postgresql-9.1.2
tar zxf postgresql-9.1.2.tar.gz
cd postgresql-9.1.2
./configure --prefix=/home/postgres/db/standby/pgsql --with-includes=/usr/local/readline/include --with-libraries=/usr/local/readline/lib --with-pgport=7432
make
make install
/home/postgres/db/standby/pgsql/bin/initdb -D /home/postgres/db/standby/pgsql/data

尽管编译时候把端口号编译进去了,但是为防止可能出现的问题,此处仍建议修改master的postgresql.conf的端口号:
port = 6432
和standby的postgresql.conf的端口号:
port = 7432
分别在master和standby上创建apache用户:
/home/postgres/db/master/pgsql/bin/createuser --no-createdb --no-superuser --createrole apache
/home/postgres/db/
standby/pgsql/bin/createuser --no-createdb --no-superuser --createrole apache
由于pgpool需要和master和standby进行流复制时的通讯,先把pgpool-recovery,pgpool-regclass,pgpool-walrecrunning库安装到/usr/local/pgsql/lib:
cd /home/postgres/develop/pgpool-II-3.1.2/sql/pgpool-recovery
make
make install
cd ../pgpool-regclass
make
make install
cd ../pgpool-walrecrunning
make
make install
还需要在master和standby上安装相应的函数:
master:
cp /usr/local/pgsql/lib/pgpool-*.*  /home/postgres/db/master/pgsql/lib/
/home/postgres/db/master/pgsql/bin/postmaster -D /home/postgres/db/master/pgsql/data
cd /home/postgres/develop/pgpool-II-3.1.2/sql/pgpool-recovery
 /home/postgres/db/master/pgsql/bin/psql -f pgpool-recovery.sql template1 --port=6432
 /home/postgres/db/master/pgsql/bin/psql -f pgpool-recovery.sql postgres --port=6432
cd ../pgpool-regclass
 /home/postgres/db/master/pgsql/bin/psql -f pgpool-regclass.sql template1 --port=6432
 /home/postgres/db/master/pgsql/bin/psql -f pgpool-regclass.sql postgres --port=6432
cd ../pgpool-walrecrunning
 /home/postgres/db/master/pgsql/bin/psql -f pgpool-walrecrunning.sql template1 --port=6432
 /home/postgres/db/master/pgsql/bin/psql -f pgpool-walrecrunning.sql postgres --port=6432
standby上类似:
cp /usr/local/pgsql/lib/pgpool-*.*  /home/postgres/db/standby/pgsql/lib/
/home/postgres/db/standby/pgsql/bin/postmaster -D /home/postgres/db/standby/pgsql/data
cd /home/postgres/develop/pgpool-II-3.1.2/sql/pgpool-recovery
 /home/postgres/db/standby/pgsql/bin/psql -f pgpool-recovery.sql template1 --port=7432
 /home/postgres/db/standby/pgsql/bin/psql -f pgpool-recovery.sql postgres --port=7432
cd ../pgpool-regclass
 /home/postgres/db/standby/pgsql/bin/psql -f pgpool-regclass.sql template1 --port=7432
 /home/postgres/db/standby/pgsql/bin/psql -f pgpool-regclass.sql postgres --port=7432
cd ../pgpool-walrecrunning
 /home/postgres/db/standby/pgsql/bin/psql -f pgpool-walrecrunning.sql template1 --port=7432
 /home/postgres/db/standby/pgsql/bin/psql -f pgpool-walrecrunning.sql postgres --port=7432

3.然后进入激动人心的HA的配置了(激动是没用的,DBA需要的是不激动,^_^)
首先是配置master和standby,二者的postgresql.conf一样一样滴:
wal_level = hot_standby
max_wal_senders = 1
hot_standby = on
二者pg_hba.conf也一样(若更复杂的请自行设置):
#host    replication     postgres        ::1/128                 trust
local   replication      postgres                                trust

在master的data目录下放上面介绍的文件basebackup.sh和这个让master远程启动standby的pgpool_remote_start文件。
然后是配置pgpool了,先捡容易的pcp.conf修改:
首先使用md5工具生成密码postgres的md5:
/usr/local/bin/pg_md5 postgres
e8a48653851e28c69d0506508fb27fc5

然后修改pcp.conf:
# USERID:MD5PASSWD
postgres:e8a48653851e28c69d0506508fb27fc5

接着修改/usr/local/etc/pgpool.conf:
listen_addresses = '*'

backend_hostname0 = 'localhost'
backend_port0 = 6432
backend_weight0 = 1
backend_data_directory0 = '/home/postgres/db/master/pgsql/data'
backend_flag0= 'ALLOW_TO_FAILOVER'
backend_hostname1 = 'localhost'
backend_port1 = 7432
backend_weight1 = 1
backend_data_directory1 = '/home/postgres/db/standby/pgsql/data'
backend_flag1= 'ALLOW_TO_FAILOVER'

pid_file_name = '/home/postgres/pgpool/pgpool.pid'
logdir = '/home/postgres/pgpool'

master_slave_mode = on
master_slave_sub_mode = 'stream'

health_check_user = 'apache'
health_check_password = ''

failover_command = '/usr/local/etc/failover.sh %d "%h" %p %D %m %M "%H" %P'
failback_command = '/bin/rm -f /home/postgres/var/log/pgpool/trigger/'

recovery_user = 'postgres'
recovery_password = 'postgres'
recovery_1st_stage_command = 'basebackup.sh'
recovery_2nd_stage_command = ''

内容有些多,我把pgpool.conf放到这里供参考(你也可以通过pgpoolAdmin来修改这些参数)。

4.检验
依次启动master,pgpool,apache:
/home/postgres/db/master/pgsql/bin/postmaster -D /home/postgres/db/master/pgsql/data

pgpool -n &
sh /home/postgres/website/apache-tomcat-7.0.16/bin/startup.sh
打开浏览器:http://localhost:8080/pgpooladmin/login.php,输入用户名postgres,密码postgres。你应该可以看到这个页面:

点击7432端口的Recovery,若成功则为:

然后通过如下命令模拟master死掉:
/home/postgres/db/master/pgsql/bin/pg_ctl stop -D /home/postgres/db/master/pgsql/data
你就会看到:

然后点promote,似乎不能成功(因为hot-standby的原因?我试过warm-standby是可以promote的,不知到hot-standby是否promote?有谁能继续写写,我的土办法是在standby下面强行建立recovery.conf文件,启动master,然后启动standby,然后启动pgpool,最后是tomcat里的pgpoolAdmin)。
好,回到我们最开始的状态,通过连接pgpool执行:
[postgres@localhost ~]$ psql -p 9999 mydb;
psql (8.4.4, server 9.1.2)
WARNING: psql version 8.4, server version 9.1.
        Some psql features might not work.
Type "help" for help.

mydb=# insert into foo select * from generate_series(1,1000000);  #假定已经有了mydb库
INSERT 0 1000000
mydb=# select count(*) from foo;
 count  
---------
5000000
(1 row)

然后单独链接hot-standby:
[postgres@localhost ~]$ /home/postgres/db/master/pgsql/bin/psql mydb --port=7432
psql (9.1.2)
Type "help" for help.

mydb=# select count(*) from foo;
 count  
---------
5000000
(1 row)
mydb=#  insert into foo select * from generate_series(1,1000000);  
ERROR:  cannot execute INSERT in a read-only transaction


是不是成功了。
最后说说结论,若DBA想使用pgpool-II的Master-Slave模式来做HA的话,由于配置相对注意的事情比较多,应多次反复练习配置,理解其内涵,以避免在各种情况下可能出现的问题。另外既然pgpool-II默认的Master-Slave模式使用slony来做,就应多试试pgpool-II+slony的方式。总之要多练习,谨慎行事。
至此完毕。

参考:
[1] pgpool-II レプリケーション構成
[2] Running PHP applications in Tomcat 6
[3] Installing pgpool Administration Tool
[4] Simple Streaming replication setting with pgpool-II
[5] pgpool-II Tutorial
[6] pgpool-II manual (English)
[7] linux下配置pgpool-II 3.1
[8] PostgreSQL 利用Pgpool-II的集群搭建方案(Partition+LoadBalance+Replication)
[9] PGPool-II master/slave mode using caveat
[10] 使用pgpool-II进行postgresql的replication


加我私人微信,交流技术。




https://blog.sciencenet.cn/blog-419883-541912.html

上一篇:PostgreSQL9.1 Hot-Standby ---之基于同步复制的方法
下一篇:PG9.1+pgpool-II3.1--之Load Balancing
收藏 IP: 223.72.72.*| 热度|

2 黄富强 zhaolinzzu

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-4-26 11:06

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部