智能体重塑Aiot新时代

随风

Aiot并不是一个很新的概念,物联网本身连接了大量的物理设备,而连接本身并不能带来真正的价值,真正有价值的是其收集的海量数据以及远程控制能力,因此当物联网与AI结合,AI所带来的巨大数据处理能力和规划能力,能够使得物联网应用的优点得到更大的发挥,因此Aiot成了物联网未来发展的重要赛道。

创万联社区很早就关注Aiot的发展,但是前期的Ai通用性不足,模型能力破碎,必须要将针对不同的场景开发不同的模型和算法,这将导致通用物联网平台与AI的结合变得困难,这将极大的约束Aiot的发展,但是大模型和agent技术极大的解决了这一困境,这是因为agent技术具有一定的自主规划能力和通用性,因此可以给场景各异的iot应用赋予通用的AI能力。

然而当前agent框架仍在发展,比较著名的如国外的langchain,国产的agently和promptulate,这些框架都是给予python和通用能力构建的,并不是针对iot的场景进行开发的,因此需要自行开发相应的tools,tools的开发其实看似简单,但实际上开发合适的tools也是一个颇具挑战的工作。

因此创万联根据实际需求,开发出了一款agent+iot的平台inteliconnect,这款平台能够支持常见的物联网协议,支持使用时序数据库influxdb进行时序数据存储以提升存储效率。还搭载有脚本引擎,支持后续的自主脚本开发。关键的是这款平台可以全面支持agent能力引入,开箱即用,下面是其能力展示,后续我们将在github进行开源,我们还将提供了一个开源社区技术交流群,欢迎你来分享知识,交流技术。

欢迎关注我们的社区公众号

谷歌iot core停止运行,chatGPT大火,我们如何看待

wonder

2023年注定是不平凡的一年,年初我们经历了疫情政策的开放,过了一个史上最长寒假。然而科技却并没有停下它的脚步,反而迎来了人工智能的iphone时刻,chatgpt和gpt4以及国内的文心一言,通义千问,这些AI大模型引爆了人们对于人工智能的关注和对人类社会未来的思考。

作为一个人工智能和物联网社区,我们很早就开始关注gpt的发展,chatgpt没有推出前,我们就观察到了gpt系列产品。但令人遗憾的是我们一直觉得ai语言模型不具备很强的推理能力,主要是依赖于语料库进行回答。虽然我们很早就觉得采用语言模型可能是实现AGI以及物联网平台接口的最佳选择,实际我们也是这么做的。我们开发出了家庭安全卫士,它的主要交互方式就是基于语言交互的。但是我们从来就没有想象过语言模型能发展到如此高度,其中的智能涌现令人细思极恐。

人工智能的大火的时候,物联网却遭到了诸多不利消息。谷歌iot core平台将于今年8月正式停止服务。物联网似乎已经成了时代的弃子。iot服务的领域主要是制造业,农业等一些领域。而现有的物联网平台主要是互联网大厂制作,对于行业的垂直理解不够丰富,使得云平台成了中看不中用的东西。并没有发挥出预想的生产力。因此iot的未来必须是结合人工智能,而不是继续像互联网一样的扩张思维。

然而chatgpt诞生之后,小社区和个人已经宣告无力参与Ai的研发。大模型所需要的资金和计算资源都是难以想象的。而我们能做的只能是参与应用开发。将大模型作为AI时代的操作系统,去发挥它的价值,因此我们无需悲观。有了大模型,物联网会得到更好的发展,我们将真正迎来数据时代。

物联网操作系统的任务调度初步认识

随风

任务调度是操作系统的基本功能。任务调度使得用户程序感觉自己在独占cpu。但是cpu是有限的,任务又那么多,操作系统是如何做到合理分配cpu时间的呢。其实rtos基本上是根据优先级抢占式调度,然后如果任务优先级相同就按照时间轮转片调度。

时间轮转片算法并不难,然而我阅读了大量的时间轮转片介绍的博客始终没有具体的硬件实现介绍。大部分博客都是仿真实现,这其实和真实的内核实现有着很大的差距。因为仿真切换你根本没法实现硬件中断机制,而真实的内核是利用中断机制来实现任务调度的。

