0%

引子

架构风格

人才,以及合理的人才结构,是软件公司乃至软件业发展的关键
成才,并在企业中承担重要职责,是个人职业发展的关键

从程序员到架构师转型

一个软企发展的好坏,极大的取决于如下人才因素:

  • 员工素质
  • 人才结构
  • 员工职业技能的纵深积累
  • 员工职业技能的适时更新

要让软件企业适应发展,需要:

  • 定期分析和掌握本公司员工能力状况、人才结构状况
  • 员工专项技能的渐进提升(如架构技能,设计重构技能)
  • 研发骨干整体技能的跨越转型(例如 高级工程师向架构师,系统工程师和技术经理的转型)

提升个人技能,也是让企业获取人才的合适途径

阅读全文 »

Makefile其实不难学,对于一些基本概念百度上应该很多,这里分享一个循序渐进的学习方式,保证让你快速掌握Makefile的编写。本文首发于公众号:良许Linux,里面有一个 Makefile 系列,欢迎关注交流!

阅读全文 »

你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。

CMake就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等 [1]。

在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

  1. 编写 CMake 配置文件 CMakeLists.txt 。
  2. 执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile ( ccmakecmake 的区别在于前者提供了一个交互式的界面)。其中, PATH 是 CMakeLists.txt 所在的目录。
  3. 使用 make 命令进行编译。

本文将从实例入手,一步步讲解 CMake 的常见用法,文中所有的实例代码可以在这里找到。如果你读完仍觉得意犹未尽,可以继续学习我在文章末尾提供的其他资源。

阅读全文 »

引子

为了让逐个编译的过程变成一条命令

前提:了解Makefile是什么?

Makefile 关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,Makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 Makefile 就像一个 Makefile 脚本一样,其中也可以执行操作系统的命令。 Makefile 带来的好处就是——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。 make 是一个命令工具,是一个解释 Makefile 中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的 make ,Visual C++ 的 nmake,Linux下GNU的 make 。可见, Makefile 都成为了一种在工程方面的编译方法。

在这篇文档中,将以C/C++的源码作为我们基础,所以必然涉及一些关于C/C++的编译的知识,相关于这方面的内容,还请各位查看相关的编译器的文档。这里所默认的编译器是UNIX下的GCC和CC。

阅读全文 »

引子

为了让逐个编译的过程变成一条命令

2 Makefile 总述

2.1 Makefile里有什么?

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

  1. 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
  2. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
  3. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  4. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
  5. 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”。

最后,还值得一提的是,在Makefile中的命令,必须要以[Tab]键开始。

阅读全文 »

引子

为了让逐个编译的过程变成一条命令

1、Makefile 介绍

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。

首先,我们用一个示例来说明Makefile的书写规则。以便给大家一个感性认识。这个示例来源于GNU的make使用手册,在这个示例中,我们的工程有8个C文件,和3个头文件,我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是:

  1. 如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
  2. 如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
  3. 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。

阅读全文 »

Makefile其实不难学,对于一些基本概念百度上应该很多,这里分享一个循序渐进的学习方式,保证让你快速掌握Makefile的编写。本文首发于公众号:良许Linux,里面有一个 Makefile 系列,欢迎关注交流!

1. 前言

通过之前章节的学习,我们对Makefile有个基础的认识,现在开始自己动手写Makefile。目前网络上有不少可以自动生成Makefile的工具,但很多项目其实没必要那么复杂,完全可以自己动手写出来。而且对于初学者来说,自己动手写一遍Makefile可以顶看十遍高手写的Makefile,也可以加深对Makefile的理解,将来公司的Makefile有需要修改的时候自己就可以动手搞定,不需要依靠他人,何乐而不为?

2. 源代码

介绍在本教程中用于示例的代码很简单,仅仅是在main函数中调用了fun1及fun2函数,而fun1及fun2独立写在fun1.c及fun2.c里。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//main.c  
int main()
{
printf("hello world\n");
fun1();
fun2();
}

//fun1.c
void fun1()
{
printf("this is fun1\n");
}
//fun2.c
void fun2()
{
printf("this is fun2\n");
}
阅读全文 »

发布者确认

发布者确认

发布者确认是RabbitMQ的扩展,可以实现可靠的发布。在channel上启用发布者确认后,代理将异步确认客户端发布的消息,这意味着他们已在服务器端处理。

在频道上启用发布者确认

