Gearman是当年LiveJournal用来做图片resize的,大家也明白图片resize是一个高CPU的操作,如果让web网站去做这个高CPU的功能,有可能会拖垮你的

web应用,那本篇我们来看看gearman是如何解决这个问题的,它的架构图类似下面这样:

从上面这张图,你应该会看到,Gearman是由三个部分组成:

1. Job Server

这个就是Gearman的Job Server,通过它对Client 和 jobwork 进行桥接,是不是想起来了中介者模式。。。

2. Client

Gearman提供了Client API 给客户端调用,Client只需要将一个高CPU的业务函数名丢给Job Server,然后等待JobServer的返回执行结果。

3. jobwork

Gearman提供了work API 给work客户端进行调用。jobserver会根据后端的work集群的负载情况,分发给一个合适的work去执行,并等待结果。

说到这里,你应该就明白了,本质上它属于那种分布式的RPC调用,而且非常牛逼的地方在于Client 和 Work 可以用不同的语言实现。

一:安装部署

1.  下载地址:https://github.com/gearman/gearmand/releases

目前gearman的JobServer 有C,JAVA,Perl三种语言实现,由于C版本的JobServer是最活跃的,所以这里采用目前最新的1.1.17版本的gearmand在CentOS

上进行安装部署。

2.  快速安装

可以通过官网http://gearman.org/getting-started/中的getting-started进行快速安装。

<1> 基础依赖库安装和gearmand下载

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

<2> 然后就是常规的./configure --prefix=/usr/myapp/gearman && make && make install  这个过程超级慢,可以出去抽跟烟,

顺便再去拉泡屎。。。

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

<3> 若干年后,当你看到这个就算安装成功了。。。还是得恭喜一下。。。。至少没让你踩到缺少各种依赖包的界面。

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

<4> 启动gearmand,你也可以用 -d 开启后台运行的模式,这里加上DEBUG只是看一下实时的DEBUG信息,如下所示:

 [root@localhost myapp]# cd /usr/myapp/gearman
[root@localhost gearman]# ls
bin include lib sbin share
[root@localhost gearman]# cd bin
[root@localhost bin]# ls
gearadmin gearman
[root@localhost bin]# cd /usr/myapp/gearman
[root@localhost gearman]# cd sbin
[root@localhost sbin]# ls
gearmand
[root@localhost sbin]# ./gearmand --verbose DEBUG
./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)
DEBUG -- ::10.796259 [ main ] THREADS: -> libgearman-server/gearmand.cc:
INFO -- ::10.796374 [ main ] Initializing Gear on port with SSL: false
INFO -- ::10.796487 [ main ] Starting up with pid , verbose is set to DEBUG
DEBUG -- ::10.796637 [ main ] Method for libevent: epoll -> libgearman-server/gearmand.cc:
DEBUG -- ::10.798874 [ main ] Trying to listen on 0.0.0.0: -> libgearman-server/gearmand.cc:
INFO -- ::10.800151 [ main ] Listening on 0.0.0.0: ()
DEBUG -- ::10.800175 [ main ] Trying to listen on ::: -> libgearman-server/gearmand.cc:
INFO -- ::10.800307 [ main ] Listening on ::: ()
DEBUG -- ::10.800333 [ main ] Creating wakeup pipe -> libgearman-server/gearmand.cc:
DEBUG -- ::10.800344 [ main ] Creating threads -> libgearman-server/gearmand.cc:
DEBUG -- ::10.800357 [ main ] Initializing libevent for IO thread -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800406 [ main ] Creating IO thread wakeup pipe -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800467 [ main ] Thread created -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800507 [ main ] Initializing libevent for IO thread -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800550 [ main ] Creating IO thread wakeup pipe -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800585 [ main ] Thread created -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800594 [ main ] Initializing libevent for IO thread -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800632 [ main ] Creating IO thread wakeup pipe -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800669 [ main ] Thread created -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800677 [ main ] Initializing libevent for IO thread -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800714 [ main ] Creating IO thread wakeup pipe -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800753 [ main ] Thread created -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.800761 [ main ] replaying queue: begin -> libgearman-server/gearmand.cc:
DEBUG -- ::10.800766 [ main ] __replay -> libgearman-server/plugins/queue/default/queue.cc:
DEBUG -- ::10.800774 [ main ] replaying queue: end -> libgearman-server/gearmand.cc:
INFO -- ::10.800780 [ main ] Adding event for listening socket ()
INFO -- ::10.800787 [ main ] Adding event for listening socket ()
DEBUG -- ::10.800794 [ main ] Adding event for wakeup pipe -> libgearman-server/gearmand.cc:
DEBUG -- ::10.800801 [ main ] Entering main event loop -> libgearman-server/gearmand.cc:
DEBUG -- ::10.801186 [ ] Entering thread event loop -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.801277 [ ] Entering thread event loop -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.801507 [ main ] staring up Epoch thread -> libgearman-server/timer.cc:
DEBUG -- ::10.801635 [ ] Entering thread event loop -> libgearman-server/gearmand_thread.cc:
DEBUG -- ::10.802426 [ ] Entering thread event loop -> libgearman-server/gearmand_thread.cc:

