Gearman Architecture of Task Distribution Scheme in High CPU Business Scenario

Keywords: Java github Makefile socket

Gearman was used by LiveJournal to make picture resize. You also know that picture resize is a high CPU operation. If you let the web site do this high CPU function, it may bring you down.

In web applications, let's look at how gearman solves this problem. Its architecture is similar to the following:

From the picture above, you should see that Gearman is made up of three parts:

1. Job Server

This is Gearman's Job Server, which bridges Client and jobwork. Does it remind you of the mediator model?

2. Client

Gearman provides Client API to call the client. Client only needs to drop the name of a high CPU business function to Job Server and wait for Job Server to return the execution result.

3. jobwork

Gearman provides the work API for the work client to make calls. jobserver will distribute to an appropriate work to execute and wait for the results according to the load of the back-end work cluster.

 

At this point, you should understand that it's essentially a distributed RPC call, and what's so compelling is that Client and Work can be implemented in different languages.

 

First: Installation and Deployment

1. Download address: https://github.com/gearman/gearmand/releases

At present, gearman's Job Server is implemented in three languages: C, JAVA and Perl. Because the C version of Job Server is the most active, we use the latest 1.1.17 version of gearman in CentOS.

Installation and deployment.

2. Fast installation

Quick installation can be done through get-start in http://gearman.org/get-start/on the official website.

Basic Dependency Library Installation and gearmand Download

 1 yum -y install boost-devel gperf libevent-devel libuuid-devel gcc44 gcc-c++ 
 2 wget https://github.com/gearman/gearmand/releases/download/1.1.17/gearmand-1.1.17.tar.gz
 3 cd gearmand-1.1.17.tar.gz
 4 tar xzvf gearmand-1.1.17.tar.gz
 5 cd gearmand-1.1.17
 6 [root@localhost gearmand-1.1.17]# ls
 7 aclocal.m4  build-aux     configure.ac  gear_config.in  libgearman-1.0     libhashkit-1.0  Makefile.am  rpm      THANKS
 8 AUTHORS     ChangeLog     COPYING       gearmand        libgearmancore     libhostile      Makefile.in  scripts  util
 9 benchmark   configmake.h  docs          HACKING         libgearman-server  libtest         man          support  version.m4
10 bin         configure     examples      libgearman      libhashkit         m4              NEWS         tests

 

<2> Then there's the conventional. / configure -- prefix=/usr/myapp/gearman & make & make install, which is a very slow process to go out and smoke.

By the way, go and shit.

1 ./configure --prefix=/usr/myapp/gearman && make && make install

 

<3> Years later, when you see this, you will be able to install it successfully... Congratulations... At least it doesn't let you step on the interface that lacks all kinds of dependency packages.

 1 See any operating system documentation about shared libraries for
 2 more information, such as the ld(1) and ld.so(8) manual pages.
 3 ----------------------------------------------------------------------
 4  /usr/bin/mkdir -p '/usr/myapp/gearman/sbin'
 5  /usr/bin/install -c -m 644 man/gearman_worker_create.3 man/gearman_worker_define_function.3 man/gearman_worker_echo.3 man/gearman_worker_errno.3 man/gearman_worker_error.3 man/gearman_worker_free.3 man/gearman_worker_function_exist.3 man/gearman_worker_grab_job.3 man/gearman_worker_options.3 man/gearman_worker_register.3 man/gearman_worker_remove_options.3 man/gearman_worker_remove_servers.3 man/gearman_worker_set_context.3 man/gearman_worker_set_log_fn.3 man/gearman_worker_set_namespace.3 man/gearman_worker_set_options.3 man/gearman_worker_set_timeout.3 man/gearman_client_has_option.3 man/gearman_client_options_t.3 man/gearman_task_attr_init.3 man/gearman_task_attr_init_background.3 man/gearman_task_attr_init_epoch.3 man/gearman_task_attr_t.3 man/gearman_worker_set_identifier.3 man/gearman_worker_set_workload_free_fn.3 man/gearman_worker_set_workload_malloc_fn.3 man/gearman_worker_st.3 man/gearman_worker_timeout.3 man/gearman_worker_unregister.3 man/gearman_worker_unregister_all.3 man/gearman_worker_wait.3 man/gearman_worker_work.3 man/libgearman.3 '/usr/myapp/gearman/share/man/man3'
 6   /bin/sh ./libtool   --mode=install /usr/bin/install -c gearmand/gearmand '/usr/myapp/gearman/sbin'
 7 libtool: install: /usr/bin/install -c gearmand/gearmand /usr/myapp/gearman/sbin/gearmand
 8  /usr/bin/mkdir -p '/usr/myapp/gearman/bin'
 9   /bin/sh ./libtool   --mode=install /usr/bin/install -c bin/gearman bin/gearadmin '/usr/myapp/gearman/bin'
