IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

后端

共 1964 篇文章

IT 2016-03-18 17:09:16 / 累计浏览 1,924

图解微服务架构演进

这篇讲的是随着业务规模扩大,传统单体应用架构如何一步步演进到微服务架构的完整路径。 作者从 Dubbo 用户手册的一句话切入,点明了架构升级的背景:常规垂直应用架构已无法应对互联网发展,服务化改造势在必行。文章核心是梳理了服务化架构的演进阶梯:最初是功能内聚但扩展性差的 **ORM 单体架构**;随后为提升性能,演化出便于前后端分离、加速开发的 **MVC 垂直应用架构**;当应用间交互增多,便抽离核心业务,通过 **RPC 分布式服务框架** 实现复用与整合;随着服务数量激增,需要调度中心进行统一管控,形成了 **SOA 流动计算架构**。 最终,演进到了微服务架构。它的核心思想是将功能进一步拆解为更原子、自治的服务单元进行高密度部署,以实现彻底的解耦。文章也结合敏捷开发、持续交付和容器化(Docker)等趋势,指出微服务是应对复杂度的必然演进方向。整个梳理逻辑清晰,从历史演进的角度为理解微服务提供了一个扎实的上下文。

本机暂存
IT 2016-03-18 17:07:19 / 累计浏览 1,566

缓存系列文章–无底洞问题

这篇讲的是分布式缓存系统中一个经典陷阱——“无底洞”问题。作者从Facebook大规模Memcached集群的实践切入,指出盲目增加节点不仅无法提升性能,反而会导致批量操作(如mget)因网络次数暴增而变慢。 问题的根源在于哈希分布下,大量key被打散到不同节点。一次批量获取需要跨多次网络IO,机器越多,耗时越长。文章对比了哈希分布与顺序分布的特点,并给出了四种应对方案:最简单的串行mget(O(n)网络时间),优化后的按节点分组串行IO(O(节点数)网络时间),以及多线程并行IO和利用Redis的hash-tag强制key到同一节点。 通过对比可以看出,从“串行mget”到“hash-tag”,核心思路都是尽可能减少网络往返次数。文章最后用清晰的表格总结了各方案的优劣与适用场景,为开发者在数据分散与访问效率之间做出权衡提供了明确参考。

本机暂存
IT 2016-03-18 17:04:16 / 累计浏览 2,802

Java注解全面解析

这篇讲的是Java注解的核心机制与演进。作者从注解的基本语法切入,解释了它看起来像接口但本质不同的特点,并重点拆解了`@Target`和`@Retention`这两个元注解如何控制注解的“作用目标”与“生命周期”。 文章不仅梳理了四大元注解的功能,还深入探讨了注解元素的类型限制(比如不能用包装类型,不能用`null`作为默认值)以及“快捷方式”语法的实现条件——即只有名为`value`的唯一元素时方可省略键名。 对于开发者更关心的JDK 1.8增强,文章详细对比了新增的`TYPE_PARAMETER`和`TYPE_USE`枚举成员的使用场景差异,并通过`@Repeatable`注解的案例,展示了如何实现同一位置的重复声明及其底层的容器收集机制。这使得全文不仅是一份语法手册,更像一份从基础到实战特性的清晰路线图。

本机暂存
IT 2016-03-18 17:01:41 / 累计浏览 2,963

操作系统基础知识

这篇讲的是操作系统面试中几个核心概念的对比与辨析。文章没有泛泛而谈,而是聚焦于死锁、链接库、进程与线程、进程间通信这几个经典问题,通过问答的形式拆解要点。 关于死锁,它清晰地列出了四个必要条件(互斥、请求与保持、非剥夺、循环等待),并介绍了从忽略问题到破除条件的四种处理策略,其中“鸵鸟算法”的比喻让策略选择显得更生动。 在链接库部分,文章对比了静态库(.lib)与动态库(.dll)的核心区别:前者在编译时代码被强制装入程序,增大体积但运行稳定;后者在运行时按需加载,能有效节省内存空间,更适合大型项目。 对于进程与线程,文章从概念、执行过程和逻辑关系三个层面展开。关键在于,进程是资源分配的基本单位,拥有独立内存空间;而线程是CPU调度的基本单位,共享进程资源,是程序中可并行执行的路径。 最后,文章梳理了用户进程间通信的几种主要方式,如管道、消息队列等,并以管道为例说明了其单向、阻塞的特性。整体而言,文章将抽象的理论知识梳理成了清晰的对比条目,有助于读者快速建立起对这些基础概念的理解框架。