<5> 最后通过netstat,lsof, ps -ef 三板斧可以找出来gearmand大概占用的端口号,就如你看到的默认占用的4370端口,

当然你也可以在启动的时候用help命令也是能够知道的。

 [root@localhost ~]# netstat -tln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 192.168.122.1: 0.0.0.0:* LISTEN
tcp 0.0.0.0: 0.0.0.0:* LISTEN
tcp 127.0.0.1: 0.0.0.0:* LISTEN
tcp 127.0.0.1: 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:4730 0.0.0.0:* LISTEN
tcp6 ::: :::* LISTEN
tcp6 ::: :::* LISTEN
tcp6 ::: :::* LISTEN
tcp6 ::: :::* LISTEN
tcp6 ::: :::* LISTEN
tcp6 ::: :::* LISTEN
tcp6 127.0.0.1: :::* LISTEN
[root@localhost ~]# ps -ef | grep gearmand
root 40299 15869 0 02:31 pts/1 00:00:00 ./gearmand --verbose DEBUG
root : pts/ :: grep --color=auto gearmand
[root@localhost ~]# lsof -i :
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
gearmand 40299 root 8u IPv4 322550 0t0 TCP *:gearman (LISTEN)
gearmand root 9u IPv6 0t0 TCP *:gearman (LISTEN)
[root@localhost ~]#

二:Java Driver 在 Gearman上的使用

为了演示,我可以做一个简单的 “字符串.ToUpper”的业务逻辑来验证一下这个架构是否可以跑的起来。

1. java 充当 Gearman 的 work

首先需要在mvn仓库中拉一下jar包:http://www.mvnrepository.com/artifact/org.gearman/gearman-java/0.6。

<1> UpperFunction类,这个类用于定义work具体的业务逻辑:

 package com.datamip.gearmanwork;

 import java.text.SimpleDateFormat;
import java.util.Date; import org.gearman.client.GearmanJobResult;
import org.gearman.client.GearmanJobResultImpl;
import org.gearman.util.ByteUtils;
import org.gearman.worker.AbstractGearmanFunction; //字符串大写的业务Function
public class UpperFunction extends AbstractGearmanFunction { @Override
public GearmanJobResult executeFunction() { String param = ByteUtils.fromUTF8Bytes((byte[]) this.data); byte[] mybytes = param.toUpperCase().getBytes(); GearmanJobResultImpl result = new GearmanJobResultImpl(mybytes, true, mybytes, null, null, -1, -1); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = formatter.format(new Date()); System.out.println(String.format("当前时间:%s, 过来的字符串:%s,返回的字符串:%s", dateString, param,new String(mybytes))); return result;
}
}

<2>  将UpperFunction注册到gearmand中,从红色代码可以看到,其实是一个kv模式,这里的key="myUpperFunc”的对应执行业务就是new UpperFunction。