为什么内核要使用中断机制而不是直接像普通程序那样编写。这其实非常显然,内核调度肯定占用的资源越低越好,实时性越高越好。而中断机制就能提供这样的属性。中断机制可以说是计算机最具革命性的一个设计,它大大提高了计算机系统的响应速度。

当看到利用中断机制的介绍时候我脑海里已经浮现了一种内核调度的实现方法。利用systick中断,systick中断实现流水灯相信大家都曾经编写过,因此想到利用systick来进行任务切换是一种非常自然的想法,只要设置好中断的时间间隔,那么就实现了时间片的设计。事实上tos系统同样利用了systick中断。下面是tos系统时基的入口。

void SysTick_Handler(void)
{
	if (tos_knl_is_running())
  {
    tos_knl_irq_enter();
    tos_tick_handler();
    tos_knl_irq_leave();
  }
}

但是仅仅利用systick中断肯定是要出问题的。因为单片机还要处理其它中断,在Cortex-M3中,如果OS在某个中断活跃时,抢占了该中断,而且又发生了任务调度,并执行了任务,切换到了线程运行模式,将直接触发Fault异常。这肯定是不行的,但你肯定会说不如直接把systick设置为最低优先级不就行了吗?当然可以,异常确实不会发生了,然而你仔细想想会发生什么?

仔细想好像也没问题啊。的确很难想到,但是我们处理中断的流程是怎么样的?第一步是关中断,然后保存现场再开中断,执行程序,最后再关中断,恢复现场然后开中断。这样做就是为了现场不被破坏同时保证更高优先级的中断能够正常被响应。一般OS在调度任务时,也是要关闭中断,也就是进入临界区,而OS任务调度是要耗时的,这就会出现一种情况:
在任务调度期间,如果新的外部中断发生,CPU将不能够快速响应处理。这对于智能驾驶这样的场景你能忍受吗?

到了这里你似乎走到了穷途末路。的确只利用systick在软件层面已经没有更好的办法了。但是狡猾的内核大佬利用了一个叫做pendsv的中断,这个中断很奇怪,它能够像普通外设中断一样悬起。它是系统级别的异常,但它又天生支持缓期执行。有了它我们就能轻松避免了不能快速处理的问题,pendsv会自动等待所有的中断处理完毕再进行任务切换的操作。

到了这里我们已经得到了一种较为成熟的os调度方案。

  1. 滴答定时器中断,制作业务调度前的判断工作,不做任务切换。
  2. 触发PendSV,PendSV并不会立即执行,因为PendSV的优先级最低,如果此时正好有中断请求,那么先响应中断,最后等到所有优先级高于PendSV的中断都执行完毕,再执行PendSV,进行任务调度。

那么tos系统也是这样实现的吗?我们看看源码。那么我们要启动tos系统,入口肯定要调用启动的api,源代码api头文件注释是get the kernel start to run, which means start the multitask scheduling.中文意思就是启动内核,意味着启动多任务调度。

__API__ k_err_t tos_knl_start(void)
{
    if (tos_knl_is_running()) {
        return K_ERR_KNL_RUNNING;
    }

    k_next_task = readyqueue_highest_ready_task_get();
    k_curr_task = k_next_task;
    k_knl_state = KNL_STATE_RUNNING;
    cpu_sched_start();

    return K_ERR_NONE;
}

可以看到前几行都是检测和获取任务的一些信息,重点在于 cpu_sched_start() ,这个是用来启动cpu任务调度的。刚才那个加了宏_api_表明它还是属于用户api,下面正式进入内核世界。

可奇怪的是它的实现c文件很短,就又调用了一个函数 port_sched_start 。

__KERNEL__ void cpu_sched_start(void)
{
    port_sched_start();
}

继续前进已经没有c文件了,迎面而来的是汇编语言。我熟悉8051汇编,但是对于arm的汇编,我并不是很熟悉,但是又不是考试,还是可以查手册的。粗略看了看,大概知道这是一段很重要的代码。

