julien6387 / supvisors Goto Github PK
View Code? Open in Web Editor NEWSupvisors: A Control System for Distributed Applications
Home Page: http://supvisors.readthedocs.io
License: Apache License 2.0
Supvisors: A Control System for Distributed Applications
Home Page: http://supvisors.readthedocs.io
License: Apache License 2.0
XML-RPCs are blocking
synchronous calls may lead to a deadlock when used from multiple Supervisor instances
when a FATAL state is forced by supvisors, it is displayed differently in procaddress and application pages.
the procaddress seems correct (e.g. 'no resource available'), whereas the application page displays 'unknown error (try "tail xxx")'
in program rules, it is stated that "This use of the reference element is exclusive to the use of the following elements."
it may be interesting to consider that it is not exclusive so that the following elements could supersede the information defaulted in the program model
Installed the plugin following the docs and the example conf.
All works well, supvisorsctl
command works, but if I try to do anything specific to supvisors plugin, for example to update_numprocs
it throws the above error
The supervisord logs the following:
2022-05-25 16:02:38,530;INFO;SupvisorsInstanceStatus.state: Supvisors=test-server.addpipe.com is CHECKING
2022-05-25 16:02:38,530;WARN;InitializationState.next: synchro timed out
2022-05-25 16:02:38,531;INFO;SupvisorsInstanceStatus.state: Supvisors=test-server.addpipe.com is CHECKING
2022-05-25 16:02:38,531;CRIT;SupervisorListener.on_remote_event: Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/supvisors-0.14-py3.6.egg/supvisors/listener.py", line 285, in on_remote_event
self.unstack_event(event.data)
File "/usr/local/lib/python3.6/site-packages/supvisors-0.14-py3.6.egg/supvisors/listener.py", line 297, in unstack_event
self.supvisors.fsm.on_tick_event(event_identifier, event_data)
File "/usr/local/lib/python3.6/site-packages/supvisors-0.14-py3.6.egg/supvisors/statemachine.py", line 493, in on_tick_event
self.context.on_tick_event(identifier, event)
File "/usr/local/lib/python3.6/site-packages/supvisors-0.14-py3.6.egg/supvisors/context.py", line 429, in on_tick_event
self.check_instance(status)
File "/usr/local/lib/python3.6/site-packages/supvisors-0.14-py3.6.egg/supvisors/context.py", line 632, in check_instance
self.supvisors.zmq.pusher.send_check_instance(status.identifier)
AttributeError: 'NoneType' object has no attribute 'pusher'
example detected:
lo
Rx 1790086.29 -33158.31 713.10 2801189.77
Tx 1790086.29 -33158.31 713.10 2801189.77
in Supvisors, ZMQ sockets are not used by multiple threads.
however, they are all created in the Supervisor thread, which is not the correct way to do it.
Recommendation is to create / use / destroy the ZMQ socket in the same thread.
that would explain random crashes like this one:
Bad file descriptor (src/epoll.cpp:119)
Bad file descriptor (src/tcp.cpp)
in pattern rules, the definition of '#' applies to the whole list of nodes that are declared in the supvisors section of the supervisor configuration file.
it could be considered to apply '#'to a subset of these nodes by declaring something like:
#, subnode1, subnode2
When
stopasgroup=true
killasgroup=true
are set, the user probably want to see the CPU/mem utilization over the group, and not just the parent process.
Would this be possible to add?
Hello
Of course thanks for this project
I have another case similar to #99, but I want to run in
separate docker supervisor+supvisors (need update_numprocs feature) + workers managed by supvisors of course
I wated to run 3 or 4 containers based on one universal image with entrypoint-app.sh (simple dispatch supervisord.conf depends on env) - I have similar solution but only with supervisord and static config (with static numprocs=x values)
Is it possible to run supervisord + supvisors rpc api without all that stuff connected with statistic sync etc? because my example log looks like below:
INFO Set uid to user 0 succeeded
INFO RPC interface 'supervisor' initialized
WARN;SupervisorData.read_disabilities: no persistence for program disabilities
INFO;SupvisorsMapper.configure: identifiers=['server_1']
INFO;SupvisorsMapper.find_local_identifier: local_identifier=server_1
INFO;SupvisorsMapper.configure: core_identifiers=['server_1']
WARN;Supvisors: cannot parse rules files: None - 'NoneType' object is not iterable
INFO;RPCInterface: using Supvisors=0.15 Supervisor=4.2.4
INFO;RPC interface 'supvisors' initialized
CRIT;Server 'inet_http_server' running without any HTTP authentication checking
INFO;RPC interface 'supervisor' initialized
WARN;SupervisorData.read_disabilities: no persistence for program disabilities
INFO;SupvisorsMapper.configure: identifiers=['server_1']
INFO;SupvisorsMapper.find_local_identifier: local_identifier=server_1
INFO;SupvisorsMapper.configure: core_identifiers=['server_1']
WARN;Supvisors: cannot parse rules files: None - 'NoneType' object is not iterable
INFO;RPCInterface: using Supvisors=0.15 Supervisor=4.2.4
INFO;RPC interface 'supvisors' initialized
CRIT;Server 'unix_http_server' running without any HTTP authentication checking
INFO;supervisord started with pid 1
INFO;SupervisorListener.on_running: local supervisord is RUNNING
INFO;FiniteStateMachine.set_state: Supvisors in INITIALIZATION
INFO;Context.master_identifier:
INFO;SupervisorListener.on_running: local supervisord is RUNNING
CRIT;PublisherServer._bind: failed to bind the Supvisors publisher on port 61001
INFO;FiniteStateMachine.set_state: Supvisors in INITIALIZATION
INFO;Context.master_identifier:
INFO;SupvisorsInstanceStatus.state: Supvisors=server_1 is CHECKING
INFO;SupvisorsInstanceStatus.state: Supvisors=server_1 is CHECKING
WARN;Context.on_authorization: failed to get auth status from Supvisors=server_1
CRIT;Context.invalid: local Supvisors instance is either SILENT or inconsistent
INFO;SupvisorsInstanceStatus.state: Supvisors=server_1 is SILENT
I dont want in my logs stuff like:
CRIT;PublisherServer._bind: failed to bind the Supvisors publisher on port xxxxx
Context.invalid: local Supvisors instance is either SILENT or inconsistent
is it possible , or maybe need some futuer development?
Thanks for any help you can provide!
ps. of course I can ignore content of logs (and run containers without looking on logs) but, maybe it is possible to do that in the right way :)
idea is to decide what to do when a required process cannot be started when starting an application.
the parser already gets the rule
push the definition at application level
idea is to declare at once :
something like:
<all_of>program_x, program_y, program_z</all_of>
<one_of>program_a, program_b</one_of>
<one_of>program_00, program_01, program_02</one_of>
push the doc in readthedocs
it is only used in supvisors.address_mapper, in ipv4 method
if optional (change doc in README and introduction), the local address would be restricted to the host name (host node from a Supervisor point of view), which could be acceptable if documented
=> move the import in ipv4 and deal with ImportError
=> update documentation about address_list configuration and places where 'IP address' may be referred to.
=> update setup.py dependencies
2018-01-05 23:27:30,995 ERRO Web interface error:Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/supervisor/web.py", line 45, in more
response = self.callback()
File "/usr/local/lib/python2.7/dist-packages/supervisor/web.py", line 162, in call
body = self.render()
File "/usr/local/lib/python2.7/dist-packages/supvisors/viewprocaddress.py", line 47, in render
return ViewHandler.render(self)
File "/usr/local/lib/python2.7/dist-packages/supvisors/viewhandler.py", line 74, in render
self.write_contents(root)
File "/usr/local/lib/python2.7/dist-packages/supvisors/viewprocaddress.py", line 75, in write_contents
self.write_process_table(root)
File "/usr/local/lib/python2.7/dist-packages/supvisors/viewprocaddress.py", line 93, in write_process_table
for info in self.info_source.supervisor_rpc_interface.getAllProcessInfo():
File "/usr/local/lib/python2.7/dist-packages/supvisors/infosource.py", line 48, in supervisor_rpc_interface
self._supervisor_rpc_interface = self.httpserver.handlers[0].rpcinterface.supervisor
AttributeError: supervisor_auth_handler instance has no attribute 'rpcinterface'
CSS rules rely on process / application ids, absent from HTML
problem with 12 cores
possible same issue with network
The Supvisors TICK shared across all nodes is driven by Supervisor TICK_5.
On a special event (suspected to be a HTTP issue when requesting a refresh on the web ui), Supervisor freezes momentarily and the Supervisor TICK_5 is deferred - sometimes more than 30s. This causes Supvisors to believe that the Supervisor instance has been stopped on the related node.
Supvisors needs a more reliable source for its own TICK. Perhaps it should be generated from its own thread.
explain how to subscribe to the Supervisors events
add a python childutils.py and sample
It makes it easier to debug or just understand what happens when Supervisor and Supvisors output sequentially in the same log file.
idea is to follow an order when stopping the processes of an application.
the stop_sequence element is already parsed and stored in rules.
a value of 0 stands for an immediate stop, then stop processes of group 1, 2, etc.
the process info of the ProcessStatus class contains lots of useless attributes got from Supervisors
Recently we've tried to implement 'supvisors' into our supervisord for easier management using the dashboard, we've encountered an error where the it keep stuck in 'INITIALIZATION' state. We are new to UNIX OS as well as the supervisor, hope that you can help us resolve this issue. Here is error we keep receiving upon re-synchronyzation.
2022-09-19 16:15:45,356;CRIT;SupervisorListener.on_tick: Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/supvisors/listener.py", line 265, in on_tick
self.pusher.send_tick_event(payload)
AttributeError: 'NoneType' object has no attribute 'send_tick_event'
2022-09-19 16:15:46,361;INFO;SupvisorsInstanceStatus.state: Supvisors=htv-virtual-machine is CHECKING
2022-09-19 16:15:46,362;WARN;InitializationState.next: synchro timed out
2022-09-19 16:15:46,362;INFO;SupvisorsInstanceStatus.state: Supvisors=htv-virtual-machine is CHECKING
2022-09-19 16:15:46,362;CRIT;SupervisorListener.on_remote_event: Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/supvisors/listener.py", line 285, in on_remote_event
self.unstack_event(event.data)
File "/usr/local/lib/python3.10/dist-packages/supvisors/listener.py", line 297, in unstack_event
self.supvisors.fsm.on_tick_event(event_identifier, event_data)
File "/usr/local/lib/python3.10/dist-packages/supvisors/statemachine.py", line 493, in on_tick_event
self.context.on_tick_event(identifier, event)
File "/usr/local/lib/python3.10/dist-packages/supvisors/context.py", line 429, in on_tick_event
self.check_instance(status)
File "/usr/local/lib/python3.10/dist-packages/supvisors/context.py", line 632, in check_instance
self.supvisors.zmq.pusher.send_check_instance(status.identifier)
AttributeError: 'NoneType' object has no attribute 'pusher'
2022-09-19 16:15:46,364;WARN;Context.on_authorization: failed to get auth status from Supvisors=htv-virtual-machine
2022-09-19 16:15:46,364;CRIT;Context.invalid: local Supvisors instance is either SILENT or inconsistent
2022-09-19 16:15:46,364;INFO;SupvisorsInstanceStatus.state: Supvisors=htv-virtual-machine is SILENT
2022-09-19 16:15:46,364;WARN;Context.on_authorization: failed to get auth status from Supvisors=htv-virtual-machine
2022-09-19 16:15:46,364;CRIT;Context.invalid: local Supvisors instance is either SILENT or inconsistent
2022-09-19 16:15:46,364;INFO;SupvisorsInstanceStatus.state: Supvisors=htv-virtual-machine is SILENT
2022-09-19 16:15:46,364;CRIT;SupervisorListener.on_remote_event: Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/supvisors/listener.py", line 283, in on_remote_event
self.authorization(event.data)
File "/usr/local/lib/python3.10/dist-packages/supvisors/listener.py", line 338, in authorization
self.supvisors.fsm.on_authorization(identifier, authorized, master_identifier)
File "/usr/local/lib/python3.10/dist-packages/supvisors/statemachine.py", line 604, in on_authorization
if self.context.on_authorization(identifier, authorized):
File "/usr/local/lib/python3.10/dist-packages/supvisors/context.py", line 380, in on_authorization
self.invalid(status)
File "/usr/local/lib/python3.10/dist-packages/supvisors/context.py", line 198, in invalid
self.supvisors.zmq.publisher.send_instance_status(status.serial())
AttributeError: 'NoneType' object has no attribute 'publisher'
this possibility does not exist in Supervisor
create new supervisorctl commands:
start_process_args strategy namespec arg_list
start_args namespec arg_list
update documentation
ubuntu 14.04.1
pip install supvisors[all]
Requirement already satisfied: supvisors[all] in /usr/local/lib/python2.7/dist-packages
Requirement already satisfied: supervisor>=3.3.0 in /usr/local/lib/python2.7/dist-packages (from supvisors[all])
Requirement already satisfied: pyzmq>=15.2.0 in /usr/local/lib/python2.7/dist-packages (from supvisors[all])
Requirement already satisfied: netifaces>=0.10.4 in /usr/local/lib/python2.7/dist-packages (from supvisors[all])
Requirement already satisfied: psutil>=4.3.0 in /usr/local/lib/python2.7/dist-packages (from supvisors[all])
Requirement already satisfied: matplotlib>=1.5.2 in /usr/local/lib/python2.7/dist-packages (from supvisors[all])
Requirement already satisfied: lxml>=3.2.1 in /usr/local/lib/python2.7/dist-packages (from supvisors[all])
Requirement already satisfied: meld3>=0.6.5 in /usr/local/lib/python2.7/dist-packages (from supervisor>=3.3.0->supvisors[all])
Requirement already satisfied: numpy>=1.7.1 in /usr/local/lib/python2.7/dist-packages (from matplotlib>=1.5.2->supvisors[all])
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python2.7/dist-packages (from matplotlib>=1.5.2->supvisors[all])
Requirement already satisfied: six>=1.10 in /usr/local/lib/python2.7/dist-packages (from matplotlib>=1.5.2->supvisors[all])
Requirement already satisfied: pytz in /usr/local/lib/python2.7/dist-packages (from matplotlib>=1.5.2->supvisors[all])
Requirement already satisfied: subprocess32 in /usr/local/lib/python2.7/dist-packages (from matplotlib>=1.5.2->supvisors[all])
Requirement already satisfied: python-dateutil>=2.0 in /usr/local/lib/python2.7/dist-packages (from matplotlib>=1.5.2->supvisors[all])
Requirement already satisfied: backports.functools-lru-cache in /usr/local/lib/python2.7/dist-packages (from matplotlib>=1.5.2->supvisors[all])
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python2.7/dist-packages (from matplotlib>=1.5.2->supvisors[all])
use the config of example,
and can't startup the supervisord.But it can run when I remove the supvisors configs.
supvisors log:
2018-01-05 17:40:24,743 INFO Expected addresses: ['119.23.xxx.xxx']
2018-01-05 17:40:24,744 INFO Local addresses: ['cluster-1', u'10.27.xxx.xxx', u'119.23.xxx.xxx'] - Local address: 119.23.xxx.xxx
2018-01-05 17:40:24,752 INFO using lxml.etree parser
2018-01-05 17:40:24,752 WARN cannot parse rules file: None
2018-01-05 17:40:24,780 INFO Expected addresses: ['119.23.xxx.xxx']
2018-01-05 17:40:24,780 INFO Local addresses: ['cluster-1', u'10.27.xxx.xxx', u'119.23.xxx.xxxx'] - Local address: 119.23.xxx.xxx
2018-01-05 17:40:24,780 INFO using lxml.etree parser
2018-01-05 17:40:24,780 WARN cannot parse rules file: None
2018-01-05 17:40:24,783 INFO local supervisord is RUNNING
2018-01-05 17:40:24,783 WARN Server running without any HTTP authentication checking
2018-01-05 17:40:24,783 INFO binding local Supvisors EventPublisher to tcp://127.0.0.1:60002
2018-01-05 17:40:24,783 INFO binding InternalEventPublisher to tcp://*:60001
2018-01-05 17:40:24,783 INFO binding RequestPuller to inproc://supvisors
supervisord.log
2018-01-05 17:40:24,737 CRIT Supervisor running as root (no user in config file)
2018-01-05 17:40:24,738 INFO RPC interface 'supervisor' initialized
2018-01-05 17:40:24,773 INFO RPC interface 'supvisors' initialized
2018-01-05 17:40:24,773 CRIT Server 'inet_http_server' running without any HTTP authentication checking
2018-01-05 17:40:24,773 INFO RPC interface 'supervisor' initialized
2018-01-05 17:40:24,781 INFO RPC interface 'supvisors' initialized
2018-01-05 17:40:24,781 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2018-01-05 17:40:24,782 INFO daemonizing the supervisord process
2018-01-05 17:40:24,783 INFO supervisord started with pid 19830
the document link in readme needs permission to view...
seen on 'eth1' interface having no AF_INET entry
issue title renamed as it was a consequence of another problem
it may happen that the main loop does not stop on restart
seems to be a problem in the pool
supervisors name is too close from supervisor
that's confusing
the C++ code should be able to perform rpc requests to Supervisors ans to subscribe to events
for now, it requires that the local Supervisor knows the process, which may be not the case.
we can imagine that the Supervisor configuration are different on every machines.
fix to be made in Deployer.process_failure
as said in documentation, the aim is to tell that an instance of this process is running on each board
idea is to stop all applications in the specified order when requesting a shutdown of Supervsors.
current behaviour is to let Supervisor stop the process in an undefined order.
very unlikely in normal operation but possible anyway .
if supvisors sshutdown or sreload is requested from node X and immediately after, a supervisor shutdown is sent to node X, then the stopping sequence of the applications is aborted and the initial shutdown/restart cannot be completed.
that leads the other nodes to eventually re-sync but the situation is a bit strange as there has been some applications properly stopped and there's nothing to be repaired.
this is not blocking but definitely not desired.
easy scenario to repeat:
supervisorctl sshutdown ; supervisorctl shutdown
idea is to check if the resources are compliant with the application that the user wants to start, iaw the declared theoretical loading and the strategy requested.
raise an exception if ko.
use screenshots to illustrate
when leaving the INITIALIZATION state, only the Supvisors master stays in DEPLOYMENT state. other instances move quickly to OPERATION. OPERATION state should be reached for all instances at the same time.
hi:
i'm running supvisors on Centos7.0, but that error has happened, and can u help
me troubleshoot this error?
##supervisord.conf:
[inet_http_server] ; inet (TCP) server disabled by default
port=:10000 ; (ip_address:port specifier, :port for all iface)
;username=football ; (default is no username (open server))
;password=k@hu19k8aQ1 ; (default is no password (open server))
[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=500MB ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
loglevel=warn ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
minprocs=200 ; (min. avail process descriptors;default 200)
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=http://localhost:10000 ; use an http:// url to specify an inet socket
[include]
files = /etc/supervisor/conf.d/*.conf
[supvisors]
address_list=47.92.152.87
deployment_file=/etc/supervisors/application.xml
auto_fence=false
internal_port=10001
event_port=10002
synchro_timeout=20
deployment_strategy=LESS_LOADED
conciliation_strategy=INFANTICIDE
stats_periods=5,60,600
stats_histo=100
logfile=/etc/supervisors/supvisors.log
logfile_maxbytes=50MB
logfile_backups=10
loglevel=info
[rpcinterface:supvisors]
supervisor.rpcinterface_factory = supvisors.plugin:make_supvisors_rpcinterface
[ctlplugin:supvisors]
supervisor.ctl_factory = supvisors.supvisorsctl:make_supvisors_controller_plugin
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<!-- lunar_spider application -->
<application name="lunar_spider">
<start_sequence>3</start_sequence>
<stop_sequence>2</stop_sequence>
<program name="scrape_api_server">
<addresses>*
<start_sequence>1</start_sequence>
<stop_sequence>2</stop_sequence>
<required>true
<expected_loading>5</expected_loading>
</program>
</application>
</root>
thx!
idea is to decide what to do when a required process crashes when the application is running.
the parser already gets the value
no statistics available in this case
so they are common to each client
find a way to keep the information in url (if possible)
when receiving the first event from B, the event is posted through a RemoteCommEvent to the Supervisor main thread.
Then A performs a XML-RPC to B in order to know if A is considered ISOLATED by B.
The use of the RemoteCommEvent blocks any RPC coming to A as long as it did not receive a response from B.
with bad luck, B is doing the same with A at the same time and A and B block themselves.
detected with the supvisors test configuration when starting on node cliche82 only.
the application player contains only processes that can run on cliche81.
both processes are declared FATAL / no resource available but the next deployment phase doesn't trigger
simple scenario:
start node1, node2, node3 => node1 is the master, as seen by nodes 2 and 3
stop node1 => node2 becomes master, as seen by node3
restart node1 => node1 declares itself as a master. node2 still the master, as seen by node3
restart node3 => node1 master, node2 master, node3 sees node1 as master
when a new node becomes active, it is needed to come back to INITIALIZATION state.
From what I can tell, the disabilities_file only gets generated once we run enable
or disable
but I noticed the full directory needs to also exist. Otherwise, it never gets created but it also doesn't throw an error.
use the internal event publish / subscribe to share the information
export using the event interface
the java code should be able to perform xml-rpc requests and to subscribe to Supervisors events
should replace the autostart option
Hello,
Thanks for this well documented project!
I'm considering using supvisors as a unified view of a set of Docker containers with supervisor servers inside of them. In my use case, I have a few (currently 3) docker containers where each one of them runs a series of processes. These containers are running in the same machine and they use the host network stack (due to internal random port usage dependencies).
I was trying to do a simple test where I am running a supervisor + supvisor on the host (with the aim that this one would have just a couple of jobs to start the docker containers) and a supervisor + supvisor in a container with a group of processes. I expected to be able to see them in the web ui.
However, I'm finding myself running into socket already in use errors (zmq.error.ZMQError: Address already in use
).
I wonder if I'm just trying to do something that is impossible or I'm just getting the config wrong.
I have/want:
Host OS --> supervisor + supvisors with default config
|__ run docker image(s) start script
Docker container 1 --> supervisor + supvisors with default config
|__ run process(es) inside of container
Docker container 2 ...
Docker container N
I see in your example scenarios that the different supervisor+supvisors config are executed by ssh-ing into another machine and running supervisor. Being in a different machine I understand that they can re-use ports. If I try to use another port, it runs, but then, I do not know how to tell... the web ui part? to find the additional servers?
I'm still testing, but I thought I'd post already in case someone can give me pointers. Thanks for any help you can provide!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.