diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26c7b84 --- /dev/null +++ b/.gitignore @@ -0,0 +1,136 @@ +.deps +.libs/ +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +acinclude.m4 +aclocal.m4 +autom4te.cache/ +build/ +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.in +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +run-tests.php +tags + +# Created by https://www.gitignore.io/api/linux,osx,vim,c + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon +# Thumbnails +._* +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### Vim ### +# swap +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +# session +Session.vim +# temporary +.netrwhist +# auto-generated tag files +tags + + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +modules.order +Module.symvers +Mkfile.old +dkms.conf + +pool.ini diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..10ae491 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM php:7.0 + +# install phpredis extension +ENV PHPREDIS_VERSION 3.0.0 + +RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz \ + && tar xfz /tmp/redis.tar.gz \ + && rm -r /tmp/redis.tar.gz \ + && mkdir -p /usr/src/php/ext \ + && mv phpredis-$PHPREDIS_VERSION /usr/src/php/ext/redis \ + && docker-php-ext-install redis + +# build php-cp +COPY . /usr/src/php/ext/php-cp +RUN docker-php-ext-install php-cp +COPY ./pool.ini /etc/pool.ini + +# aliases for dev +RUN echo "alias start='php pool_server start'" >> "/root/.bashrc" \ + && echo "alias stop='php pool_server stop'" >> "/root/.bashrc" \ + && echo "alias mi='make && make install'" >> "/root/.bashrc" \ + && echo "alias clean='make clean'" >> "/root/.bashrc" + +# workdir +COPY . /usr/src/php-cp +WORKDIR /usr/src/php-cp + +CMD ['/bin/bash'] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..80e7c19 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright Tianfeng.Han [mikan.tenny@gmail.com] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 018670e..0000000 --- a/Makefile +++ /dev/null @@ -1,193 +0,0 @@ -srcdir = /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool -builddir = /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool -top_srcdir = /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool -top_builddir = /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool -EGREP = /bin/grep -E -SED = /bin/sed -CONFIGURE_COMMAND = './configure' '--with-php-config=/xinhua/bin/php-config' -CONFIGURE_OPTIONS = '--with-php-config=/xinhua/bin/php-config' -SHLIB_SUFFIX_NAME = so -SHLIB_DL_SUFFIX_NAME = so -ZEND_EXT_TYPE = zend_extension -RE2C = re2c -AWK = gawk -shared_objects_connect_pool = connect_pool.lo Server.lo Worker.lo connect_pool_client.lo cpFunction.lo cpMemory.lo NetWork.lo ClientNet.lo msgpack/msgpack.lo msgpack/msgpack_pack.lo msgpack/msgpack_unpack.lo msgpack/msgpack_convert.lo -PHP_PECL_EXTENSION = connect_pool -PHP_MODULES = $(phplibdir)/connect_pool.la -PHP_ZEND_EX = -all_targets = $(PHP_MODULES) $(PHP_ZEND_EX) -install_targets = install-modules install-headers -prefix = /xinhua -exec_prefix = $(prefix) -libdir = ${exec_prefix}/lib -prefix = /xinhua -phplibdir = /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/modules -phpincludedir = /xinhua/include/php -CC = cc -CFLAGS = -g -CFLAGS_CLEAN = $(CFLAGS) -CPP = cc -E -CPPFLAGS = -DHAVE_CONFIG_H -CXX = -CXXFLAGS = -CXXFLAGS_CLEAN = $(CXXFLAGS) -EXTENSION_DIR = /xinhua/lib/php/extensions/no-debug-non-zts-20090626 -PHP_EXECUTABLE = /xinhua/bin/php -EXTRA_LDFLAGS = -EXTRA_LIBS = -INCLUDES = -I/xinhua/include/php -I/xinhua/include/php/main -I/xinhua/include/php/TSRM -I/xinhua/include/php/Zend -I/xinhua/include/php/ext -I/xinhua/include/php/ext/date/lib -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/include -LFLAGS = -LDFLAGS = -SHARED_LIBTOOL = -LIBTOOL = $(SHELL) $(top_builddir)/libtool -SHELL = /bin/bash -INSTALL_HEADERS = -mkinstalldirs = $(top_srcdir)/build/shtool mkdir -p -INSTALL = $(top_srcdir)/build/shtool install -c -INSTALL_DATA = $(INSTALL) -m 644 - -DEFS = -DPHP_ATOM_INC -I$(top_builddir)/include -I$(top_builddir)/main -I$(top_srcdir) -COMMON_FLAGS = $(DEFS) $(INCLUDES) $(EXTRA_INCLUDES) $(CPPFLAGS) $(PHP_FRAMEWORKPATH) - -all: $(all_targets) - @echo - @echo "Build complete." - @echo "Don't forget to run 'make test'." - @echo - -build-modules: $(PHP_MODULES) $(PHP_ZEND_EX) - -libphp$(PHP_MAJOR_VERSION).la: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) - $(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -rpath $(phptempdir) $(EXTRA_LDFLAGS) $(LDFLAGS) $(PHP_RPATHS) $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@ - -@$(LIBTOOL) --silent --mode=install cp $@ $(phptempdir)/$@ >/dev/null 2>&1 - -libs/libphp$(PHP_MAJOR_VERSION).bundle: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) - $(CC) $(MH_BUNDLE_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) $(EXTRA_LDFLAGS) $(PHP_GLOBAL_OBJS:.lo=.o) $(PHP_SAPI_OBJS:.lo=.o) $(PHP_FRAMEWORKS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@ && cp $@ libs/libphp$(PHP_MAJOR_VERSION).so - -install: $(all_targets) $(install_targets) - -install-sapi: $(OVERALL_TARGET) - @echo "Installing PHP SAPI module: $(PHP_SAPI)" - -@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir) - -@if test ! -r $(phptempdir)/libphp$(PHP_MAJOR_VERSION).$(SHLIB_DL_SUFFIX_NAME); then \ - for i in 0.0.0 0.0 0; do \ - if test -r $(phptempdir)/libphp$(PHP_MAJOR_VERSION).$(SHLIB_DL_SUFFIX_NAME).$$i; then \ - $(LN_S) $(phptempdir)/libphp$(PHP_MAJOR_VERSION).$(SHLIB_DL_SUFFIX_NAME).$$i $(phptempdir)/libphp$(PHP_MAJOR_VERSION).$(SHLIB_DL_SUFFIX_NAME); \ - break; \ - fi; \ - done; \ - fi - @$(INSTALL_IT) - -install-modules: build-modules - @test -d modules && \ - $(mkinstalldirs) $(INSTALL_ROOT)$(EXTENSION_DIR) - @echo "Installing shared extensions: $(INSTALL_ROOT)$(EXTENSION_DIR)/" - @rm -f modules/*.la >/dev/null 2>&1 - @$(INSTALL) modules/* $(INSTALL_ROOT)$(EXTENSION_DIR) - -install-headers: - -@if test "$(INSTALL_HEADERS)"; then \ - for i in `echo $(INSTALL_HEADERS)`; do \ - i=`$(top_srcdir)/build/shtool path -d $$i`; \ - paths="$$paths $(INSTALL_ROOT)$(phpincludedir)/$$i"; \ - done; \ - $(mkinstalldirs) $$paths && \ - echo "Installing header files: $(INSTALL_ROOT)$(phpincludedir)/" && \ - for i in `echo $(INSTALL_HEADERS)`; do \ - if test "$(PHP_PECL_EXTENSION)"; then \ - src=`echo $$i | $(SED) -e "s#ext/$(PHP_PECL_EXTENSION)/##g"`; \ - else \ - src=$$i; \ - fi; \ - if test -f "$(top_srcdir)/$$src"; then \ - $(INSTALL_DATA) $(top_srcdir)/$$src $(INSTALL_ROOT)$(phpincludedir)/$$i; \ - elif test -f "$(top_builddir)/$$src"; then \ - $(INSTALL_DATA) $(top_builddir)/$$src $(INSTALL_ROOT)$(phpincludedir)/$$i; \ - else \ - (cd $(top_srcdir)/$$src && $(INSTALL_DATA) *.h $(INSTALL_ROOT)$(phpincludedir)/$$i; \ - cd $(top_builddir)/$$src && $(INSTALL_DATA) *.h $(INSTALL_ROOT)$(phpincludedir)/$$i) 2>/dev/null || true; \ - fi \ - done; \ - fi - -PHP_TEST_SETTINGS = -d 'open_basedir=' -d 'output_buffering=0' -d 'memory_limit=-1' -PHP_TEST_SHARED_EXTENSIONS = ` \ - if test "x$(PHP_MODULES)" != "x"; then \ - for i in $(PHP_MODULES)""; do \ - . $$i; $(top_srcdir)/build/shtool echo -n -- " -d extension=$$dlname"; \ - done; \ - fi; \ - if test "x$(PHP_ZEND_EX)" != "x"; then \ - for i in $(PHP_ZEND_EX)""; do \ - . $$i; $(top_srcdir)/build/shtool echo -n -- " -d $(ZEND_EXT_TYPE)=$(top_builddir)/modules/$$dlname"; \ - done; \ - fi` -PHP_DEPRECATED_DIRECTIVES_REGEX = '^(define_syslog_variables|register_(globals|long_arrays)?|safe_mode|magic_quotes_(gpc|runtime|sybase)?|(zend_)?extension(_debug)?(_ts)?)[\t\ ]*=' - -test: all - -@if test ! -z "$(PHP_EXECUTABLE)" && test -x "$(PHP_EXECUTABLE)"; then \ - INI_FILE=`$(PHP_EXECUTABLE) -d 'display_errors=stderr' -r 'echo php_ini_loaded_file();' 2> /dev/null`; \ - if test "$$INI_FILE"; then \ - $(EGREP) -h -v $(PHP_DEPRECATED_DIRECTIVES_REGEX) "$$INI_FILE" > $(top_builddir)/tmp-php.ini; \ - else \ - echo > $(top_builddir)/tmp-php.ini; \ - fi; \ - INI_SCANNED_PATH=`$(PHP_EXECUTABLE) -d 'display_errors=stderr' -r '$$a = explode(",\n", trim(php_ini_scanned_files())); echo $$a[0];' 2> /dev/null`; \ - if test "$$INI_SCANNED_PATH"; then \ - INI_SCANNED_PATH=`$(top_srcdir)/build/shtool path -d $$INI_SCANNED_PATH`; \ - $(EGREP) -h -v $(PHP_DEPRECATED_DIRECTIVES_REGEX) "$$INI_SCANNED_PATH"/*.ini >> $(top_builddir)/tmp-php.ini; \ - fi; \ - TEST_PHP_EXECUTABLE=$(PHP_EXECUTABLE) \ - TEST_PHP_SRCDIR=$(top_srcdir) \ - CC="$(CC)" \ - $(PHP_EXECUTABLE) -n -c $(top_builddir)/tmp-php.ini $(PHP_TEST_SETTINGS) $(top_srcdir)/run-tests.php -n -c $(top_builddir)/tmp-php.ini -d extension_dir=$(top_builddir)/modules/ $(PHP_TEST_SHARED_EXTENSIONS) $(TESTS); \ - rm $(top_builddir)/tmp-php.ini; \ - else \ - echo "ERROR: Cannot run tests without CLI sapi."; \ - fi - -clean: - find . -name \*.gcno -o -name \*.gcda | xargs rm -f - find . -name \*.lo -o -name \*.o | xargs rm -f - find . -name \*.la -o -name \*.a | xargs rm -f - find . -name \*.so | xargs rm -f - find . -name .libs -a -type d|xargs rm -rf - rm -f libphp$(PHP_MAJOR_VERSION).la $(SAPI_CLI_PATH) $(OVERALL_TARGET) modules/* libs/* - -distclean: clean - rm -f Makefile config.cache config.log config.status Makefile.objects Makefile.fragments libtool main/php_config.h stamp-h sapi/apache/libphp$(PHP_MAJOR_VERSION).module buildmk.stamp - $(EGREP) define'.*include/php' $(top_srcdir)/configure | $(SED) 's/.*>//'|xargs rm -f - -.PHONY: all clean install distclean test -.NOEXPORT: -connect_pool.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/connect_pool.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/connect_pool.c -o connect_pool.lo -Server.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/Server.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/Server.c -o Server.lo -Worker.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/Worker.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/Worker.c -o Worker.lo -connect_pool_client.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/connect_pool_client.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/connect_pool_client.c -o connect_pool_client.lo -cpFunction.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/cpFunction.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/cpFunction.c -o cpFunction.lo -cpMemory.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/cpMemory.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/cpMemory.c -o cpMemory.lo -NetWork.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/NetWork.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/NetWork.c -o NetWork.lo -ClientNet.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/ClientNet.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/ClientNet.c -o ClientNet.lo -msgpack/msgpack.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/msgpack/msgpack.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/msgpack/msgpack.c -o msgpack/msgpack.lo -msgpack/msgpack_pack.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/msgpack/msgpack_pack.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/msgpack/msgpack_pack.c -o msgpack/msgpack_pack.lo -msgpack/msgpack_unpack.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/msgpack/msgpack_unpack.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/msgpack/msgpack_unpack.c -o msgpack/msgpack_unpack.lo -msgpack/msgpack_convert.lo: /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/msgpack/msgpack_convert.c - $(LIBTOOL) --mode=compile $(CC) -I. -I/root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c /root/.netbeans/remote/192.168.20.131/localhost-MacOSX-x86_64/Users/guoxinhua/Documents/connect_pool/msgpack/msgpack_convert.c -o msgpack/msgpack_convert.lo -$(phplibdir)/connect_pool.la: ./connect_pool.la - $(LIBTOOL) --mode=install cp ./connect_pool.la $(phplibdir) - -./connect_pool.la: $(shared_objects_connect_pool) $(CONNECT_POOL_SHARED_DEPENDENCIES) - $(LIBTOOL) --mode=link $(CC) $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) -o $@ -export-dynamic -avoid-version -prefer-pic -module -rpath $(phplibdir) $(EXTRA_LDFLAGS) $(shared_objects_connect_pool) $(CONNECT_POOL_SHARED_LIBADD) - diff --git a/README.md b/README.md index a2d9e64..c21a793 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,210 @@ -## redis and pdo sync connect pool, just for PHP +## php-cp(php-connect-pool),redis和pdo的本地代理 +[中文简介] http://blog.sina.com.cn/s/blog_9eaa0f400102v9fd.html -Provide local connection pool like java +提供连接池,读写分离,负载均衡,慢查询日志,大数据块日志等功能 +## 要求 -## Requirement - -- PHP 5.3 + +- PHP 5.3 + (no zts) - linux 2.6+ - pdo and redis extension install -## Install +## 安装 + ++ `phpize && ./configure && make install` ++ `echo "extension=xx/connect_pool.so" >> php.ini` -phpize=>./configure=>make install=>echo "extensions=xx/connect_pool.so">php.ini +## 使用Docker安装 +可以使用Docker编译,需要在项目的根目录下运行: -## 解决的问题 +1. 根据自己的配置,复制 `php.ini.example` 文件为 `php.ini` 文件,修改 `pool.ini` 文件 +2. `docker build -t php-cp .` -- 1.php的短链接对mysql和redis造成了大量的资源消耗。 -- 2.想象一个这样的业务:请求过来连了一次redis,又去连了mysql,之后去调用其他的rpc请求,而某种原因导致请求非常慢,那么之前的tcp连接就会一直占用。 -- 3.现有开源产品(Atlas,TDDL等)需要单独部署集群,在架构上引入了外部依赖,经过中间网络转发效率变低,没法同时支持redis和pdo。 +## 技术特性: -## 技术特性 +- 提供了release方法,在每次fetch数据后(redis的get set) 调用,将连接放回到池子里面,避免其他耗时操作导致的db层连接数过高问题。 +- 提供最大最小连接数配置支持。 +- 连接自动ping 数据库, 防止压力小长时间不请求导致的gone away问题 +- 根据压力自动获取(最大到最大连接数)或者释放(释放最小到最小连接数)池子里面的连接。 +- 做了大量优化虽然请求经过代理进程转发但基本没有性能损耗. +- 当池子里面的连接被占用没了,接下来的挣钱连接的进程将会排队,直到持有连接的进程release连接. +- 使用透明化,相对于传统的pdo和redis操作,只需要修改new的类名,以及适当时机release连接即可(可以集成到db层框架) +- 支持pdo的读写分离和从库的负载均衡。 +- 支持cli模式下的pdo和redis异步查询。 +- 支持慢查询日志(max_hold_time_to_log)以及大的数据块(max_data_size_to_log)日志功能。 -- 1.在model框架里面做集成,每次fetchAll(set/get)后执行release方法,释放所占用的连接,防止因为脚本卡住导致的连接占用过高问题。 -- 2.支持最大最小连接数配置。 -- 3.支持压力小自动回收连接(可配置)。 -- 4.支持平滑重启。 -- 5.减少php短连接对db层的压力。 -- 6.做了大量优化,虽然请求经过连接池进程转发,但是基本无qps损耗。 -- 7.支持连接用光的排队机制。 -- 8.框架简单整合后(修改new 方法),现有业务一行代码都不用改即可用上连接池。 -## Example -step 1 move the pool.ini file to /etc/ and modify it as you need. +## 提示 +- 请求结束(rshutdown/mshutdown阶段)会调用自动调用release,不要依赖于这个release,否则连接利用率会很低 +- 关于异常: +pdoProxy和原生Pdo不一样的一点是 默认pdo是静默模式 不抛异常 pdoProxy是抛异常的(且是用Exception类抛出的 不是PDOException) +- pool_server 必须以root用户启动 +- redis不支持pub/sub方法 +- 当你用完一个连接后(例如:fetchAll调用结束),请调用release来马上释放连接到池子里面(如果事务需要在事务commit或者rollback后release),如果不想改业务代码可以在框架层每次fetch(或者get/set)用完之后调用release方法。 -step 2 start the pool_server process: -```./pool_server start -```support "start" "stop" "restart" "reload" +## 集成好的框架 +- yii请参考项目中的frame_example +- redis请参考项目中的frame_example +- ci 请参考此项目 https://github.com/ethenoscar2011/codeigniter-phpcp +- thinkphp 请参考 http://git.oschina.net/xavier007/THINKPHP_phpcp_driver +- discuz 请参考 https://github.com/xluohome/php-cp-for-discuz -step 3 modify you php script: +## 使用 +* 将项目中的pool.ini文件mv到/etc/pool.ini,并根据需求修改配置文件 +``` +$ mv ./pool.ini.example /etc/pool.ini +$ chmod +x ./pool_server //pool_server为php脚本 可自行修改 +$ mv pool_server /usr/local/bin/ +``` + +* 日常运维使用 +``` +$ pool_server start //启动服务 如果配置文件的daemonize开启则后台运行 否则为前台运行 Ctrl+c结束服务 +$ pool_server stop //停止服务 +$ pool_server restart //重启服务 +$ pool_server status //查看服务状态 ``` - $db = new pdo_connect_pool(xxxx);//dont use persistent -$redis = new Redis(); -=》$redis = new redis_connect_pool();//dont use pconnect +* 日常开发使用 + 将该项目源码加入IDE的外部库中, 即可有代码提示, 切记不要加入php配置的include path中 -tips:use $db($redis)->release() to release the connection as early as you can; -?> +``` php +connect("192.168.20.130"); +$obj->select(5); +$obj->set("test", '1111'); +var_dump($obj->get("test")); + +$obj = new PDO('mysql:host=192.168.20.130;dbname=test1', "admin", "admin"); +$rs = $obj->query("show tables"); +var_dump($rs->fetchAll()); + +//*****************use pool(使用了连接池)*********************************/ +$obj = new redisProxy(); +$rs = $obj->connect("192.168.20.130"); +$obj->select(5); +$obj->set("test", '1111'); +var_dump($obj->get("test")); +$obj->release(); + +$obj1 = new pdoProxy('mysql:host=192.168.20.131;dbname=db1', "admin", "admin"); +$rs = $obj1->query("show tables"); +var_dump($rs->fetchAll()); +$obj1->release(); + + +/* * ****************异步 pdo和redis操作********************************************** + * 依赖 swoole的event函数 + */ +include './asyncClass.php'; +$obj = new asyncRedisProxy(); +$obj->connect("127.0.0.1", "6379"); +$obj->set("a", 11111, function($obj, $ret) { + $obj->get("a", function($obj, $data) { + var_dump($data); + $obj->release(); //release to con pool + }); +}); + + +$obj2 = new asyncPdoProxy('mysql:host=192.168.1.19;dbname=mz_db', "public_user", "1qa2ws3ed"); +$obj2->query("select 1 from mz_user where user_id=299", function($obj, $stmt) { + $arr = $stmt->fetchAll(); + var_dump($arr); + $obj->query("select 2 from mz_user where user_id=299", function($obj, $stmt) { + $arr = $stmt->fetchAll(); + var_dump($arr); + $obj->release(); //release to con pool + }); +}); + + +$obj3 = new asyncPdoProxy('mysql:host=192.168.1.19;dbname=mz_db', "public_user", "1qa2ws3ed"); +$obj3->exec("insert into t1(name) values('111111')", function($obj, $data) { + var_dump($data); + $obj->release(); ////release to con pool +}); + + +$obj4 = new asyncPdoProxy('mysql:host=192.168.1.19;dbname=mz_db', "public_user", "1qa2ws3ed"); +$stmt = $obj4->prepare("select * from mz_account where user_id=:user_id"); +$stmt->bindParam(':user_id', "311"); +$stmt->execute(function($stmt, $ret) { + $data = $stmt->fetchAll(); + var_dump($data); + $stmt->release(); +}); + +//*******************use master slave(最新版本支持了读写分离和从库的负载均衡 用法如下)***********************/ +$config = array( + 'master' => array( + 'data_source' => "mysql:host=192.168.1.19;dbname=db1", + 'username' => "public_user", + 'pwd' => "1qa2ws3ed", + 'options' => array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_TIMEOUT => 3, + PDO::ATTR_CASE => PDO::CASE_UPPER, + ), + ), + 'slave' => array( + "0" => array( + 'data_source' => "mysql:host=192.168.1.20;dbname=db2", + 'username' => "public_user", + 'pwd' => "1qa2ws3ed", + 'options' => array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_TIMEOUT => 3, + PDO::ATTR_CASE => PDO::CASE_UPPER, + ), + ), + "1" => array( + 'data_source' => "mysql:host=192.168.1.21;dbname=db3", + 'username' => "public_user", + 'pwd' => "1qa2ws3ed", + 'options' => array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_TIMEOUT => 3, + PDO::ATTR_CASE => PDO::CASE_LOWER, + ), + ), + ), +); +/* * *************************"select"和"show"开头的语句 走随机从库********** */ +$obj1 = new pdoProxy($config); +$rs = $obj1->query("select * from test limit 1"); +var_dump($rs->fetchAll()); //走随机从库 +$obj1->release(); + +/* * **************************读强行走主库*************************** */ +$obj1->enable_slave = false; +$rs = $obj1->query("select * from test limit 1"); +var_dump($rs->fetchAll()); //读主库 +$obj1->release(); + +/* * *************************除了"select"和"show"开头的语句 都走主库********** */ +$sql = "insert into `test` (tid) values (5)"; +$rs = $obj1->exec($sql); //走主库 +$obj1->release(); ``` + +## 测试 + +运行命令 + +`php tests/RunTest.php --host 172.17.0.2 --class RedisTest --test test_set_get` + + 执行测试,该命令接受三个参数: + ++ --host 设置主机地址,可选,默认值为"127.0.0.1" ++ --class 设置要运行的测试类名称,可选,默认值为"RedisTest" ++ --test 设置要运行的测试函数名称,可选,如果不设置则执行所有测试 + +## contact us +- http://weibo.com/u/2661945152 +- 83212019@qq.com +- qq群号 538716391 diff --git a/asyncClass.php b/asyncClass.php new file mode 100644 index 0000000..80e69ae --- /dev/null +++ b/asyncClass.php @@ -0,0 +1,153 @@ +obj + + public function __construct() { + $this->redisProxy = new redisProxy(); + $this->syncFunArr = array( + 'connect', + 'pconnect' + ); + $this->redisProxy->setAsync(1); + } + + /* + * every obj have a tcp with pool_server,so close it when __destruct + */ + + public function __destruct() { + $this->redisProxy->close(); + } + + public function __call($name, $arguments) { + if (in_array($name, $this->syncFunArr)) { + if (is_callable(end($arguments))) { + throw new Exception("the function " . json_encode($this->syncFunArr) . " can not async"); + } + return call_user_func_array(array($this->redisProxy, $name), $arguments); + } + if (is_callable(end($arguments))) {//can async and last args is callback + $callback = array_pop($arguments); + $this->redisProxy->setAsync(1); + $fd = call_user_func_array(array($this->redisProxy, $name), $arguments); + $this->map[$fd] = $this->redisProxy; + swoole_event_add($fd, function($fd) use ($callback) { + swoole_event_del($fd); + $data = $this->map[$fd]->done(); //set sync + call_user_func($callback, $this, $data); + }); + } else { + return call_user_func_array(array($this->redisProxy, $name), $arguments); + } + } + +} + +class asyncPdoProxy { + + private $pdoProxy = null; + private $syncFunArr; + private $map; //fd=>obj + + public function __construct($dns, $user, $pwd, $arr = array()) { + $this->pdoProxy = new pdoProxy($dns, $user, $pwd, $arr); + $this->asyncFunArr = array( + 'exec', + 'query', + 'commit' + ); + $this->pdoProxy->setAsync(1); //set the next cmd async/sync + } + + /* + * every obj have a tcp with pool_server,so close it when __destruct + */ + + public function __destruct() { + $this->pdoProxy->close(); + } + + public function __call($name, $arguments) { + if (in_array($name, $this->asyncFunArr)) { + if (is_callable(end($arguments))) {//can async and last args is callback + $callback = array_pop($arguments); + $this->pdoProxy->setAsync(1); //set pdo async + $fd = call_user_func_array(array($this->pdoProxy, $name), $arguments); + $this->map[$fd] = $this->pdoProxy; + swoole_event_add($fd, function($fd) use ($callback) { + swoole_event_del($fd); + $data = $this->map[$fd]->done(); //set pdo sync inner + call_user_func($callback, $this, $data); + }); + } else { + $this->pdoProxy->setAsync(0); //set pdo sync + return call_user_func_array(array($this->pdoProxy, $name), $arguments); + } + } else { + if (is_callable(end($arguments))) { + throw new Exception("only the function " . json_encode($this->syncFunArr) . " can be async"); + } + $this->pdoProxy->setAsync(0); //set pdo sync + $data = call_user_func_array(array($this->pdoProxy, $name), $arguments); + if (is_object($data)) { + $stmt = new asyncSTMTProxy(); //warpper it + $stmt->setSTMTObj($data); + $stmt->setPDOObj($this->pdoProxy); + $data->setAsync(0); //set stmt async + return $stmt; + } else { + return $data; + } + } + } + +} + +class asyncSTMTProxy { + + private $stmtProxy = null; + private $pdoProxy = null; + private $syncFunArr; + private $map; //fd=>obj + private $asyncFunArr = array( + 'execute', + ); + + public function setSTMTObj($obj) { + $this->stmtProxy = $obj; + } + + public function setPDOObj($obj) { + $this->pdoProxy = $obj; + } + + public function __call($name, $arguments) { + if (in_array($name, $this->asyncFunArr)) { + if (is_callable(end($arguments))) {//can async and last args is callback + $callback = array_pop($arguments); + $this->stmtProxy->setAsync(1); //set pdo async + $fd = call_user_func_array(array($this->stmtProxy, $name), $arguments); + $this->map[$fd] = $this->stmtProxy; + swoole_event_add($fd, function($fd) use ($callback) { + swoole_event_del($fd); + $data = $this->map[$fd]->done(); //set pdo sync + call_user_func($callback, $this->stmtProxy, $data); + }); + } else { + $this->stmtProxy->setAsync(0); //set pdo sync + return call_user_func_array(array($this->stmtProxy, $name), $arguments); + } + } else { + if (is_callable(end($arguments))) { + throw new Exception("only the function " . json_encode($this->syncFunArr) . " can be async"); + } + $this->stmtProxy->setAsync(0); //set pdo sync + return call_user_func_array(array($this->stmtProxy, $name), $arguments); + } + } + +} diff --git a/config.m4 b/config.m4 index d663ba6..ccbc98b 100644 --- a/config.m4 +++ b/config.m4 @@ -17,46 +17,28 @@ dnl Make sure that the comment is aligned: dnl [ --enable-connect_pool Enable connect_pool support]) if test "$PHP_CONNECT_POOL" != "no"; then - dnl Write more examples of tests here... - dnl # --with-connect_pool -> check with-path - dnl SEARCH_PATH="/usr/local /usr" # you might want to change this - dnl SEARCH_FOR="/include/connect_pool.h" # you most likely want to change this - dnl if test -r $PHP_CONNECT_POOL/$SEARCH_FOR; then # path given as parameter - dnl CONNECT_POOL_DIR=$PHP_CONNECT_POOL - dnl else # search default path list - dnl AC_MSG_CHECKING([for connect_pool files in default path]) - dnl for i in $SEARCH_PATH ; do - dnl if test -r $i/$SEARCH_FOR; then - dnl CONNECT_POOL_DIR=$i - dnl AC_MSG_RESULT(found in $i) - dnl fi - dnl done - dnl fi - dnl - dnl if test -z "$CONNECT_POOL_DIR"; then - dnl AC_MSG_RESULT([not found]) - dnl AC_MSG_ERROR([Please reinstall the connect_pool distribution]) - dnl fi +AC_MSG_CHECKING(PHP version) +AC_TRY_COMPILE([#include "$phpincludedir/main/php_version.h"], [ +#if PHP_MAJOR_VERSION < 5 +#error this extension requires at least PHP version 5 or newer +#endif +], +[AC_MSG_RESULT(ok)], +[AC_MSG_ERROR([need at least PHP 5 or newer])]) - dnl # --with-connect_pool -> add include path - dnl PHP_ADD_INCLUDE($CONNECT_POOL_DIR/include) - dnl # --with-connect_pool -> check for lib and symbol presence - dnl LIBNAME=connect_pool # you may want to change this - dnl LIBSYMBOL=connect_pool # you most likely want to change this +AC_MSG_CHECKING(ZTS) +AC_TRY_COMPILE([#include "$phpincludedir/main/php_config.h"], [ +#ifdef ZTS +#error this extension requires no zts, please do not add ' --enable-maintainer-zts' when you configure php +#endif +], +[AC_MSG_RESULT(ok)], +[AC_MSG_ERROR([need php no zts, please do not add ' --enable-maintainer-zts' when you configure php])]) - dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, - dnl [ - dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $CONNECT_POOL_DIR/lib, CONNECT_POOL_SHARED_LIBADD) - dnl AC_DEFINE(HAVE_CONNECT_POOLLIB,1,[ ]) - dnl ],[ - dnl AC_MSG_ERROR([wrong connect_pool lib version or lib not found]) - dnl ],[ - dnl -L$CONNECT_POOL_DIR/lib -lm - dnl ]) - dnl - dnl PHP_SUBST(CONNECT_POOL_SHARED_LIBADD) + CFLAGS="-Wall -pthread $CFLAGS" + LDFLAGS="$LDFLAGS -lpthread" PHP_NEW_EXTENSION(connect_pool, connect_pool.c cpServer.c cpWorker.c \ connect_pool_client.c \ @@ -64,10 +46,12 @@ if test "$PHP_CONNECT_POOL" != "no"; then cpMemory.c \ cpNetWork.c \ cpClientNet.c \ + cpPingWorker.c \ msgpack/msgpack.c \ msgpack/msgpack_pack.c\ msgpack/msgpack_unpack.c\ msgpack/msgpack_convert.c\ + msgpack7/swoole_serialize.c\ , $ext_shared) PHP_ADD_INCLUDE([$ext_srcdir/include]) fi diff --git a/connect_pool.c b/connect_pool.c old mode 100755 new mode 100644 index 97478eb..77e3a04 --- a/connect_pool.c +++ b/connect_pool.c @@ -17,69 +17,54 @@ #include "php_connect_pool.h" #include #include "zend_interfaces.h" -#include +#include "zend_exceptions.h" #include +#include + +#include -static HashTable pdo_object_table; -static HashTable redis_object_table; +#include static zval* pdo_stmt = NULL; -static int warning_gone_away = 0; +extern zval* pdo_object; +extern zval* redis_object; cpServerG ConProxyG; -cpServerGS *ConProxyGS; +cpServerGS *ConProxyGS = NULL; cpWorkerG ConProxyWG; -cpMasterInfo info; extern sapi_module_struct sapi_module; -static void cp_destory_client(zend_rsrc_list_entry *rsrc TSRMLS_DC); +static void cp_destory_client(zend_resource *rsrc TSRMLS_DC); static void pdo_dispatch(zval *args); -static void pdo_proxy_connect(zval *args, int reconnect); static void pdo_proxy_pdo(zval *args); static void pdo_proxy_stmt(zval *args); +static void cp_add_fail_into_mem(zval *conf, zval *data_source); + +#define CP_VERSION "1.5.0" #define CP_INTERNAL_ERROR_SEND(send_data)\ - ({ \ - zval send_zval;\ - ZVAL_STRING(&send_zval,send_data,0);\ - CP_INTERNAL_SERIALIZE_SEND_MEM(&send_zval,CP_SIGEVENT_EXCEPTION);\ - }) + ({ \ + CP_INTERNAL_SEND_RAW(send_data,CP_SIGEVENT_EXCEPTION)\ + }) #define CP_INTERNAL_NORMAL_SEND(send_data)\ - ({ \ - zval send_zval;\ - ZVAL_STRING(&send_zval,send_data,0);\ - CP_INTERNAL_SERIALIZE_SEND_MEM(&send_zval,CP_SIGEVENT_TURE);\ - }) - -#define CP_SEND_EXCEPTION(str,use_mem) do{zval *exception = EG(exception);\ - zend_class_entry *ce_exception = Z_OBJCE_P(exception);\ - EG(exception) = NULL;\ - zend_call_method_with_0_params(&exception, ce_exception, NULL, "__tostring", str);\ - if(use_mem){\ - CP_INTERNAL_SERIALIZE_SEND_MEM(*str,CP_SIGEVENT_EXCEPTION);\ - }else{\ - CP_INTERNAL_ERROR_SEND(Z_STRVAL_P(*str));}\ - }while(0); - -#ifdef SW_ASYNC_MYSQL -#include "ext/mysqlnd/mysqlnd.h" -#include "ext/mysqli/mysqli_mysqlnd.h" -#include "ext/mysqli/php_mysqli_structs.h" -#endif + ({ \ + CP_INTERNAL_SEND_RAW(send_data,CP_SIGEVENT_TURE)\ + }) +#define CP_SEND_EXCEPTION do{zval *str;CP_SEND_EXCEPTION_ARGS(&str);cp_zval_ptr_dtor(&str);}while(0); +#define CP_INTERNAL_NORMAL_SEND_RETURN(send_data)({CP_INTERNAL_NORMAL_SEND(send_data);return CP_TRUE;}) +#define CP_INTERNAL_ERROR_SEND_RETURN(send_data) ({ CP_INTERNAL_ERROR_SEND(send_data);return CP_FALSE;}) +#define CP_SEND_EXCEPTION_RETURN do{CP_SEND_EXCEPTION;return CP_FALSE;}while(0); +#define CP_TEST_RETURN_TRUE(flag) ({if(flag==CP_CONNECT_PING)return CP_TRUE;}) -#include "zend_exceptions.h" const zend_function_entry cp_functions[] = { PHP_FE(pool_server_create, NULL) + PHP_FE(pool_server_status, NULL) PHP_FE(pool_server_shutdown, NULL) PHP_FE(pool_server_reload, NULL) - PHP_FE(get_disable_list, NULL) - PHP_FE(set_disable_list, NULL) - PHP_FE(shit_pdo_warning_function, NULL) - PHP_FE(redis_connect, NULL) - PHP_FE(client_close, NULL) + PHP_FE(pool_server_version, NULL) PHP_FE_END /* Must be the last line in cp_functions[] */ }; @@ -90,30 +75,113 @@ ZEND_END_ARG_INFO() const zend_function_entry pdo_connect_pool_methods[] = { PHP_ME(pdo_connect_pool, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(pdo_connect_pool, __destruct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(pdo_connect_pool, __destruct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR) PHP_ME(pdo_connect_pool, __call, __call_args, ZEND_ACC_PUBLIC) - // PHP_ME(pdo_connect_pool, quote, NULL, ZEND_ACC_PUBLIC) PHP_ME(pdo_connect_pool, release, NULL, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool, close, NULL, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool, setAsync, NULL, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool, done, NULL, ZEND_ACC_PUBLIC) PHP_FE_END }; +ZEND_BEGIN_ARG_INFO_EX(arginfo_statement_void, 0, 0, 0) +ZEND_END_ARG_INFO() + const zend_function_entry pdo_connect_pool_PDOStatement_methods[] = { PHP_ME(pdo_connect_pool_PDOStatement, __call, __call_args, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool_PDOStatement, setAsync, NULL, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool_PDOStatement, release, NULL, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool_PDOStatement, done, NULL, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool_PDOStatement, rewind, arginfo_statement_void, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool_PDOStatement, next, arginfo_statement_void, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool_PDOStatement, current, arginfo_statement_void, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool_PDOStatement, key, arginfo_statement_void, ZEND_ACC_PUBLIC) + PHP_ME(pdo_connect_pool_PDOStatement, valid, arginfo_statement_void, ZEND_ACC_PUBLIC) PHP_FE_END }; +PHP_METHOD(pdo_connect_pool_PDOStatement, rewind) +{ + zval *object = getThis(); + zval *method_ptr, method, *ret_value = NULL; + method_ptr = &method; + zend_class_entry *ce; + ce = Z_OBJCE_P(object); + CP_ZVAL_STRING(method_ptr, "fetchAll", 0); + if (cp_call_user_function_ex(EG(function_table), &object, method_ptr, &ret_value, 0, NULL, 0, NULL TSRMLS_CC) == FAILURE) + { + return; + } + zend_update_property_long(ce, object, "pos", sizeof ("pos") - 1, 0 TSRMLS_CC); + zend_update_property(ce, object, "rs", sizeof ("rs") - 1, ret_value TSRMLS_CC); + cp_zval_ptr_dtor(&ret_value); + +} + +PHP_METHOD(pdo_connect_pool_PDOStatement, current) +{ + zval *pos, *rs, *row = NULL; + zend_class_entry *ce; + ce = Z_OBJCE_P(getThis()); + pos = cp_zend_read_property(ce, getThis(), "pos", sizeof ("pos") - 1, 0 TSRMLS_DC); + rs = cp_zend_read_property(ce, getThis(), "rs", sizeof ("rs") - 1, 0 TSRMLS_DC); + + cp_zend_hash_index_find(Z_ARRVAL_P(rs), Z_LVAL_P(pos), (void**) &row); + RETVAL_ZVAL(row, 1, 1); +} + +PHP_METHOD(pdo_connect_pool_PDOStatement, key) +{ + zval *pos; + zend_class_entry *ce; + ce = Z_OBJCE_P(getThis()); + pos = cp_zend_read_property(ce, getThis(), "pos", sizeof ("pos") - 1, 0 TSRMLS_DC); + ZVAL_LONG(return_value, Z_LVAL_P(pos)); +} + +PHP_METHOD(pdo_connect_pool_PDOStatement, next) +{ + zval *pos; + zend_class_entry *ce; + ce = Z_OBJCE_P(getThis()); + pos = cp_zend_read_property(ce, getThis(), "pos", sizeof ("pos") - 1, 0 TSRMLS_DC); + + zend_update_property_long(ce, getThis(), "pos", sizeof ("pos") - 1, ++Z_LVAL_P(pos) TSRMLS_CC); +} + +PHP_METHOD(pdo_connect_pool_PDOStatement, valid) +{ + zval *pos, *rs, *row = NULL; + zend_class_entry *ce; + ce = Z_OBJCE_P(getThis()); + pos = cp_zend_read_property(ce, getThis(), "pos", sizeof ("pos") - 1, 0 TSRMLS_DC); + rs = cp_zend_read_property(ce, getThis(), "rs", sizeof ("rs") - 1, 0 TSRMLS_DC); + + if (cp_zend_hash_index_find(Z_ARRVAL_P(rs), Z_LVAL_P(pos), (void**) &row) == SUCCESS) + { + RETURN_BOOL(1); + } + else + { + RETURN_BOOL(0); + } + +} const zend_function_entry redis_connect_pool_methods[] = { PHP_ME(redis_connect_pool, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(redis_connect_pool, __destruct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(redis_connect_pool, __destruct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR) PHP_ME(redis_connect_pool, __call, __call_args, ZEND_ACC_PUBLIC) PHP_ME(redis_connect_pool, release, NULL, ZEND_ACC_PUBLIC) PHP_ME(redis_connect_pool, select, NULL, ZEND_ACC_PUBLIC) - PHP_FALIAS(connect, redis_connect, NULL) - PHP_FALIAS(pconnect, redis_connect, NULL) + PHP_ME(redis_connect_pool, connect, NULL, ZEND_ACC_PUBLIC) + PHP_ME(redis_connect_pool, auth, NULL, ZEND_ACC_PUBLIC) + PHP_ME(redis_connect_pool, done, NULL, ZEND_ACC_PUBLIC) + PHP_ME(redis_connect_pool, setAsync, NULL, ZEND_ACC_PUBLIC) + PHP_ME(redis_connect_pool, close, NULL, ZEND_ACC_PUBLIC) + PHP_MALIAS(redis_connect_pool, pconnect, connect, NULL, ZEND_ACC_PUBLIC) /* pconnect 别名指向connect */ PHP_FE_END }; - int le_cp_server; int le_cli_connect_pool; @@ -141,7 +209,7 @@ zend_module_entry connect_pool_module_entry = { PHP_RINIT(connect_pool), //RINIT PHP_RSHUTDOWN(connect_pool), //RSHUTDOWN PHP_MINFO(connect_pool), - "1.0", + CP_VERSION, STANDARD_MODULE_PROPERTIES }; @@ -150,37 +218,43 @@ zend_module_entry connect_pool_module_entry = { ZEND_GET_MODULE(connect_pool) #endif -PHP_MINIT_FUNCTION(connect_pool) { - sapi_module_struct *symbol = NULL; - symbol = &sapi_module; - if (symbol) { - cpArgv0 = symbol->executable_location; - } - +PHP_MINIT_FUNCTION(connect_pool) +{ le_cli_connect_pool = zend_register_list_destructors_ex(send_oob2proxy, cp_destory_client, CP_RES_CLIENT_NAME, module_number); //持久 - INIT_CLASS_ENTRY(pdo_connect_pool_ce, "pdo_connect_pool", pdo_connect_pool_methods); + INIT_CLASS_ENTRY(pdo_connect_pool_ce, "pdoProxy", pdo_connect_pool_methods); pdo_connect_pool_class_entry_ptr = zend_register_internal_class(&pdo_connect_pool_ce TSRMLS_CC); + zend_register_class_alias("pdo_connect_pool", pdo_connect_pool_class_entry_ptr); - INIT_CLASS_ENTRY(redis_connect_pool_ce, "redis_connect_pool", redis_connect_pool_methods); + INIT_CLASS_ENTRY(redis_connect_pool_ce, "redisProxy", redis_connect_pool_methods); redis_connect_pool_class_entry_ptr = zend_register_internal_class(&redis_connect_pool_ce TSRMLS_CC); + zend_register_class_alias("redis_connect_pool", pdo_connect_pool_class_entry_ptr); INIT_CLASS_ENTRY(pdo_connect_pool_PDOStatement_ce, "pdo_connect_pool_PDOStatement", pdo_connect_pool_PDOStatement_methods); + + //zend_class_entry *pdo_dbstmt_ce = cp_zend_fetch_class("PDOStatement", ZEND_FETCH_CLASS_AUTO); + pdo_connect_pool_PDOStatement_class_entry_ptr = zend_register_internal_class(&pdo_connect_pool_PDOStatement_ce TSRMLS_CC); + zend_class_implements(pdo_connect_pool_PDOStatement_class_entry_ptr TSRMLS_CC, 1, spl_ce_Iterator, spl_ce_Countable); + - zend_hash_init(&pdo_object_table, 50, NULL, ZVAL_PTR_DTOR, 1); - zend_hash_init(&redis_object_table, 50, NULL, ZVAL_PTR_DTOR, 1); + REGISTER_LONG_CONSTANT("CP_DEFAULT_PDO_PORT", CP_PORT_PDO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CP_DEFAULT_REDIS_PORT", CP_PORT_REDIS, CONST_CS | CONST_PERSISTENT); bzero(&ConProxyG, sizeof (ConProxyG)); + bzero(&ConProxyWG, sizeof (ConProxyWG)); + return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ -PHP_MSHUTDOWN_FUNCTION(connect_pool) { - if (pdo_stmt) { - zval_ptr_dtor(&pdo_stmt); +PHP_MSHUTDOWN_FUNCTION(connect_pool) +{ + if (pdo_stmt) + { + cp_zval_ptr_dtor(&pdo_stmt); } return SUCCESS; } @@ -188,554 +262,730 @@ PHP_MSHUTDOWN_FUNCTION(connect_pool) { /* {{{ PHP_MINFO_FUNCTION */ -PHP_MINFO_FUNCTION(connect_pool) { +PHP_MINFO_FUNCTION(connect_pool) +{ php_info_print_table_start(); - php_info_print_table_header(2, "connect_poll support", "enabled"); - php_info_print_table_row(2, "Version", "2.1"); - php_info_print_table_row(2, "Author", "郭新华"); + php_info_print_table_header(2, "connect_pool support", "enabled"); + php_info_print_table_row(2, "Version", CP_VERSION); + php_info_print_table_row(2, "Author", "郭新华,张磊"); php_info_print_table_row(2, "email", "woshiguo35@sina.com"); php_info_print_table_end(); } /* }}} */ -PHP_RINIT_FUNCTION(connect_pool) { +PHP_RINIT_FUNCTION(connect_pool) +{ return SUCCESS; } -PHP_RSHUTDOWN_FUNCTION(connect_pool) { +PHP_RSHUTDOWN_FUNCTION(connect_pool) +{ return SUCCESS; } -static void cp_destory_client(zend_rsrc_list_entry *rsrc TSRMLS_DC) { +static void cp_destory_client(zend_resource *rsrc TSRMLS_DC) +{ cpClient *cli = (cpClient *) rsrc->ptr; - if (cli->sock > 0) { + if (cli->sock > 0) + { cpClient_close(cli); - // pefree(cli, 1); //长连接 } } -void send_oob2proxy(zend_rsrc_list_entry *rsrc TSRMLS_DC) { +void send_oob2proxy(zend_resource *rsrc TSRMLS_DC) +{ cpClient *cli = (cpClient *) rsrc->ptr; - if (cli->sock == 0) { - pefree(cli, 1); //长连接 - } else if (cli->released == CP_FD_NRELEASED) {//防止release后rshutdown的重複释放 - // ret = cpClient_send(cli->sock, CP_RELEASE_HEADER, CP_RELEASE_HEADER_LEN, MSG_OOB); - cpTcpEvent event; - event.type = CP_TCPEVENT_RELEASE; - int ret = cpClient_send(cli->sock, (char *) &event, sizeof (event), 0); - if (ret >= 0) { - cli->released = CP_FD_RELEASED; - } else { - zend_error(E_ERROR, "pdo_connect_pool: release error. Error: %s [%d]", strerror(errno), errno); - } - } -} - -PHP_FUNCTION(shit_pdo_warning_function) { - int errorno; - char *errstr; - int errlen; - char * linstr; - int linlen; - char * filestr; - int filelen; - zval *what; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsss|z", &errorno, &errstr, &errlen, &filestr, &filelen, &linstr, &linlen, &what) == FAILURE) { - return; - } - char *p = strstr(errstr, "server has gone away"); - if (p) { - warning_gone_away = 1; - } + if (cli->sock == 0) + { + pefree(cli, 1); + } + else if (cli->server_fd != 0) + {//防止release后rshutdown的重复释放 + cpConnection *conn = CONN(cli); + if (conn->release == CP_FD_NRELEASED) + { + cpGroup* G = &CPGS->G[conn->group_id]; + if (cli->lock(G) == 0) + { + conn->release = CP_FD_RELEASED; + if (G->first_wait_id && conn->worker_index <= G->worker_max) + {//wait is not null&&use queue&&use reload to reduce max maybe trigger this + int wait_pid = cpPopWaitQueue(G, conn); + cli->unLock(G); + if (kill(wait_pid, SIGRTMIN) < 0) + { + php_printf("send sig 2 %d error. Error: %s [%d]", wait_pid, strerror(errno), errno); + // return send_oob2proxy(rsrc); -} - -PHP_FUNCTION(get_disable_list) { -} - -PHP_FUNCTION(set_disable_list) { + } + } + else + { + CPGS->G[conn->group_id].workers_status[conn->worker_index] = CP_WORKER_IDLE; + cli->unLock(G); + } + } + log_end(cli); + } + } } -PHP_FUNCTION(pool_server_create) { +PHP_FUNCTION(pool_server_create) +{ zval *conf = NULL; char *config_file = NULL; - int file_len = 0; - if (strcasecmp("cli", sapi_module.name) != 0) { - zend_error(E_ERROR, "pool_server must run at php_cli environment."); + //zend_string *config_file = NULL; + zend_size_t file_len = 0; + if (strcasecmp("cli", sapi_module.name) != 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "pool_server must run at php_cli environment."); RETURN_FALSE; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &config_file, &file_len) == FAILURE) { - zend_error(E_ERROR, "config file error"); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &config_file, &file_len) == FAILURE) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "config file error"); RETURN_FALSE; } conf = cpGetConfig(config_file); - int group_id = 0; - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(conf)); zend_hash_has_more_elements(Z_ARRVAL_P(conf)) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(conf))) { - zval **config; - zend_hash_get_current_data(Z_ARRVAL_P(conf), (void**) &config); - char *name; - int keylen; - zend_hash_get_current_key_ex(Z_ARRVAL_P(conf), &name, &keylen, NULL, 0, NULL); - int pid = fork(); - if (pid < 0) { - printf("create fork error!\n"); - } else if (pid == 0) { - cpServer_init(*config, name, config_file, group_id); - - int ret = cpServer_create(); - if (ret < 0) { - zend_error(E_ERROR, "pool_server: create server fail. Error: %s [%d]", strerror(errno), errno); - } + int sock = cpServer_init(conf, config_file); + if (sock <= 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "pool_server: start server fail. Error: %s [%d]", strerror(errno), errno); + } - ret = cpServer_start(); - if (ret < 0) { - zend_error(E_ERROR, "pool_server: start server fail. Error: %s [%d]", strerror(errno), errno); - } - } - group_id++; + int ret = cpServer_create(); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "pool_server: create server fail. Error: %s [%d]", strerror(errno), errno); } - zval_ptr_dtor(&conf); + + ret = cpServer_start(sock); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "pool_server: start server fail. Error: %s [%d]", strerror(errno), errno); + } + cp_zval_ptr_dtor(&conf); } -PHP_FUNCTION(pool_server_reload) { +PHP_FUNCTION(pool_server_reload) +{ long pid; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pid) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pid) == FAILURE) + { return; } - if (kill(pid, SIGUSR1) < 0) { - printf("reload fail. kill -SIGUSR1 master_pid[%d] fail. Error: %s[%d]\n", pid, strerror(errno), errno); + if (kill(pid, SIGUSR1) < 0) + { + php_printf("reload fail. kill -SIGUSR1 master_pid[%d] fail. Error: %s[%d]\n", (int) pid, strerror(errno), errno); RETURN_FALSE; - } else { + } + else + { RETURN_TRUE; } } -PHP_FUNCTION(pool_server_shutdown) { +PHP_FUNCTION(pool_server_version) +{ + CP_RETURN_STRING(CP_VERSION, 1); +} + +PHP_FUNCTION(pool_server_shutdown) +{ long pid; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pid) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pid) == FAILURE) + { return; } - if (kill(pid, SIGTERM) < 0) { - printf("shutdown fail. kill -SIGTERM master_pid[%d] fail. Error: %s[%d]\n", pid, strerror(errno), errno); + if (kill(pid, SIGTERM) < 0) + { + php_printf("shutdown fail. kill -SIGTERM master_pid[%d] fail. Error: %s[%d]\n", (int) pid, strerror(errno), errno); RETURN_FALSE; - } else { - RETURN_TRUE; } -} - -void cp_serialize(smart_str *ser_data, zval *array) { - // struct timeval start, end; - // gettimeofday(&start, NULL); - - php_serialize_data_t var_hash; - PHP_VAR_SERIALIZE_INIT(var_hash); - php_var_serialize(ser_data, &array, &var_hash TSRMLS_CC); - PHP_VAR_SERIALIZE_DESTROY(var_hash); - - // gettimeofday(&end, NULL); - // int timeuse = 1000000 * (end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec; - // printf("ser time: %d us\n", timeuse); -} - -zval * cp_unserialize(char *data, int len) { - zval *unser_value; - ALLOC_INIT_ZVAL(unser_value); - php_unserialize_data_t var_hash; - PHP_VAR_UNSERIALIZE_INIT(var_hash); - if (php_var_unserialize(&unser_value, (const unsigned char **) &data, (unsigned char *) data + len - 1, &var_hash TSRMLS_CC) != 1) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "unser data is corrupted"); + else + { + RETURN_TRUE; } - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); - return unser_value; } -CPINLINE int CP_INTERNAL_SERIALIZE_SEND_MEM(zval *ret_value, uint8_t __type) { - cpShareMemory *sm_obj = &(CPGS->workers[CPWG.id].sm_obj); +int CP_INTERNAL_SERIALIZE_SEND_MEM(zval *send_data, uint8_t __type) +{ + cpShareMemory *sm_obj = &(CPGS->G[CPWG.gid].workers[CPWG.id].sm_obj); + int real_len = 0; +#if PHP_MAJOR_VERSION < 7 instead_smart dest; dest.len = 0; dest.addr = sm_obj->mem; dest.max = CPGC.max_read_len; - dest.exceed = '0'; - php_msgpack_serialize(&dest, ret_value); - if (dest.exceed == '1') { - zval exceed; - ZVAL_STRING(&exceed, "data is exceed,increase max_read_len", 0); - return CP_INTERNAL_SERIALIZE_SEND_MEM(&exceed, CP_SIGEVENT_EXCEPTION); - } else { - // union sigval sigvalPara; - // CP_EVENTLEN_ADD_TYPE(dest.len,__type);//todo 2字节int 长度检查 - // sigvalPara.sival_int = dest.len; - // if (sigqueue(pid, CP_SIG_EVENT, sigvalPara) == -1) { - // cpLog("sigqueue error %d", errno); - // return FAILURE; - // } - cpWorkerInfo worker_event; - worker_event.len = dest.len; - worker_event.type = __type; - worker_event.pid = CPWG.clientPid; - int ret = write(CPGS->workers[CPWG.id].pipe_fd_write, &worker_event, sizeof (worker_event)); - if (ret == -1) { - zend_error(E_ERROR, "write error Error: %s [%d]", strerror(errno), errno); - } - + dest.exceed = 0; + php_msgpack_serialize(&dest, send_data); + real_len = dest.len; + if (dest.exceed == 1) + { + CP_INTERNAL_ERROR_SEND("data is exceed,increase max_read_len"); + return SUCCESS; + } +#else + zend_string * zstr = php_swoole_serialize(send_data); + if (zstr->len >= CPGS->max_buffer_len) + { + CP_INTERNAL_ERROR_SEND("data is exceed,increase max_read_len"); return SUCCESS; } + real_len = zstr->len; + memcpy(sm_obj->mem, zstr->val, zstr->len); + zend_string_release(zstr); +#endif + cpWorkerInfo worker_event; + worker_event.len = real_len; + worker_event.type = __type; + worker_event.pid = CPWG.event.pid; + int ret = write(CPWG.pipe_fd_write, &worker_event, sizeof (worker_event)); + if (ret == -1) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "write error Error: %s [%d]", strerror(errno), errno); + } + + return SUCCESS; } -static void pdo_proxy_connect(zval *args, int reconnect) { - zval **data_source; - zval **object; - - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("data_source"), (void **) &data_source) == SUCCESS) { - if (zend_hash_find(&pdo_object_table, Z_STRVAL_PP(data_source), Z_STRLEN_PP(data_source), (void **) &object) == SUCCESS) { - CP_INTERNAL_NORMAL_SEND("CON_SUCCESS!"); - } else { - zval **tmp_pass[4]; - zval *new_obj; - MAKE_STD_ZVAL(new_obj); - object_init_ex(new_obj, php_pdo_get_dbh_ce()); - tmp_pass[0] = data_source; - zval **username; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("username"), (void **) &username) == SUCCESS) { - tmp_pass[1] = username; - } else { - CP_INTERNAL_ERROR_SEND("PDO username null!"); - } +int pdo_proxy_connect(zval *args, int flag) +{ + zval *data_source, **tmp_pass[4], *new_obj, *username, *password, *options, * ret_pdo_obj, *null_arr = NULL; + zval pdo_name, con_fun_name; + zend_class_entry *pdo_ce; + if (pdo_object) + { + pdo_proxy_pdo(args); + return 1; + } +#if PHP_MAJOR_VERSION < 7 + CP_MAKE_STD_ZVAL(new_obj); +#else + new_obj = ecalloc(sizeof (zval), 1); +#endif + CP_ZVAL_STRING(&pdo_name, "pdo", 0); + if (cp_zend_hash_find_ptr(EG(class_table), &pdo_name, (void **) &pdo_ce) == FAILURE) + { + CP_INTERNAL_ERROR_SEND_RETURN("pdo extension is not install"); + } + object_init_ex(new_obj, pdo_ce); + cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("data_source"), (void **) &data_source); + tmp_pass[0] = &data_source; + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("username"), (void **) &username) == SUCCESS) + { + tmp_pass[1] = &username; + } - zval **password; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("password"), (void **) &password) == SUCCESS) { - tmp_pass[2] = password; - } else { - CP_INTERNAL_ERROR_SEND("PDO password null!"); - } + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("password"), (void **) &password) == SUCCESS) + { + tmp_pass[2] = &password; + } - zval **options; - zval *null_arr = NULL; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("options"), (void **) &options) == SUCCESS) { - tmp_pass[3] = options; - } else { - MAKE_STD_ZVAL(null_arr); - array_init(null_arr); - tmp_pass[3] = &null_arr; - } + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("options"), (void **) &options) == SUCCESS) + { + tmp_pass[3] = &options; + } + else + { + CP_MAKE_STD_ZVAL(null_arr); + array_init(null_arr); + tmp_pass[3] = &null_arr; + } - zval * ret_pdo_obj = NULL; - zval con_fun_name; - ZVAL_STRING(&con_fun_name, "__construct", 0); - call_user_function_ex(NULL, &new_obj, &con_fun_name, &ret_pdo_obj, 4, tmp_pass, 0, NULL TSRMLS_CC); - if (null_arr) { - zval_ptr_dtor(&null_arr); - } - if (ret_pdo_obj) - zval_ptr_dtor(&ret_pdo_obj); - if (EG(exception)) { - zval *str; - CP_SEND_EXCEPTION(&str, 0); - zval_ptr_dtor(&new_obj); - zval_ptr_dtor(&str); - } else { - //存起來 - if (zend_hash_add(&pdo_object_table, Z_STRVAL_PP(data_source), Z_STRLEN_PP(data_source), (void*) &new_obj, sizeof (zval *), NULL) == SUCCESS) { - if (!reconnect) - CP_INTERNAL_NORMAL_SEND("CON_SUCCESS!"); - } else { - CP_INTERNAL_ERROR_SEND("PDO obj add table fail!"); - } - } + CP_ZVAL_STRING(&con_fun_name, "__construct", 0); + cp_call_user_function_ex(NULL, &new_obj, &con_fun_name, &ret_pdo_obj, 4, tmp_pass, 0, NULL TSRMLS_CC); +#if PHP_MAJOR_VERSION ==7 + zval_ptr_dtor(&con_fun_name); +#endif + if (null_arr) + cp_zval_ptr_dtor(&null_arr); + if (ret_pdo_obj) + cp_zval_ptr_dtor(&ret_pdo_obj); + if (EG(exception)) + { + CP_DEL_OBJ(new_obj); + CP_SEND_EXCEPTION_RETURN; + } + else + { + pdo_object = new_obj; + if (flag == CP_CONNECT_NORMAL) + { + pdo_proxy_pdo(args); + return 1; } - } else { - CP_INTERNAL_ERROR_SEND("PDO no datasource!"); } + return CP_FALSE; } -static int cp_call_user_function(zval **object, zval *fun, zval **ret_value, zval *args) { - zval **m_args; - int count = 0; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("args"), (void **) &m_args) == SUCCESS) { - count = zend_hash_num_elements(Z_ARRVAL_PP(m_args)); - zval **tmp_pass[count]; - int i = 0; - for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(m_args)); zend_hash_has_more_elements(Z_ARRVAL_PP(m_args)) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_PP(m_args))) { - zval **ppzval; - zend_hash_get_current_data(Z_ARRVAL_PP(m_args), (void**) &ppzval); - tmp_pass[i] = ppzval; - i++; +static void pdo_proxy_pdo(zval * args) +{ + zval *str = NULL, *method = NULL, * ret_value = NULL; + if (pdo_object) + { + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method"), (void **) &method) == FAILURE) + { + CP_INTERNAL_ERROR_SEND("PDO no method!"); } - return call_user_function_ex(NULL, object, fun, ret_value, count, tmp_pass, 0, NULL TSRMLS_CC); - } else { - return call_user_function_ex(NULL, object, fun, ret_value, count, NULL, 0, NULL TSRMLS_CC); - } -} +#if PHP_MAJOR_VERSION ==7 + ret_value = (zval *) emalloc(sizeof (zval)); +#endif -static void pdo_proxy_pdo(zval *args) { - zval **data_source; - zval **object; - - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("data_source"), (void **) &data_source) == SUCCESS) { - if (zend_hash_find(&pdo_object_table, Z_STRVAL_PP(data_source), Z_STRLEN_PP(data_source), (void **) &object) == FAILURE) { - zval **con_args; - zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("con_args"), (void **) &con_args); - pdo_proxy_connect(*con_args, 1); - }//超过n次被kill - - if (zend_hash_find(&pdo_object_table, Z_STRVAL_PP(data_source), Z_STRLEN_PP(data_source), (void **) &object) == SUCCESS) {//con没返回值,这样判断 - zval **method; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method"), (void **) &method) == FAILURE) { - CP_INTERNAL_ERROR_SEND("PDO no method!"); + if (cp_internal_call_user_function(pdo_object, method, &ret_value, args) == FAILURE) + { + CP_DEL_OBJ(pdo_object); + char cp_error_str[FAILUREOR_MSG_SIZE] = {0}; + snprintf(cp_error_str, FAILUREOR_MSG_SIZE, "call pdo method( %s ) error!", Z_STRVAL_P(method)); + CP_INTERNAL_ERROR_SEND(cp_error_str); + } + else + { + if (EG(exception)) + { + CP_EXCEPTION_ARGS(&str); + char *p = strcasestr(Z_STRVAL_P(str), "server has gone away"); + char *p2 = strcasestr(Z_STRVAL_P(str), "There is already an active transaction"); + if (p || p2) + {//del reconnect and retry + cpLog("del and retry %s,%s", p, p2); + CP_DEL_OBJ(pdo_object); + pdo_proxy_connect(args, CP_CONNECT_NORMAL); + } + else + { + CP_INTERNAL_SERIALIZE_SEND_MEM(str, CP_SIGEVENT_EXCEPTION); + } + cp_zval_ptr_dtor(&str); + if (ret_value) + { + cp_zval_ptr_dtor(&ret_value); +#if PHP_MAJOR_VERSION == 7 + efree(ret_value); +#endif + } } - zval * ret_value = NULL; - warning_gone_away = 0; - if (cp_call_user_function(object, *method, &ret_value, args) == FAILURE) { - CP_INTERNAL_ERROR_SEND("call pdo method error!"); - } else { - if (EG(exception)) { - zval *str; - CP_SEND_EXCEPTION(&str, 0); - char *p = strstr(Z_STRVAL_P(str), "server has gone away"); - char *p2 = strstr(Z_STRVAL_P(str), "There is already an active transaction"); - if (p || p2) { - zend_hash_del(&pdo_object_table, Z_STRVAL_PP(data_source), Z_STRLEN_PP(data_source)); - } - zval_ptr_dtor(&str); - if (ret_value) - zval_ptr_dtor(&ret_value); - } else { - if (Z_TYPE_P(ret_value) == IS_OBJECT) { - char *name; - zend_uint name_len; - zend_get_object_classname(ret_value, &name, &name_len TSRMLS_CC); - if (strcmp(name, "PDOStatement") == 0) { - if (pdo_stmt) { - zval_ptr_dtor(&pdo_stmt); - } - pdo_stmt = ret_value; - zval send_zval; - ZVAL_STRING(&send_zval, "PDOStatement!", 0); - CP_INTERNAL_SERIALIZE_SEND_MEM(&send_zval, CP_SIGEVENT_PDO); + else + { + if (Z_TYPE_P(ret_value) == IS_OBJECT) + { +#if PHP_MAJOR_VERSION < 7 + char *name; + zend_uint name_len; + zend_get_object_classname(ret_value, (const char **) &name, &name_len TSRMLS_CC); + if (strcmp(name, "PDOStatement") == 0) + { + if (pdo_stmt) + { + zval_ptr_dtor(&pdo_stmt); + pdo_stmt = NULL; } - efree(name); - } else {//pdo - if (warning_gone_away) {//restart mysql will trigger this warning - zend_hash_del(&pdo_object_table, Z_STRVAL_PP(data_source), Z_STRLEN_PP(data_source)); - CP_INTERNAL_ERROR_SEND("Server has gone away"); - } else { - CP_INTERNAL_SERIALIZE_SEND_MEM(ret_value, CP_SIGEVENT_TURE); + pdo_stmt = ret_value; + zval send_zval = {0}; + ZVAL_STRING(&send_zval, "PDOStatement!", 0); + CP_INTERNAL_SERIALIZE_SEND_MEM(&send_zval, CP_SIGEVENT_PDO); + } + efree(name); +#else + zend_string *name = Z_OBJ_HANDLER_P(ret_value, get_class_name)(Z_OBJ_P(ret_value)); + if (strcmp(name->val, "PDOStatement") == 0) + { + if (pdo_stmt) + { + zval_dtor(pdo_stmt); + efree(pdo_stmt); + pdo_stmt = NULL; } - if (ret_value) - zval_ptr_dtor(&ret_value); + pdo_stmt = ret_value; + zval send_zval = {0}; + CP_ZVAL_STRING(&send_zval, "PDOStatement!", 0); + CP_INTERNAL_SERIALIZE_SEND_MEM(&send_zval, CP_SIGEVENT_PDO); + zval_ptr_dtor(&send_zval); + } + zend_string_release(name); +#endif + } + else + {//pdo + CP_INTERNAL_SERIALIZE_SEND_MEM(ret_value, CP_SIGEVENT_TURE); + if (ret_value) + { + cp_zval_ptr_dtor(&ret_value); +#if PHP_MAJOR_VERSION == 7 + efree(ret_value); +#endif } } } } - } else { - CP_INTERNAL_ERROR_SEND("PDO no datasource!"); + } + else + { + CP_INTERNAL_ERROR_SEND("no connect to mysql"); } } -static void pdo_proxy_stmt(zval *args) { - zval **method; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method"), (void **) &method) == FAILURE) { +static void pdo_proxy_stmt(zval * args) +{ + zval *method = NULL, * ret_value = NULL; + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method"), (void **) &method) == FAILURE) + { CP_INTERNAL_ERROR_SEND("PDO no method!"); } - - zval * ret_value = NULL; - if (cp_call_user_function(&pdo_stmt, *method, &ret_value, args) == FAILURE) { - MAKE_STD_ZVAL(ret_value); - ZVAL_STRING(ret_value, "call pdo stmt method error!", 0); - CP_INTERNAL_SERIALIZE_SEND_MEM(ret_value, CP_SIGEVENT_EXCEPTION); - } else { - if (EG(exception)) { + if (cp_internal_call_user_function(pdo_stmt, method, &ret_value, args) == FAILURE) + { + char cp_error_str[FAILUREOR_MSG_SIZE] = {0}; + snprintf(cp_error_str, FAILUREOR_MSG_SIZE, "call pdo stmt method (%s) error!", Z_STRVAL_P(method)); + CP_INTERNAL_ERROR_SEND(cp_error_str); + } + else + { + if (EG(exception)) + { zval *str; - CP_SEND_EXCEPTION(&str, 1); - zval_ptr_dtor(&str); - zval_ptr_dtor(&pdo_stmt); + CP_SEND_EXCEPTION_ARGS(&str); + char *p = strcasestr(Z_STRVAL_P(str), "server has gone away"); + char *p2 = strcasestr(Z_STRVAL_P(str), "There is already an active transaction"); + if (p || p2) + { + CP_DEL_OBJ(pdo_object); + } + cp_zval_ptr_dtor(&str); + cp_zval_ptr_dtor(&pdo_stmt); pdo_stmt = NULL; - } else { + return; //when the exception,the ret_value dont need dtor + } + if (!ret_value) + { + CP_INTERNAL_ERROR_SEND("call pdo stmt method error ret_value is null!"); + return; + } + if (Z_TYPE_P(ret_value) == IS_OBJECT) + { + CP_INTERNAL_SERIALIZE_SEND_MEM(ret_value, CP_SIGEVENT_STMT_OBJ); + } + else + { CP_INTERNAL_SERIALIZE_SEND_MEM(ret_value, CP_SIGEVENT_TURE); } } - if (ret_value) { - zval_ptr_dtor(&ret_value); + if (ret_value) + { + + cp_zval_ptr_dtor(&ret_value); } } -static void pdo_dispatch(zval *args) { - zval **m_type; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method_type"), (void **) &m_type) == SUCCESS) { - if (strcmp(Z_STRVAL_PP(m_type), "connect") == 0) { - pdo_proxy_connect(args, 0); - } else if (strcmp(Z_STRVAL_PP(m_type), "PDOStatement") == 0) { +static void pdo_dispatch(zval * args) +{ + zval *m_type; + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method_type"), (void **) &m_type) == SUCCESS) + { + if (strcmp(Z_STRVAL_P(m_type), "connect") == 0) + { + pdo_proxy_connect(args, CP_CONNECT_NORMAL); + } + else if (strcmp(Z_STRVAL_P(m_type), "PDOStatement") == 0) + { pdo_proxy_stmt(args); - } else { - pdo_proxy_pdo(args); } - } else {//操作pdo + // else + // {//not use now + // pdo_proxy_pdo(args); + // } + } + else + {//操作pdo + CP_INTERNAL_ERROR_SEND("PDO method_type is none!"); } } -static void redis_proxy_connect(zval *data_source, zval *args) { - zval *ex_arr, zdelim, **ip, **port, **db; - MAKE_STD_ZVAL(ex_arr); - array_init(ex_arr); - ZVAL_STRINGL(&zdelim, ":", 1, 0); - php_explode(&zdelim, data_source, ex_arr, LONG_MAX); +static int cp_redis_select(zval *new_obj, zval **db) +{ + //有db并且不0那么就select + if (strcmp("0", Z_STRVAL_PP(db)) != 0) + { + zval **tmp_pass[1]; + tmp_pass[0] = db; + zval * ret_redis_select = NULL; + zval select_fun_name; + CP_ZVAL_STRING(&select_fun_name, "select", 0); + cp_call_user_function_ex(NULL, &new_obj, &select_fun_name, &ret_redis_select, 1, tmp_pass, 0, NULL TSRMLS_CC); + if (ret_redis_select) + cp_zval_ptr_dtor(&ret_redis_select); + + if (EG(exception)) + { + cp_zval_ptr_dtor(&new_obj); + CP_SEND_EXCEPTION_RETURN; + } + } + return CP_TRUE; +} + +static int cp_redis_auth(zval *new_obj, zval *args) +{ + zval *auth, *ret_redis_auth = NULL; + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("auth"), (void **) &auth) == SUCCESS) + { + zval auth_fun_name; + CP_ZVAL_STRING(&auth_fun_name, "auth", 0); + zval **tmp_pass[1]; + tmp_pass[0] = &auth; + cp_call_user_function_ex(NULL, &new_obj, &auth_fun_name, &ret_redis_auth, 1, tmp_pass, 0, NULL TSRMLS_CC); + if (ret_redis_auth) + { + cp_zval_ptr_dtor(&ret_redis_auth); + } + + if (EG(exception)) + { + cp_zval_ptr_dtor(&new_obj); + CP_SEND_EXCEPTION_RETURN; + } + } + return CP_TRUE; +} - zval **tmp_pass[3]; - zval *new_obj; - MAKE_STD_ZVAL(new_obj); - zend_class_entry **redis_ce; +int redis_proxy_connect(zval *args, int flag) +{ + zval *ex_arr, *data_source, zdelim, *ip, *port, *db, *timeout, * ret_redis_obj = NULL, *new_obj, **tmp_pass[3]; + cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("data_source"), (void **) &data_source); + CP_MAKE_STD_ZVAL(ex_arr); + array_init(ex_arr); + CP_ZVAL_STRINGL(&zdelim, ":", 1, 0); + cp_explode(&zdelim, data_source, ex_arr, LONG_MAX); - if (zend_hash_find(CG(class_table), ZEND_STRS("redis"), (void **) &redis_ce) == FAILURE) { - cpLog("redis class ce error\n"); - }; - object_init_ex(new_obj, *redis_ce); +#if PHP_MAJOR_VERSION < 7 + CP_MAKE_STD_ZVAL(new_obj); +#else + new_obj = ecalloc(sizeof (zval), 1); +#endif + zend_class_entry *redis_ce = NULL; - if (zend_hash_index_find(Z_ARRVAL_P(ex_arr), 0, (void**) &ip) == SUCCESS) { - tmp_pass[0] = ip; - } else { - CP_INTERNAL_ERROR_SEND("redis ip null!"); + zval redis_name; + CP_ZVAL_STRING(&redis_name, "redis", 0); + if (cp_zend_hash_find_ptr(EG(class_table), &redis_name, (void **) &redis_ce) == FAILURE) + { + CP_INTERNAL_ERROR_SEND_RETURN("redis extension is not install"); + } + object_init_ex(new_obj, redis_ce); + if (cp_zend_hash_index_find(Z_ARRVAL_P(ex_arr), 0, (void**) &ip) == SUCCESS) + { + tmp_pass[0] = &ip; } - if (zend_hash_index_find(Z_ARRVAL_P(ex_arr), 1, (void**) &port) == SUCCESS) { - tmp_pass[1] = port; - } else { - CP_INTERNAL_ERROR_SEND("redis port null!"); + if (cp_zend_hash_index_find(Z_ARRVAL_P(ex_arr), 1, (void**) &port) == SUCCESS) + { + tmp_pass[1] = &port; } - zval *timeout; - MAKE_STD_ZVAL(timeout); - ZVAL_STRING(timeout, "10", 0); + CP_MAKE_STD_ZVAL(timeout); + CP_ZVAL_STRING(timeout, "10", 0); tmp_pass[2] = &timeout; - zval * ret_redis_obj = NULL; zval pcon_fun_name; - ZVAL_STRING(&pcon_fun_name, "connect", 0); - call_user_function_ex(NULL, &new_obj, &pcon_fun_name, &ret_redis_obj, 3, tmp_pass, 0, NULL TSRMLS_CC); - efree(timeout); - - if (ret_redis_obj) { - if (Z_BVAL_P(ret_redis_obj) == FALSE) {//这块直接抛异常比较好吧 - zval con_error; - ZVAL_STRING(&con_error, "connect redis error!", 0); - CP_INTERNAL_SERIALIZE_SEND_MEM(&con_error, CP_SIGEVENT_EXCEPTION); - zval_ptr_dtor(&ret_redis_obj); - return; - } else { - zval_ptr_dtor(&ret_redis_obj); + CP_ZVAL_STRING(&pcon_fun_name, "connect", 0); + + cp_call_user_function_ex(NULL, &new_obj, &pcon_fun_name, &ret_redis_obj, 3, tmp_pass, 0, NULL TSRMLS_CC); + if (ret_redis_obj) + { + if (Z_BVAL_P(ret_redis_obj) == FALSE) + { + cp_zval_ptr_dtor(&ex_arr); + cp_zval_ptr_dtor(&ret_redis_obj); + CP_INTERNAL_ERROR_SEND_RETURN("connect redis error!"); + } + else + { + cp_zval_ptr_dtor(&ret_redis_obj); } } - if (EG(exception)) { - zval *str; - CP_SEND_EXCEPTION(&str, 0); - zval_ptr_dtor(&str); - zval_ptr_dtor(&new_obj); - return; + if (EG(exception)) + { + CP_DEL_OBJ(new_obj); + cp_zval_ptr_dtor(&ex_arr); + CP_SEND_EXCEPTION_RETURN; } - if (zend_hash_index_find(Z_ARRVAL_P(ex_arr), 2, (void**) &db) == SUCCESS) {//有db并且不0那么就select - if (strcmp("0", Z_STRVAL_PP(db)) != 0) { - zval **tmp_pass2[1]; - tmp_pass2[0] = db; - zval * ret_redis_select = NULL; - zval select_fun_name; - ZVAL_STRING(&select_fun_name, "select", 0); - call_user_function_ex(NULL, &new_obj, &select_fun_name, &ret_redis_select, 1, tmp_pass2, 0, NULL TSRMLS_CC); - if (ret_redis_select) - zval_ptr_dtor(&ret_redis_select); - - if (EG(exception)) { - zval *str; - CP_SEND_EXCEPTION(&str, 1); - zval_ptr_dtor(&new_obj); - zval_ptr_dtor(&str); - return; - } + + if (!cp_redis_auth(new_obj, args)) + { + return CP_FALSE; + } + + if (cp_zend_hash_index_find(Z_ARRVAL_P(ex_arr), 2, (void**) &db) == SUCCESS) + { + if (!cp_redis_select(new_obj, &db)) + { + cp_zval_ptr_dtor(&ex_arr); + return CP_FALSE; } } + cp_zval_ptr_dtor(&ex_arr); //存起來 - if (zend_hash_add(&redis_object_table, Z_STRVAL_P(data_source), Z_STRLEN_P(data_source), (void*) &new_obj, sizeof (zval *), NULL) == FAILURE) { - CP_INTERNAL_ERROR_SEND("redis obj add table fail!"); - } - zval **method; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method"), (void **) &method) == FAILURE) { - CP_INTERNAL_ERROR_SEND("redis no method!"); - } - if (strcmp(Z_STRVAL_PP(method), "select") == 0) { - CP_INTERNAL_NORMAL_SEND("CON_SUCCESS!"); - } else { + redis_object = new_obj; + zval *method; + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method"), (void **) &method) == FAILURE) + { + CP_INTERNAL_ERROR_SEND_RETURN("redis no method!"); + } + if (strcmp(Z_STRVAL_P(method), "select") == 0) + { + CP_INTERNAL_NORMAL_SEND_RETURN("CON_SUCCESS!"); + } + else + { zval * ret_value = NULL; - if (cp_call_user_function(&new_obj, *method, &ret_value, args) == FAILURE) { - zval error_str; - ZVAL_STRING(&error_str, "call redis method error!", 0); - CP_INTERNAL_SERIALIZE_SEND_MEM(&error_str, CP_SIGEVENT_EXCEPTION); - } else { - if (EG(exception)) { - zval *str; - CP_SEND_EXCEPTION(&str, 1); - zval_ptr_dtor(&str); - } else { + if (cp_internal_call_user_function(new_obj, method, &ret_value, args) == SUCCESS) + { + if (!EG(exception)) + { CP_INTERNAL_SERIALIZE_SEND_MEM(ret_value, CP_SIGEVENT_TURE); + } + else + { + CP_DEL_OBJ(redis_object); + CP_SEND_EXCEPTION; + } + } + else + { + CP_INTERNAL_ERROR_SEND("call redis method error!"); } if (ret_value) - zval_ptr_dtor(&ret_value); + cp_zval_ptr_dtor(&ret_value); + + return CP_TRUE; //no use } } -static void redis_dispatch(zval * args) { - zval **data_source; - zval **object; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("data_source"), (void **) &data_source) == SUCCESS) { - if (zend_hash_find(&redis_object_table, Z_STRVAL_PP(data_source), Z_STRLEN_PP(data_source), (void **) &object) == SUCCESS) { - zval **method; - if (zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method"), (void **) &method) == FAILURE) { - zval error_str; - ZVAL_STRING(&error_str, "redis no method error!", 0); - CP_INTERNAL_SERIALIZE_SEND_MEM(&error_str, CP_SIGEVENT_EXCEPTION); +static void redis_dispatch(zval * args) +{ + zval *data_source; + if (redis_object) + { + zval *method; + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("method"), (void **) &method) == FAILURE) + { + CP_INTERNAL_ERROR_SEND("redis no method error!"); + return; + } + else if (strcmp(Z_STRVAL_P(method), "select") == 0) + { + zval select_return = {0}; + ZVAL_BOOL(&select_return, 1); + CP_INTERNAL_SERIALIZE_SEND_MEM(&select_return, CP_SIGEVENT_TURE); + return; + } + zval * ret_value = NULL; + if (cp_internal_call_user_function(redis_object, method, &ret_value, args) == FAILURE) + { + CP_INTERNAL_ERROR_SEND("call redis method error!"); + } + else + { + if (EG(exception)) + { + zval *str; + CP_SEND_EXCEPTION_ARGS(&str); + // char *p = strstr(Z_STRVAL_P(str), "server went away"); + // char *p2 = strstr(Z_STRVAL_P(str), "Connection lost"); + // char *p3 = strstr(Z_STRVAL_P(str), "read error on connection"); + // char *p4 = strstr(Z_STRVAL_P(str), "Connection closed"); + // if (p || p2 || p3 || p4) + // { + CP_DEL_OBJ(redis_object); + // } + cp_zval_ptr_dtor(&str); } - zval * ret_value = NULL; - if (cp_call_user_function(object, *method, &ret_value, args) == FAILURE) { - zval error_str; - ZVAL_STRING(&error_str, "call redis method error!", 0); - CP_INTERNAL_SERIALIZE_SEND_MEM(&error_str, CP_SIGEVENT_EXCEPTION); - } else { - if (EG(exception)) { - zval *str; - CP_SEND_EXCEPTION(&str, 1); - char *p = strstr(Z_STRVAL_P(str), "server went away"); - char *p2 = strstr(Z_STRVAL_P(str), "Connection lost"); - if (p || p2) { - zend_hash_del(&redis_object_table, Z_STRVAL_PP(data_source), Z_STRLEN_PP(data_source)); - } - zval_ptr_dtor(&str); - } else { - CP_INTERNAL_SERIALIZE_SEND_MEM(ret_value, CP_SIGEVENT_TURE); - } + else + { + CP_INTERNAL_SERIALIZE_SEND_MEM(ret_value, CP_SIGEVENT_TURE); } - if (ret_value) - zval_ptr_dtor(&ret_value); - } else { - redis_proxy_connect(*data_source, args); } - } else { - CP_INTERNAL_ERROR_SEND("redis no datasource!"); + if (ret_value) + cp_zval_ptr_dtor(&ret_value); + } + else + { + redis_proxy_connect(args, CP_CONNECT_NORMAL); } } -int worker_onReceive(zval *unser_value) { - zval **type; - if (zend_hash_find(Z_ARRVAL_P(unser_value), ZEND_STRS("type"), (void **) &type) == SUCCESS) { - if (strcmp(Z_STRVAL_PP(type), "pdo") == 0) { - pdo_dispatch(unser_value); - } else if (strcmp(Z_STRVAL_PP(type), "redis") == 0) { - redis_dispatch(unser_value); +int worker_onReceive(zval * user_value) +{ + zval *type; + if (cp_zend_hash_find(Z_ARRVAL_P(user_value), ZEND_STRS("type"), (void **) &type) == SUCCESS) + { + if (strcmp(Z_STRVAL_P(type), "pdo") == 0) + { + pdo_dispatch(user_value); } - } else { + else if (strcmp(Z_STRVAL_P(type), "redis") == 0) + { + redis_dispatch(user_value); + } + else + { + cpLog("wrong type"); + } + } + else + { cpLog("args error no type!"); } - zval_ptr_dtor(&unser_value); - return 0; + cp_zval_ptr_dtor(&user_value); + + return CP_TRUE; +} + +static void cp_add_fail_into_mem(zval *o_arg, zval * data_source) +{ + zval *args; + CP_MAKE_STD_ZVAL(args); + *args = *o_arg; + zval_copy_ctor(args); + if (!CPGL.ping_mem_addr) + { + CPGL.ping_mem_addr = CPGS->ping_workers->sm_obj.mem; + } + zval *arr = CP_PING_GET_PRO(CPGL.ping_mem_addr); + if (Z_TYPE_P(arr) == IS_NULL) + { + zval first_arr; + array_init(&first_arr); + add_assoc_long(args, "count", 1); + add_assoc_zval(&first_arr, Z_STRVAL_P(data_source), args); + cp_ser_and_setpro(&first_arr); + zval_dtor(&first_arr); + } + else if (Z_TYPE_P(arr) != IS_TRUE) + { + zval **zval_source; + if (cp_zend_hash_find(Z_ARRVAL_P(arr), Z_STRVAL_P(data_source), Z_STRLEN_P(data_source) + 1, (void **) &zval_source) == SUCCESS) + {//++ + zval **zval_probably_count; + if (cp_zend_hash_find(Z_ARRVAL_PP(zval_source), ZEND_STRS("count"), (void **) &zval_probably_count) == SUCCESS) + { + int num = (int) Z_LVAL_PP(zval_probably_count); + add_assoc_long(args, "count", ++num); + cp_zend_hash_del(Z_ARRVAL_P(arr), Z_STRVAL_P(data_source), Z_STRLEN_P(data_source)); + add_assoc_zval(arr, Z_STRVAL_P(data_source), args); + cp_ser_and_setpro(arr); + } + } + else + {//add + add_assoc_long(args, "count", 1); + add_assoc_zval(arr, Z_STRVAL_P(data_source), args); + cp_ser_and_setpro(arr); + } + + } + cp_zval_ptr_dtor(&arr); } diff --git a/connect_pool_client.c b/connect_pool_client.c old mode 100755 new mode 100644 index c09317b..09c451c --- a/connect_pool_client.c +++ b/connect_pool_client.c @@ -23,642 +23,1383 @@ extern zend_class_entry *pdo_connect_pool_class_entry_ptr; extern zend_class_entry *redis_connect_pool_class_entry_ptr; extern zend_class_entry *pdo_connect_pool_PDOStatement_class_entry_ptr; -#define CON_FORMART_KEY(str,port) sprintf((str), "connect_pool_sock%d" , (port)); -#define CON_FAIL_MESSAGE "connect to pool_server fail" - -typedef struct _cpRecvEvent { - zval *ret_value; - uint8_t type; -} cpRecvEvent; - cpRecvEvent RecvData; static int *workerid2writefd = NULL; static int *workerid2readfd = NULL; static void **semid2attbuf = NULL; static int cpPid = 0; +static int manager_pid = 0; +static int dev_random_fd = -1; +static uint16_t dummy_source_index = 0; //for multi async fun and the same source -static int php_pdo_connect_pool_close(cpClient *cli) { - char str[100] = {0}; - CON_FORMART_KEY(str, cli->port); - if (zend_hash_del(&EG(persistent_list), str, strlen(str)) == FAILURE) {//很奇怪 用不了宏定义 - zend_error(E_WARNING, "del hash error!"); - return FAILURE; +static void cpClient_weekup(int sig) +{// do nothing now +} + +static void cpClient_attach_mem() +{ + if (!CPGS) + { + int fd = open(CP_SERVER_MMAP_FILE, O_RDWR); + if (fd == -1) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "open error Error: %s [%d],%s", strerror(errno), errno, CP_SERVER_MMAP_FILE); + } + if ((CPGS = mmap(NULL, sizeof (cpServerGS), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "attach sys mem error Error: %s [%d]", strerror(errno), errno); + } + manager_pid = CPGS->manager_pid; + cpSignalSet(SIGRTMIN, cpClient_weekup, 1, 0); } - return SUCCESS; } -static int get_writefd(int worker_id) { - if (workerid2writefd == NULL) { +static void* connect_pool_perisent(zval* zres, zval* data_source) +{ + // cpLog_init("/tmp/fpmlog"); + zend_resource sock_le; + int ret; + char *pool_server; + cpClient* cli = (cpClient*) pecalloc(sizeof (cpClient), 1, 1); + if (cpClient_create(cli) < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "pdo_connect_pool: create sock fail. Error: %s [%d]", strerror(errno), errno); + } + if (!(pool_server = getenv("POOL_SERVER"))) + { + pool_server = "127.0.0.1"; + } + ret = cpClient_connect(cli, pool_server, CP_PORT, (float) 100); //所有的操作100s超时 + if (ret < 0) + { + pefree(cli, 1); + return NULL; + } + sock_le.type = le_cli_connect_pool; + sock_le.ptr = cli; + CP_ZEND_REGISTER_RESOURCE(zres, cli, le_cli_connect_pool); +#if PHP_MAJOR_VERSION < 7 + cp_zend_hash_update(&EG(persistent_list), Z_STRVAL_P(data_source), Z_STRLEN_P(data_source), (void*) &sock_le, sizeof (zend_resource), NULL); +#else + zend_hash_update_mem(&EG(persistent_list), Z_STR_P(data_source), (void *) &sock_le, sizeof (zend_resource)); +#endif + cli->lock = cpMutexLock; + cli->unLock = cpMutexUnLock; + + cpTcpEvent event = {0}; + event.type = CP_TCPEVENT_GETFD; + event.data = cpPid; + cpClient_send(cli->sock, (char *) &event, sizeof (event), 0); + cpMasterInfo info; + ret = cpClient_recv(cli->sock, &info, sizeof (cpMasterInfo), 1); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "recv from pool server error [%d],%s", errno, strerror(errno)); + } + cli->server_fd = info.server_fd; + cpClient_attach_mem(); + CONN(cli)->release = CP_FD_RELEASED; + bzero(&cli->big_data_tmp, sizeof (cli->big_data_tmp)); + bzero(&cli->slow_log_tmp, sizeof (cli->slow_log_tmp)); + return cli; +} + +static CPINLINE zval * cpConnect_pool_server(zval *data_source, int async) +{ + cpClient *cli = NULL; + zend_resource *p_sock_le; + zval *zres = NULL; + if (async) + {//make dummy source + char real_source[CP_SOURCE_MAX] = {0}; + zval dummy_source_zval; + memcpy(real_source, Z_STRVAL_P(data_source), Z_STRLEN_P(data_source)); + sprintf(real_source + Z_STRLEN_P(data_source), "_%d", dummy_source_index); + CP_ZVAL_STRING(&dummy_source_zval, real_source, 0); + data_source = &dummy_source_zval; + dummy_source_index++; + } +#if PHP_MAJOR_VERSION < 7 + CP_ALLOC_INIT_ZVAL(zres); + if (zend_hash_find(&EG(persistent_list), Z_STRVAL_P(data_source), Z_STRLEN_P(data_source), (void **) &p_sock_le) == SUCCESS) +#else + zres = (zval *) emalloc(sizeof (zval)); + if (cp_zend_hash_find_ptr(&EG(persistent_list), data_source, (void **) &p_sock_le) == SUCCESS) +#endif + { + cli = (cpClient*) p_sock_le->ptr; + CP_ZEND_REGISTER_RESOURCE(zres, cli, le_cli_connect_pool); + } + else + {//create long connect to pool_server + if (connect_pool_perisent(zres, data_source) == NULL) + {// error +#if PHP_MAJOR_VERSION < 7 + efree(zres); +#endif + php_error_docref(NULL TSRMLS_CC, E_ERROR, CON_FAIL_MESSAGE); + return NULL; + } + } + return zres; +} + +static void release_worker(zval *object) +{ + zend_resource *p_sock_le; + zval *data_source; + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("data_source"), (void **) &data_source) == SUCCESS) + { +#if PHP_MAJOR_VERSION < 7 + if (zend_hash_find(&EG(persistent_list), Z_STRVAL_P(data_source), Z_STRLEN_P(data_source), (void **) &p_sock_le) == SUCCESS) +#else + if (cp_zend_hash_find_ptr(&EG(persistent_list), data_source, (void **) &p_sock_le) == SUCCESS) +#endif + { + send_oob2proxy(p_sock_le); + } + } + else + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_source can not find,we should do something like query before we release"); + } +} + +static void close_conn(zval *object) +{//close the tcp with pool server + zval *zres, *data_source; + cpClient *cli; + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("data_source"), (void **) &data_source) == SUCCESS) + { + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) + { + char source_char[100] = {0}; + CP_ZEND_FETCH_RESOURCE_NO_RETURN(cli, cpClient*, &zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); + strcat(source_char, Z_STRVAL_P(data_source)); + if (cli->async) + { + sprintf(source_char + strlen(source_char), "_%d", cli->dummy_source_index); + } + cp_zend_hash_del(&EG(persistent_list), source_char, strlen(source_char)); + } + } +} + +static int get_writefd(int worker_id) +{ + if (workerid2writefd == NULL) + { workerid2writefd = (int *) calloc(CP_GROUP_NUM*CP_GROUP_LEN, sizeof (int)); - if (workerid2writefd == NULL) { - zend_error(E_ERROR, "calloc Error: %s [%d]", strerror(errno), errno); + if (workerid2writefd == NULL) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "calloc Error: %s [%d]", strerror(errno), errno); } - cpPid = getpid(); } int pipe_fd_write; - if (workerid2writefd[worker_id] == 0) { + if (workerid2writefd[worker_id] == 0) + { char file_c2w[CP_FIFO_NAME_LEN] = {0}; sprintf(file_c2w, "%s_%d", CP_FIFO_NAME_PRE, worker_id); pipe_fd_write = cpCreateFifo(file_c2w); - if (pipe_fd_write < 0) { - zend_error(E_ERROR, "pipe open Error: %s [%d]", strerror(errno), errno); + if (pipe_fd_write < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "pipe open Error: %s [%d]", strerror(errno), errno); } workerid2writefd[worker_id] = pipe_fd_write; - } else { + } + else + { pipe_fd_write = workerid2writefd[worker_id]; } return pipe_fd_write; } -static int get_readfd(int worker_id) { - if (workerid2readfd == NULL) { +static int get_readfd(int worker_id) +{ + if (workerid2readfd == NULL) + { workerid2readfd = (int *) calloc(CP_GROUP_NUM*CP_GROUP_LEN, sizeof (int)); - if (workerid2readfd == NULL) { - zend_error(E_ERROR, "calloc Error: %s [%d]", strerror(errno), errno); + if (workerid2readfd == NULL) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "calloc Error: %s [%d]", strerror(errno), errno); } } int pipe_fd_read; - if (workerid2readfd[worker_id] == 0) { + if (workerid2readfd[worker_id] == 0) + { char file_w2c[CP_FIFO_NAME_LEN] = {0}; sprintf(file_w2c, "%s_%d_1", CP_FIFO_NAME_PRE, worker_id); //worker 2 client pipe_fd_read = cpCreateFifo(file_w2c); - if (pipe_fd_read < 0) { - zend_error(E_ERROR, "pipe open Error: %s [%d]", strerror(errno), errno); + if (pipe_fd_read < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "pipe open Error: %s [%d]", strerror(errno), errno); } workerid2readfd[worker_id] = pipe_fd_read; - } else { + } + else + { pipe_fd_read = workerid2readfd[worker_id]; } return pipe_fd_read; } -static void* get_attach_buf(int worker_id) { - if (semid2attbuf == NULL) { +static void* get_attach_buf(int worker_id, int max, char *mm_name) +{ + if (semid2attbuf == NULL) + { semid2attbuf = (void **) calloc(CP_GROUP_NUM*CP_GROUP_LEN, sizeof (void*)); - if (semid2attbuf == NULL) { - zend_error(E_ERROR, "calloc Error: %s [%d]", strerror(errno), errno); + if (semid2attbuf == NULL) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "calloc Error: %s [%d]", strerror(errno), errno); } } void* buf = NULL; - if (semid2attbuf[worker_id] == 0) { - if ((buf = shmat(info.semid, NULL, 0)) < 0) { - zend_error(E_ERROR, "attach sys mem error Error: %s [%d]", strerror(errno), errno); + if (semid2attbuf[worker_id] == 0) + { + int fd = open(mm_name, O_RDWR); + if (fd == -1) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "open error Error: %s [%d],%s", strerror(errno), errno, mm_name); + } + if ((buf = mmap(NULL, max, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "attach sys mem error Error: %s [%d]", strerror(errno), errno); } semid2attbuf[worker_id] = buf; - } else { + } + else + { buf = semid2attbuf[worker_id]; } return buf; } -CPINLINE int CP_CLIENT_SERIALIZE_SEND_MEM(zval *ret_value, int worker_id, int max, int semid) { - int pipe_fd_write = get_writefd(worker_id); +int CP_CLIENT_SERIALIZE_SEND_MEM(zval *send_data, cpClient *cli) +{ + int pipe_fd_write = get_writefd(CONN(cli)->worker_id); + int real_len = 0; + void* attach_addr = get_attach_buf(CONN(cli)->worker_id, CPGS->max_buffer_len, CPGS->G[CONN(cli)->group_id].workers[CONN(cli)->worker_index].sm_obj.mmap_name); +#if PHP_MAJOR_VERSION < 7 instead_smart dest; dest.len = 0; - dest.addr = get_attach_buf(worker_id); - dest.max = max; - dest.exceed = '0'; - php_msgpack_serialize(&dest, ret_value); - if (dest.exceed == '1') { - zend_error(E_ERROR, "data is exceed,increase max_read_len"); - } else { - cpWorkerInfo worker_event; - worker_event.len = dest.len; - worker_event.pid = cpPid; - worker_event.type = 0; //暫時沒用 - int ret = write(pipe_fd_write, &worker_event, sizeof (worker_event)); - if (ret == -1) { - zend_error(E_ERROR, "write error Error: %s [%d]", strerror(errno), errno); - } - return SUCCESS; + dest.addr = attach_addr; + dest.max = CPGS->max_buffer_len; + dest.exceed = 0; + php_msgpack_serialize(&dest, send_data); + real_len = dest.len; + if (dest.exceed == 1) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "data is exceed,increase max_read_len Error: %s [%d] ", strerror(errno), errno); + return FAILURE; } +#else + zend_string * zstr = php_swoole_serialize(send_data); + if (zstr->len >= CPGS->max_buffer_len) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "data is exceed,increase max_read_len Error: %s [%d] ", strerror(errno), errno); + return FAILURE; + } + memcpy(attach_addr, zstr->val, zstr->len); + zend_string_release(zstr); +#endif + + cpWorkerInfo worker_event; + worker_event.len = real_len; + worker_event.pid = cpPid; + worker_event.type = 0; + int ret = write(pipe_fd_write, &worker_event, sizeof (worker_event)); + if (ret == -1) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "write error Error: %s [%d]", strerror(errno), errno); + } + return SUCCESS; + } +//core logic + +static cpGroup * cpGetWorker(cpClient *cli, zval *data_source) +{ + cpGroup *G = NULL; + int group_id, worker_index; + for (group_id = 0; group_id < CPGS->group_num; group_id++) + { + if (strcmp(Z_STRVAL_P(data_source), CPGS->G[group_id].name) == 0) + { + G = &CPGS->G[group_id]; + cpConnection *conn = CONN(cli); + if (cli->lock(G) == 0) + { + for (worker_index = 0; worker_index < G->worker_num; worker_index++) + { + if (G->workers_status[worker_index] == CP_WORKER_IDLE && worker_index < G->worker_max) + { + G->workers_status[worker_index] = CP_WORKER_BUSY; + G->workers[worker_index].CPid = cpPid; //worker for this pid + conn->release = CP_FD_NRELEASED; + conn->worker_id = CP_WORKER_ID(group_id, worker_index); + conn->group_id = group_id; + conn->worker_index = worker_index; + break; + } + } + if (conn->release == CP_FD_RELEASED) + { + if (G->worker_num < G->worker_max) + {//add + conn->worker_index = G->worker_num; + conn->release = CP_FD_NRELEASED; + conn->worker_id = CP_WORKER_ID(group_id, conn->worker_index); + conn->group_id = group_id; + G->workers_status[conn->worker_index] = CP_WORKER_BUSY; + G->workers[conn->worker_index].CPid = cpPid; //worker for this pid + cpCreate_worker_mem(conn->worker_index, group_id); + + cpTcpEvent event = {0}; + event.type = CP_TCPEVENT_ADD; + event.data = conn->worker_index; + // event.ClientPid = cpPid; + G->worker_num++; //add first, for thread safe + int ret = cpClient_send(cli->sock, (char *) &event, sizeof (event), 0); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "send to server errro %s [%d]", strerror(errno), errno); + } + } + else + {// in queue + conn->wait_fpm_pid = cpPid; + conn->next_wait_id = 0; + if (G->last_wait_id) + { + CPGS->conlist[G->last_wait_id].next_wait_id = cli->server_fd; + G->last_wait_id = cli->server_fd; + + } + else + { + G->first_wait_id = G->last_wait_id = cli->server_fd; + } + conn->release = CP_FD_WAITING; + conn->group_id = group_id; + } + } + cli->unLock(G); + } + break; + } + } + if (!G) + { + if (pthread_mutex_lock(&CPGS->mutex_lock) == 0) + { + int group_num = CPGS->group_num, group_id; + for (group_id = 0; group_id < group_num; group_id++) + { + if (strcmp(Z_STRVAL_P(data_source), CPGS->G[group_id].name) == 0) + { + pthread_mutex_unlock(&CPGS->mutex_lock); + return cpGetWorker(cli, data_source); + } + } -int connect_pool_perisent(cpClient** cli, zval* zres, int port) { - zend_rsrc_list_entry sock_le; - int ret; - (*cli) = (cpClient*) pecalloc(sizeof (cpClient), 1, 1); - if (cpClient_create((*cli)) < 0) { - zend_error(E_ERROR, "pdo_connect_pool: create sock fail. Error: %s [%d]", strerror(errno), errno); - } - (*cli)->port = port; - ret = cpClient_connect((*cli), "127.0.0.1", (*cli)->port, (float) 100, 0); //所有的操作100s超时 - if (ret < 0) { - pefree(*cli, 1); - return -1; + strcpy(CPGS->G[group_num].name, Z_STRVAL_P(data_source)); + CPGS->G[group_num].worker_max = CPGS->default_max; + CPGS->G[group_num].worker_min = CPGS->default_min; + CPGS->G[group_num].worker_num = CPGS->default_min; + CPGS->G[group_num].workers_status[CPGS->G[group_num].worker_num - 1] = CP_WORKER_IDLE; //foreach to CPGS->default_min to set status??? + cpCreate_worker_mem(CPGS->G[group_num].worker_num - 1, group_num); //foreach to CPGS->default_min to create mem??? + cpTcpEvent event = {0}; + event.type = CP_TCPEVENT_ADD; + event.data = 0; + CPGS->group_num++; + cpClient_send(cli->sock, (char *) &event, sizeof (event), 0); + + pthread_mutex_unlock(&CPGS->mutex_lock); + } + return cpGetWorker(cli, data_source); } - sock_le.type = le_cli_connect_pool; - sock_le.ptr = (*cli); - ZEND_REGISTER_RESOURCE(zres, (*cli), le_cli_connect_pool); - char str[100] = {0}; - CON_FORMART_KEY(str, (*cli)->port); - zend_hash_update(&EG(persistent_list), str, strlen(str), (void*) &sock_le, sizeof (zend_rsrc_list_entry), NULL); - return 1; + return G; } -CPINLINE int cli_real_send(cpClient *cli, zval *send_data) { +static CPINLINE int cli_real_send(cpClient **real_cli, zval *send_data) +{ int ret = 0; - if (cli->released == CP_FD_RELEASED) { - cpTcpEvent event; - event.type = CP_TCPEVENT_GET; - int ret = cpClient_send(cli->sock, (char *) &event, sizeof (event), 0); - if (ret < 0) { - zend_error(E_ERROR, "send failed in GET. Error:%d", errno); - } - int n = cpClient_recv(cli, &info, sizeof (info), 1); - if (n > 0) { - ret = CP_CLIENT_SERIALIZE_SEND_MEM(send_data, info.worker_id, info.max, info.semid); - if (ret == SUCCESS) { - cli->released = CP_FD_NRELEASED; - } - } else if (n == 0) { - php_pdo_connect_pool_close(cli); - zend_error(E_ERROR, "connect_pool: connect with conPool close"); - } else { - zend_error(E_ERROR, "connect_pool: recv failed. Error: %s [%d]", strerror(errno), errno); + cpClient *cli = *real_cli; + if (CONN(cli)->release == CP_FD_RELEASED) + { + zval *data_source = NULL; + cp_zend_hash_find(Z_ARRVAL_P(send_data), ZEND_STRS("data_source"), (void **) &data_source); + if (manager_pid != CPGS->manager_pid) + {//restart server + exit(0); + } + cpGroup *G = cpGetWorker(cli, data_source); + if (!G) + { + zend_throw_exception_ex(NULL, 0, "can not find datasource %s from pool.ini", Z_STRVAL_P(data_source) TSRMLS_CC); + return -1; } - } else { - ret = CP_CLIENT_SERIALIZE_SEND_MEM(send_data, info.worker_id, info.max, info.semid); + while (CONN(cli)->release == CP_FD_WAITING) + { + pause(); + } + log_start(cli); + log_write(send_data, cli); + ret = CP_CLIENT_SERIALIZE_SEND_MEM(send_data, cli); + } + else + { + log_write(send_data, cli); + ret = CP_CLIENT_SERIALIZE_SEND_MEM(send_data, cli); } return ret; } -static int cli_real_recv() { - int pipe_fd_read = get_readfd(info.worker_id); +static CPINLINE int cli_real_recv(cpClient *cli, int async) +{ cpWorkerInfo event; int ret = 0; - do { + zval *ret_value; + CP_ALLOC_INIT_ZVAL(ret_value); + int pipe_fd_read = get_readfd(CONN(cli)->worker_id); + if (async) + { + RecvData.type = CP_SIGEVENT_TURE; + ZVAL_LONG(ret_value, pipe_fd_read); + RecvData.ret_value = ret_value; + cli->querying = 1; + return SUCCESS; + } + do + { ret = cpFifoRead(pipe_fd_read, &event, sizeof (event)); - if (ret < 0) { - zend_error(E_ERROR, "fifo read Error: %s [%d]", strerror(errno), errno); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "fifo read Error: %s [%d]", strerror(errno), errno); + } + + if (event.type == CP_SIGEVENT_DIE) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "you send the request to pool worker ,but the worker is died"); + exit(0); } + } while (event.pid != cpPid); //有可能有脏数据 读出来 - zval *ret_value; - ALLOC_INIT_ZVAL(ret_value); - php_msgpack_unserialize(ret_value, get_attach_buf(info.worker_id), event.len); + log_increase_size(event.len, cli); + void * buf = get_attach_buf(CONN(cli)->worker_id, CPGS->max_buffer_len, CPGS->G[CONN(cli)->group_id].workers[CONN(cli)->worker_index].sm_obj.mmap_name); +#if PHP_MAJOR_VERSION < 7 + php_msgpack_unserialize(ret_value, buf, event.len); +#else + php_swoole_unserialize(buf, event.len, ret_value, NULL); +#endif RecvData.type = event.type; RecvData.ret_value = ret_value; return SUCCESS; } -PHP_METHOD(pdo_connect_pool_PDOStatement, __call) { - zval *z_args; - zval *object; - zval *pass_data; - zval **zres, **source_zval; +static void async_done(zval *object, zend_class_entry *class_entry_ptr) +{ + zval *zres; + cpClient *cli = NULL; + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) + { + CP_ZEND_FETCH_RESOURCE_NO_RETURN(cli, cpClient*, &zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); + if (!cli->querying) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "you must execute the async function before done"); + } + } + else + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "can not find zres"); + } + zend_update_property_bool(class_entry_ptr, object, ZEND_STRL("async"), 0 TSRMLS_CC); + cli->querying = 0; + cpSetIsBlock(get_readfd(CONN(cli)->worker_id), 1); + cli_real_recv(cli, 0); +} - char *cmd; - int cmd_len; +static void check_need_exchange(zval * object, char *cur_type) +{ + char *lt = NULL; + zval *last_type; + // compare with last_type + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("last_type"), (void **) &last_type) == SUCCESS) + { + lt = Z_STRVAL_P(last_type); + } + // exchange + if (cur_type != lt) + { + zend_update_property_string(pdo_connect_pool_class_entry_ptr, object, ZEND_STRL("last_type"), cur_type TSRMLS_CC); + } +} - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, pdo_connect_pool_PDOStatement_class_entry_ptr, &cmd, &cmd_len, &z_args) == FAILURE) { - RETURN_FALSE; +static char* php_check_ms(char *cmd, zval *z_args, zval* object) +{ + zval *enable_slave = NULL, *sql = NULL, *in_tran = NULL; + char *cur_type = "m"; + if (strcasecmp("beginTransaction", cmd) == 0) + { + zend_update_property_bool(pdo_connect_pool_class_entry_ptr, object, ZEND_STRL("in_tran"), 1 TSRMLS_CC); + } + if (strcasecmp("commit", cmd) == 0 || strcasecmp("rollback", cmd) == 0) + { + zend_update_property_bool(pdo_connect_pool_class_entry_ptr, object, ZEND_STRL("in_tran"), 0 TSRMLS_CC); + } + cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("enable_slave"), (void **) &enable_slave); + cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("in_tran"), (void **) &in_tran); + if (!Z_BVAL_P(enable_slave) || Z_BVAL_P(in_tran)) + {//todo + return cur_type; } - cpClient *cli; - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) { - ZEND_FETCH_RESOURCE(cli, cpClient*, zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); - } else { - zend_error(E_WARNING, "pdo_connect_pool: object is not instanceof pdo_connect_pool. "); - RETURN_FALSE; + if (strcasecmp("query", cmd) == 0 || strcasecmp("exec", cmd) == 0 || strcasecmp("prepare", cmd) == 0) + { + cp_zend_hash_index_find(Z_ARRVAL_P(z_args), 0, (void**) &sql); + cp_convert_to_string_ex(&sql); + char pre[8] = {0}; + strncpy(pre, Z_STRVAL_P(sql), 6); + int i = 0; + for (; i < 6; i++) + { + if (pre[i] == ' ') + { + pre[i] = '\0'; + break; + } + pre[i] = tolower(pre[i]); + } + if (strcmp(pre, "select") == 0 || strcmp(pre, "show") == 0) + { + cur_type = "s"; + } + else + { + cur_type = "m"; + } } - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("data_source"), (void **) &source_zval) == FAILURE) { - zend_error(E_WARNING, "pdo_connect_pool: get data_source name failed!"); - RETURN_FALSE; + return cur_type; +} + +int cp_system_random(int min, int max) +{ + char *next_random_byte; + int bytes_to_read; + unsigned random_value; + + assert(max > min); + + if (dev_random_fd == -1) + { + dev_random_fd = open("/dev/urandom", O_RDONLY); + assert(dev_random_fd != -1); + } + + next_random_byte = (char *) &random_value; + bytes_to_read = sizeof (random_value); + + if (read(dev_random_fd, next_random_byte, bytes_to_read) < 0) + { + return -1; + } + return min + (random_value % (max - min + 1)); +} + + +//create the pass args that pass to mysql + +static CPINLINE zval* create_pass_data(char* cmd, zval* z_args, zval* object, char* cur_type, zval **ret_data_source) +{ + zval *data_source = NULL, *username = NULL, *pwd = NULL, *options = NULL, *pass_data = NULL, *zval_conf = NULL, *real_data_source_arr = NULL; + zval_conf = cp_zend_read_property(pdo_connect_pool_class_entry_ptr, object, ZEND_STRL("config"), 0 TSRMLS_DC); + + zval *slave; + if (*cur_type == 's' && (cp_zend_hash_find(Z_ARRVAL_P(zval_conf), ZEND_STRS("slave"), (void **) &slave) == SUCCESS)) + { + zval *start_prt, start, end, *end_prt; + start_prt = &start; + end_prt = &end; + + cp_zend_hash_find(Z_ARRVAL_P(zval_conf), ZEND_STRS("slave"), (void **) &slave); + int slave_cnt; + slave_cnt = zend_hash_num_elements(Z_ARRVAL_P(slave)); + + if (slave_cnt > 0) + { + int index; + index = cp_system_random(0, (slave_cnt - 1)); + + if (cp_zend_hash_index_find(Z_ARRVAL_P(slave), index, (void **) &real_data_source_arr) != SUCCESS) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "not find slave ,check config"); + } + } + } + else + { + if (cp_zend_hash_find(Z_ARRVAL_P(zval_conf), ZEND_STRS("master"), (void **) &real_data_source_arr) != SUCCESS) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "not find master ,check config"); + } } + // find args + cp_zend_hash_find(Z_ARRVAL_P(real_data_source_arr), ZEND_STRS("data_source"), (void **) &data_source); + cp_zend_hash_find(Z_ARRVAL_P(real_data_source_arr), ZEND_STRS("username"), (void **) &username); + cp_zend_hash_find(Z_ARRVAL_P(real_data_source_arr), ZEND_STRS("pwd"), (void **) &pwd); - MAKE_STD_ZVAL(pass_data); + CP_MAKE_STD_ZVAL(pass_data); array_init(pass_data); - add_assoc_string(pass_data, "data_source", Z_STRVAL_PP(source_zval), 1); - add_assoc_string(pass_data, "method", cmd, 1); - zval_add_ref(&z_args); + cp_add_assoc_string(pass_data, "type", "pdo", 1); + cp_add_assoc_string(pass_data, "method_type", "connect", 1); + cp_add_assoc_string(pass_data, "method", cmd, 1); + cp_add_assoc_string(pass_data, "data_source", Z_STRVAL_P(data_source), 1); + *ret_data_source = data_source; + cp_add_assoc_string(pass_data, "username", Z_STRVAL_P(username), 1); + cp_add_assoc_string(pass_data, "password", Z_STRVAL_P(pwd), 1); + cp_zval_add_ref(&z_args); add_assoc_zval(pass_data, "args", z_args); - add_assoc_string(pass_data, "method_type", "PDOStatement", 1); - add_assoc_string(pass_data, "type", "pdo", 1); + if (cp_zend_hash_find(Z_ARRVAL_P(real_data_source_arr), ZEND_STRS("options"), (void **) &options) != SUCCESS) + { + zval *new_option = NULL; + CP_MAKE_STD_ZVAL(new_option); + array_init(new_option); + add_index_long(new_option, PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION); + cp_add_index_string(new_option, PDO_ATTR_DRIVER_SPECIFIC + 2, "SET SESSION wait_timeout=31536000", 1); + add_assoc_zval(pass_data, "options", new_option); + } + else + { + cp_zval_add_ref(&options); + add_index_long(options, PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION); //set exception mode for delete pdo object from pool when gone away + cp_add_index_string(options, PDO_ATTR_DRIVER_SPECIFIC + 2, "SET SESSION wait_timeout=31536000", 1); + add_assoc_zval(pass_data, "options", options); + } + return pass_data; +} - int ret = cli_real_send(cli, pass_data); - if (ret < 0) { - zend_error(E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); +int static stmt_fetch_obj(zval *args, zend_class_entry *ce, zval *return_value) +{ + zend_fcall_info fci = {0}; + zend_fcall_info_cache fcc = {0}; + fci.size = sizeof (zend_fcall_info); + + + if (ce->constructor) + { +#if PHP_MAJOR_VERSION == 7 + zval retval; + ZVAL_UNDEF(&fci.function_name); + fci.retval = &retval; + fci.param_count = 0; + fci.params = NULL; + fci.no_separation = 1; + fci.object = Z_OBJ_P(return_value); + + zend_fcall_info_args_ex(&fci, ce->constructor, args); + + fcc.initialized = 1; + fcc.function_handler = ce->constructor; + fcc.called_scope = Z_OBJCE_P(return_value); + fcc.object = Z_OBJ_P(return_value); +#else + zval *retval; + fci.function_table = &ce->function_table; + fci.function_name = NULL; + fci.symbol_table = NULL; + fci.retval_ptr_ptr = &retval; + if (args) + { + HashTable *ht = Z_ARRVAL_P(args); + Bucket *p; + + fci.param_count = 0; + fci.params = safe_emalloc(sizeof (zval**), ht->nNumOfElements, 0); + p = ht->pListHead; + while (p != NULL) + { + fci.params[fci.param_count++] = (zval**) p->pData; + p = p->pListNext; + } + } + else + { + fci.param_count = 0; + fci.params = NULL; + } + fci.no_separation = 1; + + fcc.initialized = 1; + fcc.function_handler = ce->constructor; + fcc.calling_scope = EG(scope); + fcc.called_scope = ce; + + object_init_ex(return_value, ce); + + fci.object_ptr = return_value; + fcc.object_ptr = return_value; +#endif + if (zend_call_function(&fci, &fcc) == FAILURE) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not call class constructor"); + return 0; + } + else + {//??? free something? +#if PHP_MAJOR_VERSION < 7 + efree(fci.params); + cp_zval_ptr_dtor(&retval); +#endif + //cp_zval_ptr_dtor(&args); + return 1; + } } - cli_real_recv(); - if (RecvData.type == CP_SIGEVENT_EXCEPTION) { - zend_throw_exception(php_pdo_get_exception(), Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); - RETVAL_BOOL(0); - } else { - RETVAL_ZVAL(RecvData.ret_value, 0, 1); + else + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "user-supplied class does not have a constructor"); + return 0; } - zval_ptr_dtor(&pass_data); } -PHP_METHOD(pdo_connect_pool, __call) { - zval *z_args; - zval *pass_data; - zval *object; - zval **zres, **source_zval, **con_args_zval; +PHP_METHOD(pdo_connect_pool_PDOStatement, __call) +{ + zval *zres, *source_zval, *pass_data, *object, *z_args, *class_name = NULL, stmt_obj_args_dup, *async_zval; char *cmd; - int cmd_len; + zend_size_t cmd_len; + int async = 0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, pdo_connect_pool_class_entry_ptr, &cmd, &cmd_len, &z_args) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, pdo_connect_pool_PDOStatement_class_entry_ptr, &cmd, &cmd_len, &z_args) == FAILURE) + { RETURN_FALSE; } - + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("async"), (void **) &async_zval) == SUCCESS) + { + async = Z_BVAL_P(async_zval); + } cpClient *cli; - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) { - ZEND_FETCH_RESOURCE(cli, cpClient*, zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); - } else { - zend_error(E_WARNING, "pdo_connect_pool: object is not instanceof pdo_connect_pool. "); - RETURN_FALSE; + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) + { + CP_ZEND_FETCH_RESOURCE_NO_RETURN(cli, cpClient*, &zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); } - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("data_source"), (void **) &source_zval) == FAILURE) { - zend_error(E_WARNING, "pdo_connect_pool: get data_source name failed!"); + else + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "pdo_connect_pool: object is not instanceof pdo_connect_pool. "); RETURN_FALSE; } - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("con_args"), (void **) &con_args_zval) == FAILURE) { - zend_error(E_WARNING, "pdo_connect_pool: get con_args name failed!"); + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("data_source"), (void **) &source_zval) == FAILURE) + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "pdo_connect_pool: get data_source name failed!"); RETURN_FALSE; } - MAKE_STD_ZVAL(pass_data); + CP_MAKE_STD_ZVAL(pass_data); array_init(pass_data); - add_assoc_string(pass_data, "data_source", Z_STRVAL_PP(source_zval), 1); - add_assoc_string(pass_data, "method", cmd, 1); - zval_add_ref(&z_args); + cp_add_assoc_string(pass_data, "data_source", Z_STRVAL_P(source_zval), 1); + cp_add_assoc_string(pass_data, "method", cmd, 1); + cp_zval_add_ref(&z_args); add_assoc_zval(pass_data, "args", z_args); - zval_add_ref(con_args_zval); - add_assoc_zval(pass_data, "con_args", *con_args_zval); - add_assoc_string(pass_data, "method_type", "others", 1); - add_assoc_string(pass_data, "type", "pdo", 1); + if (strcasecmp("fetchObject", cmd) == 0) + { + cp_zend_hash_del(Z_ARRVAL_P(pass_data), "args", strlen("args") + 1); //use no args in the pool server + cp_zend_hash_index_find(Z_ARRVAL_P(z_args), 0, (void**) &class_name); + if (class_name) + { + ZVAL_DUP(&stmt_obj_args_dup, z_args); + zend_hash_index_del(Z_ARRVAL_P(z_args), 0); + } + } + cp_add_assoc_string(pass_data, "method_type", "PDOStatement", 1); + cp_add_assoc_string(pass_data, "type", "pdo", 1); + int ret = cli_real_send(&cli, pass_data); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); + } + cli_real_recv(cli, async); + if (RecvData.type == CP_SIGEVENT_EXCEPTION) + { + release_worker(getThis()); + zend_throw_exception(NULL, Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); + RETVAL_BOOL(0); + } + else if (RecvData.type == CP_SIGEVENT_STMT_OBJ) + {//fetchObject return + if (class_name && strcasecmp("stdClass", Z_STRVAL_P(class_name)) != 0) + {//user class + zend_class_entry *class_ce = cp_zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO); + stmt_fetch_obj(&stmt_obj_args_dup, class_ce, return_value); + zval *val; + char *name; + uint klen; + int ktype; + + CP_HASHTABLE_FOREACH_START2(CP_Z_ARRVAL_P(RecvData.ret_value), name, klen, ktype, val) + { + zend_update_property(class_ce, return_value, name, klen, val TSRMLS_CC); + } + CP_HASHTABLE_FOREACH_END(); + } + else + {//default class + convert_to_object(RecvData.ret_value); +#if PHP_MAJOR_VERSION == 7 + ZVAL_OBJ(return_value, Z_OBJ_P(RecvData.ret_value)); + +#else + ZVAL_DUP(return_value, RecvData.ret_value); +#endif + + } + } + else + { - int ret = cli_real_send(cli, pass_data); - if (ret < 0) { - zend_error(E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); + RETVAL_ZVAL(RecvData.ret_value, 0, 1); } + cp_zval_ptr_dtor(&pass_data); +} - cli_real_recv(); - if (RecvData.type == CP_SIGEVENT_PDO) {//返回一个模拟pdo类 +PHP_METHOD(pdo_connect_pool, __call) +{ + zval *z_args, *pass_data, *object, *zres, *source_zval, *use_ms, *async_zval; + char *cmd, *cur_type; + zend_size_t cmd_len; + int async = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, pdo_connect_pool_class_entry_ptr, &cmd, &cmd_len, &z_args) == FAILURE) + { + RETURN_FALSE; + } + + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("async"), (void **) &async_zval) == SUCCESS) + { + async = Z_BVAL_P(async_zval); + } + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("use_ms"), (void **) &use_ms) == SUCCESS) + { + cur_type = "m"; + } + else + { + cur_type = php_check_ms(cmd, z_args, object); + check_need_exchange(getThis(), cur_type); + } + pass_data = create_pass_data(cmd, z_args, object, cur_type, &source_zval); + cpClient *cli; + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) + { + CP_ZEND_FETCH_RESOURCE_NO_RETURN(cli, cpClient*, &zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); + } + else + { + zres = cpConnect_pool_server(source_zval, async); + zend_update_property_string(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("data_source"), Z_STRVAL_P(source_zval) TSRMLS_CC); + zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("cli"), zres TSRMLS_CC); + CP_ZEND_FETCH_RESOURCE_NO_RETURN(cli, cpClient*, &zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); + if (async) + { + cli->async = async; + cli->dummy_source_index = dummy_source_index - 1; + } + } + if (async && cli->querying) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "the obj is async querying now, you can not execute async function again"); + } + int ret = cli_real_send(&cli, pass_data); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); + } + cli_real_recv(cli, async); + if (RecvData.type == CP_SIGEVENT_PDO) + { object_init_ex(return_value, pdo_connect_pool_PDOStatement_class_entry_ptr); - zend_update_property(pdo_connect_pool_PDOStatement_class_entry_ptr, return_value, ZEND_STRL("cli"), *zres TSRMLS_CC); - zend_update_property(pdo_connect_pool_PDOStatement_class_entry_ptr, return_value, ZEND_STRL("data_source"), *source_zval TSRMLS_CC); //标示这个连接的真实目标 - zval_ptr_dtor(&RecvData.ret_value); - } else if (RecvData.type == CP_SIGEVENT_EXCEPTION) { - zend_throw_exception(php_pdo_get_exception(), Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); + zend_update_property(pdo_connect_pool_PDOStatement_class_entry_ptr, return_value, ZEND_STRL("cli"), zres TSRMLS_CC); + zend_update_property(pdo_connect_pool_PDOStatement_class_entry_ptr, return_value, ZEND_STRL("data_source"), source_zval TSRMLS_CC); //标示这个连接的真实目标 + cp_zval_ptr_dtor(&RecvData.ret_value); + } + else if (RecvData.type == CP_SIGEVENT_EXCEPTION) + { + release_worker(getThis()); + zend_throw_exception(NULL, Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); RETVAL_BOOL(0); - } else { + } + else + { RETVAL_ZVAL(RecvData.ret_value, 0, 1); } - zval_ptr_dtor(&pass_data); + cp_zval_ptr_dtor(&pass_data); } -PHP_METHOD(pdo_connect_pool, __destruct) { +PHP_METHOD(pdo_connect_pool, __destruct) +{ } -PHP_METHOD(pdo_connect_pool, __construct) { - // cpLog_init(fpm_buf); - zval *zres, *zval_source, *pool_port; +PHP_METHOD(pdo_connect_pool, __construct) +{ + zval *zval_conf = NULL, *data_source = NULL, *options = NULL, *master = NULL; zval *object = getThis(); - int ret; - - char *data_source; - int data_source_len; char *username = NULL, *password = NULL; - int usernamelen, passwordlen, port = 9056; - zval *options = NULL; + zend_size_t usernamelen, passwordlen; + int port = CP_PORT_PDO; - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!a!l!", &data_source, &data_source_len, - &username, &usernamelen, &password, &passwordlen, &options, &port)) { + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s!s!a!l!", &data_source, + &username, &usernamelen, &password, &passwordlen, &options, &port)) + { ZVAL_NULL(object); return; } - zval *pass_data; - MAKE_STD_ZVAL(pass_data); - array_init(pass_data); - add_assoc_string(pass_data, "type", "pdo", 1); - add_assoc_string(pass_data, "method_type", "connect", 1); - add_assoc_string(pass_data, "data_source", data_source, 1); - if (username != NULL) { - add_assoc_string(pass_data, "username", username, 1); - } - if (password != NULL) { - add_assoc_string(pass_data, "password", password, 1); - } - if (options != NULL) { - zval_add_ref(&options); - add_assoc_zval(pass_data, "options", options); - } + CP_GET_PID; + switch (Z_TYPE_P(data_source)) + { + case IS_STRING: + { + CP_MAKE_STD_ZVAL(zval_conf); + CP_MAKE_STD_ZVAL(master); + + array_init(zval_conf); + array_init(master); + + cp_add_assoc_string(master, "data_source", Z_STRVAL_P(data_source), 1); + cp_add_assoc_string(master, "username", username, 1); + cp_add_assoc_string(master, "pwd", password, 1); + if (options != NULL) + { + cp_zval_add_ref(&options); + add_assoc_zval(master, "options", options); + } - MAKE_STD_ZVAL(zres); - cpClient *cli; - // todo长连接,为今后也支持短连接留好口 - zend_rsrc_list_entry *p_sock_le; - char str[100] = {0}; - CON_FORMART_KEY(str, port); + add_assoc_zval(zval_conf, "master", master); + // zend_print_zval_r(zval_conf, 0); - if (zend_hash_find(&EG(persistent_list), str, strlen(str), (void **) &p_sock_le) == SUCCESS) { - cli = (cpClient*) p_sock_le->ptr; - // zend_hash_index_del(&EG(regular_list),Z_RESVAL_P(file_resource)); - ZEND_REGISTER_RESOURCE(zres, cli, le_cli_connect_pool); - } else {//这个fpm进程第一次创建连接 - if (connect_pool_perisent(&cli, zres, port) < 0) {//没连上 - efree(zres); - zval_ptr_dtor(&pass_data); - zval exception_str; - ZVAL_STRINGL(&exception_str, CON_FAIL_MESSAGE, strlen(CON_FAIL_MESSAGE), 0); - zend_throw_exception(php_pdo_get_exception(), Z_STRVAL(exception_str), 0 TSRMLS_CC); - return; + zend_update_property_bool(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("use_ms"), 0 TSRMLS_CC); + zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("config"), zval_conf TSRMLS_CC); + cp_zval_ptr_dtor(&zval_conf); } + break; + case IS_ARRAY: + cp_zval_add_ref(&data_source); + zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("config"), data_source TSRMLS_CC); + + break; } + zend_update_property_bool(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("in_tran"), 0 TSRMLS_CC); + zend_update_property_bool(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("enable_slave"), 1 TSRMLS_CC); +} - MAKE_STD_ZVAL(zval_source); - ZVAL_STRING(zval_source, data_source, 1); - MAKE_STD_ZVAL(pool_port); - ZVAL_LONG(pool_port, port); - zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("cli"), zres TSRMLS_CC); - zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("pool_port"), pool_port TSRMLS_CC); - zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("data_source"), zval_source TSRMLS_CC); //标示这个连接的真实目标 - zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("con_args"), pass_data TSRMLS_CC); //用于重连 +PHP_METHOD(pdo_connect_pool, release) +{ + release_worker(getThis()); + CP_CHECK_RETURN(1); +} - // cpQueueSignalSet(CP_SIG_EVENT, HandleRecv); - ret = cli_real_send(cli, pass_data); - if (ret < 0) { - zend_error(E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); - } - cli_real_recv(); - if (RecvData.type == CP_SIGEVENT_EXCEPTION) { - zend_throw_exception(php_pdo_get_exception(), Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); - } - zval_ptr_dtor(&RecvData.ret_value); - zval_ptr_dtor(&zval_source); - zval_ptr_dtor(&pass_data); - zval_ptr_dtor(&pool_port); - zval_ptr_dtor(&zres); +PHP_METHOD(pdo_connect_pool_PDOStatement, release) +{ + release_worker(getThis()); + CP_CHECK_RETURN(1); } -PHP_FUNCTION(client_close) {//close 关闭和中间件的连接 - zval **zres; - cpClient *cli; - int ret; +PHP_METHOD(pdo_connect_pool, close) +{ + close_conn(getThis()); + CP_CHECK_RETURN(1); +} - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) { - ZEND_FETCH_RESOURCE(cli, cpClient*, zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); - } else { +PHP_METHOD(pdo_connect_pool, setAsync) +{ + int async = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l!", &async) == FAILURE) + { RETURN_FALSE; } - ret = php_pdo_connect_pool_close(cli); - CP_CHECK_RETURN(ret); + zend_update_property_bool(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("async"), async TSRMLS_CC); + CP_CHECK_RETURN(1); } -PHP_METHOD(pdo_connect_pool, release) {// relase方法 - zend_rsrc_list_entry *p_sock_le; - zval **pool_port; - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("pool_port"), (void **) &pool_port) == SUCCESS) { - char str[100] = {0}; - CON_FORMART_KEY(str, (int) Z_LVAL_PP(pool_port)); - if (zend_hash_find(&EG(persistent_list), str, strlen(str), (void **) &p_sock_le) == SUCCESS) { - send_oob2proxy(p_sock_le); - } +PHP_METHOD(pdo_connect_pool_PDOStatement, setAsync) +{ + int async = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l!", &async) == FAILURE) + { + RETURN_FALSE; } + zend_update_property_bool(pdo_connect_pool_PDOStatement_class_entry_ptr, getThis(), ZEND_STRL("async"), async TSRMLS_CC); CP_CHECK_RETURN(1); } -PHP_METHOD(redis_connect_pool, release) {//todo 提出来 - zend_rsrc_list_entry *p_sock_le; - zval **pool_port; - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("pool_port"), (void **) &pool_port) == SUCCESS) { - char str[100] = {0}; - CON_FORMART_KEY(str, (int) Z_LVAL_PP(pool_port)); - if (zend_hash_find(&EG(persistent_list), str, strlen(str), (void **) &p_sock_le) == SUCCESS) { - send_oob2proxy(p_sock_le); - } - } +PHP_METHOD(redis_connect_pool, release) +{ + + release_worker(getThis()); CP_CHECK_RETURN(1); } -PHP_METHOD(redis_connect_pool, __destruct) { - +PHP_METHOD(redis_connect_pool, close) +{ + close_conn(getThis()); + CP_CHECK_RETURN(1); } -PHP_METHOD(redis_connect_pool, __construct) { - zval *zres, *pool_port; - MAKE_STD_ZVAL(zres); - cpClient *cli; - zend_rsrc_list_entry *p_sock_le; - int port = 9057; - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &port)) { - return; +PHP_METHOD(redis_connect_pool, setAsync) +{ + int async = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l!", &async) == FAILURE) + { + RETURN_FALSE; } - char str[100] = {0}; - CON_FORMART_KEY(str, port); + zend_update_property_bool(redis_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("async"), async TSRMLS_CC); + CP_CHECK_RETURN(1); +} - if (zend_hash_find(&EG(persistent_list), str, strlen(str), (void **) &p_sock_le) == SUCCESS) { - cli = (cpClient*) p_sock_le->ptr; - ZEND_REGISTER_RESOURCE(zres, cli, le_cli_connect_pool); - } else {//这个fpm进程第一次创建连接 - if (connect_pool_perisent(&cli, zres, port) < 0) {//没连上 - efree(zres); - zval exception_str; - ZVAL_STRINGL(&exception_str, CON_FAIL_MESSAGE, strlen(CON_FAIL_MESSAGE), 0); - zend_throw_exception(NULL, Z_STRVAL(exception_str), 0 TSRMLS_CC); - return; - } - } - // cpQueueSignalSet(CP_SIG_EVENT, HandleRecv); - MAKE_STD_ZVAL(pool_port); - ZVAL_LONG(pool_port, port); - zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("cli"), zres TSRMLS_CC); - zend_update_property(pdo_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("pool_port"), pool_port TSRMLS_CC); - zval_ptr_dtor(&zres); - zval_ptr_dtor(&pool_port); +PHP_METHOD(redis_connect_pool, __destruct) +{ +} + +PHP_METHOD(redis_connect_pool, __construct) +{ + CP_GET_PID; } -PHP_FUNCTION(redis_connect) { +PHP_METHOD(redis_connect_pool, connect) +{ char *ip; - int ip_len; - char *port; - int port_len = 0; + zend_size_t ip_len; + char *port = "6379"; + zend_size_t port_len = 0; char *time; - int time_len = 0; - zval *object = getThis(); - zval *zval_ip; - zval *zval_port; + zend_size_t time_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!|s!s!", &ip, &ip_len, &port, &port_len, &time, &time_len) == FAILURE) + { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!|s!s!", &ip, &ip_len, &port, &port_len, &time, &time_len) == FAILURE) { RETURN_FALSE; } - // convert_to_string(*port); - MAKE_STD_ZVAL(zval_ip); - ZVAL_STRING(zval_ip, ip, 1); + //临时标示这个连接的真实目标,根据下一步是select还是get来确定db号,可以減少一次select操作 + zend_update_property_string(redis_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("ip"), ip TSRMLS_CC); + zend_update_property_string(redis_connect_pool_class_entry_ptr, getThis(), ZEND_STRL("port"), port TSRMLS_CC); + + RETURN_TRUE; +} + +static cpClient * cpRedis_conn_pool_server(zval *obj, char *source_char, int async) +{ + zval *zres; + cpClient *cli; + if (cp_zend_hash_find(Z_OBJPROP_P(obj), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) + { + CP_ZEND_FETCH_RESOURCE_NO_RETURN(cli, cpClient*, &zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); + } + else + {//connect local proxy + + zval z_data_source; + CP_ZVAL_STRING(&z_data_source, source_char, 0); + zval *tmp = cpConnect_pool_server(&z_data_source, async); + CP_ZEND_FETCH_RESOURCE_NO_RETURN(cli, cpClient*, &tmp, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); + if (async) + {//todo check cannot exchange sync/async + cli->async = async; + cli->dummy_source_index = dummy_source_index - 1; + } - MAKE_STD_ZVAL(zval_port); - if (port_len > 0) { - ZVAL_STRING(zval_port, port, 1); - } else { - ZVAL_STRING(zval_port, "6379", 1); + zend_update_property(redis_connect_pool_class_entry_ptr, obj, ZEND_STRL("cli"), tmp TSRMLS_CC); } + return cli; +} - zend_update_property(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("ip"), zval_ip TSRMLS_CC); //临时标示这个连接的真实目标,根据下一步是select还是get来确定db号,可以減少一次select操作 - zend_update_property(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("port"), zval_port TSRMLS_CC); +PHP_METHOD(redis_connect_pool, auth) +{ + char *auth; + zend_size_t auth_len; + zval *object; + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os!", &object, redis_connect_pool_class_entry_ptr, &auth, &auth_len) == FAILURE) + { + RETURN_FALSE; + } - zval_ptr_dtor(&zval_port); - zval_ptr_dtor(&zval_ip); + zend_update_property_string(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("auth"), auth TSRMLS_CC); - RETURN_TRUE; } -PHP_METHOD(redis_connect_pool, select) { - zval *pass_data; - zval *object; - zval **zres, *source_zval, **ip, **port, *zval_db, *z_args; - char source_char[100] = {0}; +PHP_METHOD(redis_connect_pool, select) +{ + zval *ip, *port, *z_args, *pass_data, *object, *auth; + char source_char[CP_SOURCE_MAX] = {0}; char *db; - int db_len; + zend_size_t db_len; + cpClient *cli; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os!", &object, redis_connect_pool_class_entry_ptr, &db, &db_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os!", &object, redis_connect_pool_class_entry_ptr, &db, &db_len) == FAILURE) + { RETURN_FALSE; } - if (zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("ip"), (void **) &ip) == SUCCESS) { - strcat(source_char, Z_STRVAL_PP(ip)); + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("ip"), (void **) &ip) == SUCCESS) + { + strcat(source_char, Z_STRVAL_P(ip)); strcat(source_char, ":"); - } else { - zend_error(E_ERROR, "redis_connect_pool: IP is empty "); + } + else + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "redis_connect_pool: IP is empty "); RETURN_FALSE; } - if (zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("port"), (void **) &port) == SUCCESS) { - strcat(source_char, Z_STRVAL_PP(port)); + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("port"), (void **) &port) == SUCCESS) + { + strcat(source_char, Z_STRVAL_P(port)); strcat(source_char, ":"); - } else { - zend_error(E_ERROR, "redis_connect_pool: PORT is empty"); + } + else + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "redis_connect_pool: PORT is empty"); RETURN_FALSE; } - MAKE_STD_ZVAL(zval_db); - ZVAL_STRING(zval_db, db, 1); - zend_update_property(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("db"), zval_db TSRMLS_CC); + zend_update_property_string(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("db"), db TSRMLS_CC); strcat(source_char, db); - MAKE_STD_ZVAL(source_zval); - ZVAL_STRING(source_zval, source_char, 1); - zend_update_property(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("data_source"), source_zval TSRMLS_CC); //确定数据源 - zval_ptr_dtor(&zval_db); - zval_ptr_dtor(&source_zval); + zend_update_property_string(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("data_source"), source_char TSRMLS_CC); //确定数据源 - cpClient *cli; - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) { - ZEND_FETCH_RESOURCE(cli, cpClient*, zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); - } else { - zend_error(E_WARNING, "redis_connect_pool: object is not instanceof redis_connect_pool. "); - RETURN_FALSE; - } + cli = cpRedis_conn_pool_server(getThis(), source_char, 0); - MAKE_STD_ZVAL(pass_data); + CP_MAKE_STD_ZVAL(pass_data); array_init(pass_data); - add_assoc_string(pass_data, "type", "redis", 1); - add_assoc_string(pass_data, "method", "select", 1); - add_assoc_string(pass_data, "data_source", source_char, 1); + cp_add_assoc_string(pass_data, "type", "redis", 1); + cp_add_assoc_string(pass_data, "method", "select", 1); + cp_add_assoc_string(pass_data, "data_source", source_char, 1); + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("auth"), (void **) &auth) == SUCCESS) + { + cp_add_assoc_string(pass_data, "auth", Z_STRVAL_P(auth), 1); + } - MAKE_STD_ZVAL(z_args); + CP_MAKE_STD_ZVAL(z_args); array_init(z_args); - add_assoc_string(z_args, "db", db, 1); + cp_add_assoc_string(z_args, "db", db, 1); add_assoc_zval(pass_data, "args", z_args); - int ret = cli_real_send(cli, pass_data); - if (ret < 0) { - zend_error(E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); + int ret = cli_real_send(&cli, pass_data); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); } - cli_real_recv(); - if (RecvData.type == CP_SIGEVENT_EXCEPTION) { + cli_real_recv(cli, 0); + if (RecvData.type == CP_SIGEVENT_EXCEPTION) + { + release_worker(getThis()); zend_throw_exception(NULL, Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); RETVAL_BOOL(0); - } else { + } + else + { + RETVAL_ZVAL(RecvData.ret_value, 0, 1); } - zval_ptr_dtor(&pass_data); + cp_zval_ptr_dtor(&pass_data); } -PHP_METHOD(redis_connect_pool, __call) { - zval *z_args; - zval *pass_data; - zval *object; - zval **zres, **source_zval; - char source_char[100] = {0}; - char *cmd; - int cmd_len; +PHP_METHOD(redis_connect_pool, done) +{ + async_done(getThis(), redis_connect_pool_class_entry_ptr); + if (RecvData.type == CP_SIGEVENT_EXCEPTION) + { + release_worker(getThis()); + zend_throw_exception(NULL, Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); + RETVAL_BOOL(0); + } + else + { + RETVAL_ZVAL(RecvData.ret_value, 0, 1); //no copy destroy + } +} - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, redis_connect_pool_class_entry_ptr, &cmd, &cmd_len, &z_args) == FAILURE) { - RETURN_FALSE; +PHP_METHOD(pdo_connect_pool, done) +{ + async_done(getThis(), pdo_connect_pool_class_entry_ptr); + if (RecvData.type == CP_SIGEVENT_PDO) + { + zval *zres, *source_zval; + cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres); + cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("data_source"), (void **) &source_zval); + object_init_ex(return_value, pdo_connect_pool_PDOStatement_class_entry_ptr); + zend_update_property(pdo_connect_pool_PDOStatement_class_entry_ptr, return_value, ZEND_STRL("cli"), zres TSRMLS_CC); + zend_update_property(pdo_connect_pool_PDOStatement_class_entry_ptr, return_value, ZEND_STRL("data_source"), source_zval TSRMLS_CC); //标示这个连接的真实目标 + cp_zval_ptr_dtor(&RecvData.ret_value); + } + else if (RecvData.type == CP_SIGEVENT_EXCEPTION) + { + release_worker(getThis()); + zend_throw_exception(NULL, Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); + RETVAL_BOOL(0); + } + else + { + RETVAL_ZVAL(RecvData.ret_value, 0, 1); + } +} + +PHP_METHOD(pdo_connect_pool_PDOStatement, done) +{ + async_done(getThis(), pdo_connect_pool_PDOStatement_class_entry_ptr); + if (RecvData.type == CP_SIGEVENT_EXCEPTION) + { + release_worker(getThis()); + zend_throw_exception(NULL, Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); + RETVAL_BOOL(0); + } + else if (RecvData.type == CP_SIGEVENT_STMT_OBJ) + {//fetchObject return !!!!!cos the user class name ,now just support default class + convert_to_object(RecvData.ret_value); +#if PHP_MAJOR_VERSION == 7 + ZVAL_OBJ(return_value, Z_OBJ_P(RecvData.ret_value)); +#else + ZVAL_DUP(return_value, RecvData.ret_value); +#endif } + else + { + RETVAL_ZVAL(RecvData.ret_value, 0, 1); + } +} +PHP_METHOD(redis_connect_pool, __call) +{ + zval *z_args, *object, *zres, *source_zval, *pass_data, *async_zval, *auth; + char source_char[CP_SOURCE_MAX] = {0}; + char *cmd; + zend_size_t cmd_len; cpClient *cli; - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) { - ZEND_FETCH_RESOURCE(cli, cpClient*, zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); - } else { - zend_error(E_WARNING, "redis_connect_pool: object is not instanceof redis_connect_pool. "); + int async = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osa", &object, redis_connect_pool_class_entry_ptr, &cmd, &cmd_len, &z_args) == FAILURE) + { RETURN_FALSE; } - - zval_add_ref(&z_args); - MAKE_STD_ZVAL(pass_data); + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("async"), (void **) &async_zval) == SUCCESS) + { + async = Z_BVAL_P(async_zval); + } + cp_zval_add_ref(&z_args); + CP_MAKE_STD_ZVAL(pass_data); array_init(pass_data); - add_assoc_string(pass_data, "method", cmd, 1); - add_assoc_string(pass_data, "type", "redis", 1); + cp_add_assoc_string(pass_data, "method", cmd, 1); + cp_add_assoc_string(pass_data, "type", "redis", 1); + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("auth"), (void **) &auth) == SUCCESS) + { + cp_add_assoc_string(pass_data, "auth", Z_STRVAL_P(auth), 1); + } add_assoc_zval(pass_data, "args", z_args); - if (zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("data_source"), (void **) &source_zval) == SUCCESS) { - add_assoc_string(pass_data, "data_source", Z_STRVAL_PP(source_zval), 1); - } else {//沒select 走db0 - zval **ip; - if (zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("ip"), (void **) &ip) == SUCCESS) { - strcat(source_char, Z_STRVAL_PP(ip)); + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("data_source"), (void **) &source_zval) == SUCCESS) + { + cp_add_assoc_string(pass_data, "data_source", Z_STRVAL_P(source_zval), 1); + if (cp_zend_hash_find(Z_OBJPROP_P(getThis()), ZEND_STRS("cli"), (void **) &zres) == SUCCESS) + { + CP_ZEND_FETCH_RESOURCE_NO_RETURN(cli, cpClient*, &zres, -1, CP_RES_CLIENT_NAME, le_cli_connect_pool); + } + else + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "redis_connect_pool: object is not instanceof redis_connect_pool. "); + RETURN_FALSE; + } + } + else + { + zval *ip, *port; + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("ip"), (void **) &ip) == SUCCESS) + { + strcat(source_char, Z_STRVAL_P(ip)); strcat(source_char, ":"); - } else { - zend_error(E_ERROR, "redis_connect_pool: IP is empty "); + } + else + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "redis_connect_pool: IP is empty "); RETURN_FALSE; } - zval **port; - if (zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("port"), (void **) &port) == SUCCESS) { - strcat(source_char, Z_STRVAL_PP(port)); + if (cp_zend_hash_find(Z_OBJPROP_P(object), ZEND_STRS("port"), (void **) &port) == SUCCESS) + { + strcat(source_char, Z_STRVAL_P(port)); strcat(source_char, ":"); - } else { - zend_error(E_ERROR, "redis_connect_pool: PORT is empty"); + } + else + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "redis_connect_pool: PORT is empty"); RETURN_FALSE; } strcat(source_char, "0"); - zval *definitely_source; - MAKE_STD_ZVAL(definitely_source); - ZVAL_STRING(definitely_source, source_char, 1); - zend_update_property(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("data_source"), definitely_source TSRMLS_CC); //确定数据源 - zval_ptr_dtor(&definitely_source); - add_assoc_string(pass_data, "data_source", source_char, 1); + zend_update_property_string(redis_connect_pool_class_entry_ptr, object, ZEND_STRL("data_source"), source_char TSRMLS_CC); //确定数据源 + cp_add_assoc_string(pass_data, "data_source", source_char, 1); + cli = cpRedis_conn_pool_server(getThis(), source_char, async); } - - int ret = cli_real_send(cli, pass_data); - if (ret < 0) { - zend_error(E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); + if (async && cli->querying) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "the obj is async querying now, you can not execute async function again"); + } + int ret = cli_real_send(&cli, pass_data); + if (ret < 0) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "cli_real_send faild error Error: %s [%d] ", strerror(errno), errno); } - cli_real_recv(); - if (RecvData.type == CP_SIGEVENT_EXCEPTION) { + cli_real_recv(cli, async); + if (RecvData.type == CP_SIGEVENT_EXCEPTION) + { + release_worker(getThis()); zend_throw_exception(NULL, Z_STRVAL_P(RecvData.ret_value), 0 TSRMLS_CC); RETVAL_BOOL(0); - } else { + } + else + { RETVAL_ZVAL(RecvData.ret_value, 0, 1); //no copy destroy } - zval_ptr_dtor(&pass_data); + cp_zval_ptr_dtor(&pass_data); } +PHP_FUNCTION(pool_server_status) +{ + long pid; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pid) == FAILURE || pid == 0) + { + php_printf("server is not running. \n"); + return; + } + + if (kill(pid, 0) < 0) + { + php_printf("server with pid %d has run away unexpectedly. \n", pid); + return; + } + else + { + cpClient_attach_mem(); + php_printf("\nserver with pid %d is running well. \n", pid); + + cpGroup *G = NULL; + int group_id, queue_num; + php_printf("\ngroup number: %d\n", CPGS->group_num); + for (group_id = 0; group_id < CPGS->group_num; group_id++) + { + queue_num = 0; + G = &CPGS->G[group_id]; + php_printf("\ngroup %d: %s\n", group_id, G->name); + + int current_fd = G->first_wait_id; + while (current_fd) + { + cpConnection* conn = &(CPGS->conlist[current_fd]); + current_fd = conn->next_wait_id; + queue_num++; + } + + php_printf("have used %d,the max conn num is %d, the min num is %d,the queue len is %d\n", G->worker_num, G->worker_max, G->worker_min, queue_num); + + } + + } + + +} -//PHP_METHOD(pdo_connect_pool, quote) -//{//todo -// char *str; -// int str_len; -// long paramtype = PDO_PARAM_STR; -// char *qstr; -// int qlen; -// -// if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, ¶mtype)) { -// RETURN_FALSE; -// } -// -// RETURN_FALSE; -//} diff --git a/cpClientNet.c b/cpClientNet.c old mode 100755 new mode 100644 index 3fee6c7..b166d36 --- a/cpClientNet.c +++ b/cpClientNet.c @@ -16,17 +16,23 @@ #include "php_connect_pool.h" #include -int cpClient_close(cpClient *cli) { +static int client_open_log = 0; + +int cpClient_close(cpClient *cli) +{ int ret, fd = cli->sock; cli->sock = 0; ret = close(fd); - if (ret < 0) { + if (ret < 0) + { cpLog("client close fail. Error: %s[%d]", strerror(errno), errno); } + // CPGS = NULL; return ret; } -int cpClient_recv(cpClient *cli, void *data, int len, int waitall) { +int cpClient_recv(int sock, void *data, int len, int waitall) +{ // int flag = 0, ret; // if (waitall == 1) { // flag = MSG_WAITALL; @@ -41,10 +47,11 @@ int cpClient_recv(cpClient *cli, void *data, int len, int waitall) { // return SUCCESS; // } // } - return cpNetRead(cli->sock, data, len); + return cpNetRead(sock, data, len); } -int cpClient_send(int sock, char *data, int length, int flag) { +int cpClient_send(int sock, char *data, int length, int flag) +{ int written = 0; int n; @@ -52,17 +59,23 @@ int cpClient_send(int sock, char *data, int length, int flag) { assert(data != NULL); //总超时,for循环中计时 - while (written < length) { + while (written < length) + { n = send(sock, data, length - written, flag); - if (n < 0) { + if (n < 0) + { //中断 - if (errno == EINTR) { + if (errno == EINTR) + { continue; }//让出 - else if (errno == EAGAIN) { + else if (errno == EAGAIN) + { usleep(1); continue; - } else { + } + else + { return SUCCESS; } } @@ -72,31 +85,39 @@ int cpClient_send(int sock, char *data, int length, int flag) { return written; } -int cpClient_create(cpClient *cli) { +int cpClient_create(cpClient *cli) +{ bzero(cli, sizeof (cpClient)); cli->sock = socket(AF_INET, SOCK_STREAM, 0); int flag = 1; setsockopt(cli->sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof (flag)); - if (cli->sock < 0) { + if (cli->sock < 0) + { return FAILURE; } return SUCCESS; } -static int swClient_inet_addr(struct sockaddr_in *sin, char *string) { +static int swClient_inet_addr(struct sockaddr_in *sin, char *string) +{ struct in_addr tmp; struct hostent *host_entry; - if (inet_aton(string, &tmp)) { + if (inet_aton(string, &tmp)) + { sin->sin_addr.s_addr = tmp.s_addr; - } else { - if (!(host_entry = gethostbyname(string))) { + } + else + { + if (!(host_entry = gethostbyname(string))) + { cpLog("Host lookup failed. Error: %s[%d] ", strerror(errno), errno); return SUCCESS; } - if (host_entry->h_addrtype != AF_INET) { + if (host_entry->h_addrtype != AF_INET) + { cpLog("Host lookup failed: Non AF_INET domain returned on AF_INET socket"); return 0; } @@ -105,28 +126,28 @@ static int swClient_inet_addr(struct sockaddr_in *sin, char *string) { return SUCCESS; } -int cpClient_connect(cpClient *cli, char *host, int port, double timeout, int nonblock) { +int cpClient_connect(cpClient *cli, char *host, int port, double timeout) +{ int ret; cli->serv_addr.sin_family = AF_INET; cli->serv_addr.sin_port = htons(port); - if (swClient_inet_addr(&cli->serv_addr, host) < 0) { + if (swClient_inet_addr(&cli->serv_addr, host) < 0) + { return SUCCESS; } cli->timeout = timeout; - - if (nonblock == 1) { - swSetNonBlock(cli->sock); - } else { - cpSetTimeout(cli->sock, timeout); - } + cpSetTimeout(cli->sock, timeout); // int count = 0; - while (1) { + while (1) + { ret = connect(cli->sock, (struct sockaddr *) (&cli->serv_addr), sizeof (cli->serv_addr)); - if (ret < 0) { - if (errno == EINTR) { + if (ret < 0) + { + if (errno == EINTR) + { continue; } // if (++count <= 15) {//防止重启代理导致的con refused @@ -136,8 +157,103 @@ int cpClient_connect(cpClient *cli, char *host, int port, double timeout, int no } break; } - if (ret >= 0) { - cli->released = CP_FD_RELEASED; - } return ret; -} \ No newline at end of file +} + +static void log_process(zval *send_data, smart_str *buffer) +{ + zval *args = NULL, *method = NULL, *first = NULL; + cp_zend_hash_find(Z_ARRVAL_P(send_data), ZEND_STRS("method"), (void **) &method); + // snprintf(buffer + strlen(buffer), CLIENT_LOG_BUFFER, "\nmethod:%s\n", Z_STRVAL_P(method)); + smart_str_appendl(buffer, "\nmethod:", 8); + smart_str_appendl(buffer, Z_STRVAL_P(method), Z_STRLEN_P(method)); + cp_zend_hash_find(Z_ARRVAL_P(send_data), ZEND_STRS("args"), (void **) &args); + if (cp_zend_hash_index_find(Z_ARRVAL_P(args), 0, (void**) &first) == SUCCESS) + { + if (Z_TYPE_P(first) == IS_STRING) + { + smart_str_appendl(buffer, "\nfirst arg:", 11); + smart_str_appendl(buffer, Z_STRVAL_P(first), Z_STRLEN_P(first)); + } + } +} + +void log_write(zval *send_data, cpClient* cli) +{ + if (CPGS->max_hold_time_to_log) + { + log_process(send_data, &cli->slow_log_tmp); + } + if (CPGS->max_data_size_to_log) + { + log_process(send_data, &cli->big_data_tmp); + } +} + +void log_start(cpClient* cli) +{ + if (CPGS->max_hold_time_to_log) + { + gettimeofday(&cli->log_start, NULL); + smart_str_appendl(&cli->slow_log_tmp, MAX_HOLD_START_STR, sizeof (MAX_HOLD_START_STR) - 1); + } + if (CPGS->max_data_size_to_log) + { + cli->current_len = 0; + smart_str_appendl(&cli->big_data_tmp, MAX_DATA_START_STR, sizeof (MAX_DATA_START_STR) - 1); + } +} + +void log_end(cpClient* cli) +{ + if (CPGS->max_hold_time_to_log) + { + static struct timeval log_end; + gettimeofday(&log_end, NULL); + int ms = 1000 * (log_end.tv_sec - cli->log_start.tv_sec) + (log_end.tv_usec - cli->log_start.tv_usec) / 1000; + if (CPGS->max_hold_time_to_log <= ms) + { + if (!client_open_log) + { + client_open_log = 1; + cpLog_init(CPGS->log_file); + } + smart_str_appendl(&cli->slow_log_tmp, MAX_HOLD_END_STR, sizeof (MAX_HOLD_END_STR) - 1); + smart_str_0(&cli->slow_log_tmp); +#if PHP_MAJOR_VERSION < 7 + cpLog("%s\n\n", cli->slow_log_tmp.c); +#else + cpLog("%s\n\n", ZSTR_VAL(cli->slow_log_tmp.s)); +#endif + } + smart_str_free(&cli->slow_log_tmp); + } + if (CPGS->max_data_size_to_log && CPGS->max_data_size_to_log <= cli->current_len) + { + if (!client_open_log) + { + client_open_log = 1; + cpLog_init(CPGS->log_file); + } + smart_str_appendl(&cli->big_data_tmp, MAX_DATA_END_STR, sizeof (MAX_DATA_END_STR) - 1); + smart_str_0(&cli->big_data_tmp); +#if PHP_MAJOR_VERSION < 7 + cpLog("%s\n\n", cli->big_data_tmp.c); +#else + cpLog("%s\n\n", ZSTR_VAL(cli->big_data_tmp.s)); +#endif + } + if (CPGS->max_data_size_to_log) + { + smart_str_free(&cli->big_data_tmp); + } + +} + +void log_increase_size(int size, cpClient* cli) +{ + if (CPGS->max_data_size_to_log) + { + cli->current_len += size; + } +} diff --git a/cpFunction.c b/cpFunction.c old mode 100755 new mode 100644 index b1bd687..07d4e88 --- a/cpFunction.c +++ b/cpFunction.c @@ -16,66 +16,78 @@ #include "php_connect_pool.h" - static char bufr[SW_LOG_BUFFER_SIZE]; static char bufpid[SW_PID_BUFFER_SIZE]; FILE *pid_fn = NULL; int cp_error; FILE *cp_log_fn = NULL; -char *cpArgv0 = NULL; -int cpLog_init(char *logfile) { +int cpLog_init(char *logfile) +{ cp_log_fn = fopen(logfile, "a+"); - if (cp_log_fn == NULL) { + if (cp_log_fn == NULL) + { return FAILURE; } - if (setvbuf(cp_log_fn, bufr, _IOLBF, SW_LOG_BUFFER_SIZE) < 0) { + if (setvbuf(cp_log_fn, bufr, _IOLBF, SW_LOG_BUFFER_SIZE) < 0) + { return FAILURE; } return SUCCESS; } -int pid_init() { +int pid_init() +{ char pid_name[512] = {0}; - sprintf(pid_name, "%s%s.pid", PID_FILE_PATH, CPGC.title); + sprintf(pid_name, "%s.pid", PID_FILE_PATH); pid_fn = fopen(pid_name, "w+"); - if (pid_fn == NULL) { + if (pid_fn == NULL) + { cpLog("create pid file error"); return FAILURE; } - if (setvbuf(pid_fn, bufpid, _IONBF, SW_PID_BUFFER_SIZE) < 0) { + if (setvbuf(pid_fn, bufpid, _IONBF, SW_PID_BUFFER_SIZE) < 0) + { return FAILURE; } return SUCCESS; } -int set_pid(int pid) { +int set_pid(int pid) +{ fprintf(pid_fn, "%d\n", pid); return SUCCESS; } -void swLog_free(void) { - fclose(cp_log_fn); -} - - -CPINLINE int cpWrite(int fd, void *buf, int count) { +int cpWrite(int fd, void *buf, int count) +{ int nwritten = 0, totlen = 0; - while (totlen != count) { + while (totlen != count) + { nwritten = write(fd, buf, count - totlen); - if (nwritten > 0) { + if (nwritten > 0) + { totlen += nwritten; buf += nwritten; - } else if (nwritten == 0) { + } + else if (nwritten == 0) + { return totlen; - } else { - if (errno == EINTR) { + } + else + { + if (errno == EINTR) + { continue; - } else if (errno == EAGAIN) { + } + else if (errno == EAGAIN) + { usleep(1); continue; - } else { + } + else + { return -1; } } @@ -84,122 +96,171 @@ CPINLINE int cpWrite(int fd, void *buf, int count) { return totlen; } -CPINLINE int cpFifoRead(int pipe_fd_read, void *buf, int len) { +int cpFifoRead(int pipe_fd_read, void *buf, int len) +{ int n, total = 0; - do { + do + { n = read(pipe_fd_read, buf + total, len); - if (n > 0) { + if (n > 0) + { total += n; - if (total == len) { + if (total == len) + { break; } } -// else { -// cpLog("worker fifo recive error %d,len %d\n", errno, n); -// } + else if (errno == EAGAIN) + {//for async "If no process has the pipe open for writing, read() will return 0 to indicate end-of-file" + // printf("worker fifo recive error %d,len %d\n", errno, n); + usleep(1); + } + else + { + // printf("worker fifo recive error2 %d,len %d\n", errno, n); + } } while ((n < 0 && errno == EINTR) || n > 0); return total; } -CPINLINE int cpNetRead(int fd, void *buf, int len) { +int cpNetRead(int fd, void *buf, int len) +{ int n, total = 0; - do { + do + { n = recv(fd, buf + total, len, MSG_WAITALL); - if (n > 0) { + if (n > 0) + { total += n; - if (total == len) { + if (total == len) + { break; } - } else if (n == 0) { + } + else if (n == 0) + { return 0; - } -// else { -// cpLog("worker recive error %d,len %d\n", errno, n); -// } + } + // else { + // cpLog("worker recive error %d,len %d,%d\n", errno, n,fd); + // } } while ((n < 0 && errno == EINTR) || n > 0); return total; } -void cpSettitle(char *title) { - - assert(MAX_TITLE_LENGTH > strlen(title) + 5); - - int tlen = strlen(title); - char buffer[MAX_TITLE_LENGTH]; - - memset(buffer, 0, MAX_TITLE_LENGTH); - if (tlen >= (MAX_TITLE_LENGTH - 1)) tlen = (MAX_TITLE_LENGTH - 1); - memcpy(buffer, title, tlen); - snprintf(cpArgv0, MAX_TITLE_LENGTH, "pool_%s", buffer); -} - -//将套接字设置为非阻塞方式 +void cpSettitle(char *title_name) +{ -CPINLINE void swSetNonBlock(int sock) { - int opts, ret; - do { - opts = fcntl(sock, F_GETFL); - } while (opts < 0 && errno == EINTR); - if (opts < 0) { - cpLog("fcntl(sock,GETFL) fail"); + // assert(MAX_TITLE_LENGTH > strlen(title) + 5); + int len = 0; + char name[MAX_TITLE_LENGTH + 5] = {0}; + strcat(name, "pool_"); + if (strlen(title_name) >= MAX_TITLE_LENGTH) + { + len = MAX_TITLE_LENGTH; } - opts = opts | O_NONBLOCK; - do { - ret = fcntl(sock, F_SETFL, opts); - } while (ret < 0 && errno == EINTR); - if (ret < 0) { - cpLog("fcntl(sock,SETFL,opts) fail"); + else + { + len = strlen(title_name); + } + memcpy(name + 5, title_name, len); + zval *zname; + CP_MAKE_STD_ZVAL(zname); + CP_ZVAL_STRING(zname, name, 0); + +#if PHP_MAJOR_VERSION >= 7 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 4) + zval *retval; + zval **args[1]; + args[0] = &zname; + + zval *function; + CP_MAKE_STD_ZVAL(function); + CP_ZVAL_STRING(function, "cli_set_process_title", 1); + + if (cp_call_user_function_ex(EG(function_table), NULL, function, &retval, 1, args, 0, NULL TSRMLS_CC) == FAILURE) + { + return; + } + cp_zval_ptr_dtor(&function); + if (retval) + { + cp_zval_ptr_dtor(&retval); } +#else + bzero(sapi_module.executable_location, 127); + memcpy(sapi_module.executable_location, Z_STRVAL_P(zname), Z_STRLEN_P(zname)); +#endif } -CPINLINE void swSetBlock(int sock) { +void cpSetIsBlock(int sock, int block) +{ int opts, ret; - do { + do + { opts = fcntl(sock, F_GETFL); } while (opts < 0 && errno == EINTR); - if (opts < 0) { - cpLog("fcntl(sock,GETFL) fail"); + if (opts < 0) + { + cpLog("fcntl(%d, GETFL) failed.", sock); + } + + if (!block) + { + opts = opts | O_NONBLOCK; } - opts = opts & ~O_NONBLOCK; - do { + else + { + opts = opts & ~O_NONBLOCK; + } + + do + { ret = fcntl(sock, F_SETFL, opts); } while (ret < 0 && errno == EINTR); - if (ret < 0) { - cpLog("fcntl(sock,SETFL,opts) fail"); + + if (ret < 0) + { + cpLog("fcntl(%d, SETFL, opts) failed.", sock); } } - -CPINLINE int cpSetTimeout(int sock, double timeout) { +int cpSetTimeout(int sock, double timeout) +{ int ret; struct timeval timeo; timeo.tv_sec = (int) timeout; timeo.tv_usec = (int) ((timeout - timeo.tv_sec) * 1000 * 1000); ret = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *) &timeo, sizeof (timeo)); - if (ret < 0) { + if (ret < 0) + { return FAILURE; } ret = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *) &timeo, sizeof (timeo)); - if (ret < 0) { + if (ret < 0) + { return FAILURE; } return SUCCESS; } -CPINLINE int cpCreateFifo(char *file) { +int cpCreateFifo(char *file) +{ int pipe_fd; int res; umask(0); - if (access(file, F_OK) == -1) { + if (access(file, F_OK) == -1) + { res = mkfifo(file, 0666); - if (res != 0) { + if (res != 0 && errno != EEXIST) + {//&&避免 worker和client一起创建导致的exist错误 cpLog("Could not create fifo %s Error: %s[%d]", file, strerror(errno), errno); return -1; } } pipe_fd = open(file, CP_PIPE_MOD); - if (pipe_fd == -1) { + if (pipe_fd == -1) + { cpLog("Could not open fifo %s Error: %s[%d]", file, strerror(errno), errno); return -1; } @@ -209,59 +270,131 @@ CPINLINE int cpCreateFifo(char *file) { /** * clear all singal */ -void swSingalNone() { +void swSingalNone() +{ sigset_t mask; sigfillset(&mask); int ret = pthread_sigmask(SIG_BLOCK, &mask, NULL); - if (ret < 0) { + if (ret < 0) + { cpLog("pthread_sigmask fail: %s", strerror(ret)); } } -zval * cpGetConfig(char *filename) { - zval fun_name, **args[2], *retval, *file, *section; - ZVAL_STRING(&fun_name, "parse_ini_file", 0); +//zval * cpMD5(zval *arr) +//{//pass in array , out md5 zval +// smart_str ser_data = {0}; +// cp_serialize(&ser_data, arr); +// +// zval fun_name, **args[1], *retval, *str; +// CP_ZVAL_STRING(&fun_name, "md5", 0); +// +// CP_MAKE_STD_ZVAL(str); +//#if PHP_MAJOR_VERSION < 7 +// CP_ZVAL_STRINGL(str, ser_data.c, ser_data.len, 1); +//#else +// zend_string *str_data = ser_data.s; +// CP_ZVAL_STRINGL(str, str_data->val, str_data->len, 1); +//#endif +// args[0] = &str; +// +// if (cp_call_user_function_ex(CG(function_table), NULL, &fun_name, &retval, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) +// { +// cp_zval_ptr_dtor(&str); +// smart_str_free(&ser_data); +// return NULL; +// } +// cp_zval_ptr_dtor(&str); +// smart_str_free(&ser_data); +// return retval; +//} + +void cp_serialize(smart_str *ser_data, zval *array) +{ + php_serialize_data_t var_hash; + PHP_VAR_SERIALIZE_INIT(var_hash); +#if PHP_MAJOR_VERSION < 7 + php_var_serialize(ser_data, &array, &var_hash TSRMLS_CC); +#else + php_var_serialize(ser_data, array, &var_hash TSRMLS_CC); +#endif + PHP_VAR_SERIALIZE_DESTROY(var_hash); - MAKE_STD_ZVAL(file); - ZVAL_STRING(file, filename, 1); - MAKE_STD_ZVAL(section); - ZVAL_BOOL(section, 1); - args[0] = &file; - args[1] = §ion; + // gettimeofday(&end, NULL); + // int timeuse = 1000000 * (end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec; + // printf("ser time: %d us\n", timeuse); +} - if (call_user_function_ex(CG(function_table), NULL, &fun_name, &retval, 2, args, 0, NULL TSRMLS_CC) != SUCCESS) { - zval_ptr_dtor(&file); - zval_ptr_dtor(§ion); - return NULL; +zval * cp_unserialize(char *data, int len) +{ + zval *unser_value; + CP_ALLOC_INIT_ZVAL(unser_value); + php_unserialize_data_t var_hash; + PHP_VAR_UNSERIALIZE_INIT(var_hash); + if (cp_php_var_unserialize(&unser_value, (const unsigned char **) &data, (unsigned char *) data + len - 1, &var_hash TSRMLS_CC) != 1) + { + // php_error_docref(NULL TSRMLS_CC, E_NOTICE, "unser data is corrupted"); } - zval_ptr_dtor(&file); - zval_ptr_dtor(§ion); - return retval; + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + return unser_value; +} + +void cp_ser_and_setpro(zval *arr) +{ + smart_str ser_data = {0}; + cp_serialize(&ser_data, arr); +#if PHP_MAJOR_VERSION < 7 + memcpy(CPGL.ping_mem_addr + CP_PING_MD5_LEN + CP_PING_PID_LEN + CP_PING_DIS_LEN, ser_data.c, ser_data.len); +#else + zend_string *str = ser_data.s; + memcpy(CPGL.ping_mem_addr + CP_PING_MD5_LEN + CP_PING_PID_LEN + CP_PING_DIS_LEN, str->val, str->len); +#endif + smart_str_free(&ser_data); } -cpSignalFunc cpSignalSet(int sig, cpSignalFunc func, int restart, int mask) { +void cp_ser_and_setdis(zval *arr) +{ + smart_str ser_data = {0}; + cp_serialize(&ser_data, arr); +#if PHP_MAJOR_VERSION < 7 + memcpy(CPGL.ping_mem_addr + CP_PING_MD5_LEN + CP_PING_PID_LEN, ser_data.c, ser_data.len); +#else + zend_string *str = ser_data.s; + memcpy(CPGL.ping_mem_addr + CP_PING_MD5_LEN + CP_PING_PID_LEN, str->val, str->len); +#endif + smart_str_free(&ser_data); +} + +cpSignalFunc cpSignalSet(int sig, cpSignalFunc func, int restart, int mask) +{ struct sigaction act, oact; act.sa_handler = func; - if (mask) { + if (mask) + { sigfillset(&act.sa_mask); - } else { + } + else + { sigemptyset(&act.sa_mask); } act.sa_flags = 0; // act.sa_flags = SA_SIGINFO; - if (sigaction(sig, &act, &oact) < 0) { + if (sigaction(sig, &act, &oact) < 0) + { return NULL; } return oact.sa_handler; } -int cpQueueSignalSet(int sig, cpQueueFunc func) { +int cpQueueSignalSet(int sig, cpQueueFunc func) +{ struct sigaction act, oact; sigemptyset(&act.sa_mask); act.sa_sigaction = func; act.sa_flags = SA_SIGINFO; - if (sigaction(sig, &act, &oact) < 0) { + if (sigaction(sig, &act, &oact) < 0) + { cpLog("sigaction error %d", errno); return FAILURE; } @@ -276,9 +409,11 @@ int cpQueueSignalSet(int sig, cpQueueFunc func) { #ifndef HAVE_CLOCK_GETTIME #ifdef __MACH__ -int clock_gettime(clock_id_t which_clock, struct timespec *t) { +int clock_gettime(clock_id_t which_clock, struct timespec *t) +{ // be more careful in a multithreaded environement - if (!orwl_timestart) { + if (!orwl_timestart) + { mach_timebase_info_data_t tb = {0}; mach_timebase_info(&tb); orwl_timebase = tb.numer; @@ -291,4 +426,4 @@ int clock_gettime(clock_id_t which_clock, struct timespec *t) { return 0; } #endif -#endif \ No newline at end of file +#endif diff --git a/cpMemory.c b/cpMemory.c old mode 100755 new mode 100644 index 2e8cdc6..0a5429b --- a/cpMemory.c +++ b/cpMemory.c @@ -10,13 +10,14 @@ | to obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | original: Tianfeng Han modify:Xinhua Guo | + | Xinhua Guo | +----------------------------------------------------------------------+ */ #include "php_connect_pool.h" -void *cp_mmap_calloc(int size) { +void *cp_mmap_calloc(int size) +{ void *mem; int tmpfd = -1; int flag = MAP_SHARED; @@ -25,10 +26,12 @@ void *cp_mmap_calloc(int size) { flag |= MAP_ANONYMOUS; #else char *mapfile = NULL; - if (mapfile == NULL) { + if (mapfile == NULL) + { mapfile = "/dev/zero"; } - if ((tmpfd = open(mapfile, O_RDWR)) < 0) { + if ((tmpfd = open(mapfile, O_RDWR)) < 0) + { return NULL; } strncpy(object->mapfile, mapfile, SW_SHM_MMAP_FILE_LEN); @@ -44,37 +47,75 @@ void *cp_mmap_calloc(int size) { { cpLog("mmap fail. Error: %s[%d]", strerror(errno), errno); return NULL; - } else { + } + else + { bzero(mem, size); return mem; } } -int cpShareMemory_sysv_create(cpShareMemory *object, int size, int key) { - int shmid; - void *mem = NULL; - bzero(object, sizeof (cpShareMemory)); +int cp_create_mmap_dir() +{ + umask(0); + if (access(CP_FILE_DIR, F_OK) == -1) + { + int ret = mkdir(CP_FILE_DIR, 0777); + if (ret == -1) + { + php_printf("mkdir fail. Error: %s[%d]", strerror(errno), errno); + return -1; - if (key == 0) { - key = IPC_PRIVATE; + } } - if ((shmid = shmget(key, size, SHM_R | SHM_W | IPC_CREAT | 0666)) < 0) { - cpLog("shmget Error: %s[%d]", strerror(errno), errno); - return 0; + + return 0; +}; + +int cp_create_mmap_file(cpShareMemory *object) +{ + umask(0); + char tmp[100] = {0}; + strncpy(tmp, CP_SERVER_MMAP_FILE, strlen(CP_SERVER_MMAP_FILE)); + int fd = open(object->mmap_name, O_RDWR | O_CREAT, 0777); + if (fd == -1) + { + php_printf("open fail. Error: %s[%d],%s", strerror(errno), errno, object->mmap_name); + return -1; + } - object->key = key; - object->shmid = shmid; - object->size = size; - object->mem = mem; - return shmid; -} + ftruncate(fd, object->size); //extend 黑洞 + close(fd); + return 0; +}; + +void* cp_mmap_calloc_with_file(cpShareMemory *object) +{ + + int fd = open(object->mmap_name, O_RDWR); + if (fd == -1) + { + cpLog("open fail. Error: %s[%d]", strerror(errno), errno); + return NULL; -int cpShareMemory_sysv_free(cpShareMemory *object, int rm) { - int ret = shmdt(object->mem); - if (rm == 1) { - shmctl(object->shmid, IPC_RMID, NULL); } - object->mem = NULL; - return ret; + void *mem = mmap(NULL, object->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); +#ifdef MAP_FAILED + if (mem == MAP_FAILED) +#else + if (!mem) +#endif + { + cpLog("mmap fail. Error: %s[%d]", strerror(errno), errno); + return NULL; + } + else + { + // bzero(mem, object->size); + object->mem = mem; + return mem; + } + } + diff --git a/cpNetWork.c b/cpNetWork.c old mode 100755 new mode 100644 index 3bbff0c..b9c625a --- a/cpNetWork.c +++ b/cpNetWork.c @@ -23,24 +23,27 @@ int cpEpoll_add(int epfd, int fd, int fdtype) { e.data.fd = fd; e.events = fdtype; ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &e); - if (ret < 0) { + if (ret < 0) + { cpLog("add event fail. Error: %s[%d]", strerror(errno), errno); return FAILURE; } return SUCCESS; } -int cpEpoll_del(int epfd,int fd) { +int cpEpoll_del(int epfd, int fd) { struct epoll_event e; int ret; e.data.fd = fd; - if (fd <= 0) { + if (fd <= 0) + { return FAILURE; } // e.events = EPOLLIN | EPOLLET | EPOLLOUT; ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &e); - if (ret < 0) { + if (ret < 0) + { cpLog("epoll remove fd[=%d] fail. Error: %s[%d]", fd, strerror(errno), errno); return SUCCESS; } @@ -50,7 +53,8 @@ int cpEpoll_del(int epfd,int fd) { } CPINLINE int cpReactor_error() { - switch (errno) { + switch (errno) + { case EINTR: return SUCCESS; } @@ -61,45 +65,55 @@ int cpEpoll_wait(epoll_wait_handle *handles, struct timeval *timeo, int epfd) { int i, n, ret, usec; // int pack_size = sizeof (uint32_t)*8; - if (timeo == NULL) { + if (timeo == NULL) + { usec = CP_MAX_UINT; - } else { + } + else + { usec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; } -// uint8_t *run; -// if (CPGL.process_type == CP_PROCESS_WORKER) { -// run = &CPGS->workers[CPWG.id].run; -// } else { -// run = &CPGL.running; -// } + // uint8_t *run; + // if (CPGL.process_type == CP_PROCESS_WORKER) { + // run = &CPGS->workers[CPWG.id].run; + // } else { + // run = &CPGL.running; + // } struct epoll_event events[CP_REACTOR_MAXEVENTS]; - while (CPGS->running) { + while (CPGS->running) + { n = epoll_wait(epfd, events, CP_REACTOR_MAXEVENTS, usec); - for (i = 0; i < n; i++) { + for (i = 0; i < n; i++) + { //取出事件 // ev.fd = object->events[i].data.u64; // ev.type = object->events[i].data.u64 >> pack_size; -// if (events[i].events & EPOLLPRI) { -// char buf[1]; -// ret = handles[EPOLLPRI](events[i].data.fd); -// recv(events[i].data.fd, buf, 1, MSG_OOB); -// if (ret < 0) { -// cpLog("epoll [EPOLLPRI] handle failed. fd=%d. Error: %s[%d]", events[i].data.fd, -// strerror(errno), errno); -// } -// } - if (events[i].events & EPOLLIN) { + // if (events[i].events & EPOLLPRI) { + // char buf[1]; + // ret = handles[EPOLLPRI](events[i].data.fd); + // recv(events[i].data.fd, buf, 1, MSG_OOB); + // if (ret < 0) { + // cpLog("epoll [EPOLLPRI] handle failed. fd=%d. Error: %s[%d]", events[i].data.fd, + // strerror(errno), errno); + // } + // } + if (events[i].events & EPOLLIN) + { ret = handles[EPOLLIN](events[i].data.fd); - if (ret < 0) { + if (ret < 0) + { cpLog("epoll [EPOLLIN] handle failed. fd=%d. Error: %s[%d]", events[i].data.fd, strerror(errno), errno); } - } else if (events[i].events & EPOLLOUT) { + } + else if (events[i].events & EPOLLOUT) + { ret = handles[EPOLLIN](events[i].data.fd); - if (ret < 0) { + if (ret < 0) + { cpLog("epoll [EPOLLOUT] handle failed. fd=%d. Error: %s[%d]", events[i].data.fd, strerror(errno), errno); } @@ -110,16 +124,20 @@ int cpEpoll_wait(epoll_wait_handle *handles, struct timeval *timeo, int epfd) { else if ((events[i].events & (EPOLLERR | EPOLLHUP))) #endif { - if (events[i].data.fd > 0) { + if (events[i].data.fd > 0) + { ret = handles[EPOLL_CLOSE](events[i].data.fd); - if (ret < 0) { + if (ret < 0) + { cpLog("epoll [EPOLLRDHUP] handle failed. fd=%d. Error: %s[%d]", events[i].data.fd, strerror(errno), errno); } } } } - if (n < 0) { - if (cpReactor_error() < 0) { + if (n < 0) + { + if (cpReactor_error() < 0) + { cpLog("Epoll[#%d] Error: %s[%d]", events[i].data.fd, strerror(errno), errno); return FAILURE; } diff --git a/cpPingWorker.c b/cpPingWorker.c new file mode 100644 index 0000000..83a40be --- /dev/null +++ b/cpPingWorker.c @@ -0,0 +1,187 @@ +/* + +----------------------------------------------------------------------+ + | common con pool | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Xinhua Guo | + +----------------------------------------------------------------------+ + */ + +#include "php_connect_pool.h" + +static void cpPing_add_dislist(zval *dis_arr, zval **args, char *data_source) +{ + + zval *copy; + CP_MAKE_STD_ZVAL(copy); + *copy = **args; + zval_copy_ctor(copy); + + if (Z_TYPE_P(dis_arr) == IS_NULL) + { + zval first_arr; + array_init(&first_arr); + add_assoc_zval(&first_arr, data_source, copy); + cp_ser_and_setdis(&first_arr); + zval_dtor(&first_arr); + cpLog("'%s' insert into disable list", data_source); + } + else if (CP_Z_TYPE_P(dis_arr)) + { + zval *zval_source; + if (cp_zend_hash_find(Z_ARRVAL_P(dis_arr), data_source, strlen(data_source) + 1, (void **) &zval_source) == FAILURE)//SUCCESS的跳过,证明dis后继续调用这个节点了,防止重复添加 + { + if (zend_hash_num_elements(Z_ARRVAL_P(dis_arr)) >= CPGC.max_fail_num) + { + cp_zval_ptr_dtor(©); + cpLog("the disable count exceed"); + } + else + { + add_assoc_zval(dis_arr, data_source, copy); + cp_ser_and_setdis(dis_arr); + cpLog("'%s' insert into disable list", data_source); + } + } + else + { + cp_zval_ptr_dtor(©); + } + } + +} + +static void cpPing_del_prolist(zval *pro_arr, char *data_source) +{ + zval copy; + copy = *pro_arr; + zval_copy_ctor(©); + cp_zend_hash_del(Z_ARRVAL(copy), data_source, strlen(data_source) + 1); + cp_ser_and_setpro(©); + zval_dtor(©); +} + +static void cpPingClear(int sig) +{ + bzero(CPGL.ping_mem_addr + CP_PING_MD5_LEN + CP_PING_PID_LEN, CP_PING_DIS_LEN); +} + +static int cpPing_worker_loop() +{ + cpSignalSet(SIGUSR1, cpPingClear, 1, 0); + CPGL.ping_mem_addr = CPGS->ping_workers->sm_obj.mem; + bzero(CPGL.ping_mem_addr, CP_PING_MEM_LEN); + memcpy(CPGL.ping_mem_addr + CP_PING_MD5_LEN, &CPGS->ping_workers->pid, CP_PING_PID_LEN); + + while (1) + { + sleep(1); + zval *pro_arr = CP_PING_GET_PRO(CPGL.ping_mem_addr); + zval *dis_arr = CP_PING_GET_DIS(CPGL.ping_mem_addr); + if (!CP_Z_TYPE_P(pro_arr) && CP_Z_TYPE_P(pro_arr) != IS_NULL) + { + //检查probably里面是否有可以放入disable的 + zval *row , *zval_count; + char *name; + int keytype; + uint32_t keylen; + CP_HASHTABLE_FOREACH_START2(CP_Z_ARRVAL_P(pro_arr), name, keylen, keytype, row) + if (cp_zend_hash_find(Z_ARRVAL_P(pro_arr), ZEND_STRS("count"), (void **) &zval_count) == SUCCESS) + { + if (Z_LVAL_P(zval_count) >= CPGC.ser_fail_hits) + {//连续错n次 放入dis列表 + cpPing_add_dislist(dis_arr, &row, name); + cpPing_del_prolist(pro_arr, name); + } + } + CP_HASHTABLE_FOREACH_END(); + } + + cp_zval_ptr_dtor(&dis_arr); + + dis_arr = CP_PING_GET_DIS(CPGL.ping_mem_addr); + zval copy; + copy = *dis_arr; + zval_copy_ctor(©); + if (!CP_Z_TYPE_P(dis_arr) && CP_Z_TYPE_P(dis_arr) != IS_NULL) + { + //开始检测disable中是否有恢复的 + zval *args; + char *data_source; + int keytype; + uint32_t keylen; + CP_HASHTABLE_FOREACH_START2(CP_Z_ARRVAL_P(dis_arr), data_source, keylen, keytype, args) + if (strstr(data_source, "host")) + {//mysql + if (pdo_proxy_connect(args, CP_CONNECT_PING)) + { + cp_zend_hash_del(Z_ARRVAL(copy), data_source, strlen(data_source) + 1); + cp_ser_and_setdis(©); + cpLog("'%s' remove from disable list", data_source); + } + } + else + {//redis + if (redis_proxy_connect(args, CP_CONNECT_PING)) + { + cp_zend_hash_del(Z_ARRVAL(copy), data_source, strlen(data_source) + 1); + cp_ser_and_setdis(©); + cpLog("'%s' remove from disable list", data_source); + } + } + CP_HASHTABLE_FOREACH_END(); + + } + zval_dtor(©); + cp_zval_ptr_dtor(&dis_arr); + cp_zval_ptr_dtor(&pro_arr); + } + return 0; +} + +int cpFork_ping_worker() +{ + int pid, ret; + pid = fork(); + if (pid < 0) + { + cpLog("Fork Worker failed. Error: %s [%d]", strerror(errno), errno); + return FAILURE; + } + else if (pid == 0) + { + //标识为worker进程 + CPGL.process_type = CP_PROCESS_PING; + char name[MAX_TITLE_LENGTH] = {0}; + strcat(name, "ping_worker"); + cpSettitle(name); + ret = cpPing_worker_loop(); + exit(ret); + } + else + { + return pid; + } +} + +CPINLINE int cpCreate_ping_worker_mem() +{ + cpShareMemory *sm_obj = &(CPGS->ping_workers->sm_obj); + sprintf(sm_obj->mmap_name, "%s_%d", CP_MMAP_NAME_PRE, 0x2526 + CPGC.port); + sm_obj->size = CP_PING_MEM_LEN; + if(cp_create_mmap_file(sm_obj)<0){ + return FAILURE; + } + if (!cp_mmap_calloc_with_file(sm_obj)) + { + return FAILURE; + } + return SUCCESS; +} diff --git a/cpServer.c b/cpServer.c old mode 100755 new mode 100644 index 242c0ee..59cad0e --- a/cpServer.c +++ b/cpServer.c @@ -1,195 +1,324 @@ /* - +----------------------------------------------------------------------+ - | common con pool | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Xinhua Guo | - +----------------------------------------------------------------------+ + +----------------------------------------------------------------------+ + | common con pool | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Xinhua Guo | + +----------------------------------------------------------------------+ */ #include "php_connect_pool.h" int static cpListen(); -int static cpWriter_receive(int fd); -void static cpSignalHanlde(int sig); +void static cpSignalHandle(int sig); void static cpSignalInit(void); -void static cpTryGetWorkerId(cpConnection *conn, char * data, int fd, int len); int static cpReactor_client_close(int fd); static int cpReactor_client_receive(int fd); static int cpReactor_client_release(int fd); -static void insert_into_used(); -CPINLINE int cpCreate_worker_mem(int worker_id); +static int cpReactor_start(int sock); -void cpServer_init(zval *conf, char *title, char *ini_file, int group_id) { - CPGS = (cpServerGS*) cp_mmap_calloc(sizeof (cpServerGS)); - if (CPGS == NULL) { - printf("calloc[1] fail\n"); - return; +void cpServer_init_common(zval *conf) +{ + zval *v; + //daemonize,守护进程化 + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("daemonize"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.daemonize = (int) Z_LVAL_P(v); } - bzero(&CPGL, sizeof (cpServerG)); - CPGC.backlog = CP_BACKLOG; - CPGC.reactor_num = CP_CPU_NUM; - CPGC.timeout_sec = CP_REACTOR_TIMEO_SEC; - CPGC.timeout_usec = CP_REACTOR_TIMEO_USEC; - CPGC.max_conn = CP_MAX_FDS; - CPGC.max_request = CP_MAX_REQUEST; - CPGC.idel_time = CP_IDEL_TIME; - CPGC.recycle_num = CP_RECYCLE_NUM; - CPGC.max_read_len = CP_DEF_MAX_READ_LEN; - CPGC.group_id = group_id; - CPGS->worker_max = CP_MAX_WORKER; - CPGC.worker_min = CP_MIN_WORKER; - - strcpy(CPGC.title, title); - strcpy(CPGC.ini_file, ini_file); - cpSettitle(title); + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("recycle_num"), (void **) &v) == SUCCESS) + //if (cp_zend_hash_find(Z_ARRVAL_P(conf), "recycle_num", strlen("recycle_num"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.recycle_num = (int) Z_LVAL_P(v); + } + //error_file + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("log_file"), (void **) &v) == SUCCESS) + //if (cp_zend_hash_find(Z_ARRVAL_P(conf), "log_file", strlen("log_file"), (void **) &v) == SUCCESS) + { + memcpy(CPGC.log_file, Z_STRVAL_P(v), Z_STRLEN_P(v)); + } - zval **v; - //daemonize,守护进程化 - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("daemonize"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.daemonize = (int) Z_LVAL_PP(v); + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("max_read_len"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.max_read_len = (int) Z_LVAL_P(v); } - //pool_max - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("pool_max"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGS->worker_max = (int) Z_LVAL_PP(v); + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("port"), (void **) &v) == SUCCESS) + {//todo check null + convert_to_long(v); + CPGC.port = (int) Z_LVAL_P(v); } - //pool_min - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("pool_min"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.worker_min = (int) Z_LVAL_PP(v); + + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("idel_time"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.idel_time = (int) Z_LVAL_P(v); } - //pool_min - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("recycle_num"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.recycle_num = (int) Z_LVAL_PP(v); + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("ping_time"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.ping_time = (int) Z_LVAL_P(v); } - //error_file - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("log_file"), (void **) &v) == SUCCESS) { - memcpy(CPGC.log_file, Z_STRVAL_PP(v), Z_STRLEN_PP(v)); + + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("ser_fail_hits"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.ser_fail_hits = (int) Z_LVAL_P(v); } - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("max_read_len"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.max_read_len = (int) Z_LVAL_PP(v); + + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("max_fail_num"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.max_fail_num = (int) Z_LVAL_P(v); } - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("port"), (void **) &v) == SUCCESS) {//todo check null - convert_to_long(*v); - CPGC.port = (int) Z_LVAL_PP(v); + + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("max_data_size_to_log"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.max_data_size_to_log = (int) Z_LVAL_P(v); } - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("idel_time"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.idel_time = (int) Z_LVAL_PP(v); + if (cp_zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("max_hold_time_to_log"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.max_hold_time_to_log = (int) Z_LVAL_P(v); } +} - if (zend_hash_find(Z_ARRVAL_P(conf), ZEND_STRS("use_wait_queue"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.use_wait_queue = (int) Z_LVAL_PP(v); +void cpKillClient() +{ + int i; + for (i = 0; i <= CP_MAX_FDS; i++) + { + cpConnection *conn = &(CPGS->conlist[i]); + if (conn->fpm_pid) + { + kill(conn->fpm_pid, 9); + // printf("kill %d\n",conn->fpm_pid); + } } } -int cpServer_create() { - if (CPGC.worker_min < 1 || CPGC.reactor_num < 1 || CPGC.max_read_len >= CP_MAX_READ_LEN) { - printf("Fatal Error: worker_min < 1 or reactor_num < 1 or max_read_len >%d\n", CP_MAX_READ_LEN); - return FAILURE; +static void cpServer_init_lock() +{ + int i = 0; + for (; i < CP_GROUP_NUM - 1; i++) + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + if (pthread_mutex_init(&CPGS->G[i].mutex_lock, &attr) < 0) + { + cpLog("pthread_mutex_init error!. Error: %s [%d]", strerror(errno), errno); + } + CPGS->G[i].lock = cpMutexLock; + CPGS->G[i].unLock = cpMutexUnLock; + CPGS->G[i].tryLock = cpMutexTryLock; } +} - CPGS->reactor_threads = (cpThread*) cp_mmap_calloc(CPGC.reactor_num * sizeof (cpThread)); - if (CPGS->reactor_threads == NULL) { - cpLog("calloc[1] fail"); +int cpServer_init(zval *conf, char *ini_file) +{ + int sock = 0; + size_t group_num = 0; + cpShareMemory shm = {0}; + zval *config; + char *name; + uint32_t klen; + int ktype; + HashTable *_ht = Z_ARRVAL_P(conf); + + bzero(&CPGL, sizeof (cpServerG)); + CPGC.backlog = CP_BACKLOG; + // CPGC.reactor_num = CP_CPU_NUM; + CPGC.reactor_num = 1; + CPGC.timeout_sec = CP_REACTOR_TIMEO_SEC; + CPGC.timeout_usec = CP_REACTOR_TIMEO_USEC; + CPGC.max_conn = CP_MAX_FDS; + CPGC.max_request = CP_MAX_REQUEST; + CPGC.idel_time = CP_IDEL_TIME; + CPGC.ping_time = CP_PING_SLEEP; + CPGC.recycle_num = CP_RECYCLE_NUM; + CPGC.max_read_len = CP_DEF_MAX_READ_LEN; + CPGC.ser_fail_hits = 1; + CPGC.max_fail_num = 2; + CPGC.port = CP_PORT_PDO; + CPGC.max_data_size_to_log = 0; + CPGC.max_hold_time_to_log = 0; + + if ((sock = cpListen()) < 0) + { + printf("listen[1] fail\n"); return FAILURE; } - CPGS->conlist = (cpConnection*) cp_mmap_calloc(CPGC.max_conn * sizeof (cpConnection)); - if (CPGS->conlist == NULL) { - cpLog("calloc[1] fail"); + if ((cp_create_mmap_dir()) < 0) + { + php_printf("mkdir[1] fail\n"); return FAILURE; } - CPGS->workerfd2clientfd_list = (uint32_t*) cp_mmap_calloc(CPGC.max_conn * sizeof (uint32_t)); - if (CPGS->workerfd2clientfd_list == NULL) { - cpLog("calloc[1] fail"); + shm.size = sizeof (cpServerGS); + strncpy(shm.mmap_name, CP_SERVER_MMAP_FILE, strlen(CP_SERVER_MMAP_FILE)); + if (cp_create_mmap_file(&shm) == 0) + { + CPGS = (cpServerGS*) cp_mmap_calloc_with_file(&shm); + cpKillClient(); + bzero(CPGS, shm.size); + if (CPGS == NULL) + { + php_printf("calloc[1] fail\n"); + return FAILURE; + } + } + else + { + php_printf("calloc[1] fail\n"); return FAILURE; } + strcpy(CPGC.ini_file, ini_file); + + CP_HASHTABLE_FOREACH_START2(_ht, name, klen, ktype, config) + { + if (strcmp(name, "common") == 0) + {//common config + cpServer_init_common(config); + } + else + { + zval *v; + strcpy(CPGS->G[group_num].name, name); + if (cp_zend_hash_find(Z_ARRVAL_P(config), ZEND_STRS("pool_min"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGS->G[group_num].worker_num = CPGS->G[group_num].worker_min = Z_LVAL_P(v); + } + if (cp_zend_hash_find(Z_ARRVAL_P(config), ZEND_STRS("pool_max"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGS->G[group_num].worker_max = Z_LVAL_P(v); + } + CPGS->group_num++; + group_num++; + } + + } + CP_HASHTABLE_FOREACH_END(); - CPGS->workers_status = (uint8_t*) cp_mmap_calloc(sizeof (uint8_t) * CP_GROUP_LEN); - if (CPGS->workers_status == NULL) { - cpLog("alloc for worker_status fail"); + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + if (pthread_mutex_init(&CPGS->mutex_lock, &attr) < 0) + { + cpLog("pthread_mutex_init error!. Error: %s [%d]", strerror(errno), errno); return FAILURE; } - CPGS->workers = (cpWorker*) cp_mmap_calloc(CP_GROUP_LEN * sizeof (cpWorker)); - if (CPGS->workers == NULL) { - cpLog("[Main] calloc[workers] fail"); + CPGS->default_min = CP_DEF_MIN_NUM; + CPGS->default_max = CP_DEF_MAX_NUM; + CPGS->max_buffer_len = CPGC.max_read_len; + CPGS->max_data_size_to_log = CPGC.max_data_size_to_log; + CPGS->max_hold_time_to_log = CPGC.max_hold_time_to_log; + strcpy(CPGS->log_file, CPGC.log_file); + + cpServer_init_lock(); + return sock; + +} + +int cpServer_create() +{ + if (CPGC.reactor_num < 1 || CPGC.max_read_len >= CP_MAX_READ_LEN) + { + php_printf("reactor_num < 1 or max_read_len >%d\n", CP_MAX_READ_LEN); return FAILURE; } - CPGS->spin_lock = (pthread_spinlock_t*) cp_mmap_calloc(sizeof (pthread_spinlock_t)); - //worker闲忙的锁,未做兼容,只在linux用 - if (pthread_spin_init(CPGS->spin_lock, 1) < 0) { - cpLog("pthread_spin_init error!. Error: %s [%d]", strerror(errno), errno); + if (CPGC.ser_fail_hits < 1 || CPGC.max_fail_num < 1) + { + php_printf("ping server conf error\n"); return FAILURE; } cpLog_init(CPGC.log_file); - CPGS->running = 1; + CPGS->reactor_threads = (cpThread*) cp_mmap_calloc(CPGC.reactor_num * sizeof (cpThread)); + if (CPGS->reactor_threads == NULL) + { + cpLog("calloc[1] fail"); + return FAILURE; + } - return SUCCESS; -} + CPGS->ping_workers = (cpWorker*) cp_mmap_calloc(sizeof (cpWorker)); + if (CPGS->ping_workers == NULL) + { + cpLog("[Main] calloc[ping_workers] fail"); + return FAILURE; + } + + CPGS->running = 1; -int static cpList_create() { - int i; - CPGS->WaitList = CPGS->WaitTail = NULL; return SUCCESS; } -int cpServer_start() { - int i, pid, ret; - //run as daemon - if (CPGC.daemonize > 0) { - if (daemon(0, 0) < 0) { +int cpServer_start(int sock) +{ + int w, pid, ret, g; + if (CPGC.daemonize > 0) + { + if (daemon(0, 0) < 0) + { return FAILURE; } } + CPGS->master_pid = getpid(); CPGL.process_type = CP_PROCESS_MASTER; - cpList_create(); pid = fork(); - switch (pid) { + switch (pid) + { //创建manager进程 case 0: - //********************数据库坏连接检测恢复进程****************************** - // pid = fork(); - // if (pid < 0) { - // cpLog("Fork Worker failed. Error: %s [%d]", strerror(errno), errno); - // } else if (pid == 0) { - //// cpSignalSet(SIGALRM, cpManagerSignalHanlde, 1, 0); - // } - //********************数据库坏连接检测恢复进程****************************** - for (i = 0; i < CPGC.worker_min; i++) {//alloc了max个 但是只启动min个 - pid = cpFork_one_worker(i); - cpCreate_worker_mem(i); - if (pid < 0) { - cpLog("Fork worker process fail"); - return FAILURE; - } else { - CPGS->workers[i].pid = pid; - CPGS->workers_status[i] = CP_WORKER_IDLE; + for (g = 0; g < CPGS->group_num; g++) + { + for (w = 0; w < CPGS->G[g].worker_min; w++) + { + //alloc了max个 但是只启动min个 + ret = cpCreate_worker_mem(w, g); + pid = cpFork_one_worker(w, g); + if (pid < 0 || ret < 0) + { + cpLog("Fork worker process fail"); + return FAILURE; + } + else + { + CPGS->G[g].workers[w].pid = pid; + CPGS->G[g].workers_status[w] = CP_WORKER_IDLE; + } } } + //数据库坏连接检测恢复进程 + // ret = cpCreate_ping_worker_mem(); + // ping_pid = cpFork_ping_worker(); + // if (ping_pid < 0 || ret < 0) + // { + // cpLog("Fork ping process fail"); + // return FAILURE; + // } + // CPGS->ping_workers->pid = ping_pid; + //标识为管理进程 CPGL.process_type = CP_PROCESS_MANAGER; - CPGS->worker_num = CPGC.worker_min; //初始为min个worker ret = cpWorker_manager_loop(); exit(ret); break; @@ -205,23 +334,28 @@ int cpServer_start() { } cpSignalInit(); - if (cpReactor_start() < 0) { + if (cpReactor_start(sock) < 0) + { cpLog("Reactor_start[1] fail"); return FAILURE; } return SUCCESS; } -static int cpServer_master_onAccept(int fd) { +static int cpServer_master_onAccept(int fd) +{ struct sockaddr_in client_addr; uint32_t client_addrlen = sizeof (client_addr); int conn_fd, c_pti = 0, i; - for (i = 0; i < CP_ACCEPT_MAX_COUNT; i++) { + for (i = 0; i < CP_ACCEPT_MAX_COUNT; i++) + { //accept得到连接套接字 conn_fd = accept(fd, (struct sockaddr *) &client_addr, &client_addrlen); - if (conn_fd < 0) { - switch (errno) { + if (conn_fd < 0) + { + switch (errno) + { case EAGAIN: return SUCCESS; case EINTR: @@ -232,12 +366,12 @@ static int cpServer_master_onAccept(int fd) { } } //连接过多 - if (CPGS->connect_count >= CPGC.max_conn) { - cpLog("too many connection"); + if (CPGS->connect_count >= CPGC.max_conn) + { + cpLog("too many connection,please reduce you worker num"); close(conn_fd); return SUCCESS; } -// swSetNonBlock(conn_fd); int flag = 1; setsockopt(conn_fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof (flag)); @@ -253,11 +387,14 @@ static int cpServer_master_onAccept(int fd) { setsockopt(conn_fd, IPPROTO_TCP, TCP_KEEPCNT, (void *) &keep_count, sizeof (keep_count)); #endif - if (CPGC.reactor_num > 1) { + if (CPGC.reactor_num > 1) + { int i, event_num = CPGS->reactor_threads[0].event_num; CPGS->reactor_next_i = 0; - for (i = 1; i < CPGC.reactor_num; i++) { - if (CPGS->reactor_threads[i].event_num < event_num) { + for (i = 1; i < CPGC.reactor_num; i++) + { + if (CPGS->reactor_threads[i].event_num < event_num) + { CPGS->reactor_next_i = i; event_num = CPGS->reactor_threads[i].event_num; } @@ -267,14 +404,18 @@ static int cpServer_master_onAccept(int fd) { cpConnection *conn = &(CPGS->conlist[conn_fd]); - if (conn) {//不能在add后做,线程安全,防止添加到reactor后马上就读到数据,这时候下面new_connect还没执行。 + if (conn) + {//不能在add后做,线程安全,防止添加到reactor后马上就读到数据,这时候下面new_connect还没执行。 conn->release = CP_FD_RELEASED; } - if (cpEpoll_add(CPGS->reactor_threads[c_pti].epfd, conn_fd, EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR | EPOLLPRI) < 0) { + if (cpEpoll_add(CPGS->reactor_threads[c_pti].epfd, conn_fd, EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR | EPOLLPRI) < 0) + { cpLog("[Master]add event fail Errno=%d|FD=%d", errno, conn_fd); close(conn_fd); return SUCCESS; - } else { + } + else + { CPGS->reactor_threads[c_pti].event_num++; conn->fd = conn_fd; conn->pth_id = c_pti; @@ -286,136 +427,84 @@ static int cpServer_master_onAccept(int fd) { return SUCCESS; } -CPINLINE static int MasterSend2Client(int fd, int worker_id) { - CPGS->workers[worker_id].fd = fd; - cpMasterInfo info; - int sizeinfo = sizeof (info); - info.worker_id = CPGC.group_id * CP_GROUP_LEN + worker_id; - info.semid = CPGS->workers[worker_id].sm_obj.shmid; - info.max = CPGC.max_read_len; - return cpWrite(fd, &info, sizeinfo); -} - -CPINLINE int cpCreate_worker_mem(int worker_id) { - cpShareMemory *sm_obj = &(CPGS->workers[worker_id].sm_obj); - if (!cpShareMemory_sysv_create(sm_obj, CPGC.max_read_len, 0x3526 + CPGC.port * CP_GROUP_LEN + worker_id)) {//todo check <1000 - cpLog("create sys v shm. Error: %s [%d]", strerror(errno), errno); - return FAILURE; - } - return SUCCESS; -} - -static void cpTryGetWorkerId(cpConnection *conn, char * data, int fd, int len) { - if (pthread_spin_lock(CPGS->spin_lock) == 0) { - int i; - for (i = 0; i < CPGS->worker_num; i++) { - if (CPGS->workers_status[i] == CP_WORKER_IDLE && i < CPGS->worker_max) { - CPGS->workers_status[i] = CP_WORKER_BUSY; - conn->worker_id = i; - conn->release = CP_FD_NRELEASED; - if (pthread_spin_unlock(CPGS->spin_lock) != 0) { - cpLog("pthread_spin_unlock. Error: %s [%d]", strerror(errno), errno); - } - return; - } - } - if (CPGS->worker_num < CPGS->worker_max) {//争抢失败增加一个worker - conn->release = CP_FD_NRELEASED; - conn->worker_id = CPGS->worker_num; - cpCreate_worker_mem(CPGS->worker_num); - CPGS->workers_status[CPGS->worker_num] = CP_WORKER_BUSY; //创建后立马分配,防止第一次too many connections - CPGS->worker_num++; //先加 线程安全 - int ret = kill(CPGS->manager_pid, SIGRTMIN); - if (ret < 0) { - CPGS->worker_num--; //todo - cpLog("send sig error. Error: %s [%d]", strerror(errno), errno); - } - } else if (CPGC.use_wait_queue) { - cpWaitList *node = (cpWaitList*) emalloc(sizeof (cpWaitList) + len); - node->fd = fd; - node->len = len; - node->next = NULL; - if (CPGS->WaitList) { - CPGS->WaitTail->next = node; - node->pre = CPGS->WaitTail; - CPGS->WaitTail = node; - } else { - node->pre = NULL; - CPGS->WaitList = CPGS->WaitTail = node; - } - memcpy(node->data, data, len); - conn->release = CP_FD_WAITING; - } - if (pthread_spin_unlock(CPGS->spin_lock) != 0) { - cpLog("pthread_spin_unlock. Error: %s [%d]", strerror(errno), errno); - } - } else { - cpLog("pthread_spin_lock. Error: %s [%d]", strerror(errno), errno); - } -} - -static int cpReactor_client_release(int fd) { +//void cpPrint_queue(cpGroup *G) +//{ +// int current_fd = G->first_wait_id; +// while (current_fd) +// { +// cpConnection* conn = &(CPGS->conlist[current_fd]); +// printf("fd is %d\n", conn->fd); +// current_fd = conn->next_wait_id; +// } +//} + +static int cpReactor_client_release(int fd) +{ cpConnection *conn = &(CPGS->conlist[fd]); - if (conn->release == CP_FD_NRELEASED) {//防止too many cons&&重复release - CPGS->workers[conn->worker_id].request++; - if (pthread_spin_lock(CPGS->spin_lock) == 0) { - if (CPGS->workers[conn->worker_id].request >= CP_MAX_REQUEST) { - CPGS->workers[conn->worker_id].request = 0; - CPGS->workers[conn->worker_id].run = 0; - } - if (CPGS->WaitList && CPGC.use_wait_queue && conn->worker_id > CPGS->worker_max) {//wait is not null&&use queue&&use reload to reduce max maybe trigger this - cpConnection *wait_conn = &(CPGS->conlist[CPGS->WaitList->fd]); //等待队列的连接 - wait_conn->worker_id = conn->worker_id; - wait_conn->release = CP_FD_NRELEASED; - conn->release = CP_FD_RELEASED; - cpWaitList *tmp = CPGS->WaitList; - if (CPGS->WaitList->next) { - CPGS->WaitList = CPGS->WaitList->next; - CPGS->WaitList->pre = NULL; - } else { - CPGS->WaitList = CPGS->WaitTail = NULL; - } - if (MasterSend2Client(wait_conn->fd, wait_conn->worker_id) < 0) { - cpLog("Write in cpReactor_client_release. Error: %s [%d]", strerror(errno), errno); + cpGroup *G = &CPGS->G[conn->group_id]; + + if (G->lock(G) == 0) + { + if (conn->release == CP_FD_NRELEASED) + {//防止too many cons&&重复release + if (G->first_wait_id && conn->worker_index <= G->worker_max) + {//wait is not null&&use queue&&use reload to reduce max maybe trigger this + int wait_pid = cpPopWaitQueue(G, conn); + if (kill(wait_pid, SIGRTMIN) < 0) + { + CPGS->G[conn->group_id].workers_status[conn->worker_index] = CP_WORKER_IDLE; + cpLog("send sig 2 %d error. Error: %s [%d]", wait_pid, strerror(errno), errno); } - efree(tmp); - } else { - CPGS->workers_status[conn->worker_id] = CP_WORKER_IDLE; - conn->release = CP_FD_RELEASED; + G->unLock(G); } - if (pthread_spin_unlock(CPGS->spin_lock) != 0) { - cpLog("pthread_spin_unlock. Error: %s [%d]", strerror(errno), errno); + else + { + CPGS->G[conn->group_id].workers_status[conn->worker_index] = CP_WORKER_IDLE; + G->unLock(G); } } - } else if (conn->release == CP_FD_WAITING) {//在队列里面,没等到分配就结束进程了,从queue里面删除 - if (pthread_spin_lock(CPGS->spin_lock) == 0) { - cpWaitList *p = CPGS->WaitList; - while (p) { - if (p->fd == fd) { - if (p == CPGS->WaitList) { - if (p->next) { - p->next->pre = NULL; - CPGS->WaitList = p->next; - } else {//only one - CPGS->WaitList = CPGS->WaitTail = NULL; + else if (conn->release == CP_FD_WAITING) + { + cpLog("The fd %d is closed and remove from the queue but no conn dispatch , maybe have slow query", fd); + cpConnection *pre = NULL; + int current_fd = G->first_wait_id; + while (current_fd) + { + conn = &(CPGS->conlist[current_fd]); + if (conn->fd == fd) + { + if (fd == G->first_wait_id) + { + if (conn->next_wait_id) + { + G->first_wait_id = conn->next_wait_id; + conn->next_wait_id = 0; } - } else if (p == CPGS->WaitTail) { - p->pre->next = NULL; - CPGS->WaitTail = p->pre; - } else { - p->pre->next = p->next; - p->next->pre = p->pre; + else + {//only one + G->first_wait_id = G->last_wait_id = 0; + } + } + else if (fd == G->last_wait_id) + { + pre->next_wait_id = 0; + G->last_wait_id = pre->fd; + } + else + { + pre->next_wait_id = conn->next_wait_id; + conn->next_wait_id = 0; } - efree(p); break; } - p = p->next; - } - conn->release = CP_FD_RELEASED; - if (pthread_spin_unlock(CPGS->spin_lock) != 0) { - cpLog("pthread_spin_unlock. Error: %s [%d]", strerror(errno), errno); + pre = conn; + current_fd = conn->next_wait_id; } - cpLog("The fd %d is closed and remove from the queue but no conn dispatch , maybe have slow query", fd); + G->unLock(G); + } + else + { + G->unLock(G); } } @@ -423,9 +512,11 @@ static int cpReactor_client_release(int fd) { } -static int cpReactor_client_close(int fd) {//长连接 相当于mshutdown +static int cpReactor_client_close(int fd) +{//长连接 相当于mshutdown cpReactor_client_release(fd); cpConnection *conn = &(CPGS->conlist[fd]); + conn->fpm_pid = 0; //关闭连接 cpEpoll_del(CPGS->reactor_threads[conn->pth_id].epfd, fd); (CPGS->reactor_threads[conn->pth_id].event_num <= 0) ? CPGS->reactor_threads[conn->pth_id].event_num = 0 : CPGS->reactor_threads[conn->pth_id].event_num--; @@ -434,45 +525,56 @@ static int cpReactor_client_close(int fd) {//长连接 相当于mshutdown return SUCCESS; } -static int cpReactor_client_receive(int fd) { - int n, ret; - int event_size = sizeof (cpTcpEvent); +static int cpReactor_client_receive(int fd) +{ + int event_size = sizeof (cpTcpEvent), n, ret = -1; char data[event_size]; //非ET模式会持续通知 n = cpNetRead(fd, data, event_size); - - cpConnection *conn = &(CPGS->conlist[fd]); - if (n > 0) { + if (n > 0) + { cpTcpEvent *event = (cpTcpEvent*) data; - if (event->type == CP_TCPEVENT_RELEASE) { - return cpReactor_client_release(fd); - } - if (conn->release == CP_FD_RELEASED) {//之前释放了,或者刚进来的连接,需要争抢(这个状态不需要加锁,每个con的fd只分配给一个线程) - cpTryGetWorkerId(conn, data, fd, n); - if (conn->release == CP_FD_WAITING) { - return 1; - } - if (conn->release == CP_FD_RELEASED) {//争抢失败,fork失败 - char tmp[sizeof (CP_TOO_MANY_CON_ERR) + sizeof (CP_CLIENT_EOF_STR)] = {CP_TOO_MANY_CON_ERR}; - strcat(tmp, CP_CLIENT_EOF_STR); - return cpWrite(fd, tmp, strlen(tmp)); + switch (event->type) + { + case CP_TCPEVENT_ADD: + ret = kill(CPGS->manager_pid, SIGRTMIN); + if (ret < 0) + {//TODO + cpLog("send sig error. Error: %s [%d]", strerror(errno), errno); + } + break; + case CP_TCPEVENT_GETFD: + { + cpMasterInfo info; + info.server_fd = fd; + CPGS->conlist[fd].fpm_pid = event->data; + ret = cpWrite(fd, &info, sizeof (info)); + break; } - } - return MasterSend2Client(fd, conn->worker_id); - //处理数据失败,数据将丢失 - if (ret < 0) { - cpLog("cpReactor_client_receive fail.errno=%d", errno); + default: + cpLog("wrong type"); + break; } return ret; - } else if (n == 0) { + + } + else if (n == 0) + { close_fd: return cpReactor_client_close(fd); - } else {//需要检测errno来区分是EAGAIN还是ECONNRESET - if (errno == EAGAIN) { + } + else + {//需要检测errno来区分是EAGAIN还是ECONNRESET + if (errno == EAGAIN) + { return SUCCESS; - } else if (errno == ECONNRESET) { + } + else if (errno == ECONNRESET) + { goto close_fd; - } else { + } + else + { cpLog("Read from socket[%d] fail. Error: %s [%d]", fd, strerror(errno), errno); return SUCCESS; } @@ -480,7 +582,8 @@ static int cpReactor_client_receive(int fd) { return SUCCESS; } -int static cpReactor_thread_loop(int *id) { +int static cpReactor_thread_loop(int *id) +{ struct timeval timeo; timeo.tv_sec = CP_REACTOR_TIMEO_SEC; @@ -499,19 +602,16 @@ int static cpReactor_thread_loop(int *id) { cpEpoll_wait(handles, &timeo, epfd); free(id); - pthread_exit(*id); + pthread_exit(0); return SUCCESS; } -int cpReactor_start() { - int sock, i; - if ((sock = cpListen()) < 0) { - cpLog("listen[1] fail"); - return FAILURE; - } - +int static cpReactor_start(int sock) +{ + int i; int accept_epfd = epoll_create(512); //这个参数没用 - if (cpEpoll_add(accept_epfd, sock, EPOLLIN) < 0) { + if (cpEpoll_add(accept_epfd, sock, EPOLLIN) < 0) + { return FAILURE; }; @@ -522,10 +622,12 @@ int cpReactor_start() { timeo.tv_sec = CP_REACTOR_TIMEO_SEC; timeo.tv_usec = CP_REACTOR_TIMEO_USEC; pthread_t pidt; - for (i = 0; i < CPGC.reactor_num; i++) { + for (i = 0; i < CPGC.reactor_num; i++) + { int *index = (int*) malloc(sizeof (int)); *index = i; - if (pthread_create(&pidt, NULL, (void * (*)(void *)) cpReactor_thread_loop, (void *) index) < 0) { + if (pthread_create(&pidt, NULL, (void * (*)(void *)) cpReactor_thread_loop, (void *) index) < 0) + { cpLog("pthread_create[tcp_reactor] fail"); } pthread_detach(pidt); @@ -534,12 +636,14 @@ int cpReactor_start() { epoll_wait_handle handles[CP_MAX_EVENT]; handles[EPOLLIN] = cpServer_master_onAccept; - usleep(50000); - cpLog("start %s success", CPGC.title); + // usleep(50000); + sleep(1); + cpLog("start success"); return cpEpoll_wait(handles, &timeo, accept_epfd); } -int static cpListen() { +int static cpListen() +{ int sock; int option; int ret; @@ -547,8 +651,9 @@ int static cpListen() { struct sockaddr_in addr_in4; sock = socket(PF_INET, SOCK_STREAM, 0); - if (sock < 0) { - cpLog("swSocket_listen: Create socket fail.Errno=%d", errno); + if (sock < 0) + { + printf("swSocket_listen: Create socket fail.Errno=%d\n", errno); return FAILURE; } option = 1; @@ -560,19 +665,22 @@ int static cpListen() { addr_in4.sin_family = AF_INET; ret = bind(sock, (struct sockaddr *) &addr_in4, sizeof (addr_in4)); - if (ret < 0) { - cpLog("Bind fail.port=%d. Error: %s [%d]", CPGC.port, strerror(errno), errno); + if (ret < 0) + { + printf("Bind fail.port=%d. Error: %s [%d]\n", CPGC.port, strerror(errno), errno); return FAILURE; } //开始监听套接字 ret = listen(sock, CPGC.backlog); - if (ret < 0) { - cpLog("Listen fail.port=%d. Error: %s [%d]", CPGC.port, strerror(errno), errno); + if (ret < 0) + { + printf("Listen fail.port=%d. Error: %s [%d]\n", CPGC.port, strerror(errno), errno); return FAILURE; } - swSetNonBlock(sock); + cpSetIsBlock(sock, 0); - if (sock < 0) { + if (sock < 0) + { return FAILURE; } int bufsize = CP_UNSOCK_BUFSIZE; @@ -581,25 +689,39 @@ int static cpListen() { return sock; } -static void cpSignalHanlde(int sig) { - switch (sig) { +static void cpSignalHandle(int sig) +{ + switch (sig) + { case SIGTERM: - cpLog("stop %s", CPGC.title); + cpLog("stop pool server"); CPGS->running = 0; - int i = 0; - for (; i < CPGS->worker_num; i++) { - int ret = kill(CPGS->workers[i].pid, SIGKILL); - if (ret == -1) { - cpLog("kill failed, id=%d. Error: %s [%d]", i, strerror(errno), errno); + int i, j = 0, ret; + for (; j < CPGS->group_num; j++) + { + cpGroup *G = &CPGS->G[j]; + for (i = 0; i < G->worker_num; i++) + { + ret = kill(G->workers[i].pid, SIGKILL); + if (ret == -1) + { + cpLog("kill failed, id=%d. Error: %s [%d]", i, strerror(errno), errno); + } } } + // ret = kill(CPGS->ping_workers->pid, SIGKILL); + // if (ret == -1) + // { + // cpLog("kill ping worker failed, id=%d. Error: %s [%d]", i, strerror(errno), errno); + // } exit(1); break; case SIGUSR1: - cpLog("reload %s", CPGC.title); - int ret = kill(CPGS->manager_pid, SIGUSR1); - if (ret == -1) { - cpLog("reload failed, id=%d. Error: %s [%d]", i, strerror(errno), errno); + cpLog("reload pool server"); + ret = kill(CPGS->manager_pid, SIGUSR1); + if (ret == -1) + { + cpLog("reload failed, Error: %s [%d]", strerror(errno), errno); } break; default: @@ -607,10 +729,63 @@ static void cpSignalHanlde(int sig) { } } -void static cpSignalInit(void) { +int cpMutexLock(cpGroup *G) +{ + if (pthread_mutex_lock(&G->mutex_lock) != 0) + { + cpLog("pthread_mutex_lock. Error: %s [%d]", strerror(errno), errno); + return -1; + } + return 0; +} + +int cpMutexUnLock(cpGroup *G) +{ + if (pthread_mutex_unlock(&G->mutex_lock) != 0) + { + cpLog("pthread_mutex_unlock. Error: %s [%d]", strerror(errno), errno); + return -1; + } + return 0; +} + +int cpMutexTryLock(cpGroup *G) +{ + if (pthread_mutex_trylock(&G->mutex_lock) != 0) + { + cpLog("pthread_mutex_trylock. Error: %s [%d]", strerror(errno), errno); + return -1; + } + return 0; +} + +int cpPopWaitQueue(cpGroup *G, cpConnection *conn) +{ + cpConnection *wait_conn = &CPGS->conlist[G->first_wait_id]; //等待队列的连接 + wait_conn->worker_id = conn->worker_id; + wait_conn->worker_index = conn->worker_index; + wait_conn->group_id = conn->group_id; + wait_conn->release = CP_FD_NRELEASED; + if (wait_conn->next_wait_id) + { + G->first_wait_id = wait_conn->next_wait_id; + wait_conn->next_wait_id = 0; + } + else + { + G->first_wait_id = G->last_wait_id = 0; + } + int wait_pid = wait_conn->wait_fpm_pid; + wait_conn->wait_fpm_pid = 0; + CPGS->G[wait_conn->group_id].workers[wait_conn->worker_index].CPid = wait_pid; + return wait_pid; +} + +void static cpSignalInit(void) +{ cpSignalSet(SIGHUP, SIG_IGN, 1, 0); cpSignalSet(SIGPIPE, SIG_IGN, 1, 0); - cpSignalSet(SIGUSR1, cpSignalHanlde, 1, 0); + cpSignalSet(SIGUSR1, cpSignalHandle, 1, 0); cpSignalSet(SIGUSR2, SIG_IGN, 1, 0); - cpSignalSet(SIGTERM, cpSignalHanlde, 1, 0); + cpSignalSet(SIGTERM, cpSignalHandle, 1, 0); } diff --git a/cpWorker.c b/cpWorker.c old mode 100755 new mode 100644 index cad2040..a826b21 --- a/cpWorker.c +++ b/cpWorker.c @@ -17,155 +17,324 @@ #include "php_connect_pool.h" #include #include +zval *pdo_object = NULL; +zval *redis_object = NULL; -static void cpManagerSignalHanlde(int sig); +static void cpWorker_do_stop() +{ + cpGroup *G = &CPGS->G[CPWG.gid]; + if (G->workers[CPWG.id].pid != CPWG.pid && CPWG.event.pid != 0)//G->workers[CPWG.id].pid IS 0 or new pid + {//i am die already + int ret = write(CPWG.pipe_fd_read, &CPWG.event, sizeof (cpWorkerInfo)); //write back give the real worker + if (ret < 0) + { + cpLog("fifo read Error: %s [%d]", strerror(errno), errno); + } + } + exit(0); +} + +static void cpWorker_init(int worker_id, int group_id) +{ + //标识为worker进程 + CPGL.process_type = CP_PROCESS_WORKER; + cpWorker_attach_mem(worker_id, group_id); -static int cpWorker_loop(int worker_id) { CPWG.id = worker_id; + CPWG.gid = group_id; + CPWG.pid = getpid(); + char fifo_name[CP_FIFO_NAME_LEN] = {0}; - sprintf(fifo_name, "%s_%d", CP_FIFO_NAME_PRE, CPGC.group_id * CP_GROUP_LEN + worker_id); //client 2 worker - int pipe_fd_read = cpCreateFifo(fifo_name); + sprintf(fifo_name, "%s_%d", CP_FIFO_NAME_PRE, CP_WORKER_ID(group_id, worker_id)); //client 2 worker + CPWG.pipe_fd_read = cpCreateFifo(fifo_name); - sprintf(fifo_name, "%s_%d_1", CP_FIFO_NAME_PRE, CPGC.group_id * CP_GROUP_LEN + worker_id); //worker 2 client + sprintf(fifo_name, "%s_%d_1", CP_FIFO_NAME_PRE, CP_WORKER_ID(group_id, worker_id)); //worker 2 client int pipe_fd_write = cpCreateFifo(fifo_name); - CPGS->workers[worker_id].pipe_fd_write = pipe_fd_write; - cpShareMemory *sm_obj = &(CPGS->workers[worker_id].sm_obj); - - cpWorkerInfo event; - bzero(&event, sizeof (event)); - int ret, len = 0; - int event_len = sizeof (event); - while (CPGS->running) { + CPWG.pipe_fd_write = pipe_fd_write; +} + +static int cpWorker_loop(int worker_id, int group_id) +{ + cpWorker_init(worker_id, group_id); + cpGroup *G = &CPGS->G[group_id]; + cpShareMemory *sm_obj = &(G->workers[worker_id].sm_obj); + int ret; + cpSettitle(G->name); + cpSignalSet(SIGALRM, cpWorker_do_ping, 1, 0); + cpSignalSet(SIGTERM, cpWorker_do_stop, 1, 0); + alarm(CPGC.ping_time); + // zval *ret_value; + // CP_ALLOC_INIT_ZVAL(ret_value); + while (CPGS->running) + { zval *ret_value; - ALLOC_INIT_ZVAL(ret_value); - if (CPGS->workers[worker_id].pre_len) { - len = CPGS->workers[worker_id].pre_len; - CPGS->workers[worker_id].pre_len = 0; - } else { - ret = cpFifoRead(pipe_fd_read, &event, event_len); - if (!CPGS->workers[worker_id].run) { - CPGS->workers[worker_id].pre_len = event.len; //啊~~我要挂了,赶紧存起来 下次再用 - break; - } - len = event.len; - } - if (sm_obj->mem == NULL) { - if ((sm_obj->mem = shmat(sm_obj->shmid, NULL, 0)) < 0) { - cpLog("attach sys mem error Error: %s [%d]", strerror(errno), errno); - } - } - if (ret < 0) { + CP_ALLOC_INIT_ZVAL(ret_value); + bzero(&CPWG.event, sizeof (cpWorkerInfo)); + ret = cpFifoRead(CPWG.pipe_fd_read, &CPWG.event, sizeof (cpWorkerInfo)); + if (ret < 0) + { cpLog("fifo read Error: %s [%d]", strerror(errno), errno); } - php_msgpack_unserialize(ret_value, sm_obj->mem, len); - CPWG.clientPid = event.pid; + if (CPWG.event.pid != G->workers[CPWG.id].CPid) + {//pipe数据里面的fpm pid和worker应该服务的pid不一样 + cpLog("warning: read a wrong event,maybe you restart the pool server,%d,%d,%d", CPWG.event.pid, CPWG.id, G->workers[CPWG.id].CPid); + continue; + } + CPWG.working = 1; +#if PHP_MAJOR_VERSION < 7 + php_msgpack_unserialize(ret_value, sm_obj->mem, CPWG.event.len); +#else + php_swoole_unserialize(sm_obj->mem, CPWG.event.len, ret_value, NULL); +#endif worker_onReceive(ret_value); + CPWG.working = 0; } return SUCCESS; } -int cpFork_one_worker(int worker_id) { +int cpFork_one_worker(int worker_id, int group_id) +{ int pid, ret; pid = fork(); - if (pid < 0) { + if (pid < 0) + { cpLog("Fork Worker failed. Error: %s [%d]", strerror(errno), errno); return FAILURE; - } else if (pid == 0) { - //标识为worker进程 - CPGL.process_type = CP_PROCESS_WORKER; - CPGS->workers[worker_id].run = 1; - ret = cpWorker_loop(worker_id); + } + else if (pid == 0) + { + ret = cpWorker_loop(worker_id, group_id); exit(ret); - } else { + } + else + { return pid; } } -static void cpManagerRecycle(int sig) { - int i, recycle_num = 0; - if (pthread_spin_trylock(CPGS->spin_lock) == 0) { - // for (i = CPGS->worker_num - 1; i >= 0; i--) { - // cpLog("index is %d,pid is %d,status is %d", i, CPGS->workers[i].pid, CPGS->workers_status[i]); - // } - for (i = CPGS->worker_num - 1; i >= CPGC.worker_min; i--) { - if (CPGS->workers_status[i] == CP_WORKER_BUSY) {//已经busy了就退出,否则会有跳号bug - break; - } - if (CPGS->workers[i].pid == 0) {//争抢的时候就++了 所以会出现0的情况 - continue; - } - if (CPGS->workers_status[i] == CP_WORKER_IDLE) {//当前worker数大于最小 并且空闲 - int ret = kill(CPGS->workers[i].pid, SIGTERM); - if (ret == -1) { - cpLog("[Manager]kill failed, id=%d. Error: %s [%d]", i, strerror(errno), errno); - } else { - CPGS->worker_num--; - CPGS->workers_status[i] = CP_WORKER_DEL; - CPGS->workers[i].pid = 0; - cpShareMemory *sm_obj = &(CPGS->workers[i].sm_obj); - sm_obj->mem = NULL; - if (++recycle_num >= CPGC.recycle_num) { - break; //一个一个回收 +static void cpManagerRecycle(int sig) +{ + int i, recycle_num, j; + // cpLog("monitor:start___________________"); + for (j = 0; j < CPGS->group_num; j++) + { + cpGroup *G = &CPGS->G[j]; + recycle_num = 0; + // cpLog("monitor:the '%s' have used %d,the max conn num is %d, the min num is %d", G->name, G->worker_num, G->worker_max, G->worker_min); + if (G->lock(G) == 0) + { + // for (i = G->worker_num - 1; i >= 0; i--) + // { + // cpLog("index is %d,pid is %d,status is %d", i, G->workers[i].pid, G->workers_status[i]); + // } + // cpLog("________________"); + + for (i = G->worker_num - 1; i >= G->worker_min; i--) + { + if (G->workers_status[i] == CP_WORKER_BUSY) + {//已经busy了就退出,否则会有跳号bug + break; + } + if (G->workers[i].pid == 0) + {//争抢的时候就++了 所以会出现0的情况 + continue; + } + if (G->workers_status[i] == CP_WORKER_IDLE) + {//当前worker数大于最小 并且空闲 + int ret = kill(G->workers[i].pid, SIGTERM); + if (ret == -1) + { + cpLog("[Manager]kill failed, id=%d. Error: %s [%d]", i, strerror(errno), errno); + } + else + { + G->worker_num--; + G->workers_status[i] = CP_WORKER_DEL; + G->workers[i].pid = 0; + cpShareMemory *sm_obj = &(G->workers[i].sm_obj); + sm_obj->mem = NULL; + if (++recycle_num >= CPGC.recycle_num) + { + break; //一个一个回收 + } } } } - } - if (pthread_spin_unlock(CPGS->spin_lock) != 0) { - cpLog("pthread_spin_unlock. Error: %s [%d]", strerror(errno), errno); + G->unLock(G); } } + // cpLog("monitor:end___________________\n"); alarm(CPGC.idel_time); } -static void cpManagerAdd(int sig) { - int i; - for (i = CPGS->worker_num - 1; i >= CPGC.worker_min; i--) { - if (CPGS->workers[i].pid == 0) {//只创建刚分配并且pid为0的 - int new_pid = cpFork_one_worker(i); - if (new_pid < 0) { - // CPGS->workers[i].pid = -1;//todo fork失敗的處理 - cpLog("Fork worker process failed. Error: %s [%d]", strerror(errno), errno); - } else { - CPGS->workers[i].pid = new_pid; +static void cpManagerAdd(int sig) +{ + int i, j; + + for (j = 0; j < CPGS->group_num; j++) + { + cpGroup *G = &CPGS->G[j]; + for (i = G->worker_num - 1; i >= 0; i--)//for not set source in pool.ini + //for (i = G->worker_num - 1; i >= G->worker_min; i--) + { + if (G->workers[i].pid == 0) + {//只创建刚分配并且pid为0的 + int new_pid = cpFork_one_worker(i, j); + if (new_pid < 0) + { + cpLog("Fork worker process failed. Error: %s [%d]", strerror(errno), errno); + } + else + { + G->workers[i].pid = new_pid; + } } } } + } -static void cpManagerReload(int sig) { - zval *group_conf = NULL; +static void cpManagerReload(int sig) +{ + zval *group_conf = NULL, *v; group_conf = cpGetConfig(CPGC.ini_file); - if (!Z_BVAL_P(group_conf)) { - cpLog("parse ini file[%s] error,%s reload error!", CPGC.ini_file, CPGC.title); - } else { - zval **v, **conf; - if (zend_hash_find(Z_ARRVAL_P(group_conf), CPGC.title, strlen(CPGC.title) + 1, (void **) &conf) == SUCCESS) { - if (zend_hash_find(Z_ARRVAL_PP(conf), ZEND_STRS("pool_max"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGS->worker_max = (int) Z_LVAL_PP(v); + int gid = 0; + zval *gid_ptr = NULL; + cpGroup *G = NULL; + if (!Z_BVAL_P(group_conf)) + { + cpLog("parse ini file[%s] reload error!", CPGC.ini_file); + } + else + { + zval *config; + char *name; + int keytype; + uint32_t keylen; + + CP_HASHTABLE_FOREACH_START2(CP_Z_ARRVAL_P(group_conf), name, keylen, keytype, config) + { + if (strcmp(name, "common") != 0) + { + if (cp_zend_hash_find(Z_ARRVAL_P(CPGS->group), name, strlen(name) + 1, (void **) &gid_ptr) == SUCCESS) + { + gid = Z_LVAL_P(gid_ptr); + G = &CPGS->G[gid]; + } + else + { + cpLog("can not add datasource when the server runing,if you want add it please restart"); + return; + } + if (G->lock(G) == 0) + { + if (cp_zend_hash_find(Z_ARRVAL_P(config), ZEND_STRS("pool_max"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + G->worker_max = (int) Z_LVAL_P(v); + } + if (cp_zend_hash_find(Z_ARRVAL_P(config), ZEND_STRS("pool_min"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + int new_min = (int) Z_LVAL_P(v); + if (new_min > G->worker_min) + {//增加最小 + while (G->worker_num < new_min) + { + cpCreate_worker_mem(G->worker_num, gid); + G->workers_status[G->worker_num] = CP_WORKER_IDLE; + G->worker_num++; //先加 线程安全 + int new_pid = cpFork_one_worker(G->worker_num - 1, gid); + if (new_pid < 0) + { + cpLog("Fork worker process failed. Error: %s [%d]", strerror(errno), errno); + } + else + { + G->workers[G->worker_num - 1].pid = new_pid; + } + } + } + G->worker_min = new_min; + } + + G->unLock(G); + } } - if (zend_hash_find(Z_ARRVAL_PP(conf), ZEND_STRS("pool_min"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.worker_min = (int) Z_LVAL_PP(v); + else + { + if (cp_zend_hash_find(Z_ARRVAL_P(config), ZEND_STRS("recycle_num"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.recycle_num = (int) Z_LVAL_P(v); + } + if (cp_zend_hash_find(Z_ARRVAL_P(config), ZEND_STRS("idel_time"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.idel_time = (int) Z_LVAL_P(v); + } + if (cp_zend_hash_find(Z_ARRVAL_P(config), ZEND_STRS("ping_time"), (void **) &v) == SUCCESS) + { + convert_to_long(v); + CPGC.ping_time = (int) Z_LVAL_P(v); + } } - if (zend_hash_find(Z_ARRVAL_PP(conf), ZEND_STRS("recycle_num"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.recycle_num = (int) Z_LVAL_PP(v); + } + CP_HASHTABLE_FOREACH_END(); + cp_zval_ptr_dtor(&group_conf); + } +} + +static void cpFind_restart_worker(int pid, sigset_t *block_alarm, int worker_exit_code) +{ + int i, j, new_pid; + for (j = 0; j < CPGS->group_num; j++) + { + cpGroup *G = &CPGS->G[j]; + for (i = G->worker_num; i >= 0; i--) + { + if (pid != G->workers[i].pid || G->workers_status[i] == CP_WORKER_DEL) + {//对比pid||回收的不拉起 + continue; } - if (zend_hash_find(Z_ARRVAL_PP(conf), ZEND_STRS("idel_time"), (void **) &v) == SUCCESS) { - convert_to_long(*v); - CPGC.idel_time = (int) Z_LVAL_PP(v); + else + { + cpLog("worker exit!worker index %d,worker id %d,exit code %d\n", i, pid, WEXITSTATUS(worker_exit_code)); + cpShareMemory *sm_obj = &(G->workers[i].sm_obj); + sm_obj->mem = NULL; + pid = 0; + + char fifo_name[CP_FIFO_NAME_LEN] = {0}; + sprintf(fifo_name, "%s_%d_1", CP_FIFO_NAME_PRE, CP_WORKER_ID(j, i)); //worker 2 client + int pipe_fd_write = cpCreateFifo(fifo_name); + cpWorkerInfo worker_event = {0}; + worker_event.type = CP_SIGEVENT_DIE; + int ret = write(pipe_fd_write, &worker_event, sizeof (worker_event)); + if (ret == -1) + { + cpLog("master write error Error: %s [%d]", strerror(errno), errno); + } + close(pipe_fd_write); + + new_pid = cpFork_one_worker(i, j); + if (new_pid < 0) + { + cpLog("Fork worker process failed. Error: %s [%d]", strerror(errno), errno); + sigprocmask(SIG_UNBLOCK, block_alarm, NULL); + } + else + { + G->workers[i].pid = new_pid; + } } - } else { - cpLog("find %s failed,The reload can only modify 'pool_min','pool_max','recycle_num' and 'idel_time',if you want modify other options please restart pool", CPGC.title); } - zval_ptr_dtor(&group_conf); } + } -int cpWorker_manager_loop() { - int pid, new_pid; - int i; +int cpWorker_manager_loop() +{ + int pid; int worker_exit_code; //reload config @@ -182,34 +351,93 @@ int cpWorker_manager_loop() { sigaddset(&block_alarm, SIGRTMIN); sigaddset(&block_alarm, SIGUSR1); - while (CPGS->running == 1) { + while (CPGS->running == 1) + { + if (CPGS->group_num == 0) + {//for default max,min + sleep(1); + continue; + } pid = wait(&worker_exit_code); sigprocmask(SIG_BLOCK, &block_alarm, NULL); - if (CPGS->running == 1 && pid > 0) { - for (i = CPGS->worker_num; i >= 0; i--) { - if (pid != CPGS->workers[i].pid || CPGS->workers_status[i] == CP_WORKER_DEL) {//对比pid||回收的不拉起 - continue; - } else { - if (CPGS->workers[i].run == 0) { - cpLog("restart worker!worker index %d,worker id %d,exit code %d\n", i, pid, WEXITSTATUS(worker_exit_code)); - } else { - cpLog("worker exit!worker index %d,worker id %d,exit code %d\n", i, pid, WEXITSTATUS(worker_exit_code)); - } - cpShareMemory *sm_obj = &(CPGS->workers[i].sm_obj); - sm_obj->mem = NULL; - pid = 0; - new_pid = cpFork_one_worker(i); - if (new_pid < 0) { - cpLog("Fork worker process failed. Error: %s [%d]", strerror(errno), errno); - sigprocmask(SIG_UNBLOCK, &block_alarm, NULL); - return FAILURE; - } else { - CPGS->workers[i].pid = new_pid; - } + if (CPGS->running == 1 && pid > 0) + { + + if (pid == CPGS->ping_workers->pid) + { + cpLog("ping worker exit"); + int ping_pid = cpFork_ping_worker(); + if (ping_pid < 0) + { + cpLog("Fork ping process fail"); + } + else + { + CPGS->ping_workers->pid = ping_pid; } } + cpFind_restart_worker(pid, &block_alarm, worker_exit_code); } sigprocmask(SIG_UNBLOCK, &block_alarm, NULL); } return SUCCESS; -} \ No newline at end of file +} + +CPINLINE int cpCreate_worker_mem(int worker_id, int group_id) +{ + cpShareMemory *sm_obj = &(CPGS->G[group_id].workers[worker_id].sm_obj); + sprintf(sm_obj->mmap_name, "%s_%d", CP_MMAP_NAME_PRE, CP_WORKER_ID(group_id, worker_id)); + sm_obj->size = CPGS->max_buffer_len; + if (cp_create_mmap_file(sm_obj) < 0) + { + return FAILURE; + } + return SUCCESS; +} + +CPINLINE int cpWorker_attach_mem(int worker_id, int group_id) +{ + cpShareMemory *sm_obj = &(CPGS->G[group_id].workers[worker_id].sm_obj); + if (!cp_mmap_calloc_with_file(sm_obj)) + { + return FAILURE; + } + return SUCCESS; +} + +//fix the gone away + +void cpWorker_do_ping() +{ + zval * stmt_value = NULL; + zval method, **args[1], *sql = NULL; + if (CPWG.working == 1) + { + alarm(CPGC.ping_time); + return; + } + CP_ZVAL_STRING(&method, "query", 0); + CP_MAKE_STD_ZVAL(sql); + CP_ZVAL_STRING(sql, "select 1", 0); + args[0] = &sql; + if (pdo_object != NULL) + { + cp_call_user_function_ex(NULL, &pdo_object, &method, &stmt_value, 1, args, 0, NULL TSRMLS_CC); + if (stmt_value) + cp_zval_ptr_dtor(&stmt_value); + } +#if PHP_MAJOR_VERSION==7 + zval_ptr_dtor(&method); + zval_ptr_dtor(sql); +#else + efree(sql); +#endif + if (EG(exception)) + { + cpLog("the connection is broken,we will del it and reconnect at next request,%p", pdo_object); + CP_DEL_OBJ(pdo_object); + EG(exception) = NULL; + } + alarm(CPGC.ping_time); +} + diff --git a/examples/demon.php b/examples/demon.php new file mode 100644 index 0000000..309edc9 --- /dev/null +++ b/examples/demon.php @@ -0,0 +1,131 @@ +connect("192.168.20.130"); +$obj->select(5); +$obj->set("test", '1111'); +var_dump($obj->get("test")); + +$obj = new PDO('mysql:host=192.168.20.130;dbname=test1', "admin", "admin"); +$rs = $obj->query("show tables"); +var_dump($rs->fetchAll()); + +//*****************use pool(使用了连接池)*********************************/ +$obj = new redisProxy(); +$rs = $obj->connect("192.168.20.130"); +$obj->select(5); +$obj->set("test", '1111'); +var_dump($obj->get("test")); +$obj->release(); + +$obj1 = new pdoProxy('mysql:host=192.168.20.131;dbname=db1', "admin", "admin"); +$rs = $obj1->query("show tables"); +var_dump($rs->fetchAll()); +$obj1->release(); + + +/* * ****************异步 pdo和redis操作********************************************** + * 依赖 swoole的event函数 + */ +include './asyncClass.php'; +$obj = new asyncRedisProxy(); +$obj->connect("127.0.0.1", "6379"); +$obj->set("a", 11111, function($obj, $ret) { + $obj->get("a", function($obj, $data) { + var_dump($data); + $obj->release(); //release to con pool + }); +}); + + +$obj2 = new asyncPdoProxy('mysql:host=192.168.1.19;dbname=mz_db', "public_user", "1qa2ws3ed"); +$obj2->query("select 1 from mz_user where user_id=299", function($obj, $stmt) { + $arr = $stmt->fetchAll(); + var_dump($arr); + $obj->query("select 2 from mz_user where user_id=299", function($obj, $stmt) { + $arr = $stmt->fetchAll(); + var_dump($arr); + $obj->release(); //release to con pool + }); +}); + + +$obj3 = new asyncPdoProxy('mysql:host=192.168.1.19;dbname=mz_db', "public_user", "1qa2ws3ed"); +$obj3->exec("insert into t1(name) values('111111')", function($obj, $data) { + var_dump($data); + $obj->release(); ////release to con pool +}); + +$obj4 = new asyncPdoProxy('mysql:host=192.168.1.19;dbname=mz_db', "public_user", "1qa2ws3ed"); +$stmt = $obj4->prepare("select * from mz_account where user_id=:user_id"); +$stmt->bindParam(':user_id', "311"); +$stmt->execute(function($stmt, $ret) { + $data = $stmt->fetchAll(); + var_dump($data); + $stmt->release(); +}); + + +//*******************use master slave(最新版本支持了读写分离和从库的负载均衡 用法如下)***********************/ +$config = array( + 'master' => array( + 'data_source' => "mysql:host=192.168.1.19;dbname=db1", + 'username' => "public_user", + 'pwd' => "1qa2ws3ed", + 'options' => array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_TIMEOUT => 3, + PDO::ATTR_CASE => PDO::CASE_UPPER, + ), + ), + 'slave' => array( + "0" => array( + 'data_source' => "mysql:host=192.168.1.20;dbname=db2", + 'username' => "public_user", + 'pwd' => "1qa2ws3ed", + 'options' => array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_TIMEOUT => 3, + PDO::ATTR_CASE => PDO::CASE_UPPER, + ), + ), + "1" => array( + 'data_source' => "mysql:host=192.168.1.21;dbname=db3", + 'username' => "public_user", + 'pwd' => "1qa2ws3ed", + 'options' => array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_TIMEOUT => 3, + PDO::ATTR_CASE => PDO::CASE_LOWER, + ), + ), + ), +); +/* * *************************"select"和"show"开头的语句 走随机从库********** */ +$obj1 = new pdoProxy($config); +$rs = $obj1->query("select * from test limit 1"); +var_dump($rs->fetchAll()); //走随机从库 +$obj1->release(); + +/* * **************************读强行走主库*************************** */ +$obj1->enable_slave = false; +$rs = $obj1->query("select * from test limit 1"); +var_dump($rs->fetchAll()); //读主库 +$obj1->release(); + +/* * *************************除了"select"和"show"开头的语句 都走主库********** */ +$sql = "insert into `test` (tid) values (5)"; +$rs = $obj1->exec($sql); //走主库 +$obj1->release(); + +/* tips: + * 1、The relase() method will release the connections to the pool that the process holds. + * 2、after rshutdown/mshutdown will trigger the release() function. + */ + + +/* 说明: + * 1、relase方法:通知中间件,可以将这个进程持有的链接放回连接池 + * 2、请求结束(rshutdown/mshutdown阶段)会调用自动调用release + */ diff --git a/examples/docker_demo.php b/examples/docker_demo.php new file mode 100644 index 0000000..379acb7 --- /dev/null +++ b/examples/docker_demo.php @@ -0,0 +1,23 @@ +connect($redis_host); +$obj->select(5); +$obj->set("test", '1111'); +var_dump($obj->get("test")); + +/** + * 使用连接池 + */ +$obj = new redisProxy(); +$rs = $obj->connect($redis_host); +$obj->select(5); +$obj->set("test", '1111'); +var_dump($obj->get("test")); +$obj->release(); diff --git a/examples/frame_example/FRedis.php b/examples/frame_example/FRedis.php new file mode 100644 index 0000000..316a62a --- /dev/null +++ b/examples/frame_example/FRedis.php @@ -0,0 +1,1680 @@ +select($db_name); +// } +// return $redis[$db_name]; + + return new self($db_name, $conf); + } + + //初始化 + public function __construct($db_name, $conf) + { + global $_F; + if ($_F['test_mode']) { + if ($conf == 'redis_config') { + $conf = 'redis_test_config'; + } + if ($conf == 'redis_config_1') { + $conf = 'redis_test_config_1'; + } + if ($conf == 'redis_config_87') { + $conf = 'redis_test_config_87'; + } + if ($conf == 'redis_config_5') { + $conf = 'redis_test_config_5'; + } + if ($conf == 'redis_config_9') { + $conf = 'redis_test_config_9'; + } + if ($conf == 'redis_config_10') { + $conf = 'redis_test_config_10'; + } + if ($conf == 'redis_config_12') { + $conf = 'redis_test_config_12'; + } + } + + $config = FConfig::get($conf); + + $this->_HOST = $config['REDIS_HOST']; + + $this->_PORT = $config['REDIS_PORT']; + + $this->_TIMEOUT = $config['REDIS_TIMEOUT']; + + $this->_DBNAME = $db_name; + + $this->_CTYPE = $config['REDIS_CTYPE']; + + if (!isset($this->_REDIS)) { + + if (class_exists('redisProxy')) { + $this->_REDIS = new redisProxy(); + $this->_REDIS->connect($this->_HOST, $this->_PORT); + } else { + $this->_REDIS = new Redis(); + + $this->connect($this->_HOST, $this->_PORT, $this->_TIMEOUT, $this->_CTYPE); + } + } + + } + + + /** + * 连接redis服务器 + */ + private function connect($host, $port, $timeout, $type) + { + switch ($type) { + + case 1: + $this->_REDIS->connect($host, $port, $timeout); +// $this->_REDIS->select($this->_DBNAME); + break; + + case 2: + $this->_REDIS->pconnect($host, $port, $timeout); +// $this->_REDIS->select($this->_DBNAME); + break; + + default: + + break; + + } + } + + public function selectDb($index) + { + $this->_REDIS->select($index); + } + + + /** + * 查看redis连接是否断开 + * @return $return bool true:连接未断开 false:连接已断开 + */ + public function ping() + { + + $return = null; + + + $return = $this->_REDIS->ping(); + + + return 'PONG' ? true : false; + + } + + + /** + * 设置redis模式参数 + * @param $name 参数名 + * @param $value 参数值 + * @return $return true/false + */ + public function setOption($name, $value) + { + return $this->_REDIS->setOption($name, $value); + } + + + /** + * 获取redis模式参数 + * @param $name 要获取的参数 + */ + public function getOption($name) + { + return $this->_REDIS->getOption($name); + } + + + /** + * 返回Redis实例。 + * @return null|Redis + */ + public function getRedis() + { + return $this->_REDIS; + } + + + /** + * 写入key-value + * @param $key string 要存储的key名 + * @param $value mixed 要存储的值 + * @param $type int 写入方式 0:不添加到现有值后面 1:添加到现有值的后面 默认0 + * @param $repeat int 0:不判断重复 1:判断重复 + * @param $time float 过期时间(S) + * @param $old int 1:返回旧的value 默认0 + * @return $return bool true:成功 flase:失败 + */ + public function set($key, $value, $type = 0, $repeat = 0, $time = 0, $old = 0) + { + + $return = null; + +// $value = json_encode($value); + + + if ($type) { + + $return = $this->_REDIS->append($key, $value); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } else { + + if ($old) { + + $return = $this->_REDIS->getSet($key, $value); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } else { + + if ($repeat) { + + $return = $this->_REDIS->setnx($key, $value); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } else { + + if ($time && is_numeric($time)) { + $return = $this->_REDIS->setex($key, $time, $value); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } else { + $return = $this->_REDIS->set($key, $value); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } + + } + + } + + } + + + return $return; + + } + + + /** + * 获取某个key值 如果指定了start end 则返回key值的start跟end之间的字符 + * @param $key string/array 要获取的key或者key数组 + * @param $start int 字符串开始index + * @param $end int 字符串结束index + * @return $return mixed 如果key存在则返回key值 如果不存在返回false + */ + public function get($key = null, $start = null, $end = null) + { + $return = null; + + if (is_array($key) && !empty($key)) { + + $return = $this->_REDIS->getMultiple($key); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } else { + + if (isset($start) && isset($end)) { + $return = $this->_REDIS->getRange($key, $start, $end); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } else { + $return = $this->_REDIS->get($key); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } + + } + + return $return; + } + + + /** + * 删除某个key值 + * @param $key array key数组 + * @return $return longint 删除成功的key的个数 + */ + public function delete($key = array()) + { + + $return = null; + + + $return = $this->_REDIS->delete($key); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + + return $return; + + } + + + /** + * 判断某个key是否存在 + * @param $key string 要查询的key名 + */ + public function exists($key) + { + + $return = null; + + + $return = $this->_REDIS->exists($key); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + + return $return; + + } + + + /** + * key值自增或者自减 + * @param $key string key名 + * @param $type int 0:自减 1:自增 默认为1 + * @param $n int 自增步长 默认为1 + */ + public function deinc($key, $type = 1, $n = 1) + { + $return = null; + + $n = (int)$n; + + + switch ($type) { + + case 0: + + if ($n == 1) { + $return = $this->_REDIS->decr($key); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } else if ($n > 1) { + $return = $this->_REDIS->decrBy($key, $n); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } + + break; + + case 1: + + if ($n == 1) { + $return = $this->_REDIS->incr($key); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } else if ($n > 1) { + $return = $this->_REDIS->incrBy($key, $n); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } + + break; + + default: + + $return = false; + + break; + + } + + + return $return; + + } + + + /** + * 同时给多个key赋值 + * @param $data array key值数组 array('key0'=>'value0','key1'=>'value1') + */ + public function mset($data) + { + + $return = null; + + + $return = $this->_REDIS->mset($data); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 查询某个key的生存时间 + * @param $key string 要查询的key名 + */ + public function ttl($key) + { + + $return = null; + + + $return = $this->_REDIS->ttl($key); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 删除到期的key + * @param $key string key名 + */ + public function persist($key) + { + + $return = null; + + + $return = $this->_REDIS->persist($key); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 获取某一key的value + * @param $key string key名 + */ + public function strlen($key) + { + + $return = null; + + + $return = $this->_REDIS->strlen($key); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + + //+++-------------------------队列操作-------------------------+++// + + + /** + * 入队列 + * @param $list string 队列名 + * @param $value mixed 入队元素值 + * @param $deriction int 0:数据入队列头(左) 1:数据入队列尾(右) 默认为0 + * @param $repeat int 判断value是否存在 0:不判断存在 1:判断存在 如果value存在则不入队列 + */ + public function listPush($list, $value, $direction = 0, $repeat = 0) + { + + $return = null; + + + switch ($direction) { + + case 0: + + if ($repeat) { + $return = $this->_REDIS->lPushx($list, $value); + } else { + $return = $this->_REDIS->lPush($list, $value); + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + break; + + case 1: + + if ($repeat) { + $return = $this->_REDIS->rPushx($list, $value); + } else { + $return = $this->_REDIS->rPush($list, $value); + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + break; + + default: + + $return = false; + + break; + + } + + + return $return; + + } + + + /** + * 出队列 + * @param $list1 string 队列名 + * @param $direction int 0:数据入队列头(左) 1:数据入队列尾(右) 默认为0 + * @param $list2 string 第二个队列名 默认null + * @param $timeout int timeout为0:只获取list1队列的数据 + * timeout>0:如果队列list1为空 则等待timeout秒 如果还是未获取到数据 则对list2队列执行pop操作 + */ + public function listPop($list1, $direction = 0, $list2 = null, $timeout = 0) + { + + $return = null; + + + switch ($direction) { + + case 0: + + if ($timeout && $list2) { + $return = $this->_REDIS->blPop($list1, $list2, $timeout); + } else { + $return = $this->_REDIS->lPop($list1); + } + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + break; + + case 1: + + if ($timeout && $list2) { + $return = $this->_REDIS->brPop($list1, $list2, $timeout); + } else { + $return = $this->_REDIS->rPop($list1); + } + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + break; + + default: + + $return = false; + + break; + + } + + + return $return; + + } + + + /** + * 获取队列中元素数 + * @param $list string 队列名 + */ + public function listSize($list) + { + + $return = null; + + + $return = $this->_REDIS->lSize($list); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + + } + + + /** + * 为list队列的index位置的元素赋值 + * @param $list string 队列名 + * @param $index int 队列元素位置 + * @param $value mixed 元素值 + */ + public function listSet($list, $index = 0, $value = null) + { + + $return = null; + + + $return = $this->_REDIS->lSet($list, $index, $value); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + + } + + + /** + * 获取list队列的index位置的元素值 + * @param $list string 队列名 + * @param $index int 队列元素开始位置 默认0 + * @param $end int 队列元素结束位置 $index=0,$end=-1:返回队列所有元素 + */ + public function listGet($list, $index = 0, $end = null) + { + + $return = null; + + + if ($end) { + + $return = $this->_REDIS->lRange($list, $index, $end); + + } else { + + $return = $this->_REDIS->lGet($list, $index); + + } + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 截取list队列,保留start至end之间的元素 + * @param $list string 队列名 + * @param $start int 开始位置 + * @param $end int 结束位置 + */ + public function listTrim($list, $start = 0, $end = -1) + { + + $return = null; + + + $return = $this->_REDIS->lTrim($list, $start, $end); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + + } + + + /** + * 删除list队列中count个值为value的元素 + * @param $list string 队列名 + * @param $value int 元素值 + * @param $count int 删除个数 0:删除所有 >0:从头部开始删除 <0:从尾部开始删除 默认为0删除所有 + */ + public function listRemove($list, $value, $count = 0) + { + + $return = null; + $return = $this->_REDIS->lRem($list, $value, $count); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + + } + + + /** + * 在list中值为$value1的元素前Redis::BEFORE或者后Redis::AFTER插入值为$value2的元素 + * 如果list不存在,不会插入,如果$value1不存在,return -1 + * @param $list string 队列名 + * @param $location int 插入位置 0:之前 1:之后 + * @param $value1 mixed 要查找的元素值 + * @param $value2 mixed 要插入的元素值 + */ + public function listInsert($list, $location = 0, $value1, $value2) + { + + $return = null; + + + switch ($location) { + + case 0: + + $return = $this->_REDIS->lInsert($list, Redis::BEFORE, $value1, $value2); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + break; + + case 1: + + $return = $this->_REDIS->lInsert($list, Redis::AFTER, $value1, $value2); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + break; + + default: + + $return = false; + + break; + + } + + + return $return; + + } + + + /** + * pop出list1的尾部元素并将该元素push入list2的头部 + * @param $list1 string 队列名 + * @param $list2 string 队列名 + */ + public function rpoplpush($list1, $list2) + { + + $return = null; + + + $return = $this->_REDIS->rpoplpush($list1, $list2); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + + } + + + + //+++-------------------------集合操作-------------------------+++// + + + /** + * 将value写入set集合 如果value存在 不写入 返回false + * 如果是有序集合则根据score值更新该元素的顺序 + * @param $set string 集合名 + * @param $value mixed 值 + * @param $stype int 集合类型 0:无序集合 1:有序集和 默认0 + * @param $score int 元素排序值 + */ + public function setAdd($set, $value = null, $stype = 0, $score = null) + { + + $return = null; + + + if ($stype && $score !== null) { + + $return = $this->_REDIS->zAdd($set, $score, $value); + + } else { + + $return = $this->_REDIS->sAdd($set, $value); + + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + + } + + + /** + * 移除set1中的value元素 如果指定了set2 则将该元素写入set2 + * @param $set1 string 集合名 + * @param $value mixed 值 + * @param $stype int 集合类型 0:无序集合 1:有序集和 默认0 + * @param $set2 string 集合名 + */ + public function setMove($set1, $value = null, $stype = 0, $set2 = null) + { + + $return = null; + + + if ($set2) { + + $return = $this->_REDIS->sMove($set1, $set2, $value); + + } else { + + if ($stype) { + + $return = $this->_REDIS->zRem($set1, $value); + + } else { + $return = $this->_REDIS->sRem($set1, $value); + } + + } + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 查询set中是否有value元素 + * @param $set string 集合名 + * @param $value mixed 值 + */ + public function setSearch($set, $value = null, $type = 0) + { + + $return = null; + + if ($type == 0) { + $return = $this->_REDIS->sIsMember($set, $value); + } else { + $return = $this->_REDIS->ZSCORE($set, $value); + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + + } + + + /** + * 返回set中所有元素个数 有序集合要指定$stype=1 + * 如果是有序集合并指定了$start和$end 则返回score在start跟end之间的元素个数 + * @param $set string 集合名 + * @param $stype int 集合类型 0:无序集合 1:有序集和 默认0 + * @param $start int 开始index + * @param $end int 结束index + */ + public function setSize($set, $stype = 0, $start = 0, $end = 0) + { + + $return = null; + + + if ($stype) { + + if ($start && $end) $return = $this->_REDIS->zCount($set, $start, $end); + + else $return = $this->_REDIS->zSize($set); + + } else { + + $return = $this->_REDIS->sSize($set); + + } + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 随机返回set中一个元素并可选是否删除该元素 + * @param $set string 集合名 + * @param $isdel int 是否删除该元素 0:不删除 1:删除 默认为0 + */ + public function setPop($set, $isdel = 0) + { + + $return = null; + + + if ($isdel) { + + $return = $this->_REDIS->sPop($set); + + } else { + + $return = $this->_REDIS->sRandMember($set); + + } + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 求交集 并可选是否将交集保存到新集合 + * @param $set array 集合名数组 + * @param $newset string 要保存到的集合名 默认为null 即不保存交集到新集合 + * @param $stype int 集合类型 0:无序集合 1:有序集和 默认0 + * @param $weight array 权重 执行function操作时要指定的每个集合的相同元素所占的权重 默认1 + * @param $function string 不同集合的相同元素的取值规则函数 SUM:取元素值的和 MAX:取最大值元素 MIN:取最小值元素 + */ + public function setInter($set, $newset = null, $stype = 0, $weight = array(1), $function = 'SUM') + { + + $return = array(); + + + if (is_array($set) && !empty($set)) { + + if ($newset) { + + if ($stype) $return = $this->_REDIS->zInter($newset, $set, $weight, $function); + + else $return = $this->_REDIS->sInterStore($newset, $set); + + } else { + + $return = $this->_REDIS->sInter($set); + + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } + + + return $return; + + } + + + /** + * 求并集 并可选是否将并集保存到新集合 + * @param $set array 集合名数组 + * @param $newset string 要保存到的集合名 默认为null 即不保存交集到新集合 + * @param $stype int 集合类型 0:无序集合 1:有序集和 默认0 + * @param $weight array 权重 执行function操作时要指定的每个集合的相同元素所占的权重 默认1 + * @param $function string 不同集合的相同元素的取值规则函数 SUM:取元素值的和 MAX:取最大值元素 MIN:取最小值元素 + */ + public function setUnion($set, $newset = null, $stype = 0, $weight = array(1), $function = 'SUM') + { + + $return = array(); + + + if (is_array($set) && !empty($set)) { + + if ($newset) { + + if ($stype) $return = $this->_REDIS->zUnion($newset, $set, $weight, $function); + + else $return = $this->_REDIS->sUnionStore($newset, $set); + + } else { + + $return = $this->_REDIS->sUnion($set); + + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } + + + return $return; + + } + + + /** + * 求差集 并可选是否将差集保存到新集合 + * @param $set array 集合名数组 + * @param $newset string 要保存到的集合名 默认为null 即不保存交集到新集合 + */ + public function setDiff($set, $newset = null) + { + + $return = array(); + + + if (is_array($set) && !empty($set)) { + + if ($newset) { + + $return = $this->_REDIS->sDiffStore($newset, $set); + + } else { + + $return = $this->_REDIS->sDiff($set); + + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } + + + return $return; + + } + + + /** + * 返回set中所有元素 + * @param $set string 集合名 + */ + public function setMembers($set) + { + + $return = null; + + + $return = $this->_REDIS->sMembers($set); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 排序 分页等 + * @param $set string 集合名 + * @param $option array 选项 + */ + public function setSort($set, $option) + { + + $return = null; + + $default_option = array( + + 'by' => 'some_pattern_*', //要匹配的排序value值 + + 'limit' => array(0, 1), //array(start,length) + + 'get' => 'some_other_pattern_*', //多个匹配格式:array('some_other_pattern1_*','some_other_pattern2_*') + + 'sort' => 'asc', // asc|desc 默认asc + + 'alpha' => TRUE, // + + 'store' => 'some_need_pattern_*' //永久性排序值 + + ); + + + $option = array_merge($default_option, $option); + + + $return = $this->_REDIS->sort($set, $option); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + + //+++-------------------------有序集合操作-------------------------+++// + + + /** + * ***只针对有序集合操作 + * 返回set中index从start到end的所有元素 + * @param $set string 集合名 + * @param $start int 开始Index + * @param $end int 结束Index + * @param $order int 排序方式 0:从小到大排序 1:从大到小排序 默认0 + * @param $score bool 元素排序值 false:返回数据不带score true:返回数据带score 默认false + */ + public function setRange($set, $start, $end, $order = 0, $score = false) + { + + $return = null; + + + if ($order) { + + $return = $this->_REDIS->zRevRange($set, $start, $end, $score); + + } else { + + $return = $this->_REDIS->zRange($set, $start, $end, $score); + + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * ***只针对有序集合操作 + * 删除set中score从start到end的所有元素 + * @param $set string 集合名 + * @param $start int 开始score + * @param $end int 结束score + */ + public function setDeleteRange($set, $start, $end) + { + + $return = null; + + + $return = $this->_REDIS->zRemRangeByScore($set, $start, $end); + + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * ***只针对有序集合操作 + * 获取set中某个元素的score + * 如果指定了inc参数 则给该元素的score增加inc值 + * 如果没有该元素 则将该元素写入集合 + * @param $set string 集合名 + * @param $value mixed 元素值 + * @param $inc int 要给score增加的数值 默认是null 不执行score增加操作 + */ + public function setScore($set, $value, $inc = null) + { + + $return = null; + + + if ($inc) { + + $return = $this->_REDIS->zIncrBy($set, $inc, $value); + + } else { + + $return = $this->_REDIS->zScore($set, $value); + + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + /** + * 有序集合 + * 返回有序集key中成员member的排名。其中有序集成员按score值递增(从小到大)顺序排列。 + * @param $set + * @param $value + * @return int + */ + public function setZrank($set, $value) + { + $return = $this->_REDIS->zRank($set, $value); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + } + + /** + * ***只针对有序集合操作 + * 返回有序集key中,所有score值介于min和max之间(包括等于min或max)的成员。 + * @param $set string 集合名 + * @param $start int 开始score + * @param $end int 结束score + * @param $order int 排序方式 0:从小到大排序 1:从大到小排序 默认0 + */ + public function setRangeByScore($set, $start, $end, $order = 0) + { + $return = null; + + if ($order) { + $return = $this->_REDIS->zRangeByScore($set, $start, $end, array('withscores' => TRUE)); + } else { + $return = $this->_REDIS->zRevRangeByScore($set, $start, $end, array('withscores' => TRUE)); + } + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + } + + + + //+++-------------------------哈希操作-------------------------+++// + + + /** + * 将key->value写入hash表中 + * @param $hash string 哈希表名 + * @param $data array 要写入的数据 array('key'=>'value') + */ + public function hashSet($hash, $data) + { + + $return = null; + + + if (is_array($data) && !empty($data)) { + + $return = $this->_REDIS->hMset($hash, $data); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } + + + return $return; + + } + + + /** + * 获取hash表的数据 + * @param $hash string 哈希表名 + * @param $key mixed 表中要存储的key名 默认为null 返回所有key>value + * @param $type int 要获取的数据类型 0:返回所有key 1:返回所有value 2:返回所有key->value + */ + public function hashGet($hash, $key = array(), $type = 0) + { + + $return = null; + + + if ($key) { + + if (is_array($key) && !empty($key)) { + $return = $this->_REDIS->hMGet($hash, $key); + } else { + $return = $this->_REDIS->hGet($hash, $key); + } + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } else { + + switch ($type) { + + case 0: + + $return = $this->_REDIS->hKeys($hash); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + break; + + case 1: + + $return = $this->_REDIS->hVals($hash); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + break; + + case 2: + + $return = $this->_REDIS->hGetAll($hash); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + break; + + default: + + $return = false; + + break; + + } + + } + + + return $return; + + } + + + /** + * 获取hash表中元素个数 + * @param $hash string 哈希表名 + */ + public function hashLen($hash) + { + + $return = null; + + + $return = $this->_REDIS->hLen($hash); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 删除hash表中的key + * @param $hash string 哈希表名 + * @param $key mixed 表中存储的key名 + */ + public function hashDel($hash, $key) + { + + $return = null; + + + $return = $this->_REDIS->hDel($hash, $key); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 查询hash表中某个key是否存在 + * @param $hash string 哈希表名 + * @param $key mixed 表中存储的key名 + */ + public function hashExists($hash, $key) + { + + $return = null; + + + $return = $this->_REDIS->hExists($hash, $key); + + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 自增hash表中某个key的值 + * @param $hash string 哈希表名 + * @param $key mixed 表中存储的key名 + * @param $inc int 要增加的值 + */ + public function hashInc($hash, $key, $inc) + { + + $return = null; + + + $return = $this->_REDIS->hIncrBy($hash, $key, $inc); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + + //+++-------------------------其他操作-------------------------+++// + + + /** + * 自增hash表中某个key的值 + * @param $key string 哈希表名 + * @param $time mixed 表中存储的key名 + */ + public function setKeyExpire($key, $time) + { + + $return = null; + + + $return = $this->_REDIS->setTimeout($key, $time); + +// if (class_exists('redisProxy')) { +// $this->_REDIS->release(); +// } + + return $return; + + } + + + /** + * 获取满足给定pattern的所有key + * @param $key regexp key匹配表达式 模式:user* 匹配以user开始的key + */ + public function getKeys($key = null) + { + + $return = null; + + + $return = $this->_REDIS->keys($key); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 将数据保存到硬盘 同步/异步 + * @param $type int 保存方式 0:同步 1:异步 默认0 + * @param $time int 是否要获取上次成功将数据保存到磁盘的Unix时戳 0:不返回时间 1:返回时间 + */ + public function hwSave($type = 0, $time = 0) + { + + $return = null; + + + if ($type) { + + $return = $this->_REDIS->bgsave(); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } else { + + $return = $this->_REDIS->save(); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + } + + if ($time) { + + $return = $this->_REDIS->lastSave(); + + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + } + + + return $return; + + } + + + /** + * 获取上次成功将数据保存到磁盘的Unix时戳 + */ + public function lastSave() + { + + $return = null; + + + $return = $this->_REDIS->lastSave(); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 获取redis版本信息等详情 + */ + public function info() + { + + $return = null; + + + $return = $this->_REDIS->info(); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + + return $return; + + } + + + /** + * 获取数据库中key的数目 + */ + public function dbSize() + { + + $return = null; + + + $return = $this->_REDIS->dbSize(); + + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + + } + + /** + * 清空当前数据库 + * @return bool|null + */ + public function flushDb() + { + $return = null; + $return = $this->_REDIS->flushDB(); + if (class_exists('redisProxy')) { + $this->_REDIS->release(); + } + return $return; + } + + /**scan keys + * @param int $cursor 初始游标为0 + * @param string $math + * @param int $count 返回元素数 + * @return array|bool|null + */ + public function scan($cursor = 0, $math = '', $count = 10) + { + $return = null; + $return = $this->_REDIS->scan($cursor, $math, $count); + return $return; + } + + //+++-------------------------事务操作-------------------------+++// + + +// /** +// * 开始进入事务操作 +// * @param $return object 事务对象 +// */ +// public function tranStart() +// { +// +// $this->_TRANSCATION = $this->_REDIS->multi(); +// +// } +// +// +// /** +// * 提交完成事务 +// * @param $return bool 事务执行成功 提交操作 +// */ +// public function tranCommit() +// { +// +// $return = $this->_TRANSCATION->exec(); +// if (class_exists('redisProxy')) { +// $this->_REDIS->release(); +// } +// return $return; +// +// } +// +// +// /** +// * 回滚事务 +// * @param $return bool 事务执行失败 回滚操作 +// */ +// public function tranRollback() +// { +// +// $return = $this->_TRANSCATION->discard(); +// if (class_exists('redisProxy')) { +// $this->_REDIS->release(); +// } +// return $return; +// } + + +} \ No newline at end of file diff --git a/examples/frame_example/yii1/db/CDbCommand.php b/examples/frame_example/yii1/db/CDbCommand.php new file mode 100644 index 0000000..e572681 --- /dev/null +++ b/examples/frame_example/yii1/db/CDbCommand.php @@ -0,0 +1,136 @@ +_connection->enableParamLogging && ($pars=array_merge($this->_paramLog,$params))!==array()) + { + $p=array(); + foreach($pars as $name=>$value) + $p[$name]=$name.'='.var_export($value,true); + $par='. Bound with ' .implode(', ',$p); + } + else + $par=''; + Yii::trace('Executing SQL: '.$this->getText().$par,'system.db.CDbCommand'); + try + { + if($this->_connection->enableProfiling) + Yii::beginProfile('system.db.CDbCommand.execute('.$this->getText().$par.')','system.db.CDbCommand.execute'); + + $this->prepare(); + if($params===array()) + $this->_statement->execute(); + else + $this->_statement->execute($params); + + $n=$this->_statement->rowCount(); + // modify for pool + $this->_connection->release(); + if($this->_connection->enableProfiling) + Yii::endProfile('system.db.CDbCommand.execute('.$this->getText().$par.')','system.db.CDbCommand.execute'); + + return $n; + } + catch(Exception $e) + { + if($this->_connection->enableProfiling) + Yii::endProfile('system.db.CDbCommand.execute('.$this->getText().$par.')','system.db.CDbCommand.execute'); + + $errorInfo=$e instanceof PDOException ? $e->errorInfo : null; + $message=$e->getMessage(); + Yii::log(Yii::t('yii','CDbCommand::execute() failed: {error}. The SQL statement executed was: {sql}.', + array('{error}'=>$message, '{sql}'=>$this->getText().$par)),CLogger::LEVEL_ERROR,'system.db.CDbCommand'); + + if(YII_DEBUG) + $message.='. The SQL statement executed was: '.$this->getText().$par; + + throw new CDbException(Yii::t('yii','CDbCommand failed to execute the SQL statement: {error}', + array('{error}'=>$message)),(int)$e->getCode(),$errorInfo); + } + } + + + private function queryInternal($method,$mode,$params=array()) + { + $params=array_merge($this->params,$params); + + if($this->_connection->enableParamLogging && ($pars=array_merge($this->_paramLog,$params))!==array()) + { + $p=array(); + foreach($pars as $name=>$value) + $p[$name]=$name.'='.var_export($value,true); + $par='. Bound with '.implode(', ',$p); + } + else + $par=''; + + Yii::trace('Querying SQL: '.$this->getText().$par,'system.db.CDbCommand'); + + if($this->_connection->queryCachingCount>0 && $method!=='' + && $this->_connection->queryCachingDuration>0 + && $this->_connection->queryCacheID!==false + && ($cache=Yii::app()->getComponent($this->_connection->queryCacheID))!==null) + { + $this->_connection->queryCachingCount--; + $cacheKey='yii:dbquery'.$this->_connection->connectionString.':'.$this->_connection->username; + $cacheKey.=':'.$this->getText().':'.serialize(array_merge($this->_paramLog,$params)); + if(($result=$cache->get($cacheKey))!==false) + { + Yii::trace('Query result found in cache','system.db.CDbCommand'); + return $result[0]; + } + } + + try + { + if($this->_connection->enableProfiling) + Yii::beginProfile('system.db.CDbCommand.query('.$this->getText().$par.')','system.db.CDbCommand.query'); + + $this->prepare(); + if($params===array()) + $this->_statement->execute(); + else + $this->_statement->execute($params); + + if($method===''){ + // modify for pool + $result=new CDbDataReader($this,$this->_connection); + } + else + { + $mode=(array)$mode; + call_user_func_array(array($this->_statement, 'setFetchMode'), $mode); + $result=$this->_statement->$method(); + $this->_statement->closeCursor(); + // modify for pool + $this->_connection->release(); + } + + if($this->_connection->enableProfiling) + Yii::endProfile('system.db.CDbCommand.query('.$this->getText().$par.')','system.db.CDbCommand.query'); + + if(isset($cache,$cacheKey)) + $cache->set($cacheKey, array($result), $this->_connection->queryCachingDuration, $this->_connection->queryCachingDependency); + + return $result; + } + catch(Exception $e) + { + if($this->_connection->enableProfiling) + Yii::endProfile('system.db.CDbCommand.query('.$this->getText().$par.')','system.db.CDbCommand.query'); + + $errorInfo=$e instanceof PDOException ? $e->errorInfo : null; + $message=$e->getMessage(); + Yii::log(Yii::t('yii','CDbCommand::{method}() failed: {error}. The SQL statement executed was: {sql}.', + array('{method}'=>$method, '{error}'=>$message, '{sql}'=>$this->getText().$par)),CLogger::LEVEL_ERROR,'system.db.CDbCommand'); + + if(YII_DEBUG) + $message.='. The SQL statement executed was: '.$this->getText().$par; + + throw new CDbException(Yii::t('yii','CDbCommand failed to execute the SQL statement: {error}', + array('{error}'=>$message)),(int)$e->getCode(),$errorInfo); + } + } +} + diff --git a/examples/frame_example/yii1/db/CDbConnection.php b/examples/frame_example/yii1/db/CDbConnection.php new file mode 100644 index 0000000..b6b8f4b --- /dev/null +++ b/examples/frame_example/yii1/db/CDbConnection.php @@ -0,0 +1,16 @@ +getCurrentTransaction(); + if(!empty($transaction)&&$transaction->getActive()){//事务里面不释放连接 + return; + } + $this->_pdo->release(); + } + } +} + diff --git a/examples/frame_example/yii1/db/CDbDataReader.php b/examples/frame_example/yii1/db/CDbDataReader.php new file mode 100644 index 0000000..5939f28 --- /dev/null +++ b/examples/frame_example/yii1/db/CDbDataReader.php @@ -0,0 +1,104 @@ +_statement=$command->getPdoStatement(); + $this->_statement->setFetchMode(PDO::FETCH_ASSOC); + $this->_connection = $connection; + } + public function read() + { + $data = $this->_statement->fetch(); + // add for pool + $this->_connection->release(); + return $data; + } + + public function readColumn($columnIndex) + { + $data = $this->_statement->fetchColumn($columnIndex); + // add for pool + $this->_connection->release(); + return $data; + } + + public function readObject($className,$fields) + { + $data = $this->_statement->fetchObject($className,$fields); + // add for pool + $this->_connection->release(); + return $data; + } + + public function readAll() + { + $data = $this->_statement->fetchAll(); + // add for pool + $this->_connection->release(); + return $data; + } + + public function nextResult() + { + // add for pool + throw new Exception("cp not support foreach stmt"); + if(($result=$this->_statement->nextRowset())!==false) + $this->_index=-1; + return $result; + } + + public function close() + { + $this->_statement->closeCursor(); + $this->_closed=true; + // add for pool + $this->_connection->release(); + } + + public function getRowCount() + { + $data = $this->_statement->rowCount(); + // add for pool + $this->_connection->release(); + return $data; + } + + public function getColumnCount() + { + $data = $this->_statement->columnCount(); + // add for pool + $this->_connection->release(); + return $data; + } + + public function current() + { + // add for pool + throw new Exception("cp not support foreach stmt"); + return $this->_row; + } + + public function next() + { + // add for pool + throw new Exception("cp not support foreach stmt"); + $this->_row=$this->_statement->fetch(); + $this->_index++; + } + +} + + diff --git a/examples/frame_example/yii1/db/CDbTransaction.php b/examples/frame_example/yii1/db/CDbTransaction.php new file mode 100644 index 0000000..a2d2303 --- /dev/null +++ b/examples/frame_example/yii1/db/CDbTransaction.php @@ -0,0 +1,32 @@ +_active && $this->_connection->getActive()) + { + Yii::trace('Committing transaction','system.db.CDbTransaction'); + $this->_connection->getPdoInstance()->commit(); + $this->_active=false; + // add for pool + $this->_connection->release(); + } + else + throw new CDbException(Yii::t('yii','CDbTransaction is inactive and cannot perform commit or roll back operations. ')); + } + + public function rollback() + { + if($this->_active && $this->_connection->getActive()) + { + Yii::trace('Rolling back transaction','system.db.CDbTransaction'); + $this->_connection->getPdoInstance()->rollBack(); + $this->_active=false; + // add for pool + $this->_connection->release(); + } + else + throw new CDbException(Yii::t('yii','CDbTransaction is inactive and cannot perform commit or roll back operations.')); + } + +} diff --git a/include/cpClientNet.h b/include/cpClientNet.h old mode 100755 new mode 100644 index b918910..ec25d83 --- a/include/cpClientNet.h +++ b/include/cpClientNet.h @@ -7,26 +7,51 @@ #ifndef CP_CLIENTNET_H #define CP_CLIENTNET_H +#define MAX_HOLD_START_STR "\n-------------------max hold time start-------------------------" +#define MAX_HOLD_END_STR "\n-------------------max hold time end-------------------------" +#define MAX_DATA_START_STR "\n-------------------big data size start-------------------------" +#define MAX_DATA_END_STR "\n-------------------big data size end-------------------------" #ifdef __cplusplus extern "C" { #endif -typedef struct _cpClient -{ - int sock; - int released; - int port; - double timeout; - + typedef struct _cpClient { + int sock; + double timeout; + struct sockaddr_in serv_addr; - struct sockaddr_in remote_addr; - -} cpClient; - -int cpClient_close(cpClient *cli); -int cpClient_send(int sock, char *data, int length, int flag); -int cpClient_create(cpClient *cli); -int cpClient_recv(cpClient *cli, void *data, int len, int waitall) ; + struct sockaddr_in remote_addr; + + cpMasterInfo info; + int server_fd; + uint8_t async; //是否是异步类的连接 + + int (*lock)(struct _cpGroup *); + int (*unLock)(struct _cpGroup *); + + uint16_t dummy_source_index; + uint8_t querying; //async querying + + smart_str slow_log_tmp; + smart_str big_data_tmp; + int current_len; //for big data log + struct timeval log_start; + } cpClient; + + int cpClient_close(cpClient *cli); + int cpClient_send(int sock, char *data, int length, int flag); + int cpClient_create(cpClient *cli); + int cpClient_recv(int sock, void *data, int len, int waitall); + int cpClient_connect(cpClient *cli, char *host, int port, double timeout); + + void log_start(cpClient* cli); + void log_end(cpClient* cli); + void log_write(zval *data, cpClient* cli); + void log_increase_size(int size, cpClient* cli); + +#define CONN(cli) (&CPGS->conlist[cli->server_fd]) +#define CON_FORMART_KEY(str,port) sprintf((str), "connect_pool_sock%s" , (port)); +#define CON_FAIL_MESSAGE "connect to pool_server fail" #ifdef __cplusplus } diff --git a/include/cpFunction.h b/include/cpFunction.h old mode 100755 new mode 100644 index c5fc1cc..f0fa6fd --- a/include/cpFunction.h +++ b/include/cpFunction.h @@ -11,29 +11,19 @@ #ifdef __cplusplus extern "C" { #endif -#if __STDC_VERSION__ >= 199901L || defined(__cplusplus) -#define CPINLINE inline -#elif defined(_MSC_VER) || defined(__GNUC__) -#define CPINLINE __inline -#else -#define CPINLINE -#endif -#ifdef __MACH__ -#undef CPINLINE -#define CPINLINE -#endif #define CP_FIFO_NAME_LEN 200 -#define CP_FIFO_NAME_PRE "/tmp/con_pool_c2w_pipe" +#define CP_FIFO_NAME_PRE "/var/run/cp/con_pool_c2w_pipe" +#define CP_MMAP_NAME_PRE "/var/run/cp/con_pool_mmap" #define SW_LOG_BUFFER_SIZE 1024 #define SW_PID_BUFFER_SIZE 100 #define SW_LOG_DATE_STRLEN 64 #define CP_LOG_FORMAT "[%s]\t%s\t\n" #define FAILUREOR_MSG_SIZE 1024 -#define MAX_TITLE_LENGTH 512 +#define MAX_TITLE_LENGTH 120 #define MAX_INI_LENGTH 1024 - -#define PID_FILE_PATH "/var/run/con_pool_" + +#define PID_FILE_PATH "/var/run/php_connection_pool" #define cpLog(str,...) \ do \ { \ @@ -51,21 +41,52 @@ extern "C" { typedef void (*cpSignalFunc)(int); typedef void (*cpQueueFunc) (int, siginfo_t *, void *); + + int cpLog_init(char *logfile); int pid_init(); int set_pid(int pid); - void swLog_free(void); - CPINLINE void swSetNonBlock(int sock); - CPINLINE void swSetBlock(int sock); - void swSingalNone(); - CPINLINE int cpWrite(int fd, void *buf, int count); - CPINLINE int cpSetTimeout(int sock, double timeout); - cpSignalFunc cpSignalSet(int sig, cpSignalFunc func, int restart, int mask); + int cpWrite(int fd, void *buf, int count); + int cpSetTimeout(int sock, double timeout); int cpQueueSignalSet(int sig, cpQueueFunc func); + int cpNetRead(int fd, void *buf, int len); + int cpCreateFifo(char *file); + int cpFifoRead(int pipe_fd_read, void *buf, int len); + + void cp_ser_and_setpro(zval *arr); void cpSettitle(char *title); - zval * cpGetConfig(char *filename); + void cpSetIsBlock(int sock, int flag); + void swSingalNone(); + + //zval* cpGetConfig(char *filename); + // zval * cpMD5(zval *arr); + + cpSignalFunc cpSignalSet(int sig, cpSignalFunc func, int restart, int mask); + + static CPINLINE zval* cpGetConfig(char *filename) { + zval *fun_name, **args[2], *file, *section, *retval; + + CP_MAKE_STD_ZVAL(file); + CP_ZVAL_STRING(file, filename, 1); + CP_MAKE_STD_ZVAL(section); + ZVAL_BOOL(section, 1); + + args[0] = &file; + args[1] = §ion; + CP_MAKE_STD_ZVAL(fun_name); + CP_ZVAL_STRING(fun_name, "parse_ini_file", 0); + if (cp_call_user_function_ex(EG(function_table), NULL, fun_name, &retval, 2, args, 0, NULL TSRMLS_CC) != SUCCESS) { + cp_zval_ptr_dtor(&file); + cp_zval_ptr_dtor(§ion); + return NULL; + } + cp_zval_ptr_dtor(&file); + cp_zval_ptr_dtor(§ion); + //zend_print_zval_r(retval,0); + return retval; + } #ifdef __cplusplus } diff --git a/include/cpMemory.h b/include/cpMemory.h old mode 100755 new mode 100644 index 414b482..397b6a6 --- a/include/cpMemory.h +++ b/include/cpMemory.h @@ -13,25 +13,15 @@ extern "C" { #endif #include #include -#include typedef struct _cpShareMemory { int size; - int key; - int shmid; + char mmap_name[100]; void *mem; } cpShareMemory; - typedef struct _cpSendMem { - int semid; - int len; - int type; //0正常,-1 抛异常 - } cpSendMem; - - typedef struct _cpMasterInfo {//获取连接,master进程返回的信息 - int semid; - int worker_id; - int max;//数据包max + typedef struct _cpMasterInfo { + int server_fd;//fpm in server's fd } cpMasterInfo; typedef struct _cpWorkerInfo { @@ -42,12 +32,14 @@ extern "C" { typedef struct _cpTcpEvent { int type; + int data; } cpTcpEvent; - int cpShareMemory_sysv_create(cpShareMemory *object, int size, int key); - int cpShareMemory_sysv_free(cpShareMemory *object, int rm); void *cp_mmap_calloc(int size); + void* cp_mmap_calloc_with_file(cpShareMemory *object); + int cp_create_mmap_file(cpShareMemory *object); + int cp_create_mmap_dir(); #ifdef __cplusplus diff --git a/include/cpNetWork.h b/include/cpNetWork.h old mode 100755 new mode 100644 index 3c9f212..9014518 --- a/include/cpNetWork.h +++ b/include/cpNetWork.h @@ -21,6 +21,7 @@ extern "C" { #define CP_CLIENT_EOF_STR "\r\n^CON^eof\r\n" #define CP_TOO_MANY_CON "not enough con" #define CP_TOO_MANY_CON_ERR "ERROR!not enough con" +#define CP_MULTI_PROCESS_ERR "ERROR!the connection object create in parent process and use in multi process,please create in every process" #define CP_CLIENT_EOF_LEN strlen(CP_CLIENT_EOF_STR) #define CP_HEADER_CON_SUCCESS "CON_SUCCESS!" #define CP_HEADER_ERROR "ERROR!" @@ -33,7 +34,7 @@ extern "C" { int cpEpoll_add(int epfd, int fd, int fdtype); int cpEpoll_set(int fd, int fdtype); - int cpEpoll_del(int epfd ,int fd); + int cpEpoll_del(int epfd, int fd); int cpEpoll_wait(epoll_wait_handle*, struct timeval *timeo, int epfd); void cpEpoll_free(); CPINLINE int cpEpoll_event_set(int fdtype); diff --git a/include/cpPingWorker.h b/include/cpPingWorker.h new file mode 100644 index 0000000..d539b81 --- /dev/null +++ b/include/cpPingWorker.h @@ -0,0 +1,23 @@ +/* + * File: cpPingWorker.h + * Author: guoxinhua + * + * Created on 2014年12月29日, 下午3:06 + */ + +#ifndef CPPINGWORKER_H +#define CPPINGWORKER_H + +#ifdef __cplusplus +extern "C" { +#endif + + int cpFork_ping_worker(); + int cpCreate_ping_worker_mem(); + +#ifdef __cplusplus +} +#endif + +#endif /* CPPINGWORKER_H */ + diff --git a/include/cpServer.h b/include/cpServer.h old mode 100755 new mode 100644 index 48cb341..6536e24 --- a/include/cpServer.h +++ b/include/cpServer.h @@ -16,16 +16,41 @@ extern "C" { #define CP_BACKLOG 512 #define CP_PIPES_NUM (CP_WORKER_NUM/CP_WRITER_NUM + 1) //每个写线程pipes数组大小 #define CP_PORT 6253 +#define CP_PORT_PDO CP_PORT +#define CP_PORT_REDIS CP_PORT #define CP_REACTOR_TIMEO_SEC 3 #define CP_REACTOR_TIMEO_USEC 0 -#define CP_MAX_FDS (1024*10) //最大fd值,暂不支持扩容 -#define CP_MAX_REQUEST 20000 +#define CP_MAX_FDS (1024*50) //最大fd值,暂不支持扩容 + //#define CP_MAX_REQUEST 20000 +#define CP_MAX_REQUEST 0 #define CP_MAX_WORKER 100 #define CP_MIN_WORKER 2 #define CP_IDEL_TIME 2 +#define CP_PING_SLEEP 30 #define CP_RECYCLE_NUM 2 #define CP_DEF_MAX_READ_LEN (1024*1024*5) #define CP_MAX_READ_LEN (1024*1024*20) +#define CP_SOURCE_LEN 200 +#define CP_DEF_MAX_NUM 20 +#define CP_DEF_MIN_NUM 1 +#define CP_FILE_DIR "/var/run/cp/" +#define CP_SERVER_MMAP_FILE "/var/run/cp/cp_server_mmap_file" + +#define CP_GROUP_LEN 1000 // +#define CP_GROUP_NUM 200 //the max group num of proxy process . todo check it +#define CP_MAX_ASYNC_NUM 100 //the max under async to protect db +#define CP_SOURCE_MAX 100 + +#define CP_PING_MEM_LEN 1024*1024 +#define CP_PING_DIS_LEN 409600 //disable list mem +#define CP_PING_PRO_LEN 409600 //probaly disable +#define CP_PING_MD5_LEN 32 //conf md5 value +#define CP_PING_PID_LEN 4 //conf md5 value +#define CP_PING_GET_DIS(addr) cp_unserialize((addr) + CP_PING_MD5_LEN+CP_PING_PID_LEN, CP_PING_DIS_LEN); +#define CP_PING_GET_PRO(addr) cp_unserialize((addr) + CP_PING_MD5_LEN+CP_PING_PID_LEN+CP_PING_DIS_LEN, CP_PING_PRO_LEN); +#define CP_CONNECT_NORMAL 0 +#define CP_CONNECT_RECONNECT 1 +#define CP_CONNECT_PING 2 #define CPGC ConProxyG.conf #define CPGL ConProxyG @@ -34,47 +59,75 @@ extern "C" { #define CP_UNSOCK_BUFSIZE (4*1024*1024) -#define CP_WORKER_RESTART 4 +#define CP_WORKER_STARTING 5 +#define CP_WORKER_RESTART 4 #define CP_WORKER_DELING 3 #define CP_WORKER_BUSY 2 #define CP_WORKER_IDLE 1 #define CP_WORKER_DEL 0 +#define CP_WORKER_ID(g_id, w_id) g_id * CP_GROUP_LEN + w_id -#define CP_ACCEPT_AGAIN 1 //是否循环accept,可以一次性处理完全部的listen队列,用于大量并发连接的场景 -#define CP_ACCEPT_MAX_COUNT 64 //一次循环的最大accept次数 +#define CP_ACCEPT_AGAIN 1 //是否循环accept,可以一次性处理完全部的listen队列,用于大量并发连接的场景 +#define CP_ACCEPT_MAX_COUNT 64 //一次循环的最大accept次数 #define CP_TCP_KEEPCOUNT 5 #define CP_TCP_KEEPIDLE 3600 //1小时 #define CP_TCP_KEEPINTERVAL 60 -#define CP_FD_NCON 2 -#define CP_FD_RELEASED 0 +#define CP_FD_NCON 2 +#define CP_FD_RELEASED 0 #define CP_FD_NRELEASED 2 -#define CP_FD_WAITING 1 +#define CP_FD_WAITING 1 + +#define CP_TRUE 1 +#define CP_FALSE 0 #define CP_START_SLEEP usleep(50000); + typedef volatile int8_t volatile_int8; + + // typedef struct _cpWaitList { + // int CPid; // fpm's pid + // struct _cpConnection *conn; + // struct _cpWaitList *next; + // int next_id; + // struct _cpWaitList *pre; + // } cpWaitList; + + typedef struct _cpConnection { + int fd; + + uint16_t group_id; //0 1 2 3 + uint32_t worker_id; //1001 1002 2001 + uint16_t worker_index; // 0 1 2 3 + + uint8_t release; + uint16_t pth_id; + + int wait_fpm_pid; //等待的fpm pid + int next_wait_id; //sever fd + int fpm_pid; //连接对应的fpm + // struct _cpWaitList WaitList; + } cpConnection; + typedef struct _cpIdelList { struct _cpIdelList *next; int worker_id; } cpIdelList; - typedef struct _cpWaitList { - int fd; - int len; - struct _cpWaitList *next; - struct _cpWaitList *pre; - char data[0]; - } cpWaitList; - typedef struct _cpConfig { uint16_t backlog; uint16_t reactor_num; - uint16_t worker_min; uint16_t recycle_num; uint16_t port; + //连续失败多少次算失效 + uint16_t ser_fail_hits; + + //最多可以剔除多少个结点,防止网络抖动等,导致的全部踢掉 + uint16_t max_fail_num; uint8_t idel_time; + uint8_t ping_time; uint8_t use_wait_queue; uint8_t daemonize; @@ -85,11 +138,13 @@ extern "C" { int timeout_sec; int timeout_usec; int max_request; - int group_id; - char title[MAX_TITLE_LENGTH]; + int max_hold_time_to_log; + int max_data_size_to_log; + char ini_file[MAX_INI_LENGTH]; char log_file[128]; //日志文件 + } cpConfig; typedef struct _cpThread { @@ -108,53 +163,81 @@ extern "C" { cpConfig conf; int epfd; - // swAllocator *memory_pool; - // swReactor *main_reactor; + void *ping_mem_addr; } cpServerG; - typedef struct _cpConnection { - int fd; //文件描述符 - // struct sockaddr_in addr; //socket的地址 - - uint16_t worker_id; //指定当前连接在用哪个worker - uint8_t release; //方式重复release - uint16_t pth_id; //thread id - } cpConnection; + typedef struct _cpGroup { + int id; //Current worker group id 0,1,2,3...n + uint32_t worker_num; + uint32_t worker_min; + uint32_t worker_max; + cpWorker workers[CP_GROUP_LEN]; + volatile_int8 workers_status[CP_GROUP_LEN]; + pthread_mutex_t mutex_lock; + int first_wait_id; //server fd + int last_wait_id; //server fd + // cpWaitList *WaitList; //获得失败的wait队列 + // cpWaitList *WaitTail; //获得失败的wait队列队尾 + char name[100]; //group name + + int (*lock)(struct _cpGroup *); + int (*unLock)(struct _cpGroup *); + int (*tryLock)(struct _cpGroup *); + } cpGroup; typedef struct _cpServerGS { pid_t master_pid; pid_t manager_pid; - uint32_t worker_num; - uint32_t worker_max; + uint32_t connect_count; uint16_t reactor_next_i; // uint16_t reactor_round_i; - cpConnection *conlist; - uint32_t *workerfd2clientfd_list; //workerfd和客户端fd的对应关系 - - cpWorker *workers; - uint8_t *workers_status; - pthread_spinlock_t *spin_lock; + cpConnection conlist[CP_MAX_FDS]; - cpWaitList *WaitList; //获得失败的wait队列 - cpWaitList *WaitTail; //获得失败的wait队列队尾 + cpWorker *ping_workers; cpThread *reactor_threads; int running; + cpGroup G[CP_GROUP_NUM]; //group TODO extend + zval* group; + int group_num; + int max_buffer_len; + int max_hold_time_to_log; + int max_data_size_to_log; + + pthread_mutex_t mutex_lock; + // int (*global_lock)(struct _cpGroup *); + // int (*global_unLock)(struct _cpGroup *); + + int default_min; + int default_max; + + char log_file[128]; //日志文件 } cpServerGS; typedef struct _cpWorkerG { int id; //Current Proccess Worker's id 0,1,2,3...n - int clientPid; + int gid; //current worker's group id + int pid; + int working; uint64_t max_read_len; + int pipe_fd_read; + int pipe_fd_write; + cpWorkerInfo event; } cpWorkerG; - void cpServer_init(zval *conf, char *title, char *ini_file, int group_id); + int cpServer_init(zval *conf, char *ini_file); int cpServer_create(); int cpServer_start(); + int cpMutexLock(cpGroup *); + int cpMutexUnLock(cpGroup *); + int cpMutexTryLock(cpGroup *); + void cpServer_try_get_worker(cpConnection *conn, int group_id); + int cpPopWaitQueue(cpGroup *G, cpConnection *conn); + #ifdef __cplusplus } diff --git a/include/cpWorker.h b/include/cpWorker.h old mode 100755 new mode 100644 index 5e9b047..46170a9 --- a/include/cpWorker.h +++ b/include/cpWorker.h @@ -15,19 +15,18 @@ extern "C" { typedef struct _cpWorker { int request; //worker request NUM - int last_from_id; pid_t pid; int pipe_fd_write; - int pre_len; - - int fd;//当前worker服务于哪个fd - - uint8_t run; + int CPid;//当前worker服务于哪个pid cpShareMemory sm_obj; } cpWorker; - int cpFork_one_worker(int id); + int cpFork_one_worker(int id,int gid); int cpWorker_manager_loop(); + void cpWorker_do_ping(); + int cpCreate_worker_mem(int worker_id,int group_id); + int cpWorker_attach_mem(int worker_id,int group_id); + #ifdef __cplusplus } diff --git a/include/php7_wrapper.h b/include/php7_wrapper.h new file mode 100644 index 0000000..ec71a9d --- /dev/null +++ b/include/php7_wrapper.h @@ -0,0 +1,436 @@ +#ifndef EXT_POOL_PHP7_WRAPPER_H_ +#define EXT_POOL_PHP7_WRAPPER_H_ + +#if PHP_MAJOR_VERSION < 7 +#include +typedef zend_rsrc_list_entry zend_resource; +#define CP_RETURN_STRING RETURN_STRING +#define CP_Z_ARRVAL_P Z_ARRVAL_P +#define CP_Z_ARRVAL_PP Z_ARRVAL_P +#define IS_TRUE 1 +#define cp_add_assoc_string add_assoc_string +#define cp_add_index_string add_index_string +#define cp_convert_to_string_ex convert_to_string_ex + +static CPINLINE int cp_zend_hash_find(HashTable *ht, char *k, int len, void **v) { + zval **tmp = NULL; + if (zend_hash_find(ht, k, len, (void **) &tmp) == SUCCESS) { + *v = *tmp; + return SUCCESS; + } else { + *v = NULL; + return FAILURE; + } +} + +static CPINLINE int cp_zend_hash_index_find(HashTable *ht, zend_ulong h, void **v) { + zval **tmp = NULL; + if (zend_hash_index_find(ht, h, (void **) &tmp) == SUCCESS) { + *v = *tmp; + return SUCCESS; + } else { + *v = NULL; + return FAILURE; + } +} + +#define CP_INTERNAL_SEND_RAW(send_data,type)\ + zval send_zval = {0};\ + CP_ZVAL_STRING(&send_zval,send_data,0);\ + CP_INTERNAL_SERIALIZE_SEND_MEM(&send_zval,type); + +#define cp_zend_hash_del zend_hash_del +#define cp_zend_hash_update zend_hash_update +#define cp_zend_hash_add zend_hash_add +#define cp_zend_hash_index_update zend_hash_index_update +#define cp_zend_hash_copy zend_hash_copy +#define cp_zend_hash_exists zend_hash_exists +#define cp_zend_hash_get_current_key(a,b,c,d) zend_hash_get_current_key_ex(a,b,c,d,0,NULL) + +#define cp_zend_read_property zend_read_property + +#define CP_ZVAL_STRING ZVAL_STRING +#define CP_ZVAL_STRINGL ZVAL_STRINGL +#define CP_ZEND_FETCH_RESOURCE_NO_RETURN ZEND_FETCH_RESOURCE_NO_RETURN +#define CP_ZEND_FETCH_RESOURCE ZEND_FETCH_RESOURCE +#define CP_ZEND_REGISTER_RESOURCE ZEND_REGISTER_RESOURCE +#define CP_MAKE_STD_ZVAL(p) MAKE_STD_ZVAL(p) +#define CP_ALLOC_INIT_ZVAL(p) ALLOC_INIT_ZVAL(p) +#define CP_RETVAL_STRINGL RETVAL_STRINGL +#define cp_smart_str smart_str +#define cp_php_var_unserialize php_var_unserialize +#define cp_zend_is_callable zend_is_callable +#define cp_call_user_function_ex call_user_function_ex +#define cp_add_assoc_stringl_ex add_assoc_stringl_ex +#define cp_add_assoc_stringl add_assoc_stringl +#define cp_zval_ptr_dtor zval_ptr_dtor +#define cp_zval_add_ref zval_add_ref +#define cp_strndup(v,l) estrndup(Z_STRVAL_P(v),l) +#define CP_RETURN_STRINGL RETURN_STRINGL +#define cp_explode php_explode +#define cp_zend_register_internal_class_ex zend_register_internal_class_ex +#define cp_zend_fetch_class(zval,type) zend_fetch_class(Z_STRVAL_P(zval),Z_STRLEN_P(zval),type) + +#define cp_zend_call_method_with_0_params zend_call_method_with_0_params +#define cp_zend_call_method_with_1_params zend_call_method_with_1_params +#define cp_zend_call_method_with_2_params zend_call_method_with_2_params + +#define ZVAL_DUP(z,v) *z = *v;zval_copy_ctor(z); + +typedef int zend_size_t; + +#define CP_HASHTABLE_FOREACH_START(ht, entry)\ + zval **tmp = NULL;\ + for (zend_hash_internal_pointer_reset(ht);\ + zend_hash_has_more_elements(ht) == SUCCESS; \ + zend_hash_move_forward(ht)) {\ + if (zend_hash_get_current_data(ht, (void**)&tmp) == FAILURE) {\ + continue;\ + }\ + entry = *tmp; + +#if defined(HASH_KEY_NON_EXISTANT) && !defined(HASH_KEY_NON_EXISTENT) +#define HASH_KEY_NON_EXISTENT HASH_KEY_NON_EXISTANT +#endif + +#define CP_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, entry)\ + zval **tmp = NULL; ulong idx;\ + for (zend_hash_internal_pointer_reset(ht); \ + (ktype = zend_hash_get_current_key_ex(ht, &k, &klen, &idx, 0, NULL)) != HASH_KEY_NON_EXISTENT; \ + zend_hash_move_forward(ht)\ + ) { \ + if(HASH_KEY_IS_LONG==ktype){\ + char t[20] = {0};\ + sprintf(t,"%d",idx);\ + k = t; klen = strlen(t)+1; \ + }\ + if (zend_hash_get_current_data(ht, (void**)&tmp) == FAILURE) {\ + continue;\ + }\ + entry = *tmp;\ + klen --; + +#define CP_HASHTABLE_FOREACH_END() } + +static CPINLINE int CP_Z_TYPE_P(zval *z) { + if (Z_TYPE_P(z) == IS_BOOL) { + if ((uint8_t) Z_BVAL_P(z) == 1) { + return IS_TRUE; + } else { + return 0; + } + } else { + return Z_TYPE_P(z); + } +} +#define cp_php_var_serialize(a,b,c) php_var_serialize(a,&b,c) +#define IS_TRUE 1 +inline int CP_Z_TYPE_P(zval *z); +#define CP_Z_TYPE_PP(z) CP_Z_TYPE_P(*z) +#define CP_SEND_EXCEPTION_ARGS(str) do{ zval *exception = EG(exception);\ + zend_class_entry *ce_exception = Z_OBJCE_P(exception);\ + EG(exception) = NULL;\ + cp_zend_call_method_with_0_params(&exception, ce_exception, NULL, "__tostring", str);\ + CP_INTERNAL_SERIALIZE_SEND_MEM(*str,CP_SIGEVENT_EXCEPTION);\ + cp_zval_ptr_dtor(&exception);\ + }while(0); + +#define CP_EXCEPTION_ARGS(str) do{ zval *exception = EG(exception);\ + zend_class_entry *ce_exception = Z_OBJCE_P(exception);\ + EG(exception) = NULL;\ + cp_zend_call_method_with_0_params(&exception, ce_exception, NULL, "__tostring", str);\ + cp_zval_ptr_dtor(&exception);\ + }while(0); + +#define CP_TEST_RETURN_FALSE(flag) ({if(flag==CP_CONNECT_PING){ \ + if(EG(exception)){ \ + zval *exception = EG(exception);\ + zval_ptr_dtor(&exception); \ + EG(exception) = NULL;\ + }\ + return CP_FALSE; \ + }}); + +static CPINLINE int cp_internal_call_user_function(zval *object, zval *fun, zval **ret_value, zval * args) { + zval *m_args; + int count = 0; + if (cp_zend_hash_find(Z_ARRVAL_P(args), ZEND_STRS("args"), (void **) &m_args) == SUCCESS) { + count = zend_hash_num_elements(Z_ARRVAL_P(m_args)); + zval **tmp_pass[count]; + int i = 0; + for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(m_args)); zend_hash_has_more_elements(Z_ARRVAL_P(m_args)) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(m_args))) { + zval **ppzval; + zend_hash_get_current_data(Z_ARRVAL_P(m_args), (void**) &ppzval); + tmp_pass[i] = ppzval; + i++; + } + return call_user_function_ex(NULL, &object, fun, ret_value, count, tmp_pass, 0, NULL TSRMLS_CC); + } else { + return call_user_function_ex(NULL, &object, fun, ret_value, count, NULL, 0, NULL TSRMLS_CC); + } +} + +static CPINLINE int cp_zend_hash_find_ptr(HashTable *ht, zval *k, void **ret) { + return cp_zend_hash_find(ht, Z_STRVAL_P(k), Z_STRLEN_P(k) + 1, ret); +} +#define CP_DEL_OBJ(obj)\ + cp_zval_ptr_dtor(&obj);\ + obj = NULL; +#else +//------------下面是php7版本------------------------------------ +#include +#include "zend_smart_str.h" + +#define cp_php_var_serialize php_var_serialize +typedef size_t zend_size_t; +#define CP_RETVAL_STRINGL(s, l,dup) RETVAL_STRINGL(s,l) +#define ZEND_SET_SYMBOL(ht,str,arr) zend_hash_str_update(ht, str, sizeof(str)-1, arr); + +static CPINLINE int Z_BVAL_P(zval *v) { + if (Z_TYPE_P(v) == IS_TRUE) { + return 1; + } else { + return 0; + } +} +#define cp_zend_fetch_class(zval,type) zend_fetch_class(Z_STR_P(zval),type) + +#define cp_add_assoc_stringl(__arg, __key, __str, __length, __duplicate) cp_add_assoc_stringl_ex(__arg, __key, strlen(__key)+1, __str, __length, __duplicate) + +static CPINLINE int cp_add_assoc_stringl_ex(zval *arg, const char *key, size_t key_len, char *str, size_t length, int duplicate) { + key_len--; + return add_assoc_stringl_ex(arg, key, key_len, str, length); +} + +#define CP_Z_ARRVAL_P(z) Z_ARRVAL_P(z) + +#define CP_HASHTABLE_FOREACH_START(ht, _val) ZEND_HASH_FOREACH_VAL(ht, _val); { + + +#define CP_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, _val) zend_string *_foreach_key;\ + zend_ulong knum;\ + ZEND_HASH_FOREACH_KEY_VAL(ht, knum,_foreach_key, _val);\ + if (!_foreach_key) {\ + char t[20] = {0};\ + sprintf(t,"%d",knum);\ + k = t; klen = strlen(t); ktype = 0;}\ + else {k = _foreach_key->val, klen=_foreach_key->len; ktype = 1;} { + + + +#define CP_HASHTABLE_FOREACH_END() } ZEND_HASH_FOREACH_END(); + +#define Z_ARRVAL_PP(s) Z_ARRVAL_P(*s) +#define cp_convert_to_string_ex(s) convert_to_string_ex(*s) +#define Z_BVAL_PP(s) Z_BVAL_P(*s) +#define CP_Z_TYPE_P Z_TYPE_P +#define CP_Z_TYPE_PP(s) CP_Z_TYPE_P(*s) +#define Z_STRVAL_PP(s) Z_STRVAL_P(*s) +#define Z_STRLEN_PP(s) Z_STRLEN_P(*s) +#define Z_LVAL_PP(v) Z_LVAL_P(*v) +#define cp_strndup(s,l) \ + ({zend_string *str = zend_string_copy(Z_STR_P(s));\ + str->val;}) + +#define cp_zval_add_ref(p) Z_TRY_ADDREF_P(*p) +#define cp_zval_ptr_dtor(p) zval_ptr_dtor(*p) + +static CPINLINE int cp_call_user_function_ex(HashTable *function_table, zval** object_pp, zval *function_name, zval **retval_ptr_ptr, uint32_t param_count, zval ***params, int no_separation, HashTable* ymbol_table) { + zval real_params[20]; + int i = 0, ret; + for (; i < param_count; i++) { + real_params[i] = **params[i]; + } + zval phpng_retval; + *retval_ptr_ptr = &phpng_retval; + zval *object_p = (object_pp == NULL) ? NULL : *object_pp; + ret = call_user_function_ex(function_table, object_p, function_name, &phpng_retval, param_count, real_params, no_separation, NULL); + return ret; +} + +#define cp_php_var_unserialize(rval, p, max, var_hash)\ +php_var_unserialize(*rval, p, max, var_hash) + +#define CP_MAKE_STD_ZVAL(p) zval _stack_zval_##p; p = &(_stack_zval_##p) + +#define CP_RETURN_STRINGL(z,l,t) \ + zval key;\ + ZVAL_STRING(&key, z);\ + RETURN_STR(Z_STR(key)) + +#define CP_ALLOC_INIT_ZVAL(p) CP_MAKE_STD_ZVAL(p) +#define CP_ZEND_FETCH_RESOURCE_NO_RETURN(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type) \ + (rsrc = (rsrc_type) zend_fetch_resource(Z_RES_P(*passed_id), resource_type_name, resource_type)) +#define CP_ZEND_REGISTER_RESOURCE(return_value, result, le_result) ZVAL_RES(return_value,zend_register_resource(result, le_result)) +#define CP_RETURN_STRING(val, duplicate) RETURN_STRING(val) +#define cp_add_assoc_string(array, key, value, duplicate) add_assoc_string(array, key, value) +#define cp_add_index_string(array, key, value, duplicate) add_index_string(array, key, value) +#define cp_zend_hash_copy(target,source,pCopyConstructor,tmp,size) zend_hash_copy(target,source,pCopyConstructor) +#define cp_zend_register_internal_class_ex(entry,parent_ptr,str) zend_register_internal_class_ex(entry,parent_ptr) + +#define cp_zend_call_method_with_0_params(obj, ptr, what, method, retval) zend_call_method_with_0_params(*obj,ptr,what,method,*retval) +#define cp_zend_call_method_with_1_params(obj, ptr, what, method, retval, v1) zend_call_method_with_1_params(*obj,ptr,what,method,*retval,v1) +//#define cp_zend_call_method_with_2_params(obj,ptr,what,char,return,name,cb) zend_call_method_with_2_params(*obj,ptr,what,char,*return,name,cb) +#define sw_zend_call_method_with_2_params(obj, ptr, what, method, retval, name, cb) zend_call_method_with_2_params(*obj,ptr,what,method,*retval,name,cb) +#define CP_ZVAL_STRINGL(z, s, l, dup) ZVAL_STRINGL(z, s, l) +#define CP_ZVAL_STRING(z,s,dup) ZVAL_STRING(z,s) +#define cp_smart_str smart_string + +static CPINLINE zval* cp_zend_read_property(zend_class_entry *class_ptr, zval *obj, char *s, int len, int silent) { + zval rv; + return zend_read_property(class_ptr, obj, s, len, silent, &rv); +} + +static CPINLINE int cp_zend_is_callable(zval *cb, int a, char **name) { + zend_string *key; + int ret = zend_is_callable(cb, a, &key); + char *tmp = (char *) emalloc(key->len); + memcpy(tmp, key->val, key->len); + *name = tmp; + return ret; +} + +static CPINLINE int cp_zend_hash_del(HashTable *ht, char *k, int len) { + return zend_hash_str_del(ht, k, len - 1); +} + +static CPINLINE int cp_zend_hash_add(HashTable *ht, char *k, int len, void *pData, int datasize, void **pDest) { + zval **real_p = pData; + return zend_hash_str_add(ht, k, len - 1, *real_p) ? SUCCESS : FAILURE; +} + +static CPINLINE int cp_zend_hash_index_update(HashTable *ht, int key, void *pData, int datasize, void **pDest) { + zval **real_p = pData; + return zend_hash_index_update(ht, key, *real_p) ? SUCCESS : FAILURE; +} + +static CPINLINE int cp_zend_hash_update(HashTable *ht, char *k, int len, void * val, int size, void *ptr) { + return zend_hash_str_update(ht, k, len - 1, val) ? SUCCESS : FAILURE; +} + +static CPINLINE int cp_zend_hash_get_current_key(HashTable *ht, char **key, uint32_t *keylen, ulong *num) { + zend_string *_key_ptr; + int type = zend_hash_get_current_key(ht, &_key_ptr, (zend_ulong*) num); + *key = _key_ptr->val; + *keylen = _key_ptr->len; + return type; +} + +static CPINLINE int cp_zend_hash_find(HashTable *ht, char *k, int len, void **v) { + zval *value = zend_hash_str_find(ht, k, len - 1); + + if (value == NULL) { + return FAILURE; + } else { + *v = (void *) value; + return SUCCESS; + } +} + +static CPINLINE int cp_zend_hash_find_ptr(HashTable *ht, zval *k, void **ret) { + *ret = zend_hash_find_ptr(ht, Z_STR_P(k)); +// zval_dtor(k); + if (*ret == NULL) { + return FAILURE; + } else { + return SUCCESS; + } +} + +static CPINLINE int cp_zend_hash_index_find(HashTable *ht, zend_ulong h, void **v) { + zval *value = zend_hash_index_find(ht, h); + + if (value == NULL) { + return FAILURE; + } else { + *v = (void *) value; + return SUCCESS; + } +} + +static CPINLINE int cp_zend_hash_exists(HashTable *ht, char *k, int len) { + zval key; + ZVAL_STRING(&key, k); + zval *value = zend_hash_str_find(ht, k, len - 1); + + if (value == NULL) { + return FAILURE; + } else { + return SUCCESS; + } +} + +static CPINLINE void cp_explode(zval *delim, zval *desc, zval *ex_arr, zend_long limit) { + const zend_string *str_delim; + zend_string *str_desc; + if (EXPECTED(Z_TYPE_P(delim) == IS_STRING)) { + str_delim = Z_STR_P(delim); + } else { + str_delim = zval_get_string(delim); + } + + if (EXPECTED(Z_TYPE_P(desc) == IS_STRING)) { + str_desc = Z_STR_P(desc); + } else { + str_desc = zval_get_string(desc); + } + php_explode(str_delim, str_desc, ex_arr, limit); +} + +static CPINLINE int cp_internal_call_user_function(zval *object, zval *fun, zval **ret_value, zval * args) { + zval *m_args; + zval real_params[20]; + if (!(*ret_value)) { + zval phpng_retval; + *ret_value = &phpng_retval; + } + if (cp_zend_hash_find(CP_Z_ARRVAL_P(args), ZEND_STRS("args"), (void **) &m_args) == SUCCESS) { + int i = 0; + zval *val; + CP_HASHTABLE_FOREACH_START(CP_Z_ARRVAL_P(m_args), val) + real_params[i] = *val; + i++; + CP_HASHTABLE_FOREACH_END() + return call_user_function_ex(NULL, object, fun, *ret_value, i, real_params, 0, NULL TSRMLS_CC); + } else { + return call_user_function_ex(NULL, object, fun, *ret_value, 0, NULL, 0, NULL TSRMLS_CC); + } +} + +#define CP_INTERNAL_SEND_RAW(send_data,type)\ + zval send_zval = {0};\ + CP_ZVAL_STRING(&send_zval,send_data,0);\ + CP_INTERNAL_SERIALIZE_SEND_MEM(&send_zval,type);\ + zval_ptr_dtor(&send_zval); +#define CP_GET_EXCEPTION_STR + +#define CP_EXCEPTION_ARGS(str) do{ zend_object *ex = EG(exception);\ + zval exception, tmp, rv,ret;\ + ZVAL_OBJ(&exception, ex);\ + zend_class_entry *ce_exception = Z_OBJCE(exception);\ + zend_call_method_with_0_params(&exception, ce_exception, NULL, "__tostring", &tmp);\ + *str = zend_read_property(ce_exception, &exception, "message", sizeof ("message") - 1, 1, &rv);\ + ZVAL_STRING(&ret,Z_STRVAL_P(*str));\ + *str = &ret;\ + zval_ptr_dtor(&exception);\ + zval_ptr_dtor(&tmp);\ + EG(exception) = NULL;\ + }while(0); + +#define CP_SEND_EXCEPTION_ARGS(str) do{ CP_EXCEPTION_ARGS(str);\ + CP_INTERNAL_SERIALIZE_SEND_MEM(*str,CP_SIGEVENT_EXCEPTION);\ + }while(0); + +#define CP_TEST_RETURN_FALSE(flag) ({if(flag==CP_CONNECT_PING){ \ + if(EG(exception)){ \ + zend_object_std_dtor(EG(exception)); \ + EG(exception) = NULL;\ + }\ + return CP_FALSE; \ + }}); +#define CP_DEL_OBJ(obj)\ + cp_zval_ptr_dtor(&obj);\ + efree(obj);\ + obj = NULL; +#endif /* EXT_SWOOLE_PHP7_WRAPPER_H_ */ +#endif diff --git a/msgpack/.libs/msgpack.o b/msgpack/.libs/msgpack.o deleted file mode 100644 index 515b77c..0000000 Binary files a/msgpack/.libs/msgpack.o and /dev/null differ diff --git a/msgpack/.libs/msgpack_convert.o b/msgpack/.libs/msgpack_convert.o deleted file mode 100644 index b1b0fee..0000000 Binary files a/msgpack/.libs/msgpack_convert.o and /dev/null differ diff --git a/msgpack/.libs/msgpack_pack.o b/msgpack/.libs/msgpack_pack.o deleted file mode 100644 index 922e188..0000000 Binary files a/msgpack/.libs/msgpack_pack.o and /dev/null differ diff --git a/msgpack/.libs/msgpack_unpack.o b/msgpack/.libs/msgpack_unpack.o deleted file mode 100644 index f6b7a6c..0000000 Binary files a/msgpack/.libs/msgpack_unpack.o and /dev/null differ diff --git a/msgpack/msgpack.c b/msgpack/msgpack.c old mode 100755 new mode 100644 index 0166aee..b331591 --- a/msgpack/msgpack.c +++ b/msgpack/msgpack.c @@ -4,6 +4,7 @@ #endif #include "php.h" +#if PHP_MAJOR_VERSION < 7 #include "php_ini.h" #include "ext/standard/info.h" /* for php_info */ #include "ext/standard/php_incomplete_class.h" /* for incomplete_class */ @@ -82,11 +83,6 @@ static ZEND_MINIT_FUNCTION(msgpack) REGISTER_INI_ENTRIES(); -#if HAVE_PHP_SESSION - php_session_register_serializer("msgpack", - PS_SERIALIZER_ENCODE_NAME(msgpack), - PS_SERIALIZER_DECODE_NAME(msgpack)); -#endif // msgpack_init_class(); @@ -141,95 +137,11 @@ zend_module_entry msgpack_module_entry = { ZEND_GET_MODULE(msgpack) #endif -#if HAVE_PHP_SESSION -PS_SERIALIZER_ENCODE_FUNC(msgpack) -{ - smart_str buf = {0}; - msgpack_serialize_data_t var_hash; - - msgpack_serialize_var_init(&var_hash); - - msgpack_serialize_zval(&buf, PS(http_session_vars), var_hash TSRMLS_CC); - - if (newlen) - { - *newlen = buf.len; - } - - smart_str_0(&buf); - *newstr = buf.c; - - msgpack_serialize_var_destroy(&var_hash); - - return SUCCESS; -} - -PS_SERIALIZER_DECODE_FUNC(msgpack) -{ - int ret; - HashTable *tmp_hash; - HashPosition tmp_hash_pos; - char *key_str; - ulong key_long; - uint key_len; - zval *tmp; - zval **value; - size_t off = 0; - msgpack_unpack_t mp; - msgpack_unserialize_data_t var_hash; - - ALLOC_INIT_ZVAL(tmp); - - template_init(&mp); - - msgpack_unserialize_var_init(&var_hash); - - mp.user.retval = (zval *)tmp; - mp.user.var_hash = (msgpack_unserialize_data_t *)&var_hash; - - ret = template_execute(&mp, (char *)val, (size_t)vallen, &off); - - if (ret == MSGPACK_UNPACK_EXTRA_BYTES || ret == MSGPACK_UNPACK_SUCCESS) - { - msgpack_unserialize_var_destroy(&var_hash, 0); - - tmp_hash = HASH_OF(tmp); - - zend_hash_internal_pointer_reset_ex(tmp_hash, &tmp_hash_pos); - - while (zend_hash_get_current_data_ex( - tmp_hash, (void *)&value, &tmp_hash_pos) == SUCCESS) - { - ret = zend_hash_get_current_key_ex( - tmp_hash, &key_str, &key_len, &key_long, 0, &tmp_hash_pos); - switch (ret) - { - case HASH_KEY_IS_LONG: - /* ??? */ - break; - case HASH_KEY_IS_STRING: - php_set_session_var( - key_str, key_len - 1, *value, NULL TSRMLS_CC); - php_add_session_var(key_str, key_len - 1 TSRMLS_CC); - break; - } - zend_hash_move_forward_ex(tmp_hash, &tmp_hash_pos); - } - } - else - { - msgpack_unserialize_var_destroy(&var_hash, 1); - } - - zval_ptr_dtor(&tmp); - - return SUCCESS; -} -#endif PHP_MSGPACK_API void php_msgpack_serialize(instead_smart *buf, zval *val TSRMLS_DC) //PHP_MSGPACK_API void php_msgpack_serialize(smart_str *buf, zval *val TSRMLS_DC) { + MSGPACK_G(php_only) = 1; msgpack_serialize_data_t var_hash; msgpack_serialize_var_init(&var_hash); @@ -242,6 +154,7 @@ PHP_MSGPACK_API void php_msgpack_serialize(instead_smart *buf, zval *val TSRMLS_ PHP_MSGPACK_API void php_msgpack_unserialize( zval *return_value, char *str, size_t str_len TSRMLS_DC) { + MSGPACK_G(php_only) = 1; int ret; size_t off = 0; msgpack_unpack_t mp; @@ -345,3 +258,4 @@ PHP_MSGPACK_API void php_msgpack_unserialize( // } // } //} +#endif \ No newline at end of file diff --git a/msgpack/msgpack/pack_define.h b/msgpack/msgpack/pack_define.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack/pack_template.h b/msgpack/msgpack/pack_template.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack/sysdep.h b/msgpack/msgpack/sysdep.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack/unpack_define.h b/msgpack/msgpack/unpack_define.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack/unpack_template.h b/msgpack/msgpack/unpack_template.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack/version.h b/msgpack/msgpack/version.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack_class.c b/msgpack/msgpack_class.c old mode 100755 new mode 100644 diff --git a/msgpack/msgpack_class.h b/msgpack/msgpack_class.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack_convert.c b/msgpack/msgpack_convert.c old mode 100755 new mode 100644 index 29655b0..bfed749 --- a/msgpack/msgpack_convert.c +++ b/msgpack/msgpack_convert.c @@ -1,6 +1,6 @@ #include "php.h" - +#if PHP_MAJOR_VERSION < 7 #include "php_msgpack.h" #include "msgpack_convert.h" #include "msgpack_errors.h" @@ -792,3 +792,4 @@ int msgpack_convert_template(zval *return_value, zval *tpl, zval **value) return FAILURE; } +#endif \ No newline at end of file diff --git a/msgpack/msgpack_convert.h b/msgpack/msgpack_convert.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack_errors.h b/msgpack/msgpack_errors.h old mode 100755 new mode 100644 index 262183f..2b132bb --- a/msgpack/msgpack_errors.h +++ b/msgpack/msgpack_errors.h @@ -3,14 +3,14 @@ #define MSGPACK_ERRORS_H #define MSGPACK_NOTICE(...) \ - if (MSGPACK_G(error_display)) { \ - zend_error(E_NOTICE, __VA_ARGS__); \ - } +// if (MSGPACK_G(error_display)) { \ +// zend_error(E_NOTICE, __VA_ARGS__); \ +// } #define MSGPACK_WARNING(...) \ - if (MSGPACK_G(error_display)) { \ - zend_error(E_WARNING, __VA_ARGS__); \ - } +// if (MSGPACK_G(error_display)) { \ +// zend_error(E_WARNING, __VA_ARGS__); \ +// } //#define MSGPACK_WARNING(...) \ // cpLog(__VA_ARGS__); diff --git a/msgpack/msgpack_pack.c b/msgpack/msgpack_pack.c old mode 100755 new mode 100644 index 12d01d1..aeef079 --- a/msgpack/msgpack_pack.c +++ b/msgpack/msgpack_pack.c @@ -1,5 +1,6 @@ #include "php.h" +#if PHP_MAJOR_VERSION < 7 #include "php_ini.h" #include "ext/standard/php_smart_str.h" #include "ext/standard/php_incomplete_class.h" @@ -22,7 +23,7 @@ memcpy(__dest->addr+__dest->len, (const void*)(buf), (n));\ __dest->len+=(n);\ }else{\ - __dest->exceed='1'; \ + __dest->exceed=1; \ }\ }while(0) @@ -639,3 +640,4 @@ void msgpack_serialize_zval( } return; } +#endif \ No newline at end of file diff --git a/msgpack/msgpack_pack.h b/msgpack/msgpack_pack.h old mode 100755 new mode 100644 diff --git a/msgpack/msgpack_unpack.c b/msgpack/msgpack_unpack.c old mode 100755 new mode 100644 index 14cee93..a02e386 --- a/msgpack/msgpack_unpack.c +++ b/msgpack/msgpack_unpack.c @@ -1,5 +1,6 @@ #include "php.h" +#if PHP_MAJOR_VERSION < 7 #include "php_ini.h" #include "ext/standard/php_incomplete_class.h" @@ -284,7 +285,6 @@ void msgpack_serialize_var_init(msgpack_serialize_data_t *var_hash) { HashTable **var_hash_ptr = (HashTable **)var_hash; TSRMLS_FETCH(); - if (MSGPACK_G(serialize).level) { *var_hash_ptr = MSGPACK_G(serialize).var_hash; } else { @@ -785,3 +785,4 @@ int msgpack_unserialize_map_item( return 0; } +#endif \ No newline at end of file diff --git a/msgpack/msgpack_unpack.h b/msgpack/msgpack_unpack.h old mode 100755 new mode 100644 diff --git a/msgpack/pack_define.h b/msgpack/pack_define.h old mode 100755 new mode 100644 diff --git a/msgpack/pack_template.h b/msgpack/pack_template.h old mode 100755 new mode 100644 diff --git a/msgpack/php_msgpack.h b/msgpack/php_msgpack.h old mode 100755 new mode 100644 index d0adf11..b04ef54 --- a/msgpack/php_msgpack.h +++ b/msgpack/php_msgpack.h @@ -1,11 +1,11 @@ #ifndef PHP_MSGPACK_H #define PHP_MSGPACK_H -#include "ext/standard/php_smart_str.h" /* for smart_str */ +#include /* for smart_str */ typedef struct _instead_smart{ int len; int max; - char exceed; + int exceed; void *addr; }instead_smart; #define MSGPACK_EXTENSION_VERSION "0.5.5" diff --git a/msgpack/sysdep.h b/msgpack/sysdep.h old mode 100755 new mode 100644 diff --git a/msgpack/unpack_define.h b/msgpack/unpack_define.h old mode 100755 new mode 100644 diff --git a/msgpack/unpack_template.h b/msgpack/unpack_template.h old mode 100755 new mode 100644 diff --git a/msgpack/version.h b/msgpack/version.h old mode 100755 new mode 100644 diff --git a/msgpack7/php_swoole_serialize.h b/msgpack7/php_swoole_serialize.h new file mode 100644 index 0000000..9f5ba22 --- /dev/null +++ b/msgpack7/php_swoole_serialize.h @@ -0,0 +1,157 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2016 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: woshiguo35@sina.com | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_SWOOLE_SERIALIZE_H +#define PHP_SWOOLE_SERIALIZE_H + +extern zend_module_entry swoole_serialize_module_entry; +#define phpext_swoole_serialize_ptr &swoole_serialize_module_entry + +#define PHP_SWOOLE_SERIALIZE_VERSION "0.1.1" + +#ifdef PHP_WIN32 +#define PHP_SWOOLE_SERIALIZE_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define PHP_SWOOLE_SERIALIZE_API __attribute__ ((visibility("default"))) +#else +#define PHP_SWOOLE_SERIALIZE_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +/* + Declare any global variables you may need between the BEGIN + and END macros here: + +ZEND_BEGIN_MODULE_GLOBALS(swoole_serialize) + zend_long global_value; + char *global_string; +ZEND_END_MODULE_GLOBALS(swoole_serialize) + */ + +/* Always refer to the globals in your function as SWOOLE_SERIALIZE_G(variable). + You are encouraged to rename these macros something shorter, see + examples in any other php module directory. + */ +#define SWOOLE_SERIALIZE_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(swoole_serialize, v) + +#if defined(ZTS) && defined(COMPILE_DL_SWOOLE_SERIALIZE) + +ZEND_TSRMLS_CACHE_EXTERN() +#endif + + + +#define SERIA_SIZE 1024 + +#if defined(__GNUC__) +#if __GNUC__ >= 3 +#define CPINLINE inline __attribute__((always_inline)) +#else +#define CPINLINE inline +#endif +#elif defined(_MSC_VER) +#define CPINLINE __forceinline +#else +#define CPINLINE inline +#endif + + + +typedef struct _seriaString { + size_t offset; + size_t total; + void * buffer; //zend_string +} seriaString; + +typedef struct _seriaArray { +// uint32_t nTableSize; +// uint32_t nNumOfElements; + // zend_long nNextFreeElement; +} seriaArray; + +typedef struct _SBucket { + zend_ulong h; /* hash value (or numeric index) */ + zend_value value; +} SBucket; + +typedef struct _SStringKey { + zend_ulong h; + char* val; +} SStringKey; + +typedef struct _SStringData { + size_t len; + char val[1]; +} SStringData; + +typedef struct _SBucketType { + zend_uchar key_type : 1; + zend_uchar key_len : 2; + zend_uchar data_len : 2; + zend_uchar data_type : 3; //IS_UNDEF means object now +} SBucketType; + +struct _swSeriaG { + zval sleep_fname; + zval weekup_fname; +}; + +struct _swSeriaG swSeriaG; + +#define SERIA_SET_ENTRY_TYPE(buffer,type) swoole_check_size(buffer, 1);\ + *(zend_uchar*) (buffer->buffer + buffer->offset) = *((zend_uchar*) & type);\ + buffer->offset += 1; + +#define SERIA_GET_ENTRY_TYPE(buffer) *(zend_uchar*) (buffer->buffer + buffer->offset) = *((zend_uchar*) & type);\ + buffer->offset += 1; + +#define SERIA_SET_ENTRY_SHORT(buffer,data) swoole_check_size(buffer, 2);\ + *(unsigned short*) (buffer->buffer + buffer->offset) = data;\ + buffer->offset += 2; + +#define SERIA_SET_ENTRY_ULONG(buffer,data) swoole_check_size(buffer, sizeof(zend_ulong));\ + *(zend_ulong *) (buffer->buffer + buffer->offset) = data;\ + buffer->offset += sizeof(zend_ulong); + +#define KEY_TYPE_STRING 1 +#define KEY_TYPE_INDEX 0 + + +PHP_SWOOLE_SERIALIZE_API zend_string* php_swoole_serialize(zval *val); +PHP_SWOOLE_SERIALIZE_API void php_swoole_unserialize(void * buffer, size_t len, zval *return_value, zval *object_args); + +PHP_METHOD(swSerialize, __construct); +PHP_METHOD(swSerialize, __destruct); + +#endif /* PHP_SWOOLE_SERIALIZE_H */ + + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/msgpack7/swoole_serialize.c b/msgpack7/swoole_serialize.c new file mode 100644 index 0000000..48d9a6f --- /dev/null +++ b/msgpack7/swoole_serialize.c @@ -0,0 +1,1092 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2016 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: woshiguo35@sina.com | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#if PHP_MAJOR_VERSION ==7 +#include "php_swoole_serialize.h" + + + +static int le_swoole_serialize; +static void swoole_serialize_object(seriaString *buffer, zval *zvalue, size_t start); +static void swoole_serialize_arr(seriaString *buffer, zend_array *zvalue); +static void* swoole_unserialize_arr(void *buffer, zval *zvalue, uint32_t num); +static void* swoole_unserialize_object(void *buffer, zval *return_value, zend_uchar bucket_len, zval *args); + +static CPINLINE int swoole_string_new(size_t size, seriaString *str, zend_uchar type) +{ + int total = ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + size + 1); + str->total = total; + //escape the header for later + str->offset = _STR_HEADER_SIZE; + //zend string addr + str->buffer = ecalloc(1, total); + if (!str->buffer) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "malloc Error: %s [%d]", strerror(errno), errno); + } + + SBucketType real_type; + real_type.data_type = type; + *(SBucketType*) (str->buffer + str->offset) = real_type; + str->offset += sizeof (SBucketType); + return 0; +} + +static CPINLINE void swoole_check_size(seriaString *str, size_t len) +{ + + int new_size = len + str->offset; + // int new_size = len + str->offset + 3 + sizeof (zend_ulong); //space 1 for the type and 2 for key string len or index len and(zend_ulong) for key h + if (str->total < new_size) + {//extend it + + //double size + new_size = ZEND_MM_ALIGNED_SIZE(new_size + new_size); + str->buffer = erealloc2(str->buffer, new_size, str->offset); + if (!str->buffer) + { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "realloc Error: %s [%d]", strerror(errno), errno); + } + str->total = new_size; + } + +} + +static CPINLINE void swoole_string_cpy(seriaString *str, void *mem, size_t len) +{ + swoole_check_size(str, len); + memcpy(str->buffer + str->offset, mem, len); + str->offset = len + str->offset; +} + +static CPINLINE void swoole_set_zend_value(seriaString *str, void *value) +{ + swoole_check_size(str, sizeof (zend_value)); + *(zend_value*) (str->buffer + str->offset) = *((zend_value*) value); + str->offset = sizeof (zend_value) + str->offset; +} + +static uint32_t CPINLINE cp_zend_hash_check_size(uint32_t nSize) +{ +#if defined(ZEND_WIN32) + unsigned long index; +#endif + + /* Use big enough power of 2 */ + /* size should be between HT_MIN_SIZE and HT_MAX_SIZE */ + if (nSize < HT_MIN_SIZE) + { + nSize = HT_MIN_SIZE; + }// else if (UNEXPECTED(nSize >= 1000000)) + else if (UNEXPECTED(nSize >= HT_MAX_SIZE)) + { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "invalid unserialize data"); + return 0; + } + +#if defined(ZEND_WIN32) + if (BitScanReverse(&index, nSize - 1)) + { + return 0x2 << ((31 - index) ^ 0x1f); + } + else + { + /* nSize is ensured to be in the valid range, fall back to it + rather than using an undefined bis scan result. */ + return nSize; + } +#elif (defined(__GNUC__) || __has_builtin(__builtin_clz)) && defined(PHP_HAVE_BUILTIN_CLZ) + return 0x2 << (__builtin_clz(nSize - 1) ^ 0x1f); +#else + nSize -= 1; + nSize |= (nSize >> 1); + nSize |= (nSize >> 2); + nSize |= (nSize >> 4); + nSize |= (nSize >> 8); + nSize |= (nSize >> 16); + return nSize + 1; +#endif +} + +/* + * arr layout + * type|key|bucketlen|buckets + */ +static CPINLINE void seria_array_type(zend_array *ht, seriaString *buffer, size_t type_offset, size_t blen_offset) +{ + buffer->offset = blen_offset; + if (ht->nNumOfElements <= 0xff) + { + ((SBucketType*) (buffer->buffer + type_offset))->data_len = 1; + SERIA_SET_ENTRY_TYPE(buffer, ht->nNumOfElements) + } + else if (ht->nNumOfElements <= 0xffff) + { + ((SBucketType*) (buffer->buffer + type_offset))->data_len = 2; + SERIA_SET_ENTRY_SHORT(buffer, ht->nNumOfElements); + } + else + { + ((SBucketType*) (buffer->buffer + type_offset))->data_len = 0; + swoole_string_cpy(buffer, &ht->nNumOfElements, sizeof (uint32_t)); + } +} + +/* + * buffer is bucket len addr + */ +static CPINLINE void* get_array_real_len(void *buffer, zend_uchar data_len, uint32_t *nNumOfElements) +{ + if (data_len == 1) + { + *nNumOfElements = *((zend_uchar*) buffer); + return buffer + sizeof (zend_uchar); + } + else if (data_len == 2) + { + *nNumOfElements = *((unsigned short*) buffer); + return buffer + sizeof (short); + } + else + { + *nNumOfElements = *((uint32_t*) buffer); + return buffer + sizeof (uint32_t); + } +} + +/* + * array + */ + +static void* swoole_unserialize_arr(void *buffer, zval *zvalue, uint32_t nNumOfElements) +{ + //Initialize zend array + zend_ulong h, nIndex, max_index = 0; + uint32_t size = cp_zend_hash_check_size(nNumOfElements); + if (!size) + { + return NULL; + } + ZVAL_NEW_ARR(zvalue); + + //Initialize buckets + zend_array *ht = Z_ARR_P(zvalue); + ht->nTableSize = size; + ht->nNumUsed = nNumOfElements; + ht->nNumOfElements = nNumOfElements; + ht->nNextFreeElement = 0; + ht->u.flags = HASH_FLAG_APPLY_PROTECTION; + ht->nTableMask = -(ht->nTableSize); + ht->pDestructor = ZVAL_PTR_DTOR; + + GC_REFCOUNT(ht) = 1; + GC_TYPE_INFO(ht) = IS_ARRAY; + if (ht->nNumUsed) + { + // void *arData = ecalloc(1, len); + HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); + ht->u.flags |= HASH_FLAG_INITIALIZED; + int ht_hash_size = HT_HASH_SIZE((ht)->nTableMask); + if (ht_hash_size <= 0) + { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "illegal unserialize data"); + return NULL; + } + HT_HASH_RESET(ht); + } + + + int idx; + Bucket *p; + for (idx = 0; idx < nNumOfElements; idx++) + { + SBucketType type = *((SBucketType*) buffer); + buffer += sizeof (SBucketType); + p = ht->arData + idx; + /* Initialize key */ + if (type.key_type == KEY_TYPE_STRING) + { + size_t key_len; + // h = *((zend_ulong*) buffer); + //buffer += sizeof (zend_ulong); + + if (type.key_len == 1) + { + key_len = *((zend_uchar*) buffer); + buffer += sizeof (zend_uchar); + } + else if (type.key_len == 2) + { + key_len = *((unsigned short*) buffer); + buffer += sizeof (unsigned short); + } + else + { + key_len = *((size_t*) buffer); + buffer += sizeof (size_t); + } + p->key = zend_string_init((char*) buffer, key_len, 0); + // h = zend_inline_hash_func((char*) buffer, key_len); + h = zend_inline_hash_func((char*) buffer, key_len); + p->key->h = p->h = h; + buffer += key_len; + } + else + { + if (type.key_len == 1) + { + h = *((zend_uchar*) buffer); + buffer += sizeof (zend_uchar); + } + else if (type.key_len == 2) + { + h = *((unsigned short*) buffer); + buffer += sizeof (unsigned short); + } + else + { + h = *((zend_ulong*) buffer); + buffer += sizeof (zend_ulong); + } + p->h = h; + p->key = NULL; + if (h >= max_index) + { + max_index = h + 1; + } + } + + /* Initialize hash */ + nIndex = h | ht->nTableMask; + Z_NEXT(p->val) = HT_HASH(ht, nIndex); + HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); + + /* Initialize data type */ + p->val.u1.v.type = type.data_type; + Z_TYPE_FLAGS(p->val) = 0; + + /* Initialize data */ + if (type.data_type == IS_STRING) + { + size_t data_len; + if (type.data_len == 1) + { + data_len = *((zend_uchar*) buffer); + buffer += sizeof (zend_uchar); + } + else if (type.data_len == 2) + { + data_len = *((unsigned short*) buffer); + buffer += sizeof (unsigned short); + } + else + { + data_len = *((size_t*) buffer); + buffer += sizeof (size_t); + } + p->val.value.str = zend_string_init((char*) buffer, data_len, 0); + buffer += data_len; + Z_TYPE_INFO(p->val) = IS_STRING_EX; + } + else if (type.data_type == IS_ARRAY) + { + uint32_t num = 0; + buffer = get_array_real_len(buffer, type.data_len, &num); + buffer = swoole_unserialize_arr(buffer, &p->val, num); + } + else if (type.data_type == IS_LONG) + { + + if (type.data_len == 1) + { + Z_LVAL(p->val) = *((zend_uchar*) buffer); + buffer += sizeof (zend_uchar); + } + else if (type.data_len == 2) + { + Z_LVAL(p->val) = *((unsigned short*) buffer); + buffer += sizeof (unsigned short); + } + else + { + p->val.value = *((zend_value*) buffer); + buffer += sizeof (zend_value); + } + + } + else if (type.data_type == IS_DOUBLE) + { + p->val.value = *((zend_value*) buffer); + buffer += sizeof (zend_value); + } + else if (type.data_type == IS_UNDEF) + { + buffer = swoole_unserialize_object(buffer, &p->val, type.data_len, NULL); + Z_TYPE_INFO(p->val) = IS_OBJECT_EX; + } + + } + ht->nNextFreeElement = max_index; + + return buffer; + +} + +static void swoole_serialize_arr(seriaString *buffer, zend_array *zvalue) +{ + zval *data; + zend_string *key; + zend_ulong index; + + ZEND_HASH_FOREACH_KEY_VAL(zvalue, index, key, data) + { + SBucketType type; + type.data_type = Z_TYPE_P(data); + //start point + size_t p = buffer->offset; + + //seria key + if (key) + { + type.key_type = KEY_TYPE_STRING; + if (key->len <= 0xff) + { + type.key_len = 1; + SERIA_SET_ENTRY_TYPE(buffer, type); + // SERIA_SET_ENTRY_ULONG(buffer, key->h); + SERIA_SET_ENTRY_TYPE(buffer, key->len); + swoole_string_cpy(buffer, key->val, key->len); + } + else if (key->len <= 0xffff) + {//if more than this don't need optimize + type.key_len = 2; + SERIA_SET_ENTRY_TYPE(buffer, type); + // SERIA_SET_ENTRY_ULONG(buffer, key->h); + SERIA_SET_ENTRY_SHORT(buffer, key->len); + swoole_string_cpy(buffer, key->val, key->len); + } + else + { + type.key_len = 0; + SERIA_SET_ENTRY_TYPE(buffer, type); + //swoole_string_cpy(buffer, key + XtOffsetOf(zend_string, h), sizeof (size_t) + sizeof (zend_ulong) + key->len); + swoole_string_cpy(buffer, key + XtOffsetOf(zend_string, len), sizeof (size_t) + key->len); + } + + } + else + { + type.key_type = KEY_TYPE_INDEX; + if (index <= 0xff) + { + type.key_len = 1; + SERIA_SET_ENTRY_TYPE(buffer, type); + SERIA_SET_ENTRY_TYPE(buffer, index); + } + else if (index <= 0xffff) + { + type.key_len = 2; + SERIA_SET_ENTRY_TYPE(buffer, type); + SERIA_SET_ENTRY_SHORT(buffer, index); + } + else + { + type.key_len = 0; + SERIA_SET_ENTRY_TYPE(buffer, type); + SERIA_SET_ENTRY_ULONG(buffer, index); + } + + } + + //seria data +try_again: + switch (Z_TYPE_P(data)) + { + case IS_STRING: + { + if (Z_STRLEN_P(data) <= 0xff) + { + ((SBucketType*) (buffer->buffer + p))->data_len = 1; + SERIA_SET_ENTRY_TYPE(buffer, Z_STRLEN_P(data)); + swoole_string_cpy(buffer, Z_STRVAL_P(data), Z_STRLEN_P(data)); + } + else if (Z_STRLEN_P(data) <= 0xffff) + { + ((SBucketType*) (buffer->buffer + p))->data_len = 2; + SERIA_SET_ENTRY_SHORT(buffer, Z_STRLEN_P(data)); + swoole_string_cpy(buffer, Z_STRVAL_P(data), Z_STRLEN_P(data)); + } + else + {//if more than this don't need optimize + ((SBucketType*) (buffer->buffer + p))->data_len = 0; + swoole_string_cpy(buffer, (char*) Z_STR_P(data) + XtOffsetOf(zend_string, len), sizeof (size_t) + Z_STRLEN_P(data)); + } + break; + } + case IS_LONG: + { + if (Z_LVAL_P(data) <= 0xff) + { + ((SBucketType*) (buffer->buffer + p))->data_len = 1; + SERIA_SET_ENTRY_TYPE(buffer, Z_LVAL_P(data)); + } + else if (Z_LVAL_P(data) <= 0xffff) + { + ((SBucketType*) (buffer->buffer + p))->data_len = 2; + SERIA_SET_ENTRY_SHORT(buffer, Z_LVAL_P(data)); + } + else + { + ((SBucketType*) (buffer->buffer + p))->data_len = 0; + swoole_set_zend_value(buffer, &(data->value)); + } + break; + } + case IS_DOUBLE: + swoole_set_zend_value(buffer, &(data->value)); + break; + case IS_REFERENCE: + data = Z_REFVAL_P(data); + ((SBucketType*) (buffer->buffer + p))->data_type = Z_TYPE_P(data); + goto try_again; + break; + case IS_ARRAY: + { + zend_array *ht = Z_ARRVAL_P(data); + + if (ZEND_HASH_GET_APPLY_COUNT(ht) > 1) + { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "you array have cycle ref"); + } + else + { + seria_array_type(ht, buffer, p, buffer->offset); + if (ZEND_HASH_APPLY_PROTECTION(ht)) + { + ZEND_HASH_INC_APPLY_COUNT(ht); + swoole_serialize_arr(buffer, ht); + ZEND_HASH_DEC_APPLY_COUNT(ht); + } + else + { + swoole_serialize_arr(buffer, ht); + } + + } + break; + } + //object propterty table is this type + case IS_INDIRECT: + data = Z_INDIRECT_P(data); + ((SBucketType*) (buffer->buffer + p))->data_type = Z_TYPE_P(data); + goto try_again; + break; + case IS_OBJECT: + { + /* + * layout + * type | key | namelen | name | bucket len |buckets + */ + ((SBucketType*) (buffer->buffer + p))->data_type = IS_UNDEF; + + if (ZEND_HASH_APPLY_PROTECTION(Z_OBJPROP_P(data))) + { + ZEND_HASH_INC_APPLY_COUNT(Z_OBJPROP_P(data)); + swoole_serialize_object(buffer, data, p); + ZEND_HASH_DEC_APPLY_COUNT(Z_OBJPROP_P(data)); + } + else + { + swoole_serialize_object(buffer, data, p); + } + + break; + } + default:// + break; + + } + + } + ZEND_HASH_FOREACH_END(); +} + +/* + * string + */ +static CPINLINE void swoole_serialize_string(seriaString *buffer, zval *zvalue) +{ + + swoole_string_cpy(buffer, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); +} + +static CPINLINE zend_string* swoole_unserialize_string(void *buffer, size_t len) +{ + + return zend_string_init(buffer, len, 0); +} + +/* + * raw + */ + +static CPINLINE void swoole_unserialize_raw(void *buffer, zval *zvalue) +{ + + memcpy(&zvalue->value, buffer, sizeof (zend_value)); +} + +/* + * null + */ + +static CPINLINE void swoole_unserialize_null(void *buffer, zval *zvalue) +{ + + memcpy(&zvalue->value, buffer, sizeof (zend_value)); +} + +static CPINLINE void swoole_serialize_raw(seriaString *buffer, zval *zvalue) +{ + + swoole_string_cpy(buffer, &zvalue->value, sizeof (zend_value)); +} + +/* + * obj layout + * type|name len| name| buket len |buckets + */ +static void swoole_serialize_object(seriaString *buffer, zval *obj, size_t start) +{ + zend_string *name = Z_OBJCE_P(obj)->name; + if (ZEND_HASH_GET_APPLY_COUNT(Z_OBJPROP_P(obj)) > 1) + { + zend_throw_exception_ex(NULL, 0, "the object %s have cycle ref!", name->val); + return; + } + zend_class_entry *ce = Z_OBJ_P(obj)->ce; + swoole_string_cpy(buffer, (char*) name + XtOffsetOf(zend_string, len), sizeof (size_t) + name->len); + + if (ce && zend_hash_exists(&ce->function_table, Z_STR(swSeriaG.sleep_fname))) + { + zval retval; + if (call_user_function_ex(NULL, obj, &swSeriaG.sleep_fname, &retval, 0, 0, 1, NULL) == SUCCESS) + { + if (EG(exception)) + { + zval_dtor(&retval); + return; + } + if (Z_TYPE(retval) == IS_ARRAY) + { + zend_string *prop_key; + zval *prop_value, *sleep_value; + const char *prop_name, *class_name; + size_t prop_key_len; + int got_num = 0; + + //for the zero malloc + zend_array tmp_arr; + zend_array *ht = (zend_array *) & tmp_arr; + _zend_hash_init(ht, zend_hash_num_elements(Z_ARRVAL(retval)), ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC); + ht->nTableMask = -(ht)->nTableSize; + ALLOCA_FLAG(use_heap); + void *ht_addr = do_alloca(HT_SIZE(ht), use_heap); + HT_SET_DATA_ADDR(ht, ht_addr); + ht->u.flags |= HASH_FLAG_INITIALIZED; + HT_HASH_RESET(ht); + + //just clean property do not add null when does not exist + //we double for each, cause we do not malloc and release it + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_OBJPROP_P(obj), prop_key, prop_value) + { + //get origin property name + zend_unmangle_property_name_ex(prop_key, &class_name, &prop_name, &prop_key_len); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(retval), sleep_value) + { + if (Z_TYPE_P(sleep_value) == IS_STRING && + Z_STRLEN_P(sleep_value) == prop_key_len && + memcmp(Z_STRVAL_P(sleep_value), prop_name, prop_key_len) == 0) + { + got_num++; + //add mangle key,unmangle in unseria + _zend_hash_add_or_update(ht, prop_key, prop_value, HASH_UPDATE ZEND_FILE_LINE_RELAY_CC); + + break; + } + + } + ZEND_HASH_FOREACH_END(); + + } + ZEND_HASH_FOREACH_END(); + + //there some member not in property + if (zend_hash_num_elements(Z_ARRVAL(retval)) > got_num) + { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep() retrun a member but does not exist in property"); + + } + seria_array_type(ht, buffer, start, buffer->offset); + swoole_serialize_arr(buffer, ht); + ZSTR_ALLOCA_FREE(ht_addr, use_heap); + zval_dtor(&retval); + return; + + } + else + { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, " __sleep should return an array only containing the " + "names of instance-variables to serialize"); + zval_dtor(&retval); + } + + } + } + seria_array_type(Z_OBJPROP_P(obj), buffer, start, buffer->offset); + swoole_serialize_arr(buffer, Z_OBJPROP_P(obj)); +} + +/* + * for the zero malloc + */ +static CPINLINE zend_string * swoole_string_init(const char *str, size_t len) +{ + ALLOCA_FLAG(use_heap); + zend_string *ret; + ZSTR_ALLOCA_INIT(ret, str, len, use_heap); + + return ret; +} + +/* + * for the zero malloc + */ +static CPINLINE void swoole_string_release(zend_string *str) +{ + ALLOCA_FLAG(use_heap); + ZSTR_ALLOCA_FREE(str, use_heap); +} + +static CPINLINE zend_class_entry* swoole_try_get_ce(zend_string *class_name) +{ + //user class , do not support incomplete class now + zend_class_entry *ce = zend_lookup_class(class_name); + if (ce) + { + return ce; + } + // try call unserialize callback and retry lookup + zval user_func, args[1], retval; + zend_string *fname = swoole_string_init(PG(unserialize_callback_func), strlen(PG(unserialize_callback_func))); + Z_STR(user_func) = fname; + Z_TYPE_INFO(user_func) = IS_STRING_EX; + ZVAL_STR(&args[0], class_name); + + call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL); + + swoole_string_release(fname); + + //user class , do not support incomplete class now + ce = zend_lookup_class(class_name); + if (!ce) + { + zend_throw_exception_ex(NULL, 0, "can not find class %s", class_name->val TSRMLS_CC); + return NULL; + } + else + { + return ce; + } +} + +/* + * obj layout + * type| key[0|1] |name len| name| buket len |buckets + */ +static void* swoole_unserialize_object(void *buffer, zval *return_value, zend_uchar bucket_len, zval *args) +{ + zval property; + uint32_t arr_num = 0; + size_t name_len = *((size_t*) buffer); + buffer += sizeof (size_t); + zend_string *class_name = swoole_string_init((char*) buffer, name_len); //todo alloca it + + zend_class_entry *ce = swoole_try_get_ce(class_name); + swoole_string_release(class_name); + + if (!ce) + { + return NULL; + } + buffer += name_len; + + buffer = get_array_real_len(buffer, bucket_len, &arr_num); + buffer = swoole_unserialize_arr(buffer, &property, arr_num); + + object_init_ex(return_value, ce); + + zval *data; + const zend_string *key; + zend_ulong index; + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(property), index, key, data) + { + + const char *prop_name, *tmp; + size_t prop_len; + zend_unmangle_property_name_ex(key, &tmp, &prop_name, &prop_len); + zend_update_property(ce, return_value, prop_name, prop_len, data); + } + ZEND_HASH_FOREACH_END(); + zval_dtor(&property); + + if (ce->constructor) + { +// zend_fcall_info fci = {0}; +// zend_fcall_info_cache fcc = {0}; +// fci.size = sizeof (zend_fcall_info); +// zval retval; +// ZVAL_UNDEF(&fci.function_name); +// fci.retval = &retval; +// fci.param_count = 0; +// fci.params = NULL; +// fci.no_separation = 1; +// fci.object = Z_OBJ_P(return_value); +// +// zend_fcall_info_args_ex(&fci, ce->constructor, args); +// +// fcc.initialized = 1; +// fcc.function_handler = ce->constructor; +// // fcc.calling_scope = EG(scope); +// fcc.called_scope = Z_OBJCE_P(return_value); +// fcc.object = Z_OBJ_P(return_value); +// +// if (zend_call_function(&fci, &fcc) == FAILURE) +// { +// zend_throw_exception_ex(NULL, 0, "could not call class constructor"); +// } +// zend_fcall_info_args_clear(&fci, 1); + } + + + //call object __wakeup + if (zend_hash_str_exists(&ce->function_table, "__wakeup", sizeof ("__wakeup") - 1)) + { + zval ret, wakeup; + zend_string *fname = swoole_string_init("__wakeup", sizeof ("__wakeup") - 1); + Z_STR(wakeup) = fname; + Z_TYPE_INFO(wakeup) = IS_STRING_EX; + call_user_function_ex(CG(function_table), return_value, &wakeup, &ret, 0, NULL, 1, NULL); + swoole_string_release(fname); + zval_ptr_dtor(&ret); + } + + return buffer; + +} + +/* + * dispatch + */ + +static CPINLINE void swoole_seria_dispatch(seriaString *buffer, zval *zvalue) +{ +again: + switch (Z_TYPE_P(zvalue)) + { + case IS_NULL: + case IS_TRUE: + case IS_FALSE: + break; + case IS_LONG: + case IS_DOUBLE: + swoole_serialize_raw(buffer, zvalue); + break; + case IS_STRING: + swoole_serialize_string(buffer, zvalue); + break; + case IS_ARRAY: + { + seria_array_type(Z_ARRVAL_P(zvalue), buffer, _STR_HEADER_SIZE, _STR_HEADER_SIZE + 1); + swoole_serialize_arr(buffer, Z_ARRVAL_P(zvalue)); + break; + } + case IS_REFERENCE: + zvalue = Z_REFVAL_P(zvalue); + goto again; + break; + case IS_OBJECT: + { + SBucketType* type = (SBucketType*) (buffer->buffer + _STR_HEADER_SIZE); + type->data_type = IS_UNDEF; + swoole_serialize_object(buffer, zvalue, _STR_HEADER_SIZE); + break; + } + default: + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "swoole serialize not support this type "); + + break; + } +} + +PHP_SWOOLE_SERIALIZE_API zend_string* php_swoole_serialize(zval *zvalue) +{ + + seriaString str; + swoole_string_new(SERIA_SIZE, &str, Z_TYPE_P(zvalue)); + swoole_seria_dispatch(&str, zvalue); //serialize into a string + zend_string *z_str = (zend_string *) str.buffer; + + z_str->val[str.offset] = '\0'; + z_str->len = str.offset - _STR_HEADER_SIZE; + z_str->h = 0; + GC_REFCOUNT(z_str) = 1; + GC_TYPE_INFO(z_str) = IS_STRING; + + return z_str; +} + +/* + * buffer is seria string buffer + * len is string len + * return_value is unseria bucket + * args is for the object ctor (can be NULL) + */ +PHP_SWOOLE_SERIALIZE_API void php_swoole_unserialize(void * buffer, size_t len, zval *return_value, zval *object_args) +{ + SBucketType type = *(SBucketType*) (buffer); + zend_uchar real_type = type.data_type; + buffer += sizeof (SBucketType); + switch (real_type) + { + case IS_NULL: + case IS_TRUE: + case IS_FALSE: + Z_TYPE_INFO_P(return_value) = real_type; + return; + case IS_LONG: + case IS_DOUBLE: + swoole_unserialize_raw(buffer, return_value); + Z_TYPE_INFO_P(return_value) = real_type; + return; + case IS_STRING: + len -= sizeof (SBucketType); + zend_string *str = swoole_unserialize_string(buffer, len); + RETURN_STR(str); + case IS_ARRAY: + { + uint32_t num = 0; + buffer = get_array_real_len(buffer, type.data_len, &num); + swoole_unserialize_arr(buffer, return_value, num); + break; + } + case IS_UNDEF: + swoole_unserialize_object(buffer, return_value, type.data_len, object_args); + break; + default: + ZVAL_FALSE(return_value); + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "swoole serialize not support this type "); + + break; + } +} + +//static void test() +//{ +// zend_string *fname = swoole_string_init("__wakeup", sizeof ("__wakeup") - 1); +//// zend_string *fname = zend_string_init("__wakeup", sizeof ("__wakeup") - 1, 0); +//// zend_string_release(fname); +//swoole_string_release(fname); +//} + +PHP_FUNCTION(swoole_serialize) +{ + + zval *zvalue; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zvalue) == FAILURE) + { + return; + } + zend_string *z_str = php_swoole_serialize(zvalue); + + RETURN_STR(z_str); +} + +PHP_FUNCTION(swoole_unserialize) +{ + char *buffer = NULL; + size_t arg_len; + zval *args = NULL; //for object + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buffer, &arg_len, &args) == FAILURE) + { + + return; + } + php_swoole_unserialize(buffer, arg_len, return_value, args); + return; + +} + + +const zend_function_entry swSerialize_methods[] = { + ZEND_FENTRY(pack, ZEND_FN(swoole_serialize), NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + ZEND_FENTRY(unpack, ZEND_FN(swoole_unserialize), NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swSerialize, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(swSerialize, __destruct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR) + PHP_FE_END +}; + +zend_class_entry *swoole_serialize_class_entry_ptr; + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(swoole_serialize) +{ + + ZVAL_STRING(&swSeriaG.sleep_fname, "__sleep"); + ZVAL_STRING(&swSeriaG.weekup_fname, "__weekup"); + + zend_class_entry swoole_serialize_ce; + INIT_CLASS_ENTRY(swoole_serialize_ce, "swSerialize", swSerialize_methods); + swoole_serialize_class_entry_ptr = zend_register_internal_class(&swoole_serialize_ce TSRMLS_CC); + + return SUCCESS; +} + +PHP_METHOD(swSerialize, __construct) +{//do noting now + return; +} + +PHP_METHOD(swSerialize, __destruct) +{ + //do noting now + return; +} + + + +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(swoole_serialize) +{ + + /* uncomment this line if you have INI entries + UNREGISTER_INI_ENTRIES(); + */ + return SUCCESS; +} +/* }}} */ + +/* Remove if there's nothing to do at request start */ + +/* {{{ PHP_RINIT_FUNCTION + */ +PHP_RINIT_FUNCTION(swoole_serialize) +{ +#if defined(COMPILE_DL_SWOOLE_SERIALIZE) && defined(ZTS) + + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + return SUCCESS; +} +/* }}} */ + +/* Remove if there's nothing to do at request end */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(swoole_serialize) +{ + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(swoole_serialize) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "swoole_serialize support", "enabled"); + php_info_print_table_row(2, "Author", "郭新华"); + php_info_print_table_row(2, "email", "woshiguo35@sina.com"); + php_info_print_table_end(); + + /* Remove comments if you have entries in php.ini + DISPLAY_INI_ENTRIES(); + */ +} +/* }}} */ + +/* {{{ swoole_serialize_functions[] + * + * Every user visible function must have an entry in swoole_serialize_functions[]. + */ +const zend_function_entry swoole_serialize_functions[] = { + PHP_FE(swoole_serialize, NULL) + PHP_FE(swoole_unserialize, NULL) + PHP_FE_END /* Must be the last line in swoole_serialize_functions[] */ +}; +/* }}} */ + +/* {{{ swoole_serialize_module_entry + */ +zend_module_entry swoole_serialize_module_entry = { + STANDARD_MODULE_HEADER, + "swoole_serialize", + swoole_serialize_functions, + PHP_MINIT(swoole_serialize), + PHP_MSHUTDOWN(swoole_serialize), + PHP_RINIT(swoole_serialize), /* Replace with NULL if there's nothing to do at request start */ + PHP_RSHUTDOWN(swoole_serialize), /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(swoole_serialize), + PHP_SWOOLE_SERIALIZE_VERSION, + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ +#endif +#ifdef COMPILE_DL_SWOOLE_SERIALIZE +#ifdef ZTS +ZEND_TSRMLS_CACHE_DEFINE() +#endif +ZEND_GET_MODULE(swoole_serialize) +#endif +//#endif +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..8cbfdc7 --- /dev/null +++ b/package.xml @@ -0,0 +1,103 @@ + + +php-cp + pecl.php.net + mysql and redis proxy write with php extension + + The extension start a proxy process and hold some pdo and redis connections, + It provide local connect pool like java. + + + xinhua.guo + Gary + gray198904@gmail.com + yes + + 2015-01-15 + + 1.1 + 1.1 + + + beta + beta + + Apache2.0 + +- new extension + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.3.10 + + + 1.4.0 + + + + php-cp + + + + diff --git a/php_connect_pool.h b/php_connect_pool.h old mode 100755 new mode 100644 index 73c8adf..b39ce60 --- a/php_connect_pool.h +++ b/php_connect_pool.h @@ -28,11 +28,26 @@ #include "config.h" #endif +//#ifdef HAVE_EPOLL #include //todo #ifndef EPOLLRDHUP #define EPOLLRDHUP 0x2000 #define NO_EPOLLRDHUP #endif +//#endif + +#if defined(__GNUC__) +#if __GNUC__ >= 3 +#define CPINLINE inline __attribute__((always_inline)) +#else +#define CPINLINE inline +#endif +#elif defined(_MSC_VER) +#define CPINLINE __forceinline +#else +#define CPINLINE inline +#endif + #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) #define MAP_ANONYMOUS MAP_ANON @@ -42,8 +57,6 @@ #define SOCK_NONBLOCK O_NONBLOCK #endif -typedef unsigned long int uint64_t; - #include #include #include @@ -59,20 +72,29 @@ typedef unsigned long int uint64_t; #include #include #include +#include +#include +#include "include/php7_wrapper.h" #include "cpMemory.h" #include "cpFunction.h" #include "cpWorker.h" #include "cpServer.h" #include "cpNetWork.h" #include "cpClientNet.h" +#include "cpPingWorker.h" -#include "ext/pdo/php_pdo_driver.h" -#include "ext/standard/php_smart_str.h" -#include "ext/standard/php_var.h" -#include "zend_exceptions.h" - +#include +#include +#include +#if PHP_MAJOR_VERSION < 7 #include "msgpack/php_msgpack.h" +#else +#include "msgpack7/php_swoole_serialize.h" +#endif +#ifdef ZTS +#include "TSRM.h" +#endif /** * PHP5.2 @@ -99,18 +121,26 @@ extern zend_module_entry connect_pool_module_entry; #define PHP_CONNECT_POOL_API #endif -#ifdef ZTS -#include "TSRM.h" -#endif #define CP_CHECK_RETURN(s) if(s<0){RETURN_FALSE;}else{RETURN_TRUE;}return #define CP_SIG_EVENT (SIGRTMIN+1) #define CP_TCPEVENT_GET 1 #define CP_TCPEVENT_RELEASE 2 +#define CP_TCPEVENT_ADD 3 +#define CP_TCPEVENT_GETFD 4 +#define CP_GET_PID if(cpPid==0)cpPid=getpid() + +typedef struct _cpRecvEvent { + zval *ret_value; + uint8_t type; +} cpRecvEvent; #define CP_SIGEVENT_TURE 1//01 #define CP_SIGEVENT_EXCEPTION 2//10 #define CP_SIGEVENT_PDO 3//11 +#define CP_SIGEVENT_STMT_OBJ 4//100 +#define CP_SIGEVENT_STATUS 5//101 +#define CP_SIGEVENT_DIE 6 #define CP_EVENTLEN_ADD_TYPE(len,__type) \ len = len <<2;\ len = len | __type; @@ -119,16 +149,18 @@ extern zend_module_entry connect_pool_module_entry; #define CP_RES_SERVER_NAME "ConPoolServer" #define CP_RES_CLIENT_NAME "ConPoolClient" +#define CP_ASYNC_PRE "async" #define CP_PROCESS_MASTER 1 #define CP_PROCESS_WORKER 2 #define CP_PROCESS_MANAGER 3 +#define CP_PROCESS_PING 4 #define CP_PIPE_MOD O_RDWR #define CP_TYPE_SIZE sizeof(uint8_t) -#define CP_GROUP_LEN 1000 // -#define CP_GROUP_NUM 100 //the max group num of proxy process . todo check it - +//#define CP_GROUP_LEN 1000 // +//#define CP_GROUP_NUM 100 //the max group num of proxy process . todo check it + extern int le_cli_connect_pool; extern zend_class_entry *redis_connect_pool_class_entry_ptr; @@ -142,14 +174,11 @@ PHP_RSHUTDOWN_FUNCTION(connect_pool); PHP_MINFO_FUNCTION(connect_pool); PHP_FUNCTION(pool_server_create); +PHP_FUNCTION(pool_server_status); PHP_FUNCTION(pool_server_shutdown); PHP_FUNCTION(pool_server_reload); +PHP_FUNCTION(pool_server_version); -PHP_FUNCTION(get_disable_list); -PHP_FUNCTION(set_disable_list); -PHP_FUNCTION(shit_pdo_warning_function); - -PHP_FUNCTION(client_close); @@ -157,29 +186,46 @@ PHP_METHOD(pdo_connect_pool, __construct); PHP_METHOD(pdo_connect_pool, __destruct); PHP_METHOD(pdo_connect_pool, __call); PHP_METHOD(pdo_connect_pool, release); -//PHP_METHOD(pdo_connect_pool, quote); +PHP_METHOD(pdo_connect_pool, msConfig); +PHP_METHOD(pdo_connect_pool, forceMaster); +PHP_METHOD(pdo_connect_pool, close); +PHP_METHOD(pdo_connect_pool, setAsync); +PHP_METHOD(pdo_connect_pool, done); PHP_METHOD(pdo_connect_pool_PDOStatement, __call); +PHP_METHOD(pdo_connect_pool_PDOStatement, setAsync); +PHP_METHOD(pdo_connect_pool_PDOStatement, release); +PHP_METHOD(pdo_connect_pool_PDOStatement, done); +PHP_METHOD(pdo_connect_pool_PDOStatement, rewind); +PHP_METHOD(pdo_connect_pool_PDOStatement, next); +PHP_METHOD(pdo_connect_pool_PDOStatement, current); +PHP_METHOD(pdo_connect_pool_PDOStatement, key); +PHP_METHOD(pdo_connect_pool_PDOStatement, valid); PHP_METHOD(redis_connect_pool, __construct); PHP_METHOD(redis_connect_pool, __destruct); PHP_METHOD(redis_connect_pool, __call); PHP_METHOD(redis_connect_pool, release); +PHP_METHOD(redis_connect_pool, auth); PHP_METHOD(redis_connect_pool, select); -PHP_FUNCTION(redis_connect); +PHP_METHOD(redis_connect_pool, connect); +PHP_METHOD(redis_connect_pool, done); +PHP_METHOD(redis_connect_pool, close); +PHP_METHOD(redis_connect_pool, setAsync); -void send_oob2proxy(zend_rsrc_list_entry *rsrc TSRMLS_DC); +void send_oob2proxy(zend_resource *rsrc TSRMLS_DC); extern void cp_serialize(smart_str *ser_data, zval *array); extern zval * cp_unserialize(char *data, int len); +extern int redis_proxy_connect(zval *args, int flag); +extern int pdo_proxy_connect(zval *args, int flag); + int worker_onReceive(zval *data); -CPINLINE int CP_INTERNAL_SERIALIZE_SEND_MEM(zval *ret_value, uint8_t __type); -CPINLINE int CP_CLIENT_SERIALIZE_SEND_MEM(zval *ret_value, int pid, int max, int semid); +int CP_INTERNAL_SERIALIZE_SEND_MEM(zval *ret_value, uint8_t __type); +int CP_CLIENT_SERIALIZE_SEND_MEM(zval *ret_value, cpClient *); extern cpServerG ConProxyG; extern cpServerGS *ConProxyGS; extern cpWorkerG ConProxyWG; -extern cpMasterInfo info; extern FILE *cp_log_fn; -extern char * cpArgv0; #endif /* PHP_CON_PROXY_H */ diff --git a/pool.ini b/pool.ini deleted file mode 100644 index 9ea7446..0000000 --- a/pool.ini +++ /dev/null @@ -1,29 +0,0 @@ -[redis] -pool_min = 2 -pool_max = 30 -log_file = /tmp/log -;连接空闲后的回收力度,值越大回收的越快,但是会造成更多的消耗 -recycle_num = 2 -;空闲连接回收的发呆时间 单位秒 -idel_time = 2 -;1M -max_read_len = 1048576 -daemonize = 1 -;获取连接失败,是否使用队列缓冲,设置为0直接抛异常 -use_wait_queue = 1 -port = 9057 - -[mysql] -pool_min = 2 -pool_max = 30 -log_file = /tmp/log -;连接空闲后的回收力度,值越大回收的越快,但是会造成更多的消耗 -recycle_num = 2 -;空闲连接回收的发呆时间 单位秒 -idel_time = 2 -;5M -max_read_len = 5242880 -daemonize = 1 -;获取连接失败,是否使用队列缓冲,设置为0直接抛异常 -use_wait_queue = 1 -port = 9056 \ No newline at end of file diff --git a/pool.ini.example b/pool.ini.example new file mode 100644 index 0000000..de6eb93 --- /dev/null +++ b/pool.ini.example @@ -0,0 +1,40 @@ +[common] +;log文件 +log_file = /tmp/phpcp.log +;the num of TCP connections release every idel_time cycles(连接空闲后的回收力度,值越大回收的越快,但是会造成更多的消耗) +recycle_num = 2 +;In this period of time,if no process use this connection ,the TCP connection will release(空闲连接回收的发呆时间 单位秒) +idel_time = 2 +;worker 进程ping数据库的间隔时间s +ping_time = 30 +;;max query package len,exceed will throw exception(最大转发的数据包 字节,超过抛异常) +max_read_len = 1048576 +;run as daemonize(是否开启守护进程化) +daemonize = 1 +;If the num of connection to max, whether to use the queue buffer, set to 0 throw an exception(连接都被占用后,再获取连接是否使用队列缓冲,设置为0直接抛异常) +use_wait_queue = 1 +;the get_disable_list() function returns the maximum number of IP(get_disable_list函数最多返回多少个失效结点,防止网络抖动踢掉所有的机器) +max_fail_num = 1 +;just the pool_server process port(端口) +port = 6253 +;for the slow query monitor,unit ms ,default 0 means not open(持有一个连接超过这么长时间写log日志 单位毫秒 默认为0为不开启log) +;max_hold_time_to_log = 1000 +;for the big data monitor,unit byte ,default 0 means not open(单次查询超过这么大字节写log日志 单位字节 默认为0为不开启log) +;tips你可以将这个设置非常小,那么你就可以在log里面看到所有的数据库操作 +;max_data_size_to_log = 1024 + + +;注意数据源需要加 '' +;PDO数据源要与new PDO的第一个参数完全一致(包括顺序) +;如果不配置数据源 那么默认最大是20 最小是1 ,在第一次查询的时候自动创建 +['mysql:host=192.168.1.19;dbname=mz_gay_group2;charset=utf8'];mysql配置 +pool_min = 2 +pool_max = 30 + +['mysql:host=192.168.1.19;dbname=mz_gay_group;charset=utf8'];mysql配置 +pool_min = 2 +pool_max = 30 + +['192.168.1.19:6379:0'];redis配置 ip:port:db号 +pool_min = 2 +pool_max = 30 diff --git a/pool_server b/pool_server index 1b351d3..34d5e31 100755 --- a/pool_server +++ b/pool_server @@ -1,43 +1,68 @@ -#!/usr/bin/env php +#!/bin/env php $value) { - pool_server_reload((int) file_get_contents(PID_PRE . $k . ".pid")); - } - echo "Tips: The reload can only modify 'pool_min','pool_max','recycle_num' and 'idel_time'\n"; + pool_server_reload($pid); + echo "Tips: The reload can only modify 'pool_min','pool_max','recycle_num' and 'idel_time'" . PHP_EOL; + die; break; case "stop": - foreach ($conf_arr as $k => $value) { - pool_server_shutdown((int) file_get_contents(PID_PRE . $k . ".pid")); - } + pool_server_shutdown($pid); + file_put_contents(PID_FILE, ""); break; case "restart": - foreach ($conf_arr as $k => $value) { - pool_server_shutdown((int) file_get_contents(PID_PRE . $k . ".pid")); - } + @pool_server_shutdown($pid); sleep(1); - pool_server_create($conf); + pool_server_create($config_file); break; default: - break; + echo $usage; + exit($exit_code_general); } diff --git a/stub-class.php b/stub-class.php new file mode 100644 index 0000000..38b84f7 --- /dev/null +++ b/stub-class.php @@ -0,0 +1,198 @@ +redis = new redisProxy(); + $this->redis->connect($this->getHost()); + $this->redis->select(5); + } + + public function test_class_exists() + { + $this->assertTrue(class_exists("redisProxy")); + } + + public function test_set_get() + { + $this->redis->set("phpcp", "hello world"); + $this->assertEquals("hello world", $this->redis->get("phpcp")); + } +} + +?> diff --git a/tests/RunTest.php b/tests/RunTest.php new file mode 100644 index 0000000..8ec0b68 --- /dev/null +++ b/tests/RunTest.php @@ -0,0 +1,26 @@ + diff --git a/tests/TestSuite.php b/tests/TestSuite.php new file mode 100644 index 0000000..d080f63 --- /dev/null +++ b/tests/TestSuite.php @@ -0,0 +1,171 @@ +host = $host; + } + + public function getHost() { + return $this->host; + } + + public static function make_bold($msg) { + return self::$colorize + ? self::$BOLD_ON . $msg . self::$BOLD_OFF + : $msg; + } + + public static function make_success($str) { + return self::$colorize + ? self::$GREEN . $str . self::$BOLD_OFF + : $str; + } + + public static function make_fail($str) { + return self::$colorize + ? self::$RED . $str . self::$BOLD_OFF + : $str; + } + + public static function make_warning($str) { + return self::$colorize + ? self::$YELLOW . $str . self::$BOLD_OFF + : $str; + } + + protected function assertFalse($bool) { + $this->assertTrue(!$bool); + } + + protected function assertTrue($bool) + { + if ($bool) { + return; + } + + $bt = debug_backtrace(false); + self::$errors[] = sprintf("断言失败: %s:%d (%s)\n", + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + } + + protected function assertLess($a, $b) { + if($a < $b) + return; + $bt = debug_backtrace(false); + self::$errors[] = sprintf("断言失败 (%s >= %s): %s: %d (%s\n", + print_r($a, true), print_r($b, true), + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + } + + protected function assertEquals($a, $b) { + if($a === $b) + return; + $bt = debug_backtrace(false); + self::$errors []= sprintf("断言失败 (%s !== %s): %s:%d (%s)\n", + print_r($a, true), print_r($b, true), + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + } + + protected function markTestSkipped($msg="") { + $bt = debug_backtrace(false); + self::$warnings []= sprintf("跳过测试: %s:%d (%s) %s\n", + $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); + throw new Exception($msg); + } + + private static function getMaxTestLen($methods, $limit) { + $rv = 0; + $limit = strtolower($limit); + foreach ($methods as $method) { + $name = strtolower($method->name); + if (substr($name, 0, 4) != "test") + continue; + if ($limit && !strstr($name, $limit)) + continue; + if (strlen($name) > $rv) { + $rv = strlen($name); + } + } + return $rv; + } + + /* Flag colorization */ + public static function flagColorization($override) { + self::$colorize = $override && function_exists("posix_isatty") && + posix_isatty(STDOUT); + } + + public static function run($className, $limit = NULL, $host = NULL) { + $limit = $limit ? strtolower($limit) : $limit; + + $rc = new ReflectionClass($className); + $methods = $rc->getMethods(ReflectionMethod::IS_PUBLIC); + $max_len = self::getMaxTestLen($methods, $limit); + + foreach ($methods as $method) { + $name = $method->name; + + if (substr($name, 0, 4) !== "test") { + continue; + } + + if ($limit && strstr(strtolower($name), $limit) === false) { + continue; + } + + $out_name = str_pad($name, $max_len + 1); + echo self::make_bold($out_name); + + $count = count($className::$errors); + $rt = new $className($host); + + try { + $rt->setup(); + $rt->$name(); + + if ($count === count($className::$errors)) { + $msg = self::make_success("成功"); + } else { + $msg = self::make_fail("失败"); + } + } catch (Exception $e) { + $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n"; + $msg = self::make_fail("失败"); + } + + echo "[" . $msg . "]\n"; + } + + echo implode("", $className::$warnings) . "\n"; + + if (empty($className::$errors)) { + $msg = self::make_success("通过所有测试"); + echo $msg . "\n\n"; + return 0; + } + + echo implode("", $className::$errors) . "\n"; + return 1; + } +} + +?>