NVIC_INT_CTRL   EQU     0xE000ED04                              ; Interrupt control state register.
NVIC_SYSPRI14   EQU     0xE000ED20                              ; System priority register (priority 14).
NVIC_PENDSV_PRI EQU     0x00FF0000                              ; PendSV priority value (lowest).
NVIC_PENDSVSET  EQU     0x10000000                              ; Value to trigger PendSV exception.

我们看一下注释,这不就是设置pendsv为设置为最低优先级吗,到这里我们还不清楚它与systick的关系,但至少已经证实它肯定利用了pendsv。幸运的是我滚动一下鼠标就发现的

pendsv的中断处理子程序(汇编我们还是不要叫函数吧)

这段代码很复杂,但是只要有点51单片机的功底,还是猜得出它在干什么,其实就是操作系统最重要的工作,任务堆栈的切换。它借用了寄存器实现的现场的保护。至此我们完全证明tos系统利用pendsv进行上下文切换。

我们还看到这样一段注释

@ set pendsv priority lowest
@ otherwise trigger pendsv in port_irq_context_switch will cause a context swich in irq
@ that would be a disaster

翻译过来跟我们上面描述的完全一致。

我们再去找systick,得到了这个函数

__API__ void tos_tick_handler(void)
{
    if (unlikely(!tos_knl_is_running())) {
        return;
    }

    tick_update((k_tick_t)1u);

#if TOS_CFG_TIMER_EN > 0u && TOS_CFG_TIMER_AS_PROC > 0u
    timer_update();
#endif

#if TOS_CFG_ROUND_ROBIN_EN > 0u
    robin_sched(k_curr_task->prio);
#endif
}

unlikely这是内核的一种优化函数,暂时不用管它,我们全力看 robin_sched ()函数。

__KERNEL__ void robin_sched(k_prio_t prio)
{
    TOS_CPU_CPSR_ALLOC();
    k_task_t *task;

    if (k_robin_state != TOS_ROBIN_STATE_ENABLED) {
        return;
    }

    TOS_CPU_INT_DISABLE();

    task = readyqueue_first_task_get(prio);
    if (!task || knl_is_idle(task)) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    if (readyqueue_is_prio_onlyone(prio)) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    if (knl_is_sched_locked()) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    if (task->timeslice > (k_timeslice_t)0u) {
        --task->timeslice;
    }

    if (task->timeslice > (k_timeslice_t)0u) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    readyqueue_move_head_to_tail(k_curr_task->prio);

    task = readyqueue_first_task_get(prio);
    if (task->timeslice_reload == (k_timeslice_t)0u) {
        task->timeslice = k_robin_default_timeslice;
    } else {
        task->timeslice = task->timeslice_reload;
    }

    TOS_CPU_INT_ENABLE();
    knl_sched();
}

从源码中可以看到,时间片调度算法的实现非常简单,当时钟节拍来临的时候,将就绪列表中第一个任务控制块的时间片值递减,如果递减到0,则移到就绪列表的队尾去,让出此次执行机会,内核发生调度。我们重点看 knl_sched()函数。

__KERNEL__ void knl_sched(void)
{
    TOS_CPU_CPSR_ALLOC();

    if (knl_is_inirq()) {
        return;
    }

    if (knl_is_sched_locked()) {
        return;
    }

    TOS_CPU_INT_DISABLE();
    k_next_task = readyqueue_highest_ready_task_get();
    if (knl_is_self(k_next_task)) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    cpu_context_switch();
    TOS_CPU_INT_ENABLE();
}

这个就是检查一下当前是否有中断发生,还有检查一下锁是否释放,然后调用函数 TOS_CPU_INT_DISABLE()关闭中断,调用 cpu_context_switch() 进行上下文切换。不出意外,终点一定是汇编实现。

果然我们已经离开了c语言世界,进入汇编世界。看代码

port_context_switch
    LDR     R0, =NVIC_INT_CTRL
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR

这是什么?这是什么?触发pendsv异常,到了这里我们已经破解了tos系统的任务调度器的基本原理。这表明tos系统和freertos的实现机制是大差不差的。

内核是非常复杂的东西,我们一定要充满敬畏,不要随意去修改,但是了解内核还是很有必要的,否则应用层的很多东西我们理解可能都是错误的。在此,真的太佩服太佩服内核大佬了,瞬间觉得应用层开发那点困难不算什么了。