本机暂存
IT 2016-03-18 14:03:25 / 累计浏览 3,625

PHP-FPM中backlog参数变更的一些思考

这篇讲的是PHP-FPM中`backlog`参数两次关键默认值变更背后的技术权衡。作者从2013年PHP 5.5.6将默认值从-1提升至65535说起——当时的初衷是避免因队列满而静默丢弃TCP连接,宁愿报错也不悄悄丢包。但到了2014年,这个值又被调整为511,理由是65535的队列过长,容易导致前端Nginx因等待超时而关闭连接,最终PHP-FPM处理完请求写回时遇到“Broken Pipe”,白白浪费资源。文章指出,调整后的511与Nginx、Redis等常见组件的默认值对齐,更符合实际生产中的连接超时节奏。作者还结合Linux手册中对`listen()`系统调用的说明,梳理了`backlog`队列在不同内核版本中的行为演变,并对比了各方修改补丁的论述,揭示了这个看似简单的参数在高并发场景下对系统吞吐与资源效率的深刻影响。

本机暂存
IT 2016-03-18 14:01:19 / 累计浏览 2,903

Lua C API 的正确用法

这篇讲的是在C/C++宿主程序中嵌入Lua时,最容易被忽视的陷阱:如何正确处理Lua的异常(error)机制。文章指出,很多开发者会忽略C API调用可能抛出异常这一点,导致程序出现未定义行为甚至直接崩溃。 作者从基础讲起,强调所有可能出错的API调用(如lua_tostring)都应被lua_pcall或lua_resume保护起来。文章用一个创建虚拟机的简单代码示例,清晰地展示了即使luaL_openlibs也可能因未捕获异常而带来风险,并推荐将核心逻辑封装为lua_CFunction再通过lua_pcall调用。 进阶部分深入讨论了在C++或多语言环境(如Unity的mono)中,Lua的异常机制(基于longjmp/throw)与宿主语言异常系统(如C++ RAII、.NET CLR)的协同难题。文章警告,直接用宿主语言的异常处理(如C++的try-catch)去捕获Lua API异常会破坏虚拟机状态,因此必须精心设计接口,将Lua与宿主视为两个通过消息通信的独立运行时,而非共享异常上下文。 最后,文章提到了在C扩展中初始化对象字段、管理栈空间等实用技巧,并附上了作者为解决跨语言交互问题而编写的示范代码。全文聚焦于“正确性”而非性能,对于任何需要深度集成Lua的开发者来说,这是一份避开关键坑点的实用指南。

本机暂存
IT 2016-03-16 23:42:05 / 累计浏览 2,362

记一次内存泄露的debug过程

这篇讲的是作者在压测“代码在线运行”工具时,遭遇了高并发下内存飙升且无法回落的问题。从自身代码审查无果,他转向使用Go语言内置的pprof工具进行深度剖析。 通过分析堆内存分配的热点,问题指向了goroutine与ioPipe的交互。作者进一步验证,发现大量goroutine因ioPipe未被正确关闭而持续存在,无法回收,从而造成了内存的持续累积。根源在于异常退出前未主动关闭资源。 定位后,解决方案变得清晰:在程序逻辑中确保主动关闭ioPipe的Reader。修复后,作者使用wrk工具进行了长时间压测,并监控goroutine数量,确认内存占用稳定,问题得以解决。文章结尾点出,这个因粗心引起的小bug,修复却需借助专业工具链,提醒开发者熟悉语言提供的诊断工具至关重要。

本机暂存
IT 2016-03-15 23:37:22 / 累计浏览 3,323

