Archive for 算法-编程

折腾了我一个周五下午加一个周末的问题。 写的代码上线两个周了,之前只测试了同步跟列表的功能,展示没问题,上周测试了一下更新问题,开始删除没刷新,我还以为是前端老版本的编译有问题,发现接口确实调用了就没管了。 直到测试一个更新功能,发现接口调用成功了,但是数据没更新。还返回了http 200。我就看了一下代码,返回200不可能没执行到更新保存的代码。 发现只有在提交commit之前有查询操作,就会导致表更新失败。所以应该是个session问题。 试了我本机环境没问题,然后我就想到装的python包的版本问题,踩什么bug了吧。然后我对比了一下版本,我管理都是只写主要包的版本,依赖包的版本没有写,所以依赖包有的版本会高一点。 我选了几个可能的包,更换版本,发现不是这个原因。周末想想,如果是这个包的问题,应该早就发现了,会打补丁。 然后我就想到数据库的问题,我用的mariadb,测试系统是centos,然后我看了一下有新版本的mariadb,更新了一下,这里现在想想数据库的问题的概率也十分小了,而且是这么明显的bug。 mariadb的源巨慢无比,我又在尝试更新mysqlclient包,因为我记得安装的时候mariadb-devel不能用,网上说的用mysql-devel。我又从新试了一次,各种方法,发现不成功。 到下午快下班,安装完mariadb,又试了一遍mariadb-devel也不行。觉得还是用mysql,又安装称mysql。 但是发现数据库数据文件不兼容,升级的命令好像也不太行,我备份的导出数据库数据,导入又有外键关联的限制,懒得搞数据了。下班了。 周末有时间的时候就想了想,我之前虽然抓包测试环境mysql,发现没问题,但是没对比本机的。如果周一mysql不行,试试本机环境连线上数据库,排除一下是包的问题还是数据库问题。感觉周五的时候应该先排除问题再更新数据库才对。 周一来,同步了一下数据,更新了一下发现mysql还是不行。然后重新编译mysqlclient,测试发现通过了。 最后还是mysqlclient编译依赖有问题,使用mysql-devel编译,连接mariadb有问题。好像还不是所有的版本有这个问题。

Continue

在Mac上写cocos2d游戏代码的时候,写了个label,调了几次没居中,突然想到是不是mac屏幕的原因。然后鼠标滑了一下看了一下还正好差一倍居中。 然后网上搜了一下,在github上找到了解决方案。 HiDPI屏幕都会有这种问题,有两种patch,一个改pyglet的,一个改cocos2d的,然后我手动改了一下cocos2d的代码解决了。 话说cocos2d不更新了,维护也不知道会不会改。突然感觉选择cocos2d有点虚了。 issues:https://github.com/los-cocos/cocos/issues/303

Continue