这样Client只需要传递一个"myUpperFunc",Gearmand就知道这个“字符串”对应是哪一个处理函数。。。

 public class App {
public static void main(String[] args) { GearmanWorker worker = new GearmanWorkerImpl(); GearmanNIOJobServerConnection conn = new GearmanNIOJobServerConnection("192.168.23.170", 4730);
worker.addServer(conn); // 将‘将转大写的函数注册’ 到gearmand中
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 }); System.out.println("启动服务。。。。"); worker.work();
}
}

2. java 充当 Gearman 的 client

<1> GearSubmit类【简单的一个包装类,随便定义】

 package com.datamip.gearmanclient;

 import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; import org.gearman.client.GearmanClient;
import org.gearman.client.GearmanClientImpl;
import org.gearman.client.GearmanJob;
import org.gearman.client.GearmanJobImpl;
import org.gearman.client.GearmanJobResult;
import org.gearman.common.GearmanJobServerConnection;
import org.gearman.common.GearmanNIOJobServerConnection;
import org.gearman.util.ByteUtils; public class Gearsubmit { public void process() throws InterruptedException, ExecutionException { GearmanJobServerConnection conn = new GearmanNIOJobServerConnection("192.168.23.170", 4730); GearmanClient client = new GearmanClientImpl(); client.addJobServer(conn); // 添加连接 String functionName = "myUpperFunc"; byte[] data = ByteUtils.toUTF8Bytes("hello,world"); // 创建后台任务
GearmanJob job = GearmanJobImpl.createJob(functionName, data, null); GearmanJobResult jobResult = null; Future<GearmanJobResult> gearmanJobResult = client.submit(job); jobResult = gearmanJobResult.get(); byte[] resultBytes = jobResult.getResults(); // 获取job的返回值
String value = ByteUtils.fromUTF8Bytes(resultBytes); System.out.println(value); System.out.println("执行结束"); client.shutdown();
}
}