[Java基础教程]第十三章-Java多线程

这篇讲的是Java多线程,作者从“如何让服务器同时处理多个客户端”这个实际问题出发,解释了并发编程的必要性。文章用一个生动的买票比喻,把“串行”、“并行”和“并发”这三个容易混淆的概念讲得明明白白:排队是串行,多窗口同时卖票是并行,而一个售票员同时应付多个询问者则是并发。 更深入一点,作者还探讨了为什么在多核CPU普及的今天,讨论并发时常常仍基于单核模型,点明了任务调度、内存访问等现实约束。核心部分是用Java的Thread类重构服务器代码,展示了如何为每个新连接创建独立线程来处理。文章不仅给出了基础实现,还进一步模拟了10个用户同时连接的场景,并加入了一个将聊天记录缓存后批量写入文件的ChatLogManager类,让示例更贴近生产环境中的考量。 整体而言,这篇教程从概念辨析到代码实践,一步步带着读者理解并发的本质,并快速掌握Java多线程编程的入门要点。

本机暂存
IT 2016-03-15 23:35:34 / 累计浏览 3,341

[Java基础教程]第十二章-Java输入输出流

这篇文章从计算机底层二进制存储与字符编码的原理切入,为理解Java的输入输出流(IO)体系打下了基础。作者指出,由于文件存储和网络传输的本质是字节,而文本处理需要字符,这导致了Java IO设计中经典的“三层包装”结构:先通过`FileOutputStream`等获取字节流,再用`OutputStreamWriter`指定编码(如UTF-8)转换为字符流,最后再套上`BufferedWriter`提升效率。 文章的核心部分通过清晰的代码示例,详细演示了如何操作文件:从创建、检查是否存在、获取路径,到使用缓冲字符流写入和读取内容。特别强调了`flush()`操作的重要性以及流必须正确关闭的原则。此外,内容还扩展到了网络通信场景,利用`ServerSocket`和`Socket`结合输入输出流,演示了简单的服务器与客户端(通过telnet)交互过程。 整体而言,这篇教程不仅展示了“怎么用”,更着重解释了“为什么这么设计”,将看似繁琐的流转换过程与计算机底层的数据表示逻辑联系起来,帮助初学者建立起更清晰的认知框架。文末的小练习也提供了实践巩固的机会。

本机暂存
IT 2016-03-15 23:33:40 / 累计浏览 3,483

[Java基础教程]第十一章-Java类和对象

这篇讲的是Java面向对象编程中类与对象的核心机制。作者从Object类的方法重写说起,强调了继承中`hashCode`与`equals`必须协同重写的规范,并用`toString`的重写来解释如何让对象打印变得友好。 文章的重点在于辨析继承与接口这两种实现代码复用的方式。通过一个“小明的妈妈”既需要继承`Person`类又能烹饪的例子,作者生动地引出了`extends`单继承与`implements`多实现的语法区别与设计思路,这比单纯罗列语法要直观得多。 此外,内容还系统梳理了四种访问修饰符(`private`到`public`)的权限范围,这常常是初学者容易混淆的地方。文章并未止步于此,而是进一步延伸到了反射、抽象类和枚举`enum`这些进阶主题,特别是通过反射操作`Materfamilias`类的代码示例,让“类本身也是对象”这一抽象概念变得可感。 整体而言,这不仅仅是一章语法罗列,而是围绕“类”这个核心,将继承、多态、封装以及动态特性串联起来,构建了一个完整的认知框架。

本机暂存
IT 2016-03-14 23:39:34 / 累计浏览 2,763

【Java并发编程实战】—– AQS(三):阻塞、唤醒:LockSupport