scheduler.New() 定义在kubernetes/pkg/scheduler/scheduler.go 返回一个Scheduler type Scheduler struct { config *factory.Config } 首先先用New传过来的参数替换schedulerOptions默认配置 type schedulerOptions struct { // 调度器的名字,pod创建的时候可以根据这个名字选择使用哪个调度器,默认值"default-scheduler" schedulerName string // hardPodAffinitySymmetricWeight int32 disablePreemption bool percentageOfNodesToScore int32 bindTimeoutSeconds int64 } 然后调用factory.NewConfigFactory创建一个factory.Configurator,需要传入一个factory.ConfigFactoryArgs 还有个configFactory,这几个差不都都是参数传进来的几个值,具体还不知道干什么的。先看几个参数吧。 cc.InformerFactory.Core().V1().Nodes() 对应类型 nodeInformer coreinformers.NodeInformer 其中cc.InformerFactory在kubernetes/cmd/kube-scheduler/app/options/options.go中初始化: c.InformerFactory = informers.NewSharedInformerFactory(client, 0), 创建一个新的factory, 结构体主要为cc.client和informers map[reflect.Type]cache.SharedIndexInformer. Core()是New了一个新的group, 感觉还是啥没有,还是factory那几个值。 V1()是New了一个v1,返回一个version,还是哪几个值. Nodes() 传的还是那几个值,返回一个nodeInformer. 最重要的就是保留了factory,然后能够调用nodeInformer接口下的几个方法。后边用的吧。 nodeLister: args.NodeInformer.Lister(), 在NewConfigFactory()里创建configFactory的时候,调用了.Lister()方法。 func (f *nodeInformer) defaultInformer(client kubernetes.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { return NewFilteredNodeInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } func (f *nodeInformer) Informer() cache.SharedIndexInformer { return f.factory.InformerFor(&corev1.Node{}, f.defaultInformer) } func (f *nodeInformer) Lister() v1.NodeLister { return v1.NewNodeLister(f.Informer().GetIndexer()) } 调用太多,看代码最烦的就是调来调去,然后还有各种新数据定义,翻来翻去然后回不来了。。。 corev1.Node 这个里边应该都是返回的数据格式类型定义, 没细看。 看InformerFor里边defaultInformer是个真正调用的函数, InformerFor就是用来包装了一下 v1.NewNodeLister 看起来就是定义的restful api的请求。 看的有点细,其实这部分都是client-go里的,是封装的api请求,应该是分不同资源请求不同的interface cc.PodInformer 看这个应该比较重要 c.PodInformer = factory.NewPodInformer(client, 0) 用来返回non-terminal状态的pod configFactory其他初始化参数不看了,后边用到再看。

Continue

代码读的master上最新的 1641ff411. 命令入口文件在kubernetes/cmd/kube-scheduler/scheduler.go 主要就两句,设置启动参数。其他的设置命令行参数和日志打印。具体不看了 command := app.NewSchedulerCommand() command.Execute(); NewSchedulerCommand 在文件kubernetes/cmd/kube-scheduler/app/server.go 很多代码都是设置scheduler命令参数的,也是用的cobra这个库,跟docker是一样的。 opts, err := options.NewOptions() 读取默认配置 runCommand()执行命令 runCommand 比较有用的几行: c, err := opts.Config() 这里边东西比较多,比如初始化kube-client与api交互,eventclient,选举相关的配置。没仔细看,代码在kubernetes/cmd/kube-scheduler/app/options/options.go cc := c.Complete() stopCh := make(chan struct{}) 这个channel传入了Run函数,Run只会在报异常或者这个channel关闭的时候返回 是用来配合context提示go协程退出的 函数最后调用return Run(cc, stopCh) Run() 生成一个新的scheduler, sched, err := scheduler.New() 后边启动了事件传输,监控选举的,还有监控和指标收集的,这个先不看了。 再后边就是stopChannel配合context管理协程,还有一个leader选举的功能,是通过cc.LeaderElection参数来控制的. leader选举功能开启是为了高可用的时候使用 无论是否leader选举,最后都进入sched.Run()

Continue

开始只在在线Clojure网站上进行一些测试,后来感觉不行,操作速度都太慢。决定还是安装个环境。 去clojure官网看了一下也不麻烦。 去官网下载个zip包,解压到目录。 进入目录创建一个bin目录,新建一个clojure文件.添加几行代码

if [ x$* != x ]
then
    java -cp /Users/a0x55aa/tools/clojure-1.8.0/clojure-1.8.0.jar clojure.main "$*"
else
    java -cp /Users/a0x55aa/tools/clojure-1.8.0/clojure-1.8.0.jar clojure.main
fi
然后加入环境变量path种就行了,方便到处使用.

Continue

接上次看的往下看 cli.Pidfile 创建pid文件"/var/run/docker.pid" serverConfig := &apiserver.Config{ Logging: true, SocketGroup: cli.Config.SocketGroup, Version: dockerversion.Version, EnableCors: cli.Config.EnableCors, CorsHeaders: cli.Config.CorsHeaders, } 这几个默认值为 SocketGroup 为docker, EnableCors false, CorsHeaders 这个看EnableCors api := apiserver.New(serverConfig) 这个就是实例化了个Server,cfg为上边的serverConfig cli.api = api 返回一个, 初始化cfg为serverConfig,看起来所有请求的路由都在这里了 type Server struct { cfg *Config servers []*HTTPServer routers []router.Router routerSwapper *routerSwapper middlewares []middleware.Middleware } cli.Config.Hosts没有的话创建一个长度1的数组,有的话进入循环进行监听 listeners.Init wrapListeners(proto, ls) allocateDaemonPort(addr) api.Accept(addr, ls...) 监听地址, 根据hosts初始化的多个httpserver。 migrateKey 这个看起来好像是为了升级还是啥的。把之前配置文件目录的key.json移动到/etc/docker/下边,并把旧的删除 具体不知道为啥这么做 registryService := registry.NewService(cli.Config.ServiceOptions) 返回一个DefaultService newServiceConfig(options)里边,配置镜像仓库地址 containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...) 重头戏来了, getLibcontainerdRoot /var/run/docker/libcontainerd getPlatformRemoteOptions 里边好像就是设置了一些参数 libcontainerd.WithRemoteAddr 这个是设置了ContainerdAddr配置,containerd参数走这个,调用rpcAddr(addr)。 没设置 走libcontainerd.WithStartDaemon(true), 然后调用startDaemon(start) type RemoteOption interface { Apply(Remote) error } New里边先初始化remote eventTsPath getLibcontainerdRoot/event.ts option.Apply(r)这里应该就是真正设置参数到初始化的remote了。不知道为啥这么写 创建getLibcontainerdRoot也就是stateDir 700权限 如果没有rpcAddr 设置 stateDir/docker-containerd.sock startDaemon默认是true的所以肯定执行r.runContainerdDaemon() runContainerdDaemon里 pidFilename stateDir/docker-containerd.pid,下面判断pid文件是否正确,如果进程在运行赋值r.daemonPid,就返回结束了 如果没运行,先设置运行参数,然后执行命令启动docker-containerd setOOMScore oomScoreAdjPath := fmt.Sprintf("/proc/%d/oom_score_adj", pid) 好像就把参数那个数写文件里了,默认-500 写进程号 建了一个r.daemonWaitCh channel,container进程结束后,关闭这个channel。赋值r.daemonPid 总的来说就是启动container进程 使用grpc创建rpc连接 r.rpcConn = conn r.apiClient = containerd.NewAPIClient(conn) 返回 type aPIClient struct { cc *grpc.ClientConn } 就保存了rpc连接 getLastEventTimestamp就是看 stateDir/event.ts 文件,没数据就返回当前时间,如果有,里边保存的是序列号化的,反序列化一下返回 tsp, err := ptypes.TimestampProto(t)转了一下时间 r.restoreFromTimestamp = tsp go r.handleConnectionChange() 大体看了一下,就是开个协程进行rpc连接探测, 连续失败到三次,如果进程存在kill.<-r.daemonWaitCh,然后r.runContainerdDaemon()启动 r.startEventsMonitor(); 监控请求事件 发送了一个"/types.API/Events"请求, 然后开个协程有事件就处理事件 处理完更新事件时间 处理事件这里先不仔细看了。感觉看下去脑子回不来了. libcontainerd/remote_unix.go startEventsMonitor()标记一下,以后看 主要就是server的启动和containerd的启动