最后来个传送门

物联网操作系统的初步探索

随风

近几年物联网飞速发展,市面上也出现了很多物联网操作系统。比较有名的有rt-thread,tencentos-tiny以及鸿蒙等。这些物联网系统各有差异,这符合物联网碎片化的生态现状。下面我将以tencentos-tiny为例子分析一下目前物联网操作系统的一些特点。

打开tencentos-tiny的官网,映入眼帘的是比较熟悉的代码结构。分别是内核,驱动,网络以及一些辅助的工具,这和大多数的嵌入式操作系统是类似的。

我们再来看看下面这张图。

这张图是tos系统官网给出的架构,可以看出它的模块化设计是非常优秀的。内核只包含了最为重要的特性,即任务调度,时间以及内存管理,以及ipc通讯的组件。而物联网的工具箱则被放置于内核之外。我们用vscode阅读一下它的内核源码。

可以看到它的确非常的精简,每个功能的实现代码有些甚至只有几百行。这样精简的实现,使得它可以轻松移植到stm32f1这样的芯片。

但是如果进行全量移植的话,f1这样的芯片内存空间仍然不是很足,但是tos系统可以轻松的裁剪,移植的体验也非常不错,如果你不需要其它的功能的话,你只需要把kernel和arch目录以及tos_config复制到你的工程就行了,当然你还得进行一些微调,比如系统时钟脉冲设置,但我花了一些时间也就顺利移植成功了。

可能到现在你有个疑问,stm32真的有必要使用操作系统吗?的确,裸机编程能满足大多数场景了,但是如果你想实现实时性很强,任务又非常多的话,使用操作系统的确非常便捷。tos系统采用了抢占式的任务调度。高优先级任务就绪的时候会立即打断低优先级的任务。相同优先级的就是使用时间片调度。这样能保证高优先的任务能优先被处理。tos系统还提供了任务之间通讯的机制,互斥锁,消息队列以及事件机制。这使得基于任务编程成为轻松的事情。否则的话你就只能一个循环加中断或者dma等。这样的cpu利用率以及响应速度都并不能让人非常满意。

tos系统如果仅仅有上述任务调度功能,它就不能称为物联网操作系统,而顶多叫做实时操作系统。tos系统提供了非常强大的网络工具,可以兼容esp8266等众多芯片,提供了一个统一的at指令框架以及各种协议栈的支持。都说抽象是软件的灵魂。tos系统实现了at指令的抽象框架到sal层的抽象框架,从而虚拟出socket接口。有人觉得这不是多此一举吗?这当然不是,实际上这个设计和“一切皆文件”是有异曲同工之妙的。不过由于tos系统是腾讯开发的,它当然要给很便捷的api给自家的iot平台,但这些工具的确方便了物联网的开发。下面引用一下腾讯知乎的一张图,它清晰地阐述了sal接口的好处。

不过说了这么多,sal接口对我的吸引力并没有那么大,实际上我们使用的通讯芯片一般都是确定的一种,并不太可能替换另外一种,但是移植这抽象层占用的flash空间又是真实存在的,因此我倒不如直接at指令来的方便。

物联网操作系统的产生屏蔽了纷繁复杂的物联网硬件,提供了一些较为统一的接口,据说鸿蒙还提供了分布式软总线,但我还没有看过。但是我感觉只靠这样的一个操作系统就能一统江湖,估计还是任重道远。不过我发现了一个有趣的现象,人们正打算在物联网操作系统中提供js引擎。我觉得这个技术未来确实有比较好的前途。我们下次再讨论一下js引擎对于物联网的影响。

最后来个传送门,祝大家元旦快乐。

云原生下挣扎的java

随风

java曾经的确是风光无限,现在也依然热度不减。但眼下的java的确已经面临了严峻的挑战。java语言曾经的面向对象特性和虚拟机特性使得它非常适合大规模的上层应用,更重要的是它是跨平台的,一次编译,到处运行使得java空前成功。

java不是跨平台的第一人,但绝对是那个时代做到最优秀的。在开发简易程度和性能上做到了良好的平衡。但是令人想不到的是,随着科技的发展,那些曾经的独门武功竟然成了巨大的绊脚石。