这篇讲的是LockSupport如何作为AQS框架的底层原语,实现线程的阻塞与唤醒。 作者从AQS的获取与释放锁流程切入,指出当线程获取锁失败进入CLH队列后,最终是通过`parkAndCheckInterrupt()`中的`LockSupport.park(this)`挂起的;而在释放锁后,则由`unparkSuccessor()`调用`LockSupport.unpark()`来唤醒队列中的下一个节点。 文章深入解析了LockSupport的核心机制:它基于一个与线程关联的“许可”工作。`park()`会尝试获取许可,若许可不可用则线程休眠;`unpark()`则赋予许可以唤醒目标线程。这种方法比传统的`Thread.suspend()`和`resume()`更安全,因为它不会导致死锁问题。文中也提到,许可不可重入,因此`park()`与`unpark()`通常需要成对使用,并且`unpark()`需要发生在`park()`之后,否则线程不会一直阻塞,还可以通过`parkNanos()`设置超时。 实现上,两者最终都委托给`Unsafe`类的本地方法完成,这展示了Java并发包构建在更底层原语之上的设计思想。

本机暂存
IT 2016-03-14 23:37:10 / 累计浏览 2,843

【Java并发编程实战】—– AQS(二):获取锁、释放锁

这篇讲的是Java并发编程中AQS(抽象队列同步器)如何实现锁的获取与释放,是对AQS底层机制的核心剖析。 作者从锁获取的核心方法 `acquire` 出发,结合源码与流程图,清晰地展示了完整流程:线程首先通过 `tryAcquire` 尝试获取锁;若失败,则通过 `addWaiter` 将自身封装为节点加入CLH队列队尾;随后在 `acquireQueued` 方法中,节点会自旋检查是否为前驱节点且能获取锁,若是则出队,否则通过 `parkAndCheckInterrupt` 挂起等待。文章特别指出了 `tryAcquire` 由具体子类(如ReentrantLock)实现,体现了模板方法模式的设计。 在锁释放部分,流程相对简洁:释放锁时调用 `release` 方法,其核心是 `tryRelease` 修改状态后,通过 `unparkSuccessor` 唤醒队列中下一个有效节点,使等待的线程得以继续尝试获取锁。 通过对 `acquire` 与 `release` 两大核心路径的拆解,这篇文章帮助读者直观理解了AQS如何以CLH队列为骨架,管理锁状态与线程等待,从而掌握ReentrantLock、Semaphore等并发工具背后的统一运行逻辑。

本机暂存
IT 2016-03-14 23:34:34 / 累计浏览 3,164

【Java并发编程实战】—– AQS(一):简介

这篇讲的是Java并发编程中作为JUC同步器基石的AbstractQueuedSynchronizer(AQS)框架的核心原理。作者从ReentrantLock、CountDownLatch等同步器获取锁的不同方法出发,引出对统一框架的需求,从而介绍了AQS的诞生背景。 文章着重解析了AQS实现的三大核心机制。首先,它用一个volatile的int型state变量来表示同步状态,通过CAS操作进行原子更新,这构成了锁获取与释放的状态基础。其次,AQS内部维护了一个变种的CLH双向队列,用于管理等待线程。这个设计的巧妙之处在于,它摒弃了原始CLH的自旋,转而让节点在队列中通过状态位安全地阻塞与唤醒,并能高效处理超时和取消操作。最后,AQS通过队列中Node的SHARED和EXCLUSIVE标记,统一支持了共享锁与互斥锁两种模式。 在阻塞唤醒机制上,AQS没有沿用Java内置锁的wait/notify,而是采用了更底层的LockSupport.park()与unpark()本地方法。整篇文章清晰地勾勒出AQS的骨架,为后续深入分析各类同步器的具体实现打下了扎实的基础。

本机暂存
IT 2016-03-12 22:55:19 / 累计浏览 4,107

可靠 UDP 传输