Continue

在用Grafana绘图,后端时序数据库用的InfluxDB。在进行绘图的时候用到取top n的数据。有一个top函数,但是里边不能有函数计算了。我是先group by tag 然后求和的。 查了好多看大部分都是top,开始搜的时候还搜的Grafana,后来想到这是influxdb的查询。找到官方文档,有一个limit和slimit. 这里是有区别的,slimit是相当于之前想法的limit,只去分组的top n。而limit是取每个分组下的top n。 但是order by 也不能 用聚合的字段来,还不知道排序怎么搞。 感觉influxdb还是很新的东西,好多东西太难搞了。也许是不了解。。

Continue

dockerd/daemon.go start() loadDaemonCliConfig(opts) 里 daemon.MergeDaemonConfigurations(config, flags, opts.configFile)将配置文件设置的参数和命令参数合并。 过了好多代码,感觉都是配置啥的就不看具体干啥了。 setDefaultUmask() 设置umask 0022,把goup和other的写权限干掉了。 daemon.CreateDaemonRoot(cli.Config) 先获取docker daemon根目录 默认/var/lib/docker setupRemappedRoot 根据配置映射容器用户和组的 这个参数默认是空的,也就是不映射 如果设置了这个参数 parseRemappedRoot就是解析参数的,有四种格式 username username:groupname uid uid:gid 然后解析/etc/passwd和/etc/group看看参数对不对。其中解析是否是defaultIDSpecifier 就是个'default',然后映射的host用户为dockremap。 也就是RemappedRoot这个参数传个default就行了,使用docker创建好的账户 idtools.AddNamespaceRangesUser(defaultRemappedID)就是判断是否有default的用户,没有就创建 uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname) 这个从/etc/subuid和/etc/subgid获取,根据username判断是第一个 startid是uid是第二个,length还不知道干啥的第三个 ALL不知道干啥的 返回了所有的行,就是所有用户 createIDMap返回IDMap,ContainerID开始是0然后后边的都加length。hostid,就是第二个startid, size 是length. 然后setupRemappedRoot就完了,返回了俩idmap idtools.GetRootUIDGID(uidMaps, gidMaps) 里边就一个ToHost(0, uidMap)这个单个的时候就是返回之前的hostid就是startid对应host上的uid, 之前的ALL的话,好像还是只返回了第一个的hostid setupDaemonRoot(config, realRoot, rootUID, rootGID) 修改docker的根目录权限为711 RemappedRoot 为空就直接返回了。不空的话创建一个config.Root目录在docker根目录下。目录名为rootUID.rootGID,权限700.然后好像判断目录下的rootUID都有访问权限,这个应该是为已经有这个目录的判断 然后CreateDaemonRoot完结撒花,设置了一下目录,用户的映射