发布者确认是AMQP 0.9.1协议的RabbitMQ扩展,因此默认情况下未启用它们。发布者确认是通过ConfirmSelect方法在通道级别启用的:

1
2
var channel = connection.CreateModel();
channel.ConfirmSelect();

必须在希望使用发布者确认的每个频道上调用此方法。确认仅应启用一次,而不是对每个已发布的消息都启用

策略1:Publishing Messages Individually

每条消息发步后,等待确认

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static void PublishMessagesIndividually()
{
using (var connection = CreateConnection())
using (var channel = connection.CreateModel())
{
var queueName = channel.QueueDeclare().QueueName;
channel.ConfirmSelect();

var timer = new Stopwatch();
timer.Start();
for (int i = 0; i < MESSAGE_COUNT; i++)
{
var body = Encoding.UTF8.GetBytes(i.ToString());
channel.BasicPublish(exchange: "", routingKey: queueName, basicProperties: null, body: body);
channel.WaitForConfirmsOrDie(new TimeSpan(0, 0, 5));
}
timer.Stop();
Console.WriteLine($"Published {MESSAGE_COUNT:N0} messages individually in {timer.ElapsedMilliseconds:N0} ms");
}
}

在前面的示例中,我们像往常一样发布一条消息,并等待通过Channel#WaitForConfirmsOrDie(TimeSpan)方法进行确认。确认消息后,该方法立即返回。如果未在超时时间内确认该消息或该消息没有被确认(这意味着代理出于某种原因无法处理该消息),则该方法将引发异常。异常的处理通常包括记录错误消息和/或重试发送消息。

此方法非常简单,但也有一个主要缺点:由于消息的确认会阻止所有后续消息的发布,因此它会大大降低发布速度。这种方法不会提供每秒超过数百条已发布消息的吞吐量。但是,对于某些应用程序来说这可能已经足够了。

发布者确认异步吗?

我们在一开始提到代理程序以异步方式确认发布的消息,但是在第一个示例中,代码同步等待直到消息被确认。
客户端实际上异步接收确认,并相应地取消阻止对WaitForConfirmsOrDie的调用 。将WaitForConfirmsOrDie视为依赖于后台异步通知的同步。

阅读全文 »

路由模式

Routing 路由模式

graph LR;
    id1([Product]);
    id2([ExChange]);
    id3([amq.gen-DjtYso1eaz52eM3mAJToaw])
    id4([amq.gen-nLrD6gHpPBMY-oqM-tBVcQ])
    id5([C1])
    id6([C2])

    style id1 fill:#0ff,stroke:#333;
    style id2 fill:#33c,stroke:#333;
    style id3 fill:#f00,stroke:#333;
    style id4 fill:#f00,stroke:#333;
    style id5 fill:#3cf,stroke:#333;
    style id6 fill:#3cf,stroke:#333;

    id1-->id2;

    id2-->|error|id3;
    id3-->id5;

    id2-->|error|id4;
    id2-->|info|id4;

    id4-->id6;

注意

  1. 生产者发送消息到交换机,要指定路由Key
  2. 消费者将队列绑定到交换机时需要指定路由Key

这个是一种 完全匹配 只有匹配到的消费者才能消费消息

消息中的路由键值如果和Binding中的binding key 一致,交换机就将消息发送到对应的队列中。
路由键与队列名完全匹配,如果一个队列绑定到交换机要求路由键为”dog”,则只转发routingkey 标记为”dog”的消息,不会转发”dog.puppy”,也不会转发”dog.guard”等等。这个是时 完全匹配、单播的模式

阅读全文 »

RPC——Remote Procedure Call,远程过程调用。 那RabbitMQ如何进行远程调用呢?

示意图

如下:

graph TB;
    C([Client]);
    mq1([rpc_queue])
    mq2([reply_to=amq.gen-..])
    S([Server])

    style C fill:#3cf,stroke:#333;
    style mq1 fill:#f00,stroke:#333;
    style mq2 fill:#f00,stroke:#333;
    style S fill:#3cf,stroke:#333;

    C-->|Request reply_to=amq.gen-.. correlation_id=abc |mq1;
    mq1-->S;

    S-->mq2;
    mq2-->|Reply correlation_id=abc|C;

解释

  • 第一步,主要是进行远程调用的客户端需要指定接收远程回调的队列,并申明消费者监听此队列。
  • 第二步,远程调用的服务端除了要申明消费端接收远程调用请求外,还要将结果发送到客户端用来监听回调结果的队列中去。
阅读全文 »