Gearman入门及使用(JAVA调用)

前言
因为新公司用了这个框架做任务分发,所以我才有机会知道并学习!那么Gearman到底是个什么样子的框架或系统呢?它有哪些作用、优缺点呢?
Gearman是一个用来把工作委派给其他机器、分布式的调用更适合做某项工作的机器、并发的做某项工作在多个调用间做负载均衡、或用来在调用其它语言的函数的系统。可用于sso 分发连接,但有弊端就是占用系统资源较多,例如CPU、内存。(来源于百度百科)
简单来说,Gearman就是一个分布式计算调度框架或系统,它的主要职责就是做工作分发,所以既然是用来做分布式计算,那自然资源占用会多一些,毕竟不是用来搞小事情的~
正题
如下图,Gearman的工作方式基本可以这样理解,当然既然是计算,那肯定会有结果返回,Client端在任务提交完成之后,那么就可以等待计算结果返回,然后进行汇总。为了简单便于理解,我这里只画出了一个Client,实际生产中Client和Worker肯定都是很多个。

image.png

我们大致了解了Gearman的工作方式,那现在着手编程,试试效果。
环境准备
Linux:Centos 6.10
Gearman:1.1.18
Language:Java
Jar包:gearman-java 0.6
安装好Gearman后,我们用命令启动它,进入Gearman的sbin目录下,启动命令:./gearmand --pid-file=/tmp/gearman.pid --daemon --log-file=/home/log/gearman/gearman.log。(Linux安装源码软件,先configure、再make、最后make install,关于一些安装参数,可以查一下configure的参数,基本上就没啥问题了,缺库的话对应着安装即可)
如下图,服务已经正常启动,默认端口是4730。

image.png

第一个程序

public class EchoApplication {
    private GearmanClient client;

    public EchoApplication() {
        GearmanJobServerConnection connection = new GearmanNIOJobServerConnection(Constant.GEARMAN_HOST, Constant.GEARMAN_PORT); // 创建Gearman服务connection
        client = new GearmanClientImpl();
        client.addJobServer(connection);
    }
    public String echo(String input) throws IOException {
        byte[] data = input.getBytes(); // 发送数据
        byte[] reply = ((GearmanClientImpl) client).echo(data); // 调用Gearman
        return new String(reply, "UTF-8");
    }
    public static void main(String... args) {
        EchoApplication app = new EchoApplication();
        String reply = null;
        try {
            reply = app.echo("hello");
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("reply is ===> " + reply);
    }
}

很简单的一个使用方法,首先在构造函数中初始化连接,然后主函数中调用echo。
如果运行后能够得到如下结果,那么表示Gearman调用成功!

image.png

Client、Worker实现
首先,我们清楚Client的职责,就是做工作拆分(根据实际业务逻辑)以及提交,在Gearman系统中,我们只关注工作提交即可。由于Gearman系统中,不要求Client和Worker的实现语言一直,毕竟分布式计算,所以Client和Worker可以使用不同语言实现,为了方便,我这里统一使用Java。
既然是提交工作,那么定义一个工作是必不可少的部分,如下,我就定义了一个很简答的工作(INFO日志输出内容太多,不方便看结果)。

public class ReadDataJob extends AbstractGearmanFunction {
    private Logger logger = LoggerFactory.getLogger(ReadDataJob.class);

    @Override
    public GearmanJobResult executeFunction() {
        DataInfo data = SerializationUtils.deserialize((byte[]) this.data);
        logger.warn("this thread ID is [{}], i will do [{}]", Thread.currentThread().getId(), data.toString());
        return new GearmanJobResultImpl(this.jobHandle, true, SerializationUtils.serialize(data.toString()), new byte[0], new byte[0], 0, 0);
    }
}

定义工作需要继承Gearman-Java中的AbstractGearmanFunction,实现executeFunction()方法,然后客户端提交的工作的参数,存放在data字段中。我这里就是简单的打印了提交过来的数据,然后作为返回数据返回。
有了工作,我们实现Client,那Client的做提交动作,在实际生产中,肯定是不断会提交工作,所以我们就简单的实现一个循环来模拟。

public class ClientRunner implements Runnable {
    private Logger logger = LoggerFactory.getLogger(ClientRunner.class);

    private GearmanClient client;