jvm成就了java。jvm是一种hotspot虚拟机。如果你对jvm有所了解,你一定会惊叹它的博大精深和设计巧妙。jvm的存在,产生了一大批jvm语言,这是因为jvm其实是语言无关的。jvm语言著名的有kotlin,groovy和scala。你只需要实现字节码规范,你就可以驾驭jvm。这为jvm生态打下了坚实基础,同时jvm的jit编译器极大的改善了性能,使得java语言效率远高于python。jvm的线程模型也非常不错,这比python解析器可要高级得多。jvm还有出色的垃圾回收器,你可以用指令选择不同场景下的垃圾回收器。包括串行的,并行的。以及最新的g1垃圾回收器和zgc回收器。这些垃圾回收器已经满足绝大部分场景。基于最新的JDK15来看,“停顿时间不超过10ms”和“支持16TB的堆”这两个目标已经实现,并且官方明确指出JDK15中的ZGC不再是实验性质的垃圾收集器,而且建议投入生产了。

但是风水轮流转,云原生时代的到来,使得java瑟瑟发抖。那些引以为豪的优秀特性变得不再重要,首先是一次编译,到处运行。现在但凡新一点的语言,跨平台已经成为标配,而且不需要拖着一个沉重的jvm。加上docker的流行,环境已经不是什么问题了。在无服务器应用则更惨了,无服务器应用弹性伸缩,依赖于函数模块的快速启动,而jvm启动需要预热,因为jit需要一定的预热时间,加上类加载的设计。这一下好了,启动速度直接龟速。 如果写过springboot你一定对它的启动速度印象深刻。隔壁go语言的启动简直是快如闪电啊。第三就是面向对象机制,使其适合大规模的编程,可惜随着微服务的流行,人们已经不再追求巨石应用,而是切分成一个个小应用。java冗长的语法,怎么比得过函数式的程序语言呢。最后是高并发场景,这是java当年的强项。java凭借着优秀的高并发支持而流行。但是如今它一对一的线程模型加上各种复杂的锁机制,导致新手根本无法上手。而隔壁nodejs根本就是单线程加事件循环,实现了巨大的吞吐量,不需要锁机制,加上js天生容易掌握,java再次完败。

但是java是否就此停止挣扎了呢,当然不可能。java世界看到了威胁,正开始准备一个超级大招-graalvm。有了它我们就不需要jvm这个累赘了。https://www.graalvm.org/

graalvm的官网映入眼帘的就是高性能,云原生,多种语言。下面排着各种语言的logo。这不免让人疑惑,难道它所有语言都支持。不错,它几乎全支持我们常见的语言,这使得我们多种语言互操作变得十分简单。它最重要的功能当然就是解决java的弊端。它实现的java编译为本地二进制代码。双击就能够运行。从而实现了高速启动,使得Java不至于在云原生时代无所适从。配合springnative,你就能实现java云原生场景的快速启动。

graalvm 的设计是令人惊叹的,java不是一种满足封闭原则的语言,它带有很强的动态性。如类加载机制。这些难以通过静态编译实现,但是 graalvm 有自己的独门武功,可以通过多次运行辅助分析出程序里的动态部分。限于篇幅,这里就不再赘述了。

这篇文章涉及到了较多的新名词,但只要阅读了cwl公众号的那篇云计算时代的文章应该还是对这些新名词有一定了解的。这里立一个flag,下篇文章带大家实操 graalvm ,体验一下云原生java的魅力。

最后来个传送门

浅谈物联网平台设计的那些事

随风

物联网发展到了今天,可以说是有了长足的发展。各大科技公司的公有物联网平台纷纷上线,也提供了许多不俗的功能。不过人们也开始发现,物联网时代似乎仍然离我们很远,那究竟是什么阻碍了物联网的发展?

很多人第一印象觉得是算力,通讯技术和电池技术的约束。这样认为也不是没有道理。算力,通讯技术和电池技术确实是物联网技术之中很重要的一环。通讯技术决定了物联网产品是否能够稳定地进行信息传输,算力决定了智能设备的信息处理能力。电池技术则决定了设备的续航时间。但是约束物联网发展的也许并不是这些,而是我们究竟能用物联网做些什么,以及使用物联网究竟能带来哪些根本性的好处。