10 libtool: install: /usr/bin/install -c bin/.libs/gearman /usr/myapp/gearman/bin/gearman
11 libtool: install: /usr/bin/install -c bin/gearadmin /usr/myapp/gearman/bin/gearadmin
12 make[3]: Leaving directory `/usr/myapp/gearmand-1.1.17'
13 make[2]: Leaving directory `/usr/myapp/gearmand-1.1.17'
14 make[1]: Leaving directory `/usr/myapp/gearmand-1.1.17'

 

<4> Start gearmand, you can also use - d to turn on the background mode. Add DEBUG here to just look at the real-time DEBUG information, as follows:

 1 [root@localhost myapp]# cd /usr/myapp/gearman
 2 [root@localhost gearman]# ls
 3 bin  include  lib  sbin  share
 4 [root@localhost gearman]# cd bin
 5 [root@localhost bin]# ls
 6 gearadmin  gearman
 7 [root@localhost bin]# cd /usr/myapp/gearman
 8 [root@localhost gearman]# cd sbin
 9 [root@localhost sbin]# ls
10 gearmand
11 [root@localhost sbin]# ./gearmand --verbose DEBUG
12 ./gearmand: Could not open log file "/usr/myapp/gearman/var/log/gearmand.log", from "/usr/myapp/gearman/sbin", switching to stderr. (No such file or directory)
13   DEBUG 2017-08-29 02:31:10.796259 [  main ] THREADS: 4 -> libgearman-server/gearmand.cc:263
14    INFO 2017-08-29 02:31:10.796374 [  main ] Initializing Gear on port 4730 with SSL: false
15    INFO 2017-08-29 02:31:10.796487 [  main ] Starting up with pid 40299, verbose is set to DEBUG
16   DEBUG 2017-08-29 02:31:10.796637 [  main ] Method for libevent: epoll -> libgearman-server/gearmand.cc:364
17   DEBUG 2017-08-29 02:31:10.798874 [  main ] Trying to listen on 0.0.0.0:4730 -> libgearman-server/gearmand.cc:646
18    INFO 2017-08-29 02:31:10.800151 [  main ] Listening on 0.0.0.0:4730 (8)
19   DEBUG 2017-08-29 02:31:10.800175 [  main ] Trying to listen on :::4730 -> libgearman-server/gearmand.cc:646
20    INFO 2017-08-29 02:31:10.800307 [  main ] Listening on :::4730 (9)
21   DEBUG 2017-08-29 02:31:10.800333 [  main ] Creating wakeup pipe -> libgearman-server/gearmand.cc:915
22   DEBUG 2017-08-29 02:31:10.800344 [  main ] Creating 4 threads -> libgearman-server/gearmand.cc:378
23   DEBUG 2017-08-29 02:31:10.800357 [  main ] Initializing libevent for IO thread -> libgearman-server/gearmand_thread.cc:224
24   DEBUG 2017-08-29 02:31:10.800406 [  main ] Creating IO thread wakeup pipe -> libgearman-server/gearmand_thread.cc:495
25   DEBUG 2017-08-29 02:31:10.800467 [  main ] Thread 1 created -> libgearman-server/gearmand_thread.cc:273
26   DEBUG 2017-08-29 02:31:10.800507 [  main ] Initializing libevent for IO thread -> libgearman-server/gearmand_thread.cc:224
27   DEBUG 2017-08-29 02:31:10.800550 [  main ] Creating IO thread wakeup pipe -> libgearman-server/gearmand_thread.cc:495
28   DEBUG 2017-08-29 02:31:10.800585 [  main ] Thread 2 created -> libgearman-server/gearmand_thread.cc:273
29   DEBUG 2017-08-29 02:31:10.800594 [  main ] Initializing libevent for IO thread -> libgearman-server/gearmand_thread.cc:224
30   DEBUG 2017-08-29 02:31:10.800632 [  main ] Creating IO thread wakeup pipe -> libgearman-server/gearmand_thread.cc:495
31   DEBUG 2017-08-29 02:31:10.800669 [  main ] Thread 3 created -> libgearman-server/gearmand_thread.cc:273
32   DEBUG 2017-08-29 02:31:10.800677 [  main ] Initializing libevent for IO thread -> libgearman-server/gearmand_thread.cc:224
33   DEBUG 2017-08-29 02:31:10.800714 [  main ] Creating IO thread wakeup pipe -> libgearman-server/gearmand_thread.cc:495
34   DEBUG 2017-08-29 02:31:10.800753 [  main ] Thread 4 created -> libgearman-server/gearmand_thread.cc:273
35   DEBUG 2017-08-29 02:31:10.800761 [  main ] replaying queue: begin -> libgearman-server/gearmand.cc:391
36   DEBUG 2017-08-29 02:31:10.800766 [  main ] __replay -> libgearman-server/plugins/queue/default/queue.cc:101
37   DEBUG 2017-08-29 02:31:10.800774 [  main ] replaying queue: end -> libgearman-server/gearmand.cc:397
38    INFO 2017-08-29 02:31:10.800780 [  main ] Adding event for listening socket (8)
39    INFO 2017-08-29 02:31:10.800787 [  main ] Adding event for listening socket (9)
40   DEBUG 2017-08-29 02:31:10.800794 [  main ] Adding event for wakeup pipe -> libgearman-server/gearmand.cc:966
41   DEBUG 2017-08-29 02:31:10.800801 [  main ] Entering main event loop -> libgearman-server/gearmand.cc:406
42   DEBUG 2017-08-29 02:31:10.801186 [     2 ] Entering thread event loop -> libgearman-server/gearmand_thread.cc:463
43   DEBUG 2017-08-29 02:31:10.801277 [     3 ] Entering thread event loop -> libgearman-server/gearmand_thread.cc:463
44   DEBUG 2017-08-29 02:31:10.801507 [  main ] staring up Epoch thread -> libgearman-server/timer.cc:61
45   DEBUG 2017-08-29 02:31:10.801635 [     1 ] Entering thread event loop -> libgearman-server/gearmand_thread.cc:463
46   DEBUG 2017-08-29 02:31:10.802426 [     4 ] Entering thread event loop -> libgearman-server/gearmand_thread.cc:463

 

<5> Finally, we can find out the port number of gearmand by netstat, lsof and ps-ef, just as you can see the default port number of 4370.

Of course, you can also use help command at startup.

 1 [root@localhost ~]# netstat -tln
 2 Active Internet connections (only servers)
 3 Proto Recv-Q Send-Q Local Address           Foreign Address         State      
 4 tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN     
 5 tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
 6 tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     
 7 tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN     
 8 tcp        0      0 0.0.0.0:4730            0.0.0.0:*               LISTEN     
 9 tcp6       0      0 :::8009                 :::*                    LISTEN     
10 tcp6       0      0 :::8080                 :::*                    LISTEN     
11 tcp6       0      0 :::22                   :::*                    LISTEN     
12 tcp6       0      0 ::1:631                 :::*                    LISTEN     
13 tcp6       0      0 ::1:25                  :::*                    LISTEN     
14 tcp6       0      0 :::4730                 :::*                    LISTEN     
15 tcp6       0      0 127.0.0.1:8005          :::*                    LISTEN     
16 [root@localhost ~]# ps -ef | grep gearmand
17 root      40299  15869  0 02:31 pts/1    00:00:00 ./gearmand --verbose DEBUG
18 root      40364  40327  0 02:33 pts/2    00:00:00 grep --color=auto gearmand
19 [root@localhost ~]# lsof -i :4730
20 COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
21 gearmand 40299 root    8u  IPv4 322550      0t0  TCP *:gearman (LISTEN)
22 gearmand 40299 root    9u  IPv6 322551      0t0  TCP *:gearman (LISTEN)
23 [root@localhost ~]# 

 

2: The use of Java Driver on Gearman

For demonstration, I can make a simple "string. ToUpper" business logic to verify whether the architecture can run.

 

1. java acts as Gearman's work

First, you need to pull the jar package in the mvn warehouse: http://www.mvnrepository.com/artifact/org.gearman/gearman-java/0.6.

 

<1> UpperFunction class, which defines the specific business logic of work:

 1 package com.datamip.gearmanwork;
 2 
 3 import java.text.SimpleDateFormat;
 4 import java.util.Date;
 5 
 6 import org.gearman.client.GearmanJobResult;
 7 import org.gearman.client.GearmanJobResultImpl;
 8 import org.gearman.util.ByteUtils;
 9 import org.gearman.worker.AbstractGearmanFunction;
10 
11 //String capitalization business Function
12 public class UpperFunction extends AbstractGearmanFunction {
13 
14     @Override
15     public GearmanJobResult executeFunction() {
16 
17         String param = ByteUtils.fromUTF8Bytes((byte[]) this.data);
18 
19         byte[] mybytes = param.toUpperCase().getBytes();
20 
21         GearmanJobResultImpl result = new GearmanJobResultImpl(mybytes, true, mybytes, null, null, -1, -1);
22 
23         SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
24 
25         String dateString = formatter.format(new Date());
26 
27         System.out.println(String.format("current time:%s, Coming String:%s,Return string:%s", dateString, param,new String(mybytes)));
28 
29         return result;
30     }
31 }

 

<2> Register UpperFunction in gearmand. As you can see from the red code, it's actually a kv mode, where key="myUpperFunc" implements a new UpperFunction.

Client only needs to pass a "myUpperFunc" and Gearmand knows which handler the "string" corresponds to...

 1 public class App {
 2     public static void main(String[] args) {
 3 
 4         GearmanWorker worker = new GearmanWorkerImpl();
 5 
 6         GearmanNIOJobServerConnection conn = new GearmanNIOJobServerConnection("192.168.23.170", 4730);
 7         worker.addServer(conn);
 8 
 9         // take'Register functions that will be capitalized' reach gearmand in 
10         worker.registerFunctionFactory(new GearmanFunctionFactory() {
11 
12             public String getFunctionName() {
13                 return "myUpperFunc";
14             }
15 
16             public GearmanFunction getFunction() {
17                 return new UpperFunction();
18             }
19         });
20         
21         System.out.println("Start the service...");
22 
23         worker.work();
24     }
25 }

2. java acts as Gearman's client

 

GearSubmit class

 1 package com.datamip.gearmanclient;
 2 
 3 import java.util.concurrent.ExecutionException;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6 import java.util.concurrent.Future;
 7 
 8 import org.gearman.client.GearmanClient;
 9 import org.gearman.client.GearmanClientImpl;
10 import org.gearman.client.GearmanJob;
11 import org.gearman.client.GearmanJobImpl;
12 import org.gearman.client.GearmanJobResult;
13 import org.gearman.common.GearmanJobServerConnection;
14 import org.gearman.common.GearmanNIOJobServerConnection;
15 import org.gearman.util.ByteUtils;
16 
17 public class Gearsubmit {
18 
19     public  void process() throws InterruptedException, ExecutionException {
20     
21         GearmanJobServerConnection conn = new GearmanNIOJobServerConnection("192.168.23.170", 4730);
22 
23         GearmanClient client = new GearmanClientImpl();
24 
25         client.addJobServer(conn); // add connections
26 
27         String functionName = "myUpperFunc";
28 
29         byte[] data = ByteUtils.toUTF8Bytes("hello,world");
30 
31         // Create background tasks
32         GearmanJob job = GearmanJobImpl.createJob(functionName, data, null);
33 
34         GearmanJobResult jobResult = null;
35 
36         Future<GearmanJobResult> gearmanJobResult = client.submit(job);
37 
38         jobResult = gearmanJobResult.get();
39 
40         byte[] resultBytes = jobResult.getResults();
41 
42         // Obtain job Return value
43         String value = ByteUtils.fromUTF8Bytes(resultBytes);
44 
45         System.out.println(value);
46 
47         System.out.println("end of execution");
48 
49         client.shutdown();
50     }
51 }

 

<2> Main program, multi-threaded concurrent execution.

 1 public class App {
 2     public static void main(String[] args) throws InterruptedException, ExecutionException, IOException {
 3 
 4         ExecutorService executorService = Executors.newFixedThreadPool(100);
 5 
 6         for (int i = 0; i < 10000; i++) {
 7             executorService.execute(new Runnable() {
 8                 
 9                 @Override
10                 public void run() {
11                     Gearsubmit submit=new Gearsubmit();
12                     
13                     try {
14                         submit.process();
15                     } catch (InterruptedException | ExecutionException e) {
16                         // TODO Auto-generated catch block
17                         e.printStackTrace();
18                     }
19                 }
20             });
21         }
22         
23         System.in.read();
24     }
25 }

 

Well, everything is ready. Next, for demonstration, the demonstration is explanation. I export the work program into jar and convert it into exe with Jar2Exe. Here's the following picture:

 

Then I opened 3.exe into five instances, and client called it concurrently with a thread pool of 100 threads, of course, everything was simulated... You can see, but when my client started, all five work s were executed.

If at this point you stop a work, the job server will drop the task to a relatively small workload to execute.

 

Okay, I'm going to talk about it in this article. I hope it will be helpful to you.

Posted by britey on Wed, 29 May 2019 10:56:22 -0700