    public ClientRunner() {
        GearmanJobServerConnection connection = new GearmanNIOJobServerConnection(Constant.GEARMAN_HOST, Constant.GEARMAN_PORT);
        this.client = new GearmanClientImpl();
        client.addJobServer(connection);
    }
    public void run() {
        this.start();
    }
    private void start() {
        while (true) {
            try {
                String function = ReadDataJob.class.getCanonicalName();
                DataInfo data = new DataInfo(UUID.randomUUID().toString(), "log", "/home/log/");
                // 创建一个工作,包含名字、数据(参数)、工作ID
                GearmanJob job = GearmanJobImpl.createJob(function, SerializationUtils.serialize(data), data.getDataId());
                this.client.submit(job);
                GearmanJobResult result = job.get();
                byte[] resData = result.getResults();
                String res = SerializationUtils.deserialize(resData);
                logger.warn("reply is [{}]", res);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

以上就是一个简单的Client,首先在构造函数中初始化Gearman连接,然后在循环中一直提交工作。GearmanJob需要有functionName(工作名称),数据(参数),以及ID。submit工作之后,GearmanJob对象可以等待其返回,这个实现模式是不是很像线程池提交Task得到Future对象,然后用Future对象等待获取执行结果呢~。我在这里也仅仅是简单打印返回结果。
最后我们要实现Worker了,也就是实际帮我们完成工作的地方。

public class WorkerRunner implements Runnable {
    // 所有定义好的工作
    private List<Class<? extends GearmanFunction>> functions;
    private GearmanWorker worker;

    public WorkerRunner(List<Class<? extends GearmanFunction>> functions) {
        // Gearman connection
        GearmanJobServerConnection connection = new GearmanNIOJobServerConnection(Constant.GEARMAN_HOST, Constant.GEARMAN_PORT);
        // 初始化worker
        this.worker = new GearmanWorkerImpl();
        this.worker.addServer(connection);
        this.functions = new ArrayList<Class<? extends GearmanFunction>>();
        this.functions.addAll(functions);
    }
    public void run() {
        this.start();
    }
    private void start() {
        // 将所有定义好的工作,注册到worker中
        for (Class<? extends GearmanFunction> function : functions) {
            worker.registerFunction(function);
        }
        // 启动worker
        worker.work();
    }
}

以上就是Worker的简单实现,首先在构造函数中初始化好Gearman连接,以及Worker对象,然后我们在start函数中把所有定义的工作都注册到Worker中,这里需要注意,查看Worker的源码可以发现,Worker中是把工作的名字和工作一一对应起来的;最后启动Worker,等待接受工作。
OK,我们实现了所有必要组件,那么试试运行我们的代码。为了方便观察其工作结果,所以我针对Client和Worker分别写了启动类。
Client启动

public class ClientApplication {
    private ExecutorService client = Executors.newSingleThreadExecutor();

    public static void main(String... args) {
        ClientApplication app = new ClientApplication();
        ClientRunner clientRunner = new ClientRunner();
        app.client.submit(clientRunner);
    }
}

Worker启动

public class WorkerApplication {
    private ExecutorService workers = Executors.newFixedThreadPool(2);

    public static void main(String... args) {
        WorkerApplication app = new WorkerApplication();
        List<Class<? extends GearmanFunction>> functions = new ArrayList<>();
        functions.add(ReadDataJob.class);
        WorkerRunner worker1 = new WorkerRunner(functions);
        WorkerRunner worker2 = new WorkerRunner(functions);
        app.workers.submit(worker1);
        app.workers.submit(worker2);
    }
}

针对worker,我启动了两个,我们可以看看是不是随机选取的Worker。
Worker输出

image.png

Client输出

image.png

可见两个Worker都有被分派到工作。
OK,到此我们基本对Gearman的使用入门了,后面如果要继续研究Gearman,可能需要关注它的一些关键参数、特性,以及使用场景,毕竟工具会用和拥得正确、拥得好还是有一定差距的~。
最后再插一句,关于Gearman的数据传输协议,它直接使用了字节流传输,那么业界序列化的框架很多,所以我们在开发的时候,很有必要选择一种通用的(多种语言都支持的),千万不要被数据的序列化方式所局限;所以在生产中,我上面的序列化方式并不可取,毕竟这仅仅适用于Java;然后推荐一下序列化方式,比如protocol buffer,Thrift这些,当然这些使用需要一点点学习成本,如果不愿意耗费,而且系统对于序列化性能要求不高,那么最土的方式,转成Json,然后json字符串序列化成byte数组。

如果有不正确的地方,请帮忙指正,谢谢!
github: https://github.com/zhangveiqiang/gearman-learn

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容