<2> 主程序,开多线程并发的去执行。

 public class App {
public static void main(String[] args) throws InterruptedException, ExecutionException, IOException { ExecutorService executorService = Executors.newFixedThreadPool(100); for (int i = 0; i < 10000; i++) {
executorService.execute(new Runnable() { @Override
public void run() {
Gearsubmit submit=new Gearsubmit(); try {
submit.process();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
} System.in.read();
}
}

好了,一切都准备好了,接下来为了演示,演示就是解释,我用Jar2Exe把work程序导出成jar再转换成exe,如下图:

然后我把3.exe开成5个实例,client用100个线程的线程池并发调用,当然一切都是模拟。。。。可以看到,当我client启动的时候,5个work都在执行,

如果这个时候,你把某一个work停止了,jobserver也不再将任务丢给它,而是转给其他负载相对小的work继续执行。

好了,本篇就说到这里,希望对你有帮助。

高CPU业务场景下的任务分发方案Gearman搭建一览的更多相关文章

  1. 高CPU业务

    高CPU业务 Gearman是当年LiveJournal用来做图片resize的,大家也明白图片resize是一个高CPU的操作,如果让web网站去做这个高CPU的功能,有可能会拖垮你的 web应用, ...

  2. 【智能合约】编写复杂业务场景下的智能合约——可升级的智能合约设计模式(附Demo)

    智能合约的现状 以太坊在区块链上实现了智能合约的概念,用于:同质化通证发行(ERC-20).众筹.投票.存证取证等等,共同点是:合约逻辑简单,只是业务流程中的关键节点,而非整个业务流程.而智能合约想解 ...

  3. 不同场景下 MySQL 的迁移方案

    一 目录 一 目录 二 为什么要迁移 三 MySQL 迁移方案概览 四 MySQL 迁移实战 4.1 场景一 一主一从结构迁移从库 4.2 场景二 一主一从结构迁移指定库 4.3 场景三 一主一从结构 ...

  4. 高并发应用场景下的负载均衡与故障转移实践,AgileEAS.NET SOA 负载均衡介绍与实践

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  5. 高内存 高CPU 劣质网络下的测试

    内存 先把系统的虚拟内存去掉 (右键我的电脑属性里有的.选择那个无分页文件 虚拟内存在任务管理器就不显示了), 然后机子本身内存不高,开几个网页就满了       CPU cpu可以用鲁大师测试cpu ...

  6. MvcPager.js在特定业务场景下的问题解决

    用到了MvcPager.js,在一个常见的场景中出现了不能POST表单数据的问题,场景描述如下: 日期:2012-12-12 编号:*****                             ...

  7. 各业务场景下的技术推荐 【.net】

    后端: 1.webapi的token加密:  1)JWT验证算法,不推荐:2)RSA 2.集合的扩展:C5.dll 3.对象映射工具:AutoMapper .TinyMapper 4.任务调度框架:Q ...

  8. Automl基于超大数据下的数据分发方案探讨

    先定义几个关键字: 任务:用户一次上传的数据集并发起的automl任务,比如一次ocr任务,一次图像分类任务. 模型:一次任务中,需要运行的多个模型,比如ocr任务,需要ctpn模型,需要crnn模型 ...

  9. Alibaba高并发业务秒杀系统落地实战文档,已实践某大型秒杀场景

    前言: 高并发,几乎是每个程序员都想拥有的经验.原因很简单:随着流量变大,会遇到各种各样的技术问题,比如接口响应超时.CPU load升高.GC频繁.死锁.大数据量存储等等,这些问题能推动我们在技术深 ...

随机推荐

  1. webgl开发第一道坎——矩阵与坐标变换

    一.齐次坐标 在3D世界中表示一个点的方式是:(x, y, z);然而在3D世界中表示一个向量的方式也是:(x, y, z);如果我们只给一个三元组(x, y, z)鬼知道这是向量还是点,毕竟点与向量 ...

  2. Python正则表达式指南(转)

    原文地址:http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html 1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python ...

  3. 关于解决mysql数据库乱码的问题

    最近在开发的过程中频繁的使用到了mysql'这款数据库,mysql的中文乱码问题一直让人头疼.以前遇到过几次,但是都一不小心就解决了,这次终于明白到底是怎么回事了.可能我下面手的这种解决方案只适合于我 ...

  4. 设计模式-策略模式Strategy以及消灭if else

    概述 如果在开发过程中,出现大量的if else或者switch case 语句,如果这些语句块中的代码并不是包含业务逻辑,只是单纯的分流方法,那么,每一个语句块中都是一个算法或者叫策略. 背景 比如 ...

  5. python 排序sorted

    num = [3,2,4,6,5] anum = sorted(num) dnum = sorted(num,reverse=True) print '升序:',anum # 升序: [2, 3, 4 ...

  6. JavaScript预处理

    在预处理阶段js引擎会扫描代码中用var定义的变量和用声明的方式定义的函数 用声明方式定义函数 function a(){ alert('hello world'); } 用函数表达式定义函数 var ...

  7. CentOS 下mysql ERROR&n…

    CentOS 下mysql ERROR 1045: Access denied for user: 'root@localhost' (Using password: NO) 描述:在操作数据库时会出 ...

  8. NYOJ--20--搜索(dfs)--吝啬的国度

    题意,N座城市有N-1条路,目的是找到哪个城市可以到目的城市 //NYOJ--search--吝啬的国度 #include<iostream> #include<vector> ...

  9. Presto0.157版本单节点部署教程

    因为Presto版本的更新速度较快,所以最好按照对应版本的教程进行部署,博主之前看错了版本号,拿0.100版本的教程来部署0.157版本,结果导致部署失败. 官网:https://prestodb.i ...

  10. 关于flask线程安全的简单研究

    flask是python web开发比较主流的框架之一,也是我在工作中使用的主要开发框架.一直对其是如何保证线程安全的问题比较好奇,所以简单的探究了一番,由于只是简单查看了源码,并未深入细致研究,因此 ...