目前大多数的公有云平台事实上是to c模式的。保罗万象,偏向于监控生产过程。然而实际上我们需要的恰恰是垂直应用场景,也许是智能家居的或者是工业互联网的。智能家居事实上已经比较成熟,但在工业应用物联网上仍然存在不少硬伤,稳定性以及业务多变性。就像蓬勃的互联网一样,不存在一种互联网产品能满足所有需求。互联网只是一个概念,物联网也如此。因此需要有出色的物联网应用,方能发挥物联网的最大价值。而目前物联网应用开发的生态还很薄弱,缺乏相应的框架系统,大部分仅仅只是mqtt实现库,缺乏业务级别的支撑。因此开发者不得不自行设计业务框架。这无疑耗时耗力。

物联网的中间件以及相应的开发模式仍很贫瘠,大部分物联网项目的终点是监控,这无疑难以发挥它本身应有的互联价值。设备之间的联动仍然是难以像设想中的那样完美。资本是逐利的,连接量的提升并不能直接像互联网场景那样获得巨大的商业价值。因此物联网的商业模式并不能直接复制互联网的模式,物联网时代是属于创新者的,创新者将会赢得最后的胜利。

讨厌的npe如何去除

java语言的null大概就像c语言的空指针一样非常讨厌。特别是业务复杂的时候,多层判断简直让人抓狂,kotlin语言就针对这个问题进行了优化。那么作为java语言的编程者是不是就坐以待毙了呢?当然不是。我们可以使用Optional进行优化。

先强调Optional并不能消除null,这是不现实的,也不必要。在不复杂的场合,我们直接判断就可以了。Optional本质上也不是什么黑魔法,它就是一个容器,跟arraylist是一个性质的。因而可以得知,使用它必然产生一定开销,如果是算法比赛这类场景那还是建议不要用了,毕竟输入输出我们都希望多快好省。

Optional作为返回值是非常理想的选择。聪明的你撸了一个非常好的类,但在Optional还没出现之前,你不得不使用null作为某些情况的返回值,最终使得使用者相当抓狂。Optional作为返回值,可以摆脱这一噩梦。业务上需要空值时,不要直接返回null,可以选择使用 Optional.empty()。使用者使用Optional#ifPresent就可以比较好的减少null带来的困扰。

聊聊java并发那些事情

随风

随着摩尔定律日渐失效,多核心编程已经是不得不面对的现实。而java从一开始就设计成了一种支持多线程的语言。那么我们如何使用java来进行多线程程序的编写呢?很简单,一般来说,在java开启一个多线程,我们有三种方法,这几种方法使用场合不一样,并没有孰优孰劣。

1.继承Thread类,这种方法很简单,但是java是单继承的,这阻碍了你业务的继承扩展,因此并不适用于需要继承扩展的场合。

2.实现Runnable接口,这种方法比较灵活而且简单。后面文章也主要使用这种方法。这种方法只需要实现run方法就可以轻松创建一个线程。

3.实现Callable接口,这种方法功能最为强大,可以在任务结束后返回值。call方法还能够抛出异常。而run方法并不能返回值。

下面是创建线程的简单例子。

package com.company;



class MyThread2 implements Runnable{
    private static int count = 0;// 计数器

    @Override
    public void run() {
       synchronized (this) {
           ++count;

           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println(count);
       }
    }
}
public class test {
    public static void main(String args[]){
         MyThread2 myThread2=new MyThread2();
         Thread t1=new Thread(myThread2,"t1");
         Thread t2=new Thread(myThread2,"t2");
         Thread t3=new Thread(myThread2,"t3");
         t1.start();
         t2.start();
         t3.start();
    }
}

可以看出创建一个线程还是相当的简单的,但令人头疼的其实是线程同步问题。上述代码中++count并不是一个atomic操作。实际上包含了查询,+1和赋值操作,这样的结果就是在并发中会产生所谓的线程同步问题,读者可以试着把同步字段删除。这将会导致结果为333,这与设计的初衷就发生了违背。为什么会这样呢?原因很简单,sleep的时候线程将会被挂起,这样其它线程就会相继进行++count操作然后也被挂起。最后输出的当然是最终的数值3。因此如果我们需要使其线程同步的话,我们需要加上互斥锁,通过锁的竞争确保其同步。但这样的代价是多线程实际上退化了。这样就导致了性能实际上没有提高,反而由于上下文切换等下降了。