这篇关于可靠 UDP 传输的文章,作者从对 TCP over UDP 的审慎态度出发,深入探讨了其可能的应用场景与实现路径。 作者首先指出,强行在 UDP 上复制一个完整可靠的传输协议往往得不偿失。其优势通常只在特定条件下显现,例如游戏状态同步等对包序不敏感、或采用一问一答请求模式的场景——这类小数据量交互正是 TCP 建立/拆除连接开销的短板所在。 核心方案上,作者认为一个可行的“可靠 UDP”模块,应专注于解决“如何利用不可靠传输实现可靠协议”这一逻辑问题,而非直接绑定 UDP 收发。他提出的 API 设计,将可靠化逻辑封装为独立层,业务层仅需调用发送与接收接口,而由底层的 `rudp_update` 函数处理数据包组序、重传请求与心跳维持。 作者分享了一个轻量级的 C 语言实现(约 500 行),采用了请求重发机制、16bit 包序号、以及可配置的发送延迟与超时策略。他强调,其实用性在于简化逻辑,并通过超时而非复杂确认来清理过期数据,为特定低延迟需求提供了一个灵活且易于修改的参考起点。

本机暂存
IT 2016-03-09 23:40:33 / 累计浏览 2,223

php word 转 html

这篇讲的是如何通过PHP调用Windows Office的COM接口,将Word文档高质量地转换为HTML。 作者开宗明义,指出要想获得“完美”的转换效果(尤其是保留复杂排版),依赖微软Office自身的渲染引擎是目前最可靠的路径,而开源方案如LibreOffice往往会有瑕疵。文章的核心在于一个具体的实现方案:启用PHP的COM模块,并通过几行简洁的代码调用Word应用程序的`SaveAs`方法。 实现的关键在于环境配置。文章详细指导了如何检查并启用`com_dotnet`模块,澄清了关于内置模块的常见误区,并提供了具体的`php.ini`配置示例。核心转换函数代码清晰,直接展示了打开、转换、退出的完整流程。 作者同时给出了极具实战意义的提醒:转换生成的HTML源码结构会比较冗余;进程会实际调用`winword.exe`;如果遇到转换卡死,尝试重命名文档再执行。这些细节正是实践中容易踩坑的地方,让这份简短的指南更具参考价值。

本机暂存
IT 2016-03-09 23:38:31 / 累计浏览 2,903

php 去掉 头尾   空格 2种方法

这篇讲的是PHP开发中一个常见但容易踩坑的问题:如何高效去除字符串头尾的空白字符,特别是那些由 生成的特殊空格。文章作者从实际编码困境出发,指出直接使用trim()函数无法处理 这种HTML实体编码的空格,因为它并非标准空白符。 文章核心提供了两种解决方案并做了比较。第一种是使用preg_replace正则替换,通过匹配头尾的 和\s来实现通用去除,作者特别推荐了这种方法。第二种是结合html_entity_decode先解码实体,再用trim配合UTF-8空格字符(chr(0xc2).chr(0xa0))进行去除。 作者进一步指出了编码兼容性的关键细节:第二种trim方法在UTF-8下工作良好,但在GBK或GB2312编码中可能引发乱码,同时也会影响json_encode对中文的处理。因此,文章最终给出了明确建议:在现代Web开发中,推荐统一使用UTF-8编码以避免此类陷阱。整个解析从问题现象到原理,再到具体实现与环境考量,非常贴近实际开发场景。

本机暂存
IT 2016-03-09 13:00:37 / 累计浏览 1,062

“NodeJS在大搜车” 之 应用部署篇

作者从团队实际需求出发,分享了NodeJS应用在大搜车的部署实践。文章的核心在于解决不同环境下Node进程的稳定管理与高效部署问题。 针对工具选型,作者解释了为何线上环境选用功能全面、进程管理更稳定的PM2,而在需要频繁变更端口的测试环境,则采用更轻量灵活的Forever。本地开发则配合nodemon实现代码热更新。这套组合策略平衡了稳定性与灵活性。 在环境划分上,文章详细介绍了测试、预发、生产三套环境各自的作用与配置。生产环境依托阿里云ECS、SLB、RDS等一整套云服务,基本免除了底层运维烦恼。部署流程则通过自定义Bash脚本实现:从代码拉取、打包上传,到服务器上的解压、备份、覆盖及PM2重启,整个过程在预发服务器上执行,并通过间隔部署配合负载均衡,确保线上服务零宕机。 文章最后坦诚,这套部署体系仍在不断进化,未来计划引入Docker优化测试环境,并着手应对多环境自动部署、性能优化等新挑战。

