1.GOMAXPROCS 与容器的码分相处之道
2.Golang微服务框架Kratos实现分布式计划任务队列Asynq
3.scheduler startåè½åè°ç¨schedulejobå
4.go的actor框架protoactor的底层是怎么实现actor的?
5.Golang 里一个有趣的小细节
GOMAXPROCS 与容器的相处之道
了解Golang中的GOMAXPROCS环境变量及其与容器虚拟化技术如Docker和Kubernetes的相互作用,是码分深入探讨并发处理和资源管理的关键。GOMAXPROCS用于调整Runtime Scheduler中处理器(P)的码分数量,直接影响Golang Runtime的码分并发性能。默认值为CPU核心数,码分而容器技术通过cgroup等隔离资源,码分九方智投资金流入指标源码限制CPU使用。码分本文通过实验探索容器技术对GOMAXPROCS的码分影响及其对并发表现的可能影响。
在并发处理中,码分Goroutines是码分Golang的基石,go-scheduler通过处理器(P)、码分机器(M)和goroutine(G)三个抽象来实现并发。码分P类似于CPU核心,码分控制并发M的码分数量,M与P绑定执行。码分M数量动态增长,P数量保持默认CPU核心数,由用户通过GOMAXPROCS调整。python 源码学习联盟
本文采用实验方法,针对Docker和Kubernetes进行验证,探索它们对CPU资源的隔离限制是否影响GOMAXPROCS设定,进而影响并发表现。实验环境包括XPS-笔记本的四核CPU,使用自定义Docker镜像进行测试。
在Kubernetes和Docker环境中,我们观察到,尽管对CPU资源进行了限制,GOMAXPROCS设定仍不受影响。实验结果表明,Kubernetes和Docker的CPU限制策略并不改变Runtime对CPU数量的判定。
性能测试使用上游社区提供的CPU密集型Benchmark concprime,对不同限制手段和GOMAXPROCS取值进行性能分析。结果揭示,尽管限制手段影响了CPU使用,但GOMAXPROCS设定对性能的access 系统 源码下载直接影响不大。
分析指出,Kubernetes和Docker的CFS Bandwidth Control策略限制了CPU使用,但并未影响Runtime对CPU数量的判定。Go程序在Kubernetes中始终认为可以使用所有CPU资源,导致P数量与CPU核心数相同。手动设置GOMAXPROCS后,性能显著提升。
目前,Golang官方尚无有效解决方案避免这一问题,而Uber提出的Workaround(uber-go/automaxprocs)提供了一种修改GOMAXPROCS的实现,根据cgroup或runtime选择合适的取值。这一方法值得尝试。
综上所述,容器技术如Docker和Kubernetes对CPU资源的限制对GOMAXPROCS设定影响不大,但手动调整GOMAXPROCS可以优化并发性能。了解这些相互作用有助于更高效地利用资源和提升并发处理能力。
Golang微服务框架Kratos实现分布式计划任务队列Asynq
任务队列(Task Queue)是商品展示平台源码一种在跨线程或跨计算机环境中分配任务的机制,其核心是生产者-消费者模型,其中生产者将任务发送至队列,而消费者负责处理这些任务。任务队列的输入是任务(Task),即工作单元,由专门的工作进程持续监视队列以查找新任务。 在Golang语言中,有如Asynq和Machinery等类似于Celery的分布式任务队列。然而,尽管Celery是一个知名的Python分布式任务队列,其他语言环境中的任务队列,如Asynq,也遵循类似的原理和架构。 Asynq是一个使用Go语言实现的分布式任务队列和异步处理库,其设计用于与Redis集成,提供轻量级、易于使用的mac idea 查看源码API,并支持高扩展性和自定义性。此库由Ken Hibino开发,目前在Google工作。 Asynq由几个关键组件构成,通过使用Asynq,开发人员可以轻松实现异步任务处理,并获得高效率、高可扩展性和高自定义性的解决方案。此库提供命令行工具(CLI)和基于Web的界面(Web UI)以进行监控和管理。 Asynq的核心特点包括: 可视化监控:通过CLI和Web UI进行任务和队列的实时监控。 Web UI:可使用Docker轻松部署。 在微服务框架Kratos中,分布式任务队列可以通过transport.Server的形式集成。目前,Go语言中有两个分布式任务队列可用,且它们已被支持。为了在Kratos中实现此功能,需要安装Redis服务器并通过Docker方式部署。接下来,需要在项目中添加Asynq的依赖库,并创建Server实例。注册任务回调以订阅特定任务类型,最后通过NewTask或NewPeriodicTask创建新任务。 创建任务时,可以使用NewTask或NewPeriodicTask方法,分别对应Asynq.Client和Asynq.Scheduler。普通任务和延迟任务(Delay Task)各有其特点,普通任务可能立即执行(无需排队),而延迟任务则允许在特定时间执行。周期性任务(Periodic Task)通过Crontab实现定时执行,但调度器必须持续运行以确保任务调度。 示例代码可在单元测试文件中找到,以帮助理解和实现Asynq在Kratos框架中的集成。scheduler startåè½åè°ç¨schedulejobå
ã第ä¸æ¥ï¼å¼å
ããè¦ä½¿ç¨Quartzï¼å¿ é¡»è¦å¼å ¥ä»¥ä¸è¿å 个å ï¼
ãã1ãlog4j-1.2.
ãã2ãquartz-2.1.7
ãã3ãslf4j-api-1.6.1.jar
ãã4ãslf4j-log4j-1.6.1.jar
ããè¿äºå é½å¨ä¸è½½çQuartzå éé¢å å«çï¼å æ¤æ²¡æå¿ è¦ä¸ºå¯»æ¾è¿å 个å è头ç¼ã
ãã第äºæ¥ï¼å建è¦è¢«å®æ§è¡çä»»å¡ç±»
ããè¿ä¸æ¥ä¹å¾ç®åï¼åªéè¦å建ä¸ä¸ªå®ç°äºorg.quartz.Jobæ¥å£çç±»ï¼å¹¶å®ç°è¿ä¸ªæ¥å£çå¯ä¸ä¸ä¸ªæ¹æ³execute(JobExecutionContext arg0) throws JobExecutionExceptionå³å¯ãå¦ï¼
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class myJob implements Job {
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println(sdf.format(new Date()));
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class myJob implements Job {
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println(sdf.format(new Date()));
}
}
ããè¿ä¸ªä¾åå¾ç®åï¼å°±ä¸ç¨è§£è¯´äºã
ãã第ä¸æ¥ï¼å建任å¡è°åº¦ï¼å¹¶æ§è¡
ããè¿ä¸æ¥åºè¯¥ç®æ¯æé¾çä¸æ¥çï¼ä½å ¶å®æ¯é常ç®åçï¼ç´æ¥ä¸ä»£ç
ãã
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
public class Test {
public void go() throws Exception {
// é¦å ï¼å¿ éè¦åå¾ä¸ä¸ªSchedulerçå¼ç¨
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
//jobså¯ä»¥å¨scheduledçsched.start()æ¹æ³å被è°ç¨
//job 1å°æ¯éç§æ§è¡ä¸æ¬¡
JobDetail job = newJob(myJob.class).withIdentity("job1", "group1").build();
CronTrigger trigger = newTrigger().withIdentity("trigger1", "group1").withSchedule(cronSchedule("0/ * * * * ?")).build();
Date ft = sched.scheduleJob(job, trigger);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println(job.getKey() + " 已被å®ææ§è¡äº: " + sdf.format(ft) + "ï¼å¹¶ä¸ä»¥å¦ä¸éå¤è§åéå¤æ§è¡: " + trigger.getCronExpression());
// job 2å°æ¯2åéæ§è¡ä¸æ¬¡ï¼å¨è¯¥åéç第ç§)
job = newJob(myJob.class).withIdentity("job2", "group1").build();
trigger = newTrigger().withIdentity("trigger2", "group1").withSchedule(cronSchedule(" 0/2 * * * ?")).build();
ft = sched.scheduleJob(job, trigger);
System.out.println(job.getKey() + " 已被å®ææ§è¡äº: " + sdf.format(ft) + "ï¼å¹¶ä¸ä»¥å¦ä¸éå¤è§åéå¤æ§è¡: "+ trigger.getCronExpression());
// å¼å§æ§è¡ï¼start()æ¹æ³è¢«è°ç¨åï¼è®¡æ¶å¨å°±å¼å§å·¥ä½ï¼è®¡æ¶è°åº¦ä¸å 许æ¾å ¥N个Job
sched.start();
try {
//主线ç¨çå¾ ä¸åé
Thread.sleep(L * L);
} catch (Exception e) { }
//å ³éå®æ¶è°åº¦ï¼å®æ¶å¨ä¸åå·¥ä½
sched.shutdown(true);
}
public static void main(String[] args) throws Exception {
Test test = new Test();
test.go();
}
}
go的actor框架protoactor的底层是怎么实现actor的?
探讨Go的actor框架Protoactor底层实现细节,核心在于利用类型为interface的channel进行switch case数据转换,进而调用相应方法。此过程中,依旧依托Go的scheduler来驱动任务执行。Actor模型本质上是CSP的变体,通过这种方式,Protoactor实现高效灵活的并发处理。通过interface的channel,系统能够实现多线程间的无缝数据交互,确保在复杂并发场景下,任务执行的高效与协调。Go的scheduler在此背景下,起到了关键作用,它负责调度任务,确保每一个actor能够有序、高效地执行其负责的任务。通过这种设计,Protoactor能够提供强大而简洁的并发编程模型,适用于多种场景下的并行计算需求。
Golang 里一个有趣的小细节
本文尝试解释在Golang中一个引起卡死的问题。
首先,Golang的byte被alias到uint8上,所以循环条件总是成立,i++导致i溢出,使得循环无法退出。
其次,Goroutine调度是复杂的,基于GPM模型,一个P上挂多个G。当一个G执行结束,P选择下一个G执行。如果一个G执行太久,scheduler会调度后面的G执行。但循环G无法主动让出执行权,即使执行时间长,scheduler已经打上preempt标记。
回到问题,main函数中启动的Goroutine是一个死循环,没有阻塞条件,无法主动让出执行权,即使scheduler已标记。一旦G拿到执行权,其后的G无法再获得P的执行权。为了让G获取执行权,main函数主动执行runtime.Gosched()让出执行权。
P的数量由GOMAXPROCS设置,通常为CPU数量。
问题出现在GC阶段。Golang的GC基于标记-清除,标记阶段需要STW,停止所有正在运行的Goroutine。死循环Goroutine无法停止,main Goroutine阻塞在GC STW这里,等待所有Goroutine停止执行。main Goroutine等待永远不会停止的G,程序因此卡死。
同样,设置GOMAXPROCS时也需STW,使得代码卡死。
这几行代码隐藏着复杂逻辑,揭示了Golang中Goroutine、调度和GC的巧妙之处。
2024-12-24 10:57
2024-12-24 10:54
2024-12-24 10:38
2024-12-24 10:13
2024-12-24 09:51
2024-12-24 09:34
2024-12-24 09:01
2024-12-24 08:33