可能读者会觉得这样一段小代码没有什么实际用途,其实这种想法是错误的,java springboot controller实际上是单例多线程的,因此如果我们在@controller下使用全局变量将会导致跟上面一样的错误。如果我们做的是购物系统,那么很遗憾,你这个系统将会产生超卖问题。当有人可能会说,我们呢平时crud也没感觉要加锁,其实数据库的事务本来就是atomic的,所以并不需要加锁。因此我们技术人要感谢前人的努力,才使得现在做一个web应用如此简单,但是简单的背后却是tomcat和spring全家桶这样的大黑箱。如果我们不努力研究黑箱的本身,那么最终我们就只能是一个crud程序猿。

最后卖个广告,创万联新一代物联网平台正在火热建设中,其中包含了大量的优秀特性。更安全,接口更加规范,极大提高了“柔性”程度。平台跟项目绑定的时代也许将要彻底告别,我们欢迎各位开源人士的加入,你们的加入是我们组织的福气。让我们共同迎接aiot和数字孪生的美好未来。

go语言在爬虫中的应用

随风

go语言有21世纪c语言的美誉,实际也表现出了极好的性质。go语言具有轻量级的协程goroutine,能轻而易举地实现数万并发。并且可以真正多核并行,充分利用cpu。语法明快,使用起来没有臃肿和历史包袱。标准库文档齐全而且极易阅读。

在爬虫领域,人们使用最多的当属python,python有数不清的爬虫库。以至于人们几乎把python等价成了爬虫专用。似乎人工智能和数据科学领域也如此。实际上,无论是人工智能还是爬虫,它们都是一种算法或者说是一种技术手段,使用什么语言其实影响并不大。

什么是爬虫?爬虫违法吗?爬虫用一句话来概括就是the website is the api。爬虫程序利用网络请求或者selium一样的模拟点击自动化测试工具来获取互联网上的海量信息。百度等搜索引擎其实也是这样做的,所以爬虫技术本身其实并不违法。但是造成了网站服务器崩溃以及其它恶劣影响事件则有可能会违法甚至犯罪。

go语言有强大的net库,本身带有cookiejar等组件,来保持会话,还有健壮的并发机制。对于海量信息的获取,go语言能够使用简单的算法就能获取极高的爬虫效率。而不需要像python scrapy 那样使用twisted以及队列模型,这极大的增加了编程的复杂性。至于chanel(管道),go语言更是原生支持。所以go语言在爬虫领域有着十分光明的前景。

go语言缺乏很好的大而全的爬虫框架,但是也有诸多工具,如goquery,colly,chromedv,运用这些工具我们已经足以完成一般的爬虫软件开发。当然纵观go语言领域,大而全的框架其实很少,更多的是轻量级框架如gin。也许是吸取了spring全家桶笨重的教训,即使是springboot,大大简化了配置,但是由于历史包袱原因,至今底层仍需要netty或者tomcat的支持,排错比较麻烦,对新手不那么友好。

爬虫学得好。。。,下面我就不说了,各位还是在尊重法律的基础上畅享爬虫带来的乐趣。

谈谈go语言的关键字

随风

go语言的关键字极其少,充分体现了少就是多的哲学。go一共有多少关键字呢?我在论坛搜索了一波,发现只有25个关键字。

go的25个关键字:
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

可以看出这些关键字里面居然连常见的int等类型定义都没有,却添加了内置支持类型map和chan。map的作用不用多说,键值对处理的必备,而chan则是go语言处理协程通讯的基本工具。但是int等类型真的不是关键字吗?我的回答是,那当然,看看下面这段奇怪的代码你就明白了。

代码“居然”正常编译运行了,不过这样的写法估计你离离职也不远了吧。

不得不说go的关键字设计确实有点大胆,只能说佩服佩服。

欢迎关注创万联公众号。