本机暂存
IT 2016-03-09 12:59:48 / 累计浏览 2,167

“NodeJS在大搜车” 之 MVC基础结构

这篇讲的是大搜车团队如何在实际项目中落地NodeJS的MVC架构。作者从前端是否适合直接转向NodeJS服务端开发这个话题切入,提出了一个颇具启发性的观点:服务端开发所需的架构、性能、运维等综合能力,可能让一名经验丰富的PHP工程师转型得更顺畅。这其实引出了本文的核心——扎实的服务端思维与架构设计至关重要。 文章随后详细拆解了他们项目中的MVC实践。除了Controller、Model、View这些基本要素,重点阐述了几个关键扩展层。例如,Service层被定义为整个架构中最重的一环,它封装了跨数据库、跨缓存的复杂数据操作,为Controller提供简洁的“服务”。Route层则通过自研的rainbow库,实现了基于文件目录的自动化路由管理与API文档生成,便于维护成百上千个接口。此外,团队还在ORM之上封装了SuperModel,旨在统一MySQL和MongoDB的操作接口,并提供类似Mongoose的链式调用体验。 这些细节不仅展示了一套清晰、可维护的NodeJS项目结构,也体现了团队在工程化方面的深度思考。对于正探索NodeJS服务端开发的前端工程师,或是寻求不同技术视角的后端开发者,文中关于架构分层与工具封装的具体实践,提供了扎实的参考案例。

本机暂存
IT 2016-03-09 00:16:23 / 累计浏览 2,647

系统设计典型问题的思考

系统设计面试题没有标准答案,但思考过程有章可循。这篇文章就从“问题该怎么想”入手,梳理了一套从外到内的解题框架。作者的核心观点是:不要急于画架构图,而是反复沟通澄清需求——优先搞定2-3个核心用例,明确用户与数据规模,并识别请求模型(比如读远多于写)。 在此基础上,先定义核心模型与API,再划分系统层次与组件,最后逐层细化。在细化过程中,文章重点讨论了存储选型(关系型分库分表 vs. NoSQL的CAP权衡)、集群策略、消息队列与缓存设计这几个关键环节,并强调所有优化都应建立在明确的系统瓶颈识别之上。 文章后半部分将这套思路应用到了三个经典案例中:设计微博信息流时,需权衡消息推送的push模型与拉取模型,并设计分级的缓存;设计短网址系统时,核心挑战是如何在分布式环境下高效生成全局唯一ID;而设计实时聊天系统,则需解决服务端到客户端的消息推送问题,比如采用Comet技术维持长连接。 最终,文章落脚于工程师对这类开放性问题的反复琢磨与沉淀。这些思考虽不像算法题有唯一正解,却能在实际工程中建立起至关重要的宏观设计直觉。

本机暂存
IT 2016-03-07 23:37:40 / 累计浏览 3,021

[JavaWeb教程]第三章-Servlet开发

这篇教程从 form 表单的 action 属性切入,讲解了服务器端如何用 Java Servlet 处理提交的数据。作者首先演示了在 Eclipse 中配置 Tomcat 服务器并运行 Web 工程的基本流程。 核心部分是通过两个实例介绍 Servlet 开发:第一个是创建 HelloWorldServlet,利用 resp.getWriter() 向客户端输出当前时间,展示了 doGet 和 doPost 方法的对应关系以及 web.xml 中的 URL 映射配置。第二个实例更进一步,构建了一个 SubmitStudentInfoServlet 来处理学生信息表单提交。这里详细演示了如何通过 req.getParameter() 获取表单参数、用 req.getParameterValues() 获取多选框数组值,并将数据封装到 StudentVo 对象中。同时,文章指出了解决中文乱码的两个关键点:req.setCharacterEncoding("UTF-8") 和 resp.setContentType("text/html; charset=UTF-8")。 最后,通过将学生信息列表存入 Session 来实现跨请求的数据共享,引出了 Session 的基本用法。整个过程清晰地完成了从静态页面访问到动态数据处理与存储的过渡。

本机暂存