Continue

东西太多了,大体过了一下,感觉会少了很多东西。有一些三方库,也没去看库怎么用。就看函数名大体猜一下功能。 完全就是从main函数一步一步往下看,这种看法就是缺少一个大局的认识,开始会有很多不理解的做法。 cmd/dockerd/docker.go reexec.Init() 必须要提前调用的方法,用来初始化。如果初始化过了就会返回true,main函数也退出了。 判断是否调用过,就是看看registeredInitializers里有没有os.Args[0]的key。 如果有调用value,没有直接返回false,接着走main函数 具体value是个啥函数,慢慢看.里边还有个Register(函数用来往registeredInitializers里添加值。 cmd := newDaemonCommand() cmd.Execute() 这里一看就跟client的结构一样,突然就感觉好看多了。。 daemonOptions daemon.NewConfig() 返回一个Config,所有配置应该都在这,太多了,也看不到啥意思先过了。 cliflags.NewCommonOptions() 对应client TLS相关的东西,这里一样。 其他都一样。 opts.daemonConfig.InstallFlags(flags) 定义都有哪些参数,太多不看,在help命令里应该是有的。配置了对应的Config里边的字段。 看看return runDaemon(opts) daemonCli := NewDaemonCli() 返回一个DaemonCli type DaemonCli struct { *daemon.Config configFile *string flags *pflag.FlagSet api *apiserver.Server d *daemon.Daemon authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins } err = daemonCli.start(opts) 这里流程就完了。详细看看start是干嘛的

Continue

哈哈,找了个跨平台的游戏框架玩玩。 在mac平台上安装试了一下。下载需要注册,第一次启动也需要填注册的账号,所以还是注册了。 安装完启动输入账号,完成后创建一个项目。直接点击是使用的手机模拟器启动的。 官方给出的命令启动方法,-no-console YES是不显示debug信息。 "/Applications/CoronaSDK/Corona Simulator.app/Contents/MacOS/Corona Simulator" ~/CoronaApps/MyApp 作为桌面软件启动的时候,还是要先开模拟器编译通过后,菜单选择File → Build → macOS.填填选项。选择 Open application编译完启动,或者编译完手动启动也行。

Continue