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. java基础06 Java中的递归

      一.递归是指直接或间接地调用自身. 二.递归的注意事项:             A:要有出口,否则就是死递归 B:次数不能过多,否则内存溢出 C:构造方法不能递归使用     三.举例子  递归 ...

  2. docker~Dockerfile方式建立镜像HelloWorld

    回到目录 Dockerfile可以便捷的建立一个image,它可以在一个镜像基础上,去构建另一个镜像,这也许就是它的特色,也是docker的本意! 我们下载一个mono的镜像 docker pull ...

  3. Java 9 揭秘(15. 增强的弃用注解)

    Tips 做一个终身学习的人. 主要介绍以下内容: 如何弃用API @deprecate Javadoc标签和@Deprecation注解在弃用的API中的角色 用于生成弃用警告的详细规则 在JDK ...

  4. python爬虫从入门到放弃(二)之爬虫的原理

    在上文中我们说了:爬虫就是请求网站并提取数据的自动化程序.其中请求,提取,自动化是爬虫的关键!下面我们分析爬虫的基本流程 爬虫的基本流程 发起请求通过HTTP库向目标站点发起请求,也就是发送一个Req ...

  5. 第一个java——swap()交换方法

    其实这学期的java课开了将近四星期了,加上开学前的小小预习.编写不下于二十几个java了. 可这一有关swap()方法的java确实是首次迷惑不解到处寻求解决的程序. 课堂上老师有关类.方法.对象的 ...

  6. css3关键帧动画实现轮播效果

    实现效果:打开手机京东,可以看到首页的头部,以这个头部为基础,仿写一个类似的样式. 思路:仔细观察可以发现,手机京东的头部是以一个搜索栏和轮播特效组成的,而这个搜索栏是以轮播特效做为背景的,现在运用c ...

  7. 初学Python(六)——输入输出

    初学Python(六)——输入输出 初学Python,主要整理一些学习到的知识点,这次是输入输出. 输入: # -*- coding:utf-8 -*- ''''' python中的输出为print ...

  8. Entity Framework Core 执行SQL语句和存储过程

    无论ORM有多么强大,总会出现一些特殊的情况,它无法满足我们的要求.在这篇文章中,我们介绍几种执行SQL的方法. 表结构 在具体内容开始之前,我们先简单说明一下要使用的表结构. public clas ...

  9. 亚马逊AWS EC2云实例AMI安装LNMP环境(2)——PHP5.6

    概括:这里选择亚马逊EC2的Linux AMI实例,该Linux服务器是亚马逊预配置的Linux环境,内置多个YUM源,属于亚马逊首推的稳定Linux服务器.默认登录用户名为ec2-user,执行ro ...

  10. Java 字符串截取问题

    编程:编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串. 但是要保证汉字不被截半个,如"我ABC"4 public class StringSplit  ...