[{"categories":["python"],"content":"Python 日志模块logging分析及使用-1 掘金原文\n开发部署项目时，不可能将所有的信息都输出到控制台中，因此我们将这些信息记录到日志文件中，不仅方便查看程序运行的情况，也可以在项目出现故障时根据该运行时产生的日志快速定位问题。\nPython中提供了日志模块logging，可以方便的记录日志信息。\n本文主要分析了logging模块的相关知识及具体使用。\n当前介绍该日志模块的文章有很多，之所以还要进行记录，一方面是因为没有看到系统的介绍，二是通过梳理此部分内容，做到不止会用，还明白内部函数调用。\n一、日志级别 Python标准库logging用做记录日志，默认分为五种日志级别：\nDEBUG(10)：详细信息，调试问题时所需 INFO(20)：证明事情可按预期工作 WARNING(30)：有意外，将来可能发生问题，但依然可用 ERROR(40)：严重问题，程序不能执行一些功能 CRITICAL(50)：严重错误 我们自定义日志级别时注意不要和默认的日志级别数值型相同（这是什么意思呢？），logging执行时输出大于等于设置的值日级别的日志信息，如设置级别为INFO，则INFO, WARNING, ERROR, CRITICAL级别的日志都会输出。\n5种等级分别对应5种日志打印方法：debug, info, warning, error, critical。\n默认的是WARNING，即当等级大于等于WARNING才被跟踪。\n二、logging流程 官方的logging模块工作流程图如下：\nlogging流程图\nlogging流程图\n从上图中可以看到这几种python类型，Logger, LogRecord, Filter, Handler, Formatter。\n类型说明\nLogger：日志，暴露函数给应用程序，基于日志记录器和过滤器级别决定哪些日志有效。 LogRecord：日志记录器，将日志传到相应的处理器处理。 Handler：处理器，将（日志记录器产生的）日志记录发送至合适的目的地。 Filter：过滤器，提供了更好的粒度控制，它可以决定输出哪些日志记录。 Formatter：格式化器，指明了最终输出中日志记录的布局。 基本流程:\n判断Logger对象对于设置的级别是否可用，如果可用，则往下执行，否则，流程结束。 创建LogRecord对象，如果注册到Logger对象中的Filter对象过滤后返回False， 则不记录日志，流程结束，否则，向下执行。 LogRecord对象将Handler对象传入当前的Logger对象，（图中子流程）如果Handler对象的日志级别大于设置的日记级别，再判断注册到Handler对象中的Filter对象过滤后是否返回True而放行输出日志信息，否则不放行，流程结束。 如果传入的Handler大于Logger中设置的级别，也即Handler有效，则往下执行，否则，流程结束。 判断这个Logger对象是否还有父Logger对象，如果没有（代表当前Logger对象是最顶层的Logger对象root Logger），流程结束。否则将Logger对象设置为它的父Logger对象，重复上面3,4两步，输出父类Logger对象中的日志输出，直到是root Logger对象。 三、几个重要的类 前面讲logging流程的时候，提到几个概念：Logger，Handler，Filter，Formatter。\n3.1 Logger类 Logger对象有3个任务要做：\n向应用程序代码暴露info, debug等方法，使应用程序可以在运行时记录日志消息 基于日志级别（默认的过滤设施）或Filter对象来决定要对哪些日志进行后续处理 将日志消息传送给所有感兴趣的日志handlers 一个系统只有一个根Logger对象，并且该对象不能被直接实例化。\n通过调用logging.getLogger(name)函数创建Logger类对象。\nLogger对象最常用的方法有两类：（1）配置方法，（2）消息发送方法\nLogger对象可以设置多个Handler对象和Filter对象，Handler对象可以设置Formatter对象。\nFormatter对象用来设置消息的具体输出格式。\n3.1.1 最常用的配置方法 方法 描述 Logger.setLevel 设置日志器将会处理的日志消息的最低严重级别 Logger.addHandler() 为该Logger对象添加一个handler对象 Logger.removeHandler 为该Logger对象移除一个handler对象 Logger.addFilter()和Logger.removeFilter() 为该Logger对象添加 和 移除一个filter对象 logger对象配置完成后，可使用下面的方法来创建日志记录：\nLogger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical()：创建一个与他们的方法名对应等级的日志记录 Logger.exception()：创建一个类似于Logger.error()的日志消息 Logger.log()：需要获取一个明确的日志level参数来创建一个日志记录 1. 获取Logger对象\n通常是通过--logging.getLogger()的方法。该方法有一个可选参数name,该参数表示将要返回的日志器的名称标识，如果不提供该参数，则其值为root。\n若以相同的name参数值多次调用getLogger()方法，将会返回指向同一个Logger对象的引用。\n2. logger的层级结构与有效等级\nlogger的名称是一个以 '.' 分割的层级结构，每个'.'后面的logger都是'.'前面的logger的children，例如，有一个名称为 foo 的logger，其它名称分别为foo.bar, foo.bar.baz和 foo.bam都是 foo 的后代。\nlogger有一个\u0026quot;有效等级（effective level）\u0026ldquo;的概念。如果一个logger上没有被明确设置一个level，那么该logger就是使用它parent的level;如果它的parent也没有明确设置level则继续向上查找parent的parent的有效level，依次类推，直到找到个一个明确设置了level的祖先为止。需要说明的是，root logger总是会有一个明确的level设置（默认为 WARNING）。当决定是否去处理一个已发生的事件时，logger的有效等级将会被用来决定是否将该事件传递给该logger的handlers进行处理。\nchild loggers在完成对日志消息的处理后，默认会将日志消息传递给与它们的祖先loggers相关的handlers。因此，我们不必为一个应用程序中所使用的所有loggers定义和配置handlers，只需要为一个顶层的logger配置handlers，然后按照需要创建child loggers就可足够了。我们也可以通过将一个logger的propagate属性设置为False来关闭这种传递机制。\n3.1.2 创建Logger对象logging.getLogger([name]) 日志记录的工作主要由Logger对象来完成。前面讲到一个系统只有一个根Logger对象，并且该对象不能被直接实例化。\n需要通过logging.getLogger([name])来获取Logger对象。\n在调用getLogger时要提供Logger的名称（多次使用相同名称来调用getLogger，返回的是同一个对象的引用。），Logger实例之间有层次关系，这些关系通过Logger名称来体现。\npy\n复制代码\nPython 1 2 3 p = logging.getLogger(\u0026#34;root\u0026#34;) c1 = logging.getLogger(\u0026#34;root.c1\u0026#34;) c2 = logging.getLogger(\u0026#34;root.c2\u0026#34;) 例子中，p是父logger, c1,c2分别是p的子logger。c1, c2将继承p的设置。如果省略了name参数, getLogger将返回日志对象层次关系中的根Logger。\n每个Logger对象都可以设置一个名字，如果设置logger = logging.getLogger(__name__)，__name__是Python中的一个特殊内置变量，他代表当前模块的名称（默认为__main__）。\n该函数内部根据是否指定名称返回对应的Logger对象。\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 root = RootLogger(WARNING) Logger.root = root Logger.manager = Manager(Logger.root) def getLogger(name=None): \u0026#34;\u0026#34;\u0026#34; Return a logger with the specified name, creating it if necessary. If no name is specified, return the root logger. \u0026#34;\u0026#34;\u0026#34; if name: return Logger.manager.getLogger(name) else: return root 示例：\n1import logging 2logger = logging.getLogger('test') 3logger.warning('this is a warning info') 4# 输出 5WARNING:test:this is a warning info\nPython 1 2 3 4 5 import logging logger = logging.getLogger(\u0026#39;test\u0026#39;) logger.warning(\u0026#39;this is a warning info\u0026#39;) # 输出 WARNING:test:this is a warning info 3.2 Handler类 Handler对象的作用是（基于日志消息的等级）将消息分发到handler指定的位置（文件、网络、邮件等）。Logger对象可以通过addHandler()方法为自己添加handler对象。\n应用程序代码不应该直接实例化和使用Handler实例。因为Handler是一个基类，只定义了所有handlers都应该有的接口，同时提供了一些子类可以直接使用或覆盖的默认行为。\n常用接口有：\nsetLevel() setFormatter() set_name() 常用的Handler：\nHandler 描述 logging.StreamHandler 将日志消息发送到输出到Stream，如std.out, std.err或任何file-like对象。 logging.FileHandler 将日志消息发送到磁盘文件，默认情况下文件大小会无限增长 logging.handlers.RotatingFileHandler 将日志消息发送到磁盘文件，并支持日志文件按大小切割 logging.hanlders.TimedRotatingFileHandler 将日志消息发送到磁盘文件，并支持日志文件按时间切割 logging.handlers.HTTPHandler 将日志消息以GET或POST的方式发送给一个HTTP服务器 logging.handlers.SMTPHandler 将日志消息发送给一个指定的email地址 logging.NullHandler 该Handler实例会忽略error messages，通常被想使用logging的library开发者使用来避免'No handlers could be found for logger XXX'信息的出现。 类之间的继承关系如下：\n在这里插入图片描述\n示例：\nPython 1 2 3 4 5 6 7 8 9 import logging logger = logging.getLogger() # 获取Logger对象 handler = logging.FileHandler(\u0026#39;output.log\u0026#39;, mode=\u0026#39;a\u0026#39;, encoding=None, delay=False) formatter = logging.Formatter(\u0026#39;%(asctime)s - %(name)s - %(levelname)s - %(message)s\u0026#39;, datefmt=\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39;, style=\u0026#39;%\u0026#39;) handler.setFormatter(formatter) # handler对象设置格式 handler.setLevel(logging.DEBUG) # handler设置日志级别 logger.addHandler(handler) # 添加handler对象 3.3 Filter类 Filter实例用于执行日志记录的任意筛选。\nLoggers和Handlers可以根据需要使用筛选器实例筛选记录.\nlogging标准库只提供了一个base filter，其构造函数__init__接收name，默认的行为是名为name的logger及其子logger的消息能通过过滤器，其余的被过滤掉。\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Filter(object): \u0026#34;\u0026#34;\u0026#34; Filter instances are used to perform arbitrary filtering of LogRecords. \u0026#34;\u0026#34;\u0026#34; def __init__(self, name=\u0026#39;\u0026#39;): \u0026#34;\u0026#34;\u0026#34; Initialize a filter. Initialize with the name of the logger which, together with its children, will have its events allowed through the filter. If no name is specified, allow every event. \u0026#34;\u0026#34;\u0026#34; self.name = name self.nlen = len(name) 3.4 Formatter类 Formatter对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是，应用代码可以直接实例化Formatter类。\nFormatter类的构造方法定义如下：\nlogging.Formatter.__init__(fmt=None, datefmt=None, style='%')\n该方法可接收3个可选参数：\nfmt：指定消息格式化字符串，如果不指定该参数则默认使用message的原始值 datefmt：指定日期格式字符串，如果不指定，则默认使用\u0026quot;%Y-%m-%d %H:%M:%S\u0026quot; style：可取值为'%', '{'和'$'，如果不指定，则默认使用'%' 格式 含义 %(levelno)s 打印日志级别的数值 %(levelname)s 打印日志级别名称 %(pathname)s 打印当前执行程序的路径 %(filename)s 打印当前执行程序名 %(funcName)s 打印日志的当前函数 %(lineno)d 打印日志的当前行号 %(asctime)s 打印日志的时间 %(thread)d 打印线程ID %(message)s 打印日志信息 %(process)s 当前进程，进程ID %(module)s 调用日志输出函数的模块名，filename的名称部分，不包含后缀 %(msecs)d 日志事件发生事件的毫秒部分。logging.basicConfig()中用参数datefmt将会去掉asctime中产生的毫秒部分，可以用这个加上。 一般直接使用logging.Formatter(fmt, datefmt)\nPython 1 2 3 4 5 import logging fmt = \u0026#34;%(asctime)s [%(filename)s:%(lineno)d] %(levelname)s: %(message)s\u0026#34; datefmt=\u0026#34;%Y-%m-%d %H:%M:%S\u0026#34; logFormatter = logging.Formatter(fmt, datefmt) 四、使用示例 该部分主要展示如何使用logging模块记录日志信息的使用方法。\n默认的输出格式如下：\n在这里插入图片描述\n示例：\n在这里插入图片描述\nlogging模块如何使用呢？下面介绍几种常用的使用方式：\n一是使用logging的对外函数接口；二是使用basicConfig()方法，该方法能够满足基本的使用需求；三是通过创建Logger对象，可以进行更加复杂的处理。\n4.1 直接调用logging接口 最简单的使用方法，就是直接调用logging标准库的接口，如logging.debug()，logging.info()等函数。\n默认的日志级别是WARNING，当等级大于等于WARNING才被跟踪。\n内部默认调用的是root.log(level, mas)函数\n接口函数如下：\ndebug() info() warning() error() critical() 也可以直接使用同一接口logging.log()，输入级别及具体信息。\nlog(level, msg)：level表示日志级别，输入日志级别对应的整数或代码，如下： Python 1 2 3 4 5 6 7 8 CRITICAL = 50 FATAL = CRITICAL ERROR = 40 WARNING = 30 WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0 这些接口函数内部实际调用的都是Logger类的对应函数。如果logger没有handler时，可默认调用basicConfig()添加控制台handler。\n如，logging.info()函数内部调用root.info()函数，如下\nPython 1 2 3 4 5 6 7 8 9 def info(msg, *args, **kwargs): \u0026#34;\u0026#34;\u0026#34; Log a message with severity \u0026#39;INFO\u0026#39; on the root logger. If the logger has no handlers, call basicConfig() to add a console handler with a pre-defined format. \u0026#34;\u0026#34;\u0026#34; if len(root.handlers) == 0: basicConfig() root.info(msg, *args, **kwargs) 示例如下：\nPython 1 2 3 4 5 6 7 8 9 10 11 12 import logging logging.warning(\u0026#39;this is a warning info.\u0026#39;) logging.error(\u0026#39;this is a error info.\u0026#39;) logging.debug(\u0026#39;this is a debuginfo.\u0026#39;) # 输出 WARNING:root:this is a warning info. ERROR:root:this is a error info. logging.log(20, \u0026#39;this is a info msg.\u0026#39;) logging.log(30, \u0026#39;this is a warn msg.\u0026#39;) # 输出： WARNING:root:this is a warn msg. 记录具体异常信息\n当发生异常时，直接使用无参数的debug(), info(), warning(), error(), critical()方法并不能记录异常信息。\n我们看一下各个接口的定义及参数：\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 普通接口 def warning(msg, *args, **kwargs) # 内部调用如下函数： self._log(WARNING, msg, args, **kwargs) # 统一接口 def log(level, msg, *args, **kwargs) # 内部调用如下函数： self._log(level, msg, args, **kwargs) # exception函数，参数中默认exc_info为True def exception(msg, *args, exc_info=True, **kwargs): error(msg, *args, exc_info=exc_info, **kwargs) # 内部调用如下函数： self._log(ERROR, msg, args, **kwargs) 由上面代码可见，其内部都是调用函数Logger._log(level, msg, args, **kwargs)。不同的是参数，如level日志级别。\n函数Logger._log()的定义如下：\nPython 1 2 def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False) pass 可见，其除了level， msg以外，还有参数exc_info。该参数用来控制是否打印具体信息。\n函数exception()就等于error(msg, exc_info=True)\n因此：\n记录异常信息的方法：\n设置exc_info参数为True，指示是否打印执行信息 或者使用exception()方法，同时记录当前的堆栈追踪（仅从异常处理程度调用此方法） 还可以使用log()方法，但是要设置日志级别和exce_info参数。 logger.log(level, msg, exc_info=True)是各个接口函数的统一调用方式。\n示例代码：\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 import logging logging.basicConfig(filename=\u0026#34;test.log\u0026#34;, filemode=\u0026#34;w\u0026#34;, format=\u0026#34;%(asctime)s %(name)s:%(levelname)s:%(message)s\u0026#34;, datefmt=\u0026#34;%d-%M-%Y %H:%M:%S\u0026#34;, level=logging.DEBUG) a = 5 b = 0 try: c = a / b except Exception as e: # 下面三种方式三选一，推荐使用第一种 logging.exception(\u0026#34;Exception occurred\u0026#34;) logging.error(\u0026#34;Exception occurred\u0026#34;, exc_info=True) logging.log(level=logging.DEBUG, msg=\u0026#34;Exception occurred\u0026#34;, exc_info=True) 输出文件test.log的内容:\n在这里插入图片描述\n4.2 使用basicConfig()函数 使用basicConfig()方法就能够满足基本的使用需要，如果方法没有传入参数，会根据默认的配置创建Logger对象，默认的日志级别被设置为WARNING。\n默认的日志输出格式：\nWARNING root message 日志级别 Logger实例 日志信息 函数定义：\ndef basicConfig(**kwargs)\n该函数可选的参数如下表所示：\n参数名称 参数描述 filename 日志输出到文件的文件名 filemode 文件模式，r[+]、w[+]、a[+] format 日志输出的格式 datefmt 日志附带日期时间的格式 style 格式占位符，默认为“%”和“{}” level 设置日志输出级别 stream 定义输出流，用来初始化 StreamHandler对象，不能和 filename参数一起使用，否则会ValueError异常 handles 定义处理器，用来创建Handler对象，不能和filename，stream参数一起使用，否则也会抛出ValueError异常 stream输出流不与filename输出文件一起使用 handler处理器不与stream输出流或filename输出文件一起用 即stream、filename和handler互相排斥 若想将日志信息同时输出到文件和控制台，则需要使用Logger处理器增加对应的处理方法。 basicConfig()方法可以实现将日志信息输出到文件中或者输出到定义的输出流中。\n输出到文件中，可以通过filename参数，或者通过handler处理器创建相应文件对象实现 内部创建FileHandle()对象 打印到输出流中，使用默认参数即可 内部创建StreamHandler()对象 其中，format指定输出的格式和内容，format可以输出很多有用信息，具体格式见Formatter类。\n该方法相当于显示调用basicConfig()进行参数的配置，而直接调用logging.info()接口函数，若没有handler时，是隐式调用basicConfig()的默认参数形式。\n1）日志信息打印到输出流\npy\n复制代码\n1import logging 2# 直接调用basicConfig()函数，使用默认参数 3logging.basicConfig() 4logging.debug('This is a debug message') 5logging.info('This is an info message') 6logging.warning('This is a warning message') 7logging.error('This is an error message') 8logging.critical('This is a critical message') 9 10# 输出：(默认级别是WARNING) 11WARNING:root:This is a warning message 12ERROR:root:This is an error message 13CRITICAL:root:This is a critical message\n2）格式化输出\n格式化输出时间：\n通过datefmt参数可以格式化输出log的时间。\n常用的输出格式如下：\nformat = '%(asctime)s - %(filename)s[line:%(lineno)d]- %(levelname)s: %(message)s'\n该格式可以输出日志的打印时间，输出模块及行号，日志级别和输出的日志内容。\npy\n复制代码\n1import logging 2logging.basicConfig(format='%(asctime)s %(message)s', datefmt = '%Y-%m-%d %I:%M:%S %p') 3logging.warning('is when this event was occurred.') 4 5# 输出结果 62019-10-15 04:03:17 PM is when this event was occurred.\n3） 将日志输出到指定文件\n指定filename， filemode参数，可将日志内容输出到指定文件，示例代码如下：\npy\n复制代码\n1import logging 2 3logging.basicConfig(filename='test.log', filemode='w', format=\u0026quot;%(asctime)s %(name)s:%(levelname)s:%(message)s\u0026quot;, datefmt=\u0026quot;%d-%M-%Y %H:%M:%S\u0026quot;, level=logging.DEBUG) 4logging.debug('This is a debug message') 5logging.info('This is an info message') 6logging.warning('This is a warning message') 7logging.error('This is an error message')\n生成的日志文件test.log，内容如下：\n在这里插入图片描述\n4.3 一步步配置Logger basicConfig函数提供了一种较为简便的使用日志的方式，如果想要获取更加复杂的使用，则需要自己一步步配置来实现。\n配置逻辑：\n一个logger对象可以添加多个handler对象，通过addHandler()函数 每个handler对象可以有一个Formatter对象来指定格式，通过setFormatter()函数 handler和logger对象都需要设置一个日志等级，通过setLevel()函数 根据需要logger和handler对象可以添加多个Filter对象，通过addFilter()函数 示例：\npy\n复制代码\n1import logging 2 3logger = logging.getLogger(__name__) 4logger.setLevel(level=logging.INFO) 5 6handler = logging.FileHandler('output.log', mode='a', encoding=None, delay=False) 7formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', 8 datefmt='%Y-%m-%d %H:%M:%S', 9 style='%') 10handler.setFormatter(formatter) 11handler.setLevel(logging.DEBUG) 12handler.set_name('output-log') 13 14logger.addHandler(handler) 15 16a=0 17try: 18 res = 100 / a 19except: 20 logger.error('Divisor is equal to 0.', exc_info=True)\n将日志同时输出到屏幕和文件\npy\n复制代码\n1import logging 2logger = logging.getLogger(__name__) 3logger.setLevel(level = logging.DEBUG) 4handler = logging.FileHandler(\u0026quot;log.txt\u0026quot;) 5handler.setLevel(logging.INFO) 6formatter = logging.Formatter('%(asctime)s - %(name)s - %(message)s') 7handler.setFormatter(formatter) 8 9console = logging.StreamHandler() 10console.setLevel(logging.INFO) 11 12logger.addHandler(handler) 13logger.addHandler(console) 14 15logger.info(\u0026quot;Start print log\u0026quot;) 16logger.debug(\u0026quot;Do something\u0026quot;) 17logger.warning(\u0026quot;Something maybe fail.\u0026quot;) 18logger.info(\u0026quot;Finish\u0026quot;)\n在log.txt文件和控制台均可看到输出，控制台的输出，其中文件中内容（有格式）如下：\n在这里插入图片描述\n通过这段代码可以看出，logging有一个日志处理的主对象(logger=logging.getLogger())，其他的处理方法均是通过addHandler添加进去。\n4.4 通过dictConfig配置 通过dictConfig配置方法是通过python代码构建一个dict对象，也可以通过JSON或yaml文件来进行配置。\n这种方式使用起来更加灵活，强大。我们可以把很多的数据转换成自字典形式，然后将他们填充到一个配置字典中。\n具体如何转换可以参考logging.config.py文件。\n4.4.1 dictConfig() 该函数可以从一个字典对象中获取日志配置信息，config参数就是这个字典对象。\n定义如下：\npy\n复制代码\n1def dictConfig(config): 2 \u0026quot;\u0026quot;\u0026quot;Configure logging using a dictionary.\u0026quot;\u0026quot;\u0026quot; 3 dictConfigClass(config).configure()\n如使用YAML格式的文件类配置(配置内容见下方示例)，则使用示例：\npy\n复制代码\n1import logging 2import logging.config 3import yaml 4 5with open('logging.yml', 'r') as f_config: 6 dict_cfg = yaml.load(f_config) 7logging.config.dictConfig(dict_cfg) 8 9logger = logging.getLogger('console') 10logger.debug('debug msg')\n4.4.2 配置规则 key名称 描述 version 必选项，整数值，表示配置格式版本，当前唯一可用值是1 formatters 可选项，其值是字典对象，该字典对象每个元素的key为要定义的格式器名称，value为格式器的配置信息组成的dict。一般会配置format，用于指定输出字符串的格式，datefmt用于指定输出的时间字符串格式，默认为%Y-%m-%d %H:%M:%S。 filters 可选项，其值是字典对象，该字典对象每个元素的key为要定义的过滤器名称，value为过滤器的配置信息组成的dict。 handlers 可选项，其值是字典对象，该字典对象每个元素的key为要定义的处理器名称，value为处理器的配置信息组成的dict。如class（必选项）, formatter, filters。其他配置信息将会传递给class所指定的处理器类的构造函数。如使用logging.handlers.RotatingFileHandler，使用maxBytes, backupCount等参数。 loggers 可选项，其值是字典对象，该字典对象每个元素的key为要定义的日志器名称，value为日志器的配置信息组成的dict。如level, handler, filter等 root 可选项，这是root logger的配置项，其值是字典对象。除非在定义其它logger时明确指定propagate值为no，否则root logger定义的handlers都会被作用到其它logger上。 incremental 可选项，默认值为False。该选项的意义在于，如果这里定义的对象已经存在，那么这里对这些对象的定义是否应用到已存在的对象上。值为False表示，已存在的对象将会被重新定义。 具体配置结构如下所示：\nversion： 必要参数，必须为1\nincremental：是否将此配置文件解释为现有配置的增量, 默认为False\ndisable_existing_loggers：是否要禁用现有的非 root logger，默认为True\nhandlers：字典形式\nclass：必须有，所使用的handler类，如logging.handlers.RotatingFileHandler level：（可选），handler的等级 formatter：（可选），当前handler的formatter的id filters：（可选），当前handler的filters的id列表 loggers：字典形式\nlevel：（可选），logger的等级 propagate：（可选）：默认值为1，表示将消息传递给父logger的handler进行处理。 filters：（可选） handlers：（可选） ，当前logger的handlers的id列表 qualname：代码中使用logging.getLogger()时，读取的就是qualname这个选项。 root：这是root logger的配置项\nlevel：日志等级 handlers ：当前root logger的handlers的id列表 4.4.3 配置示例 yaml\n复制代码\n1version:1 2root: 3 level:DEBUG 4 handlers:[filehandler, ] 5loggers: 6 console: 7 level:WARNING 8 handlers:[consolehandler,] 9 propagate:1 10handlers: 11 filehandler: 12 class:logging.FileHandler 13 filename:logs/sys.log 14 level:WARNING 15 formatter:fileformatter 16 consolehandler: 17 class:logging.StreamHandler 18 stream:ext://sys.stdout 19 level:DEBUG 20 formatter:consoleformatter 21formatters: 22 fileformatter: 23 format:'%(asctime)s [%(name)s][%(levelname)s]：%(message)s' 24 consoleformatter: 25 format:'%(asctime)s[%(levelname)s]:%(message)s'\n外部对象访问：外部对象是指不能直接进行访问，需要import导入的对象，这时候需要加一个ext://前缀以便识别，然后系统就会import这个前缀后面的内容。\n参考资料：\nPython日志库logging总结-可能是目前为止将logging库总结的最好的一篇文章\npython基础学习十 logging模块详细使用【转载】 - louis_w - 博客园\nPython之路(第十七篇)logging模块 - Nicholas- - 博客园\n","date":"2026-10-03","permalink":"/posts/python-logging-%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90%E5%8F%8A%E4%BD%BF%E7%94%A8/","title":"Python logging 模块分析及使用"},{"categories":["Linux"],"content":"以往只知道扫描端口用nmap，可以看出 open closed filtered 对应的状态是 开 关 防火墙拦截\nnmap 10.1.1.1 -p 8080 -Pn\n其实这个用法不对，-Pn是不使用ping，主要是在扫描多台机器的时候使用\n对于我查看某一台机器的端口跟协议\nnmap 10.1.1.1 -sU -p 514 # 扫描upd端口，在服务器上可以抓包看到udp nmap 10.1.1.1 -sP -p 8080 # 扫描tcp端口，在服务器上可以抓包看到tcp\n同时检查两个类型\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@10.200.13.86 ~]# nmap 10.1.10.251 -sS -sU -p 514 Starting Nmap 6.40 ( http://nmap.org ) at 2023-04-13 12:53 CST Nmap scan report for 10.1.10.251 Host is up (0.00071s latency). PORT STATE SERVICE 514/tcp closed shell 514/udp open|filtered syslog Nmap done: 1 IP address (1 host up) scanned in 0.31 seconds [root@10.200.13.86 ~]# nmap 10.1.10.251 -sT -sU -p 514 Starting Nmap 6.40 ( http://nmap.org ) at 2023-04-13 12:53 CST Nmap scan report for 10.1.10.251 Host is up (0.00087s latency). PORT STATE SERVICE 514/tcp closed shell 514/udp open|filtered syslog Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds 使用wireshark抓包显示(yum install -y wireshark)\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 [root@10.1.10.251 ~]# tshark -i eth0 host 10.200.13.86 Running as user \u0026#34;root\u0026#34; and group \u0026#34;root\u0026#34;. This could be dangerous. Capturing on \u0026#39;eth0\u0026#39; 1 0.000000000 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 58 52956 \u0026gt; https [SYN] Seq=0 Win=1024 Len=0 MSS=1460 2 0.000025685 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 https \u0026gt; 52956 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 3 0.000684895 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 42 Echo (ping) request id=0xfd77, seq=0/0, ttl=58 4 0.000691926 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 42 Echo (ping) reply id=0xfd77, seq=0/0, ttl=64 (request in 3) 5 0.000700081 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 54 Timestamp request id=0x0a0f, seq=0/0, ttl=47 6 0.000703470 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 54 Timestamp reply id=0x0a0f, seq=0/0, ttl=64 7 0.001042903 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 54 52956 \u0026gt; http [ACK] Seq=1 Ack=1 Win=1024 Len=0 8 0.001046910 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 http \u0026gt; 52956 [RST] Seq=1 Win=0 Len=0 9 0.027864364 10.200.13.86 -\u0026gt; 10.1.10.251 Syslog 42 [Malformed Packet] 10 0.127943082 10.200.13.86 -\u0026gt; 10.1.10.251 Syslog 42 [Malformed Packet] 11 14.128607309 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 54 39871 \u0026gt; http [ACK] Seq=1 Ack=1 Win=1024 Len=0 12 14.128627342 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 http \u0026gt; 39871 [RST] Seq=1 Win=0 Len=0 13 14.128641456 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 42 Echo (ping) request id=0x0cd3, seq=0/0, ttl=45 14 14.128645487 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 42 Echo (ping) reply id=0x0cd3, seq=0/0, ttl=64 (request in 13) 15 14.128700635 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 54 Timestamp request id=0x548d, seq=0/0, ttl=38 16 14.128703903 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 54 Timestamp reply id=0x548d, seq=0/0, ttl=64 17 14.128931731 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 58 39871 \u0026gt; https [SYN] Seq=0 Win=1024 Len=0 MSS=1460 18 14.128934644 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 https \u0026gt; 39871 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 19 14.144575335 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 74 45644 \u0026gt; shell [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=1253548131 TSecr=0 WS=128 20 14.144579164 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 shell \u0026gt; 45644 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 21 110.999002405 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 58 47406 \u0026gt; https [SYN] Seq=0 Win=1024 Len=0 MSS=1460 22 110.999020511 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 https \u0026gt; 47406 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 23 110.999740448 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 42 Echo (ping) request id=0x52de, seq=0/0, ttl=44 24 110.999745364 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 42 Echo (ping) reply id=0x52de, seq=0/0, ttl=64 (request in 23) 25 110.999802251 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 54 Timestamp request id=0x51bd, seq=0/0, ttl=39 26 110.999805574 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 54 Timestamp reply id=0x51bd, seq=0/0, ttl=64 27 111.000109881 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 54 47406 \u0026gt; http [ACK] Seq=1 Ack=1 Win=1024 Len=0 28 111.000113185 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 http \u0026gt; 47406 [RST] Seq=1 Win=0 Len=0 29 111.014724099 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 74 48930 \u0026gt; 5140 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=1253645001 TSecr=0 WS=128 30 111.014728072 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 5140 \u0026gt; 48930 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 31 124.893938777 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 54 40999 \u0026gt; http [ACK] Seq=1 Ack=1 Win=1024 Len=0 32 124.893957805 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 http \u0026gt; 40999 [RST] Seq=1 Win=0 Len=0 33 124.894706497 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 42 Echo (ping) request id=0xc1d2, seq=0/0, ttl=54 34 124.894711077 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 42 Echo (ping) reply id=0xc1d2, seq=0/0, ttl=64 (request in 33) 35 124.894756587 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 54 Timestamp request id=0xf398, seq=0/0, ttl=52 36 124.894759362 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 54 Timestamp reply id=0xf398, seq=0/0, ttl=64 37 124.895121682 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 58 40999 \u0026gt; https [SYN] Seq=0 Win=1024 Len=0 MSS=1460 38 124.895124588 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 https \u0026gt; 40999 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 39 124.920843490 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 41255 Destination port: 5140 40 124.920851871 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 41 1105.706644739 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 58 48674 \u0026gt; https [SYN] Seq=0 Win=1024 Len=0 MSS=1460 42 1105.706670049 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 https \u0026gt; 48674 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 43 1105.706680241 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 54 48674 \u0026gt; http [ACK] Seq=1 Ack=1 Win=1024 Len=0 44 1105.706682527 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 http \u0026gt; 48674 [RST] Seq=1 Win=0 Len=0 45 1105.707410604 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 42 Echo (ping) request id=0xb2e7, seq=0/0, ttl=53 46 1105.707415647 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 42 Echo (ping) reply id=0xb2e7, seq=0/0, ttl=64 (request in 45) 47 1105.707491509 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 54 Timestamp request id=0x2406, seq=0/0, ttl=55 48 1105.707494181 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 54 Timestamp reply id=0x2406, seq=0/0, ttl=64 多个不连续的端口，用逗号隔开。如果是连续的端口则可以写成 1-100。如果不加 p 参数，则默认是常用的 1000 个端口号（80、3306 这种）。示例使用500-520，如下：\n客户端命令 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@10.200.13.86 ~]# nmap 10.1.10.251 -sU -p 500-520 Starting Nmap 6.40 ( http://nmap.org ) at 2023-04-13 12:44 CST Nmap scan report for 10.1.10.251 Host is up (0.0020s latency). PORT STATE SERVICE 500/udp closed isakmp 501/udp open|filtered stmf 502/udp closed asa-appl-proto 503/udp open|filtered intrinsa 504/udp closed citadel 505/udp closed mailbox-lm 506/udp closed ohimsrv 507/udp open|filtered crs 508/udp closed xvttp 509/udp open|filtered snare 510/udp closed fcp 511/udp open|filtered passgo 512/udp closed biff 513/udp open|filtered who 514/udp open|filtered syslog 515/udp open|filtered printer 516/udp closed videotex 517/udp closed talk 518/udp closed ntalk 519/udp closed utime 520/udp closed route Nmap done: 1 IP address (1 host up) scanned in 7.34 seconds [root@10.200.13.86 ~]# 服务端抓包 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 [root@10.1.10.251 ~]# tshark -i eth0 host 10.200.13.86 Running as user \u0026#34;root\u0026#34; and group \u0026#34;root\u0026#34;. This could be dangerous. Capturing on \u0026#39;eth0\u0026#39; 1 0.000000000 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 54 62247 \u0026gt; http [ACK] Seq=1 Ack=1 Win=1024 Len=0 2 0.000012221 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 http \u0026gt; 62247 [RST] Seq=1 Win=0 Len=0 3 0.000762781 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 42 Echo (ping) request id=0xb3b0, seq=0/0, ttl=36 4 0.000766991 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 42 Echo (ping) reply id=0xb3b0, seq=0/0, ttl=64 (request in 3) 5 0.000838519 10.200.13.86 -\u0026gt; 10.1.10.251 ICMP 54 Timestamp request id=0x06f6, seq=0/0, ttl=51 6 0.000841192 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 54 Timestamp reply id=0x06f6, seq=0/0, ttl=64 7 0.000951930 10.200.13.86 -\u0026gt; 10.1.10.251 TCP 58 62247 \u0026gt; https [SYN] Seq=0 Win=1024 Len=0 MSS=1460 8 0.000954938 10.1.10.251 -\u0026gt; 10.200.13.86 TCP 54 https \u0026gt; 62247 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 9 0.027985374 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: fcp 10 0.027994873 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 11 0.028001484 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: mailbox-lm 12 0.028003845 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 13 0.028022978 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: comsat 14 0.028025210 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 15 0.028027405 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: talk 16 0.028029347 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 17 0.028032578 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: utime 18 0.028034575 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 19 0.028036755 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: ntalk 20 0.028038599 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 21 0.028045655 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: ohimsrv 22 0.028948371 10.200.13.86 -\u0026gt; 10.1.10.251 RIPv1 66 Request 23 0.028969173 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: xvttp 24 0.029197536 10.200.13.86 -\u0026gt; 10.1.10.251 Syslog 42 [Malformed Packet] 25 0.031863656 10.200.13.86 -\u0026gt; 10.1.10.251 ISAKMP 234 26 0.032008756 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: crs 27 0.032016229 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: snare 28 0.032033619 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: passgo 29 0.032036457 10.200.13.86 -\u0026gt; 10.1.10.251 WHO 42 [Malformed Packet] 30 0.032189560 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: stmf 31 0.032713768 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: citadel 32 0.033107393 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: printer 33 0.033109814 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: mbap 34 0.033278278 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: intrinsa 35 0.033298074 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62503 Destination port: videotex 36 1.129203755 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: citadel 37 1.129223655 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 38 1.129284377 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: ohimsrv 39 1.129309553 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: videotex 40 1.129312606 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: xvttp 41 1.129528131 10.200.13.86 -\u0026gt; 10.1.10.251 RIPv1 66 Request 42 1.130112522 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: intrinsa 43 1.130166382 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: stmf 44 1.130325399 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: printer 45 1.130332607 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: mbap 46 1.130349056 10.200.13.86 -\u0026gt; 10.1.10.251 WHO 42 [Malformed Packet] 47 1.130432807 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: passgo 48 1.130480550 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: snare 49 1.130491911 10.200.13.86 -\u0026gt; 10.1.10.251 ISAKMP 234 50 1.130498465 10.200.13.86 -\u0026gt; 10.1.10.251 Syslog 42 [Malformed Packet] 51 1.130722396 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62504 Destination port: crs 52 2.230257219 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: xvttp 53 2.230278546 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 54 2.231344354 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: ohimsrv 55 2.231462577 10.200.13.86 -\u0026gt; 10.1.10.251 RIPv1 66 Request 56 2.231471661 10.200.13.86 -\u0026gt; 10.1.10.251 Syslog 42 [Malformed Packet] 57 3.330665951 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62506 Destination port: ohimsrv 58 3.330688702 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 59 3.331863055 10.200.13.86 -\u0026gt; 10.1.10.251 RIPv1 66 Request 60 3.335033853 10.200.13.86 -\u0026gt; 10.1.10.251 Syslog 42 [Malformed Packet] 61 4.431829038 10.200.13.86 -\u0026gt; 10.1.10.251 Syslog 42 [Malformed Packet] 62 4.431842894 10.200.13.86 -\u0026gt; 10.1.10.251 RIPv1 66 Request 63 4.431861359 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 94 Destination unreachable (Port unreachable) 64 4.482917482 10.200.13.86 -\u0026gt; 10.1.10.251 ISAKMP 234 65 5.534215739 10.200.13.86 -\u0026gt; 10.1.10.251 ISAKMP 234 66 5.534233734 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 262 Destination unreachable (Port unreachable) 67 5.583253458 10.200.13.86 -\u0026gt; 10.1.10.251 Syslog 42 [Malformed Packet] 68 5.633197137 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: crs 69 5.683380838 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: intrinsa 70 5.733662264 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62506 Destination port: crs 71 5.783480335 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62506 Destination port: intrinsa 72 5.833938985 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62507 Destination port: crs 73 5.883631178 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62507 Destination port: intrinsa 74 5.934858792 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62508 Destination port: crs 75 5.984879106 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62508 Destination port: intrinsa 76 6.034732591 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: mbap 77 6.034746749 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 78 6.085044904 10.200.13.86 -\u0026gt; 10.1.10.251 WHO 42 [Malformed Packet] 79 6.135110230 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: stmf 80 6.185641460 10.200.13.86 -\u0026gt; 10.1.10.251 WHO 42 [Malformed Packet] 81 6.235075713 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62506 Destination port: stmf 82 6.284280617 10.200.13.86 -\u0026gt; 10.1.10.251 WHO 42 [Malformed Packet] 83 6.334345777 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62507 Destination port: stmf 84 6.384432615 10.200.13.86 -\u0026gt; 10.1.10.251 WHO 42 [Malformed Packet] 85 6.434504759 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62508 Destination port: stmf 86 6.484691016 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: passgo 87 6.534721820 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: printer 88 6.586292165 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62506 Destination port: passgo 89 6.634804117 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62506 Destination port: printer 90 6.685869463 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62507 Destination port: passgo 91 6.734998702 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62507 Destination port: printer 92 6.785940760 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62508 Destination port: passgo 93 6.836494406 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62508 Destination port: printer 94 6.885275082 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: snare 95 6.935324065 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62505 Destination port: videotex 96 6.986555913 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62506 Destination port: snare 97 7.035634422 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62506 Destination port: videotex 98 7.035648502 10.1.10.251 -\u0026gt; 10.200.13.86 ICMP 70 Destination unreachable (Port unreachable) 99 7.085655195 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62507 Destination port: snare 100 7.188406089 10.200.13.86 -\u0026gt; 10.1.10.251 UDP 42 Source port: 62508 Destination port: snare 如果要扫描多个 ip，可以把 ip 放入到文件中，然后通过 iL 参数指定即可，格式：nmap -iL iplist.txt -sU -p 1-100。 nmap 通过 sU 参数使用 udp 进行端口发现，原理是根据是否有返回信息，是否包含端口不可达来判断。\nnmap 在二层做主机发现时使用的参数是 sn（ping 扫描，不做端口扫描）。在三层做主机发现时也是使用的 sn 参数，这里二三层都用的 sn 参数，而具体使用的是二层协议 arp 还是三层协议 icmp，判断依据是是否属于同一个网段。在四层发现时主要利用的是 tcp 和 udp，而 nmap 用到的主要参数就是 PU 和 PA。\nnmap 使用 udp 来做端口扫描时，用到的参数是 sU，来看下 man 手册的介绍： ","date":"2026-10-03","permalink":"/posts/nmap-%E7%AB%AF%E5%8F%A3%E6%89%AB%E6%8F%8F%E7%AC%94%E8%AE%B0/","title":"Nmap 端口扫描笔记"},{"categories":["mysql"],"content":"#mysql #sql #mysqldump\nmysqldump mysqldump 5.7 1. mysql 导出表结构和表数据 mysqldump用法，命令行下具体用法如下: 代码 1 mysqldump -u用戶名 -p密码 -d 数据库名 表名 \u0026gt; 脚本名; 2. 导出整个数据库结构和数据 代码 1 mysqldump -h localhost -uroot -p123456 database \u0026gt; dump.sql 3. 导出单个数据表结构和数据 代码 1 mysqldump -h localhost -uroot -p123456 database table \u0026gt; dump.sql 4.导出整个数据库结构（不包含数据） 代码 1 mysqldump -h localhost -uroot -p123456 -d database \u0026gt; dump.sql 5.导出单个数据表结构（不包含数据） 代码 1 mysqldump -h localhost -uroot -p123456 -d database table \u0026gt; dump.sql 6.mysqldump 备份导出数据排除某张表 就用 --ignore-table=dbname.tablename 参数就行了。\n代码 1 mysqldump -uusername -ppassword -h192.168.0.1 -P3306 dbname --ignore-table=dbname.dbtanles \u0026gt; dump.sql mysqldump 8.0 mysqldump8.0 需要设置--column-statistics=0参数\n代码 1 2 3 --column-statistics Add an ANALYZE TABLE statement to regenerate any existing column statistics. (Defaults to on; use --skip-column-statistics to disable.) 代码 1 2 3 4 5 mysqldump --column-statistics=0 -uusername -ppassword -hhostname -P3306 cmdb_appops --ignore-table=cmdb_appops.appops_application \\ --ignore-table=cmdb_appops.techops_aliyun_daily_billing_item_bill \\ --ignore-table=cmdb_appops.techops_split_bill \\ --ignore-table=cmdb_appops.techops_split_bill_instance \\ --ignore-table=cmdb_appops.wfh_comment \u0026gt; dump.sql 建议使用参数\n代码 1 /usr/bin/mysqldump -u moba -pmoba2016 -h 10.162.84.200 --events --set-gtid-purged=OFF --single-transaction --databases db_log_zone_96005 \u0026gt; /data/db_backup_mysqldump/db_backup_db_log/db_log_zone_96005.sql --databases 使用此参数导出数据时，会生成对应的创建数据库sql，免去要手动创建数据的麻烦\n生成的文件样例如下\nSQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 -- MySQL dump 10.13 Distrib 5.7.24-26, for Linux (x86_64) -- -- Host: 10.162.84.200 Database: db_log_zone_96005 -- ------------------------------------------------------ -- Server version 5.7.23-log /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE=\u0026#39;+00:00\u0026#39; */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE=\u0026#39;NO_AUTO_VALUE_ON_ZERO\u0026#39; */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; /*!50717 SELECT COUNT(*) INTO @rocksdb_has_p_s_session_variables FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = \u0026#39;performance_schema\u0026#39; AND TABLE_NAME = \u0026#39;session_variables\u0026#39; */; /*!50717 SET @rocksdb_get_is_supported = IF (@rocksdb_has_p_s_session_variables, \u0026#39;SELECT COUNT(*) INTO @rocksdb_is_supported FROM performance_schema.session_variables WHERE VARIABLE_NAME=\\\u0026#39;rocksdb_bulk_load\\\u0026#39;\u0026#39;, \u0026#39;SELECT 0\u0026#39;) */; /*!50717 PREPARE s FROM @rocksdb_get_is_supported */; /*!50717 EXECUTE s */; /*!50717 DEALLOCATE PREPARE s */; /*!50717 SET @rocksdb_enable_bulk_load = IF (@rocksdb_is_supported, \u0026#39;SET SESSION rocksdb_bulk_load = 1\u0026#39;, \u0026#39;SET @rocksdb_dummy_bulk_load = 0\u0026#39;) */; /*!50717 PREPARE s FROM @rocksdb_enable_bulk_load */; /*!50717 EXECUTE s */; /*!50717 DEALLOCATE PREPARE s */; -- -- Current Database: `db_log_zone_96005` -- CREATE DATABASE /*!32312 IF NOT EXISTS*/ `db_log_zone_96005` /*!40100 DEFAULT CHARACTER SET latin1 */; USE `db_log_zone_96005`; -- -- Table structure for table `161activity_plugin_check` -- DROP TABLE IF EXISTS `161activity_plugin_check`; CREATE DATABASE /!32312 IF NOT EXISTS/ db_log_zone_96005 /*!40100 DEFAULT CHARACTER SET latin1 */; 这句 sql ai的解释如下\nCREATE DATABASE: 这是一个SQL命令，用于创建一个新的数据库。 /*!32312 IF NOT EXISTS*/: 这部分是一个条件注释。MySQL会解析并执行这些注释中的内容，只要MySQL的版本高于或等于注释中指定的版本号（在这里是3.23.12）。这是一种确保向后兼容性的方法，允许在旧版本的MySQL中运行SQL脚本，而不会因为不支持的语法而出错。在这个例子中，IF NOT EXISTS子句会被MySQL 3.23.12或更高版本执行，它确保只有在数据库db_log_zone_96005不存在时才会创建它。 db_log_zone_96005: 这是要创建的数据库的名称。 /*!40100 DEFAULT CHARACTER SET latin1 */: 这又是一个条件注释，它指定了数据库的默认字符集为latin1。这个注释中的内容会被MySQL 4.1.0或更高版本执行。字符集latin1是西欧语言的字符集，也称为ISO 8859-1。 综上所述，这条SQL语句的作用是：在MySQL版本3.23.12或更高的环境中，如果数据库db_log_zone_96005不存在，则创建它，并在MySQL版本4.1.0或更高的环境中将其默认字符集设置为latin1。如果数据库已经存在，这条命令将不会执行任何操作。\n","date":"2026-10-03","permalink":"/posts/mysqldump-%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/","title":"mysqldump 常用操作"},{"categories":["mysql"],"content":"#mysql #python #install #mysqlclient\nmysqlclient 安装 一、官方安装操作 pypi 安装\n代码 1 2 3 4 # Assume you are activating Python 3 venv $ brew install mysql-client pkg-config $ export PKG_CONFIG_PATH=\u0026#34;/opt/homebrew/opt/mysql-client/lib/pkgconfig\u0026#34; $ pip install mysqlclient 二、安装问题解决 1. xcode 问题 解决操作命令\n代码 1 xcode-select --install xcrun error xcode error\n报错内容\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 (venv) clin:devops-api/ (master) $ pip install mysqlclient [10:54:12] Looking in indexes: https://pypi.mirrors.ustc.edu.cn/simple/ Collecting mysqlclient Using cached https://mirrors.bfsu.edu.cn/pypi/web/packages/37/fb/d9a8f763c84f1e789c027af0ffc7dbf94c9a38db961484f253f0552cbb47/mysqlclient-2.2.1.tar.gz (89 kB) Installing build dependencies ... done Getting requirements to build wheel ... done Installing backend dependencies ... done Preparing metadata (pyproject.toml) ... done Building wheels for collected packages: mysqlclient Building wheel for mysqlclient (pyproject.toml) ... error error: subprocess-exited-with-error × Building wheel for mysqlclient (pyproject.toml) did not run successfully. │ exit code: 1 ╰─\u0026gt; [41 lines of output] # Options for building extension module: extra_compile_args: [\u0026#39;-I/usr/local/Cellar/mysql-client/8.3.0/include/mysql\u0026#39;, \u0026#39;-std=c99\u0026#39;] extra_link_args: [\u0026#39;-L/usr/local/Cellar/mysql-client/8.3.0/lib\u0026#39;, \u0026#39;-lmysqlclient\u0026#39;] define_macros: [(\u0026#39;version_info\u0026#39;, (2, 2, 1, \u0026#39;final\u0026#39;, 0)), (\u0026#39;__version__\u0026#39;, \u0026#39;2.2.1\u0026#39;)] running bdist_wheel running build running build_py creating build creating build/lib.macosx-10.9-x86_64-cpython-39 creating build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/release.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/cursors.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/connections.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/__init__.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/times.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/converters.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/_exceptions.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb creating build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/FLAG.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/CLIENT.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/__init__.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/ER.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/CR.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/FIELD_TYPE.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants running egg_info writing src/mysqlclient.egg-info/PKG-INFO writing dependency_links to src/mysqlclient.egg-info/dependency_links.txt writing top-level names to src/mysqlclient.egg-info/top_level.txt reading manifest file \u0026#39;src/mysqlclient.egg-info/SOURCES.txt\u0026#39; reading manifest template \u0026#39;MANIFEST.in\u0026#39; adding license file \u0026#39;LICENSE\u0026#39; writing manifest file \u0026#39;src/mysqlclient.egg-info/SOURCES.txt\u0026#39; copying src/MySQLdb/_mysql.c -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb running build_ext building \u0026#39;MySQLdb._mysql\u0026#39; extension creating build/temp.macosx-10.9-x86_64-cpython-39 creating build/temp.macosx-10.9-x86_64-cpython-39/src creating build/temp.macosx-10.9-x86_64-cpython-39/src/MySQLdb gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch x86_64 -g \u0026#34;-Dversion_info=(2, 2, 1, \u0026#39;final\u0026#39;, 0)\u0026#34; -D__version__=2.2.1 -I/Users/clin/File/Project/shuhe/devops-api/venv/include -I/Library/Frameworks/Python.framework/Versions/3.9/include/python3.9 -c src/MySQLdb/_mysql.c -o build/temp.macosx-10.9-x86_64-cpython-39/src/MySQLdb/_mysql.o -I/usr/local/Cellar/mysql-client/8.3.0/include/mysql -std=c99 xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun error: command \u0026#39;/usr/bin/gcc\u0026#39; failed with exit code 1 [end of output] note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheel for mysqlclient Failed to build mysqlclient ERROR: Could not build wheels for mysqlclient, which is required to install pyproject.toml-based projects 1. mysql 8.0 兼容问题 解决操作命令\n代码 1 pip install -e git+https://github.com/etripier/mysqlclient.git@ee1882e02e3c73910b1d6df86bbdce784edbb881#egg=mysqlclient git fix link https://github.com/PyMySQL/mysqlclient/issues/688\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 (venv) clin:devops-api/ (master) $ pip install mysqlclient [10:54:54] Looking in indexes: https://pypi.mirrors.ustc.edu.cn/simple/ Collecting mysqlclient Using cached https://mirrors.bfsu.edu.cn/pypi/web/packages/37/fb/d9a8f763c84f1e789c027af0ffc7dbf94c9a38db961484f253f0552cbb47/mysqlclient-2.2.1.tar.gz (89 kB) Installing build dependencies ... done Getting requirements to build wheel ... done Installing backend dependencies ... done Preparing metadata (pyproject.toml) ... done Building wheels for collected packages: mysqlclient Building wheel for mysqlclient (pyproject.toml) ... error error: subprocess-exited-with-error × Building wheel for mysqlclient (pyproject.toml) did not run successfully. │ exit code: 1 ╰─\u0026gt; [58 lines of output] # Options for building extension module: extra_compile_args: [\u0026#39;-I/usr/local/Cellar/mysql-client/8.3.0/include/mysql\u0026#39;, \u0026#39;-std=c99\u0026#39;] extra_link_args: [\u0026#39;-L/usr/local/Cellar/mysql-client/8.3.0/lib\u0026#39;, \u0026#39;-lmysqlclient\u0026#39;] define_macros: [(\u0026#39;version_info\u0026#39;, (2, 2, 1, \u0026#39;final\u0026#39;, 0)), (\u0026#39;__version__\u0026#39;, \u0026#39;2.2.1\u0026#39;)] running bdist_wheel running build running build_py creating build creating build/lib.macosx-10.9-x86_64-cpython-39 creating build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/release.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/cursors.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/connections.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/__init__.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/times.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/converters.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb copying src/MySQLdb/_exceptions.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb creating build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/FLAG.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/CLIENT.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/__init__.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/ER.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/CR.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants copying src/MySQLdb/constants/FIELD_TYPE.py -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb/constants running egg_info writing src/mysqlclient.egg-info/PKG-INFO writing dependency_links to src/mysqlclient.egg-info/dependency_links.txt writing top-level names to src/mysqlclient.egg-info/top_level.txt reading manifest file \u0026#39;src/mysqlclient.egg-info/SOURCES.txt\u0026#39; reading manifest template \u0026#39;MANIFEST.in\u0026#39; adding license file \u0026#39;LICENSE\u0026#39; writing manifest file \u0026#39;src/mysqlclient.egg-info/SOURCES.txt\u0026#39; copying src/MySQLdb/_mysql.c -\u0026gt; build/lib.macosx-10.9-x86_64-cpython-39/MySQLdb running build_ext building \u0026#39;MySQLdb._mysql\u0026#39; extension creating build/temp.macosx-10.9-x86_64-cpython-39 creating build/temp.macosx-10.9-x86_64-cpython-39/src creating build/temp.macosx-10.9-x86_64-cpython-39/src/MySQLdb gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch x86_64 -g \u0026#34;-Dversion_info=(2, 2, 1, \u0026#39;final\u0026#39;, 0)\u0026#34; -D__version__=2.2.1 -I/Users/clin/File/Project/shuhe/devops-api/venv/include -I/Library/Frameworks/Python.framework/Versions/3.9/include/python3.9 -c src/MySQLdb/_mysql.c -o build/temp.macosx-10.9-x86_64-cpython-39/src/MySQLdb/_mysql.o -I/usr/local/Cellar/mysql-client/8.3.0/include/mysql -std=c99 src/MySQLdb/_mysql.c:527:9: error: call to undeclared function \u0026#39;mysql_ssl_set\u0026#39;; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] mysql_ssl_set(\u0026amp;(self-\u0026gt;connection), key, cert, ca, capath, cipher); ^ src/MySQLdb/_mysql.c:527:9: note: did you mean \u0026#39;mysql_close\u0026#39;? /usr/local/Cellar/mysql-client/8.3.0/include/mysql/mysql.h:797:14: note: \u0026#39;mysql_close\u0026#39; declared here void STDCALL mysql_close(MYSQL *sock); ^ src/MySQLdb/_mysql.c:1795:9: error: call to undeclared function \u0026#39;mysql_kill\u0026#39;; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] r = mysql_kill(\u0026amp;(self-\u0026gt;connection), pid); ^ src/MySQLdb/_mysql.c:1795:9: note: did you mean \u0026#39;mysql_ping\u0026#39;? /usr/local/Cellar/mysql-client/8.3.0/include/mysql/mysql.h:525:13: note: \u0026#39;mysql_ping\u0026#39; declared here int STDCALL mysql_ping(MYSQL *mysql); ^ src/MySQLdb/_mysql.c:2011:9: error: call to undeclared function \u0026#39;mysql_shutdown\u0026#39;; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] r = mysql_shutdown(\u0026amp;(self-\u0026gt;connection), SHUTDOWN_DEFAULT); ^ 3 errors generated. error: command \u0026#39;/usr/bin/gcc\u0026#39; failed with exit code 1 [end of output] note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheel for mysqlclient Failed to build mysqlclient ERROR: Could not build wheels for mysqlclient, which is required to install pyproject.toml-based projects 2. arm 芯片 安装异常报错 应用启动报错\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 /Users/clin/File/Project/shuhe/devops-api/venv/bin/python3.8 /Users/clin/File/Project/shuhe/devops-api/manage.py runserver 0.0.0.0:8000 Traceback (most recent call last): File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/manage.py\u0026#34;, line 20, in main django.setup() File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/django/__init__.py\u0026#34;, line 24, in setup apps.populate(settings.INSTALLED_APPS) File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/django/apps/registry.py\u0026#34;, line 91, in populate app_config = AppConfig.create(entry) File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/django/apps/config.py\u0026#34;, line 224, in create import_module(entry) File \u0026#34;/usr/local/var/pyenv/versions/3.8.15/lib/python3.8/importlib/__init__.py\u0026#34;, line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File \u0026#34;\u0026lt;frozen importlib._bootstrap\u0026gt;\u0026#34;, line 1014, in _gcd_import File \u0026#34;\u0026lt;frozen importlib._bootstrap\u0026gt;\u0026#34;, line 991, in _find_and_load File \u0026#34;\u0026lt;frozen importlib._bootstrap\u0026gt;\u0026#34;, line 975, in _find_and_load_unlocked File \u0026#34;\u0026lt;frozen importlib._bootstrap\u0026gt;\u0026#34;, line 671, in _load_unlocked File \u0026#34;\u0026lt;frozen importlib._bootstrap_external\u0026gt;\u0026#34;, line 843, in exec_module File \u0026#34;\u0026lt;frozen importlib._bootstrap\u0026gt;\u0026#34;, line 219, in _call_with_frames_removed File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/apps/cmdb/__init__.py\u0026#34;, line 6, in \u0026lt;module\u0026gt; from cmdb.task.sync_system_projects import sync_system_projects File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/apps/cmdb/task/sync_system_projects.py\u0026#34;, line 2, in \u0026lt;module\u0026gt; import MySQLdb File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/MySQLdb/__init__.py\u0026#34;, line 17, in \u0026lt;module\u0026gt; from . import _mysql ImportError: dlopen(/Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/MySQLdb/_mysql.cpython-38-darwin.so, 0x0002): tried: \u0026#39;/Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/MySQLdb/_mysql.cpython-38-darwin.so\u0026#39; (mach-o file, but is an incompatible architecture (have \u0026#39;x86_64\u0026#39;, need \u0026#39;arm64\u0026#39;)), \u0026#39;/System/Volumes/Preboot/Cryptexes/OS/Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/MySQLdb/_mysql.cpython-38-darwin.so\u0026#39; (no such file), \u0026#39;/Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/MySQLdb/_mysql.cpython-38-darwin.so\u0026#39; (mach-o file, but is an incompatible architecture (have \u0026#39;x86_64\u0026#39;, need \u0026#39;arm64\u0026#39;)) The above exception was the direct cause of the following exception: Traceback (most recent call last): File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/manage.py\u0026#34;, line 32, in \u0026lt;module\u0026gt; main() File \u0026#34;/Users/clin/File/Project/shuhe/devops-api/manage.py\u0026#34;, line 23, in main raise ImportError( ImportError: Couldn\u0026#39;t import Django. Are you sure it\u0026#39;s installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment? Process finished with exit code 1 解决方案，先卸载，然后安装对应arm版本\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 (venv) clin:devops-api/ (master) $ pip uninstall mysqlclient [17:01:12] Found existing installation: mysqlclient 2.2.4 Uninstalling mysqlclient-2.2.4: Would remove: /Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/MySQLdb/* /Users/clin/File/Project/shuhe/devops-api/venv/lib/python3.8/site-packages/mysqlclient-2.2.4.dist-info/* Proceed (Y/n)? y Successfully uninstalled mysqlclient-2.2.4 (venv) clin:devops-api/ (master) $ ARCHFLAGS=\u0026#34;-arch arm64\u0026#34; pip install mysqlclient --compile --no-cache-dir [17:03:01] Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/ Collecting mysqlclient Downloading https://pypi.tuna.tsinghua.edu.cn/packages/79/33/996dc0ba3f03e2399adc91a7de1f61cb14b57ebdb4cc6eca8a78723043cb/mysqlclient-2.2.4.tar.gz (90 kB) |████████████████████████████████| 90 kB 256 kB/s Installing build dependencies ... done Getting requirements to build wheel ... done Installing backend dependencies ... done Preparing metadata (pyproject.toml) ... done Building wheels for collected packages: mysqlclient Building wheel for mysqlclient (pyproject.toml) ... done Created wheel for mysqlclient: filename=mysqlclient-2.2.4-cp38-cp38-macosx_12_0_arm64.whl size=75702 sha256=a3e9ae6f73d411a05e3af30579534c66ae3e5e1b9087b0fc89b2b84987e3bd3d Stored in directory: /private/var/folders/y7/s2k99dl93dz6gwl73yxjtn2c0000gn/T/pip-ephem-wheel-cache-xf7j4ax5/wheels/34/f1/30/55bf7960d0b0ff6257e4a1462e0c08c5748202e5eaeeef826c Successfully built mysqlclient Installing collected packages: mysqlclient Successfully installed mysqlclient-2.2.4 WARNING: You are using pip version 21.3.1; however, version 24.0 is available. You should consider upgrading via the \u0026#39;/Users/clin/File/Project/shuhe/devops-api/venv/bin/python -m pip install --upgrade pip\u0026#39; command. 3. 另辟蹊径，安装pymysql解决问题 pip install pymysql\n在项目的__init__文件里配置(与settings同级)添加模块\n代码 1 2 3 import pymysql pymysql.install_as_MySQLdb() ","date":"2026-10-03","permalink":"/posts/mysqlclient-%E5%AE%89%E8%A3%85%E8%AE%B0%E5%BD%95/","title":"mysqlclient 安装记录"},{"categories":["mysql"],"content":"#sql #mysql #command\n1 库表结构操作 1.1 建库 1.1.1 DDL 创建数据库设置字符串集utf8 SQL 1 2 CREATE DATABASE `db_name` CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE DATABASE IF NOT EXISTS db_name; 字符集 长度 说明 GBK 2 支持中文，但是不是国际通用字符集 UTF-8 3 支持中英文混合场景，是国际通用字符集 latin1 2 MySQL默认字符集 utf8mb4 4 完全兼容UTF-8，用四个字节存储更多的字符 1.1.2 DDL 删库 SQL 1 DROP DATABASE `db_name`; 用户赋权\n查询当前用户权限 SQL 1 2 show grants; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO \u0026#39;username\u0026#39;@\u0026#39;%\u0026#39; WITH GRANT OPTION 删除权限 SQL 1 revoke SELECT, INSERT, UPDATE, DELETE ON `db_name`.`table_name` from \u0026#39;user_name\u0026#39;@\u0026#39;%\u0026#39;; mysql 8.0 创建用户 SQL 1 create user \u0026#39;username\u0026#39;@\u0026#39;host\u0026#39; identified by \u0026#39;password\u0026#39;; 给用户赋权 SQL 1 2 3 4 5 6 7 8 9 10 GRANT all privileges ON zabbix.* TO zabbix@localhost identified BY \u0026#39;password\u0026#39;; -- mysql 8.0 用户授权 grant all privileges on *.* to \u0026#39;username\u0026#39;@\u0026#39;%\u0026#39; with grant option; grant all privileges on jumpserver.* to \u0026#39;jumpserver\u0026#39;@\u0026#39;127.0.0.1\u0026#39; with grant option; -- 表粒度赋权，不能一次执行多条赋权 GRANT SELECT ON `db_zone_global`.`t_activity` TO \u0026#39;activity_rd\u0026#39;@\u0026#39;%\u0026#39;; GRANT SELECT ON `db_zone_global`.`t_activity_battle` TO \u0026#39;activity_rd\u0026#39;@\u0026#39;%\u0026#39;; GRANT SELECT ON `db_web_gm`.`t_common_op_log` TO \u0026#39;activity_rd\u0026#39;@\u0026#39;%\u0026#39;; 赋权 代码 1 2 3 4 5 GRANT USAGE ON *.* TO \u0026#39;prod_order_center\u0026#39;@\u0026#39;%\u0026#39; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `order_center`.* TO \u0026#39;prod_order_center\u0026#39;@\u0026#39;%\u0026#39;; select user,host from mysql.user; show grants for \u0026#39;prod_sysmgr\u0026#39;; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `yimi`.`erp_clients` TO \u0026#39;prod_sysmgr\u0026#39;@\u0026#39;%\u0026#39; 授权之后刷新权限 SQL 1 flush privileges; 查询用户权限 查询指定用户权限\nSQL 1 2 3 4 show grants for \u0026#39;stage-user\u0026#39;@\u0026#39;%\u0026#39;; -- 查看怎么进入的 select user(); 删除用户 SQL 1 drop user Jenkins@\u0026#39;localhost\u0026#39;; 数据库新建用户 数据库用户修改密码\n修改用户密码(登录状态) 代码 1 2 3 4 5 6 7 8 9 GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO \u0026#39;csz\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#34;asdfghjkl\u0026#34;; set password for \u0026#39;user\u0026#39;@\u0026#39;%\u0026#39; = password(\u0026#39;your_password\u0026#39;); UPDATE mysql.user SET password=PASSWORD(’新密码’) WHERE User=’root’; # the first time update password SET PASSWORD = PASSWORD(\u0026#39;password\u0026#39;); # mysql8 ALTER USER \u0026#39;root\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED WITH mysql_native_password BY \u0026#39;password\u0026#39;; 数据库用户赋权\n1.2 数据库表的增删改查 1.2.1 DDL 新增表 建表 SQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 CREATE TABLE `bo_eu_yw_gzsbsq_guanyuan` ( `id` char(36) NOT NULL COMMENT \u0026#39;唯一标志\u0026#39;, `orgid` varchar(36) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;组织id\u0026#39;, `bindid` char(36) NOT NULL DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;关联值\u0026#39;, `createdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT \u0026#39;创建时间\u0026#39;, `createuser` varchar(36) DEFAULT NULL COMMENT \u0026#39;创建人\u0026#39;, `updatedate` timestamp NULL DEFAULT NULL COMMENT \u0026#39;更新时间\u0026#39;, `updateuser` varchar(36) DEFAULT NULL COMMENT \u0026#39;更新人\u0026#39;, `processdefid` char(36) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;流程定义id\u0026#39;, `isend` smallint(1) NOT NULL DEFAULT \u0026#39;0\u0026#39; COMMENT \u0026#39;是否结束\u0026#39;, `djbh` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;单据编号\u0026#39;, `sqrq` datetime DEFAULT NULL COMMENT \u0026#39;申请日期\u0026#39;, `sqr` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;上报人\u0026#39;, `sqrzh` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;申请人账户\u0026#39;, `suborderno` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;子工单号\u0026#39;, `subbindid` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;子工单bindid\u0026#39;, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT \u0026#39;创建时间\u0026#39;, `updated_at` timestamp NULL DEFAULT NULL COMMENT \u0026#39;更新时间\u0026#39;, `status_flag` int(11) NOT NULL DEFAULT \u0026#39;0\u0026#39; COMMENT \u0026#39;更新状态\u0026#39;, `handler_name` varchar(255) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;处理人\u0026#39;, `handler_dep` varchar(255) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;处理人部门\u0026#39;, `comment_create_success_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT \u0026#39;制单成功时间\u0026#39;, `isend_name` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;是否结束name\u0026#39;, `sffp_name` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;是否复盘name\u0026#39;, `sfyjkfx_name` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;是否有监控发现name\u0026#39;, `sfqx_name` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;是否缺陷name\u0026#39;, `circulation_times` varchar(128) DEFAULT \u0026#39;\u0026#39; COMMENT \u0026#39;转办次数\u0026#39;, PRIMARY KEY (`bindid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT=\u0026#39;故障上报观远报表关联字段\u0026#39;; 1.2.2 DDL 删除表 SQL 1 2 -- 直接删除表，不检查是否存在 DROP TABLE table_name ; SQL 1 2 -- 检查是否存在表，然后删除表 DROP TABLE IF EXISTS table_name; 1.2.3 DDL 表新增字段，并指定字段位置 SQL 1 ALTER TABLE `cmdb_appops`.`sysops_aliyun_sls` ADD `product_key` varchar(50) NOT NULL DEFAULT \u0026#39;NA\u0026#39; COMMENT \u0026#39;数字产品 key\u0026#39; AFTER `project_id`; 1.2.4 DDL 修改表字段 SQL 1 ALTER TABLE cmdb_appops.appops_kuber_dump_file CHANGE `oss_download_url` `download_url` varchar(350) NOT NULL DEFAULT \u0026#39;NA\u0026#39; COMMENT \u0026#39;下载url\u0026#39;; 修改表字段属性(类型, 长度)\nSQL 1 ALTER TABLE `cmdb_appops`.`techops_split_bill_instance` MODIFY `project_id_name` varchar(150) NOT NULL DEFAULT \u0026#39;NA\u0026#39; COMMENT \u0026#39;关联项目名\u0026#39;; 修改表字段顺序 也可以用change\n代码 1 alter table app03_book modify `price` decimal(8,2) DEFAULT NULL after publishDate; mysql自增ID起始值修改方法 代码 1 2 3 4 5 6 7 8 9 10 11 12 alter table app03_authordetail AUTO_INCREMENT=10000; # 创建语句 CREATE TABLE `orders` ( `order_num` int(11) NOT NULL auto_increment, `order_date` datetime NOT NULL, `cust_id` int(11) NOT NULL, PRIMARY KEY (`order_num`), KEY `fk_orders_customers` (`cust_id`), CONSTRAINT `fk_orders_customers` FOREIGN KEY (`cust_id`) REFERENCES `customers` (`cust_id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8; 查看建表语句 代码 1 show create table [table_name] 1.2.5 DDL 删除表字段 SQL 1 ALTER TABLE `cmdb_appops`.`techops_split_bill_instance` DROP `product_key`; 数据库表的索引操作\n1.2.6 DDL 新增表索引 SQL 1 2 3 ALTER TABLE `cmdb_appops`.`appops_kubernetes_events` ADD UNIQUE INDEX `uid_v_unique_index`(`metadata_uid`, `metadata_resource_version`); ALTER TABLE `question_use_records` ADD INDEX `IDX_question_id`(`question_id`,`student_id`) USING BTREE; 1.2.7 DDL 删除表索引 SQL 1 ALTER TABLE `cmdb_appops`.`appops_kubernetes_events` DROP INDEX `uid_v_unique_index`; DCL 控制语句，设置自动提交 SQL 1 set autocommit=0; 修改index值，新表默认从1开始 SQL 1 ALTER TABLE project_mark_table auto_increment = 2; 表数据的增删改查\nDML 表数据插入 SQL 1 2 3 4 5 6 7 8 9 10 11 12 13 INSERT INTO cmdb_appops.techops_bill_flow_process_node_definition (node_id,node_name,next_node,back_node,repeat_exec,task_name,comment,created_at,updated_at) VALUES (\u0026#39;1\u0026#39;,\u0026#39;起始\u0026#39;,\u0026#39;2\u0026#39;,\u0026#39;1\u0026#39;,0,\u0026#39;NA\u0026#39;,\u0026#39;不可重复，赛峰解析阿里云数据\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:29\u0026#39;), (\u0026#39;2\u0026#39;,\u0026#39;初始化分账账单\u0026#39;,\u0026#39;3\u0026#39;,\u0026#39;1\u0026#39;,0,\u0026#39;apps.billmanagement.task.split_bill_2_instance.handle_split_bill_to_instance\u0026#39;,\u0026#39;不可重复，将阿里云数据生成id/split_id唯一\u0026#39;,\u0026#39;2023-03-06 23:33:11\u0026#39;,\u0026#39;2023-03-06 23:36:20\u0026#39;), (\u0026#39;3\u0026#39;,\u0026#39;一级实例id数据生成\u0026#39;,\u0026#39;4\u0026#39;,\u0026#39;2\u0026#39;,0,\u0026#39;apps.billmanagement.task.split_bill_2_instance.handle_split_bill_to_instance_rule_product\u0026#39;,\u0026#39;不可重复，将阿里云数据生成id/split_id唯一\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;4\u0026#39;,\u0026#39;一级分账规则生成\u0026#39;,\u0026#39;5\u0026#39;,\u0026#39;3\u0026#39;,0,\u0026#39;apps.billmanagement.task.split_bill_2_instance.handle_split_bill_to_instance_rule_product_init\u0026#39;,\u0026#39;不可重复，1将阿里云数据生成产品唯一，2同时获取上个月分摊逻辑数据写入当月数据\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;5\u0026#39;,\u0026#39;一级特殊实例id配置\u0026#39;,\u0026#39;6\u0026#39;,\u0026#39;4\u0026#39;,0,\u0026#39;apps.billmanagement.task.split_bill_2_instance.handle_split_bill_instance_particular_init\u0026#39;,\u0026#39;不可重复，将上个月特殊实例id拷贝写入当月\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;6\u0026#39;,\u0026#39;一级分账规则写入实例\u0026#39;,\u0026#39;7\u0026#39;,\u0026#39;5\u0026#39;,1,\u0026#39;apps/billmanagement/task/split_bill_rule_to_instance.handle_split_bill_rule_to_instance_main\u0026#39;,\u0026#39;根据一级分摊规则写入数据\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;7\u0026#39;,\u0026#39;二级分摊配置\u0026#39;,\u0026#39;8\u0026#39;,\u0026#39;6\u0026#39;,0,\u0026#39;NA\u0026#39;,\u0026#39;拷贝同步上个月录入二级分摊规则配置\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;8\u0026#39;,\u0026#39;二级分摊规则关联\u0026#39;,\u0026#39;9\u0026#39;,\u0026#39;7\u0026#39;,1,\u0026#39;NA\u0026#39;,\u0026#39;同步上个月录入二级分摊关联\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;9\u0026#39;,\u0026#39;二级分摊结果生成\u0026#39;,\u0026#39;10\u0026#39;,\u0026#39;8\u0026#39;,1,\u0026#39;NA\u0026#39;,\u0026#39;根据关联逻辑分摊二级账单\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;10\u0026#39;,\u0026#39;三级分摊配置\u0026#39;,\u0026#39;11\u0026#39;,\u0026#39;9\u0026#39;,0,\u0026#39;NA\u0026#39;,\u0026#39;拷贝同步上个月录入三级分摊规则配置\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;11\u0026#39;,\u0026#39;三级分摊规则关联\u0026#39;,\u0026#39;12\u0026#39;,\u0026#39;10\u0026#39;,1,\u0026#39;NA\u0026#39;,\u0026#39;同步上个月录入三级分摊关联\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;), (\u0026#39;12\u0026#39;,\u0026#39;三级分摊结果生成\u0026#39;,\u0026#39;12\u0026#39;,\u0026#39;11\u0026#39;,1,\u0026#39;NA\u0026#39;,\u0026#39;根据关联逻辑分摊三级账单\u0026#39;,\u0026#39;2023-03-01 23:59:35\u0026#39;,\u0026#39;2023-03-06 23:35:30\u0026#39;); DML 表删除数据 SQL 1 DELETE FROM `company_devops`.`rbac_permission` WHERE `id`=169; DML 清空表数据 SQL 1 TRUNCATE TABLE table_name; DML 表数据更新 SQL 1 2 3 -- 使用语法 UPDATE table_name SET field1=new-value1, field2=new-value2 [WHERE Clause] UPDATE `company_devops`.`rbac_permission` SET status=1,permission=2 WHERE `id`=168; DML 表数据查询 代码 1 select * from server_domain_and_port where type=\u0026#34;1\u0026#34;; DML 查询指定行数的数据，从第10行向后查询5条数据 SQL 1 SELECT userMobile FROM mis_user_info limit 10,5; sql 其他操作 https://blog.csdn.net/yageeart/article/details/7973381\n手动灌sql 代码 1 mysql -h db.adress.mysql.rds.aliyuncs.com -P 3306 -u user_name -p\u0026#39;password\u0026#39; db_name \u0026lt; insert.sql 也可以进入mysql，source file\n查看慢查询设置时间，单位秒 代码 1 show VARIABLES like \u0026#39;long_query_time\u0026#39;; 查询存储过程创建命令 代码 1 show create procedure sp_add_view_lesson_doc; 查询从库master信息 查看主库状态 SQL 1 SHOW MASTER STATUS; 主库状态结果\n代码 1 2 3 4 5 6 7 8 9 10 mysql\u0026gt; show master status\\G *************************** 1. row *************************** File: master-bin.000868 Position: 782491827 Binlog_Do_DB: Binlog_Ignore_DB: information_schema,mysql,test,performance_schema Executed_Gtid_Set: 7fb22524-166f-11ef-94d3-3cecefba3d48:1-9691144 1 row in set (0.01 sec) mysql\u0026gt; Bye 查看从库状态 SQL 1 SHOW SLAVE STATUS\\G 查看结果\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 mysql\u0026gt; show slave status\\G *************************** 1. row *************************** Slave_IO_State: Reconnecting after a failed master event read Master_Host: 10.64.57.88 Master_User: replicate Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.044525 Read_Master_Log_Pos: 948993872 Relay_Log_File: relay-bin.133574 Relay_Log_Pos: 948994087 Relay_Master_Log_File: master-bin.044525 Slave_IO_Running: Connecting Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: information_schema,mysql,test,performance_schema Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 948993872 Relay_Log_Space: 948994345 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: NULL Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 2003 Last_IO_Error: error reconnecting to master \u0026#39;replicate@10.64.57.88:3306\u0026#39; - retry-time: 60 retries: 11 Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 11 Master_UUID: d2748d8a-3475-11eb-b6ae-ac1f6bf552dc Master_Info_File: /data/percona_slave/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: 240604 22:33:51 Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: d2748d8a-3475-11eb-b6ae-ac1f6bf552dc:1-1098648649 Executed_Gtid_Set: d2748d8a-3475-11eb-b6ae-ac1f6bf552dc:1-1098648649 Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.00 sec)v 在主库查看从库的信息\nSQL 1 show processlist; 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 mysql\u0026gt; show processlist; +--------+-----------+---------------------+----------------------------+-------------+---------+---------------------------------------------------------------+------------------+-----------+---------------+ | Id | User | Host | db | Command | Time | State | Info | Rows_sent | Rows_examined | +--------+-----------+---------------------+----------------------------+-------------+---------+---------------------------------------------------------------+------------------+-----------+---------------+ | 42423 | replicate | 10.117.62.200:34662 | NULL | Binlog Dump | 1189092 | Master has sent all binlog to slave; waiting for more updates | NULL | 0 | 0 | | 70508 | moba | 10.117.67.101:35914 | db_zone_battlereport_12460 | Sleep | 13 | | NULL | 0 | 0 | | 70552 | moba | 10.117.67.101:36140 | db_zone_12460 | Sleep | 13 | | NULL | 0 | 1 | | 70651 | moba | 10.117.67.101:36302 | db_zone_12460 | Sleep | 3 | | NULL | 1 | 1 | | 70680 | moba | 10.117.67.101:36408 | db_zone_battlereport_12460 | Sleep | 1 | | NULL | 6 | 6 | | 71506 | moba | 10.117.67.101:37400 | db_zone_battlereport_8074 | Sleep | 0 | | NULL | 0 | 0 | | 71544 | moba | 10.117.67.101:37624 | db_zone_8074 | Sleep | 9 | | NULL | 0 | 1 | | 71590 | moba | 10.117.67.101:37786 | db_zone_8074 | Sleep | 5 | | NULL | 1 | 1 | | 71623 | moba | 10.117.67.101:37890 | db_zone_battlereport_8074 | Sleep | 0 | | NULL | 5 | 5 | | 71941 | moba | 10.117.67.101:38536 | db_zone_battlereport_9414 | Sleep | 6 | | NULL | 0 | 0 | | 71985 | moba | 10.117.67.101:38748 | db_zone_9414 | Sleep | 6 | | NULL | 0 | 1 | | 72070 | moba | 10.117.67.101:39002 | db_zone_9414 | Sleep | 2 | | NULL | 1 | 1 | | 72100 | moba | 10.117.67.101:39136 | db_zone_battlereport_9414 | Sleep | 8 | | NULL | 6 | 6 | | 72756 | moba | 10.117.67.101:40528 | db_zone_battlereport_12582 | Sleep | 3 | | NULL | 0 | 0 | | 72801 | moba | 10.117.67.101:40762 | db_zone_12582 | Sleep | 3 | | NULL | 0 | 1 | | 72886 | moba | 10.117.67.101:41066 | db_zone_12582 | Sleep | 1 | | NULL | 1 | 1 | | 72919 | moba | 10.117.67.101:41218 | db_zone_battlereport_12582 | Sleep | 8 | | NULL | 1 | 1 | | 73191 | moba | 10.117.67.101:41966 | db_zone_battlereport_12738 | Sleep | 0 | | NULL | 0 | 0 | | 73226 | moba | 10.117.67.101:42222 | db_zone_12738 | Sleep | 13 | | NULL | 0 | 1 | | 73288 | moba | 10.117.67.101:42510 | db_zone_12738 | Sleep | 0 | | NULL | 1 | 1 | | 73322 | moba | 10.117.67.101:42678 | db_zone_battlereport_12738 | Sleep | 17 | | NULL | 6 | 6 | | 153875 | moba | 10.117.67.101:50042 | db_zone_battlereport_2295 | Sleep | 1 | | NULL | 0 | 0 | | 153913 | moba | 10.117.67.101:50366 | db_zone_2295 | Sleep | 1 | | NULL | 0 | 1 | | 153991 | moba | 10.117.67.101:50754 | db_zone_2295 | Sleep | 7 | | NULL | 1 | 1 | | 154006 | moba | 10.117.67.101:50850 | db_zone_battlereport_2295 | Sleep | 18 | | NULL | 6 | 6 | | 154644 | moba | 10.117.67.101:54054 | db_zone_9414 | Sleep | 0 | | NULL | 0 | 0 | | 154645 | moba | 10.117.67.101:54132 | db_zone_9414 | Sleep | 22 | | NULL | 0 | 0 | | 154646 | moba | 10.117.67.101:54220 | db_zone_12460 | Sleep | 3 | | NULL | 0 | 0 | | 154647 | moba | 10.117.67.101:54262 | db_zone_12460 | Sleep | 12 | | NULL | 0 | 0 | | 154648 | moba | 10.117.67.101:54264 | db_zone_12460 | Sleep | 13 | | NULL | 0 | 0 | | 154652 | moba | 10.117.67.101:54358 | db_zone_8074 | Sleep | 0 | | NULL | 0 | 0 | | 154664 | moba | 10.117.67.101:54652 | db_zone_8074 | Sleep | 6 | | NULL | 0 | 0 | | 154665 | moba | 10.117.67.101:54654 | db_zone_8074 | Sleep | 7 | | NULL | 0 | 0 | | 154666 | moba | 10.117.67.101:54738 | db_zone_9414 | Sleep | 21 | | NULL | 0 | 0 | | 154668 | moba | 10.117.67.101:54892 | db_zone_2295 | Sleep | 0 | | NULL | 0 | 0 | | 154682 | moba | 10.117.67.101:55058 | db_zone_2295 | Sleep | 9 | | NULL | 0 | 0 | | 154683 | moba | 10.117.67.101:55060 | db_zone_2295 | Sleep | 9 | | NULL | 0 | 0 | | 154726 | moba | 10.117.67.101:55610 | db_zone_12582 | Sleep | 2 | | NULL | 0 | 0 | | 154727 | moba | 10.117.67.101:55666 | db_zone_12582 | Sleep | 36 | | NULL | 0 | 0 | | 154728 | moba | 10.117.67.101:55667 | db_zone_12582 | Sleep | 37 | | NULL | 0 | 0 | | 154771 | moba | 10.117.67.101:56176 | db_zone_12738 | Sleep | 0 | | NULL | 0 | 0 | | 154786 | moba | 10.117.67.101:56284 | db_zone_12738 | Sleep | 18 | | NULL | 0 | 0 | | 154787 | moba | 10.117.67.101:56300 | db_zone_12738 | Sleep | 198 | | NULL | 0 | 0 | | 321956 | moba | 10.117.67.101:53854 | NULL | Query | 0 | starting | show processlist | 0 | 0 | +--------+-----------+---------------------+----------------------------+-------------+---------+---------------------------------------------------------------+------------------+-----------+---------------+ 44 rows in set (0.00 sec) mysql\u0026gt; Bye 删除索引\nSQL 1 ALTER TABLE `duty_schedule_group_rule_relationship` DROP INDEX duty_schedule_group_rule_group_id_rule_id_a918fd5a_uniq; 复杂sql\nSQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 select distinct server from t_server where app = \u0026#39;MOBA\u0026#39; and server not in ( select distinct server from t_server where IF( SUBSTRING_INDEX(division, \u0026#39;.\u0026#39;,-1)\u0026gt;1000, FLOOR(SUBSTRING_INDEX(division, \u0026#39;.\u0026#39;,-1)/ 1000), SUBSTRING_INDEX(division, \u0026#39;.\u0026#39;,-1) ) in (55, 56, 57, 58, 59) ) and IF( SUBSTRING_INDEX(division, \u0026#39;.\u0026#39;,-1)\u0026gt;1000, FLOOR(SUBSTRING_INDEX(division, \u0026#39;.\u0026#39;,-1)/ 1000), SUBSTRING_INDEX(division, \u0026#39;.\u0026#39;,-1) ) in (100, 200); res\n代码 1 2 3 4 5 6 7 8 9 10 AccountProtectServer DataBridgeServer DataQueryServer GMServer NetEaseYidunServer PassProxyServer ProtoGateServer SmsServer TranslateServer ZoneManagerServer server 的分区是 100，200，且不是55, 56, 57, 58, 59分区\n","date":"2026-10-03","permalink":"/posts/mysql-sql-%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/","title":"MySQL SQL 常用命令"},{"categories":["Linux"],"content":"#iptable #linux\n本文主要介绍如下几点内容。\n禁止Filewalld开机启动 安装iptables 启动iptables并设置为开机启动 查看并修改iptables默认规则 禁止Filewalld开机启动 为了防止与iptables冲突，您必须先禁止Filewalld开机启动。\n1. 查看firewalld服务状态。 Bash 1 systemctl status firewalld 2. 当服务处于active状态，运行以下命令关闭Firewalld服务。 Bash 1 systemctl stop firewalld 3. 执行如下命令，禁止Filewalld开机启动。 Bash 1 systemctl disable firewalld 安装iptables 安装iptables。 Bash 1 yum install -y iptables-services 启动iptables并设置为开机启动 1. 启动iptables。 Bash 1 systemctl start iptables 2. 查看iptables是否成功启动。 Bash 1 systemctl status iptables 3. 设置iptables开机启动。 Bash 1 systemctl enable iptables.service 4. 设置完成后，重启实例验证配置。 Bash 1 systemctl reboot 查看并修改iptables默认规则 执行iptables -L命令，查看iptables默认规则，发现在默认规则下，INTPUT链允许来自任何主机的访问。可以参考如下步骤修改默认规则。\n1. 如果之前已经设置过规则，建议执行如下命令，备份原有的iptables文件，避免之前设置的规则丢失。 Bash 1 cp -a /etc/sysconfig/iptables /etc/sysconfig/iptables.bak 2. 清空所有规则。 Bash 1 iptables -F 3. 根据业务需求添加规则，放行或者禁用端口。示例：依次执行如下命令，放行80端口和22端口。 Bash 1 2 iptables -I INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT iptables -I INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT 示例：依次执行如下命令，添加规则，使INPUT链拒绝所有请求，即ECS实例会拒绝所有请求。如果是线上业务请勿直接操作，会直接中断业务。\nBash 1 iptables -P INPUT DROP 4. 确认新规则生效。 Bash 1 iptables -L 5. 保存添加的规则。 Bash 1 iptables-save \u0026gt; /etc/sysconfig/iptables 6. 将规则导入到iptables Bash 1 iptables-restore \u0026lt; /etc/sysconfig/iptables 案例：实际操作命令 仅对相关ip开放22端口 Bash 1 2 3 4 5 6 7 sudo iptables -A INPUT -p tcp -s 10.20.105.62 --dport 22 -j ACCEPT sudo iptables -A INPUT -p tcp -s 10.20.105.56 --dport 22 -j ACCEPT sudo iptables -A INPUT -p tcp -s 10.20.105.57 --dport 22 -j ACCEPT sudo iptables -A INPUT -p tcp -s 10.20.105.54 --dport 22 -j ACCEPT sudo iptables -A INPUT -p tcp -s 10.20.105.55 --dport 22 -j ACCEPT sudo iptables -A INPUT -p tcp --dport 22 -j DROP iptables-save \u0026gt;/etc/sysconfig/iptables ","date":"2026-10-03","permalink":"/posts/linux-iptables-%E4%BD%BF%E7%94%A8%E7%AC%94%E8%AE%B0/","title":"Linux iptables 使用笔记"},{"categories":["Kubernetes"],"content":"Pod异常问题排查 阿里云原文链接\n源码 https://github.com/kubernetes/kubernetes/blob/ad9b60e2c9ddb21e8b00cabbe27e639638a0ea88/pkg/apis/core/types.go#L2711\nhttps://github.com/kubernetes/kubernetes/blob/022d50fe3a1bdf2386395da7c266fede0c110040/pkg/kubelet/images/types.go#L29\n本文目录 类别 内容 诊断流程 诊断流程 常见排查方法 检查Pod的状态、检查Pod的详情、检查Pod的配置、检查Pod的事件、检查Pod的日志、检查Pod的监控、使用终端进入容器、Pod故障诊断 常见问题及解决方案 常见的Pod异常状态及处理方式、Pod OOM异常问题处理 诊断流程 查看Pod是否处于异常状态，具体操作，请参见检查Pod的状态。 a. 如果Pod状态异常，可通过查看Pod的事件、Pod的日志、Pod的配置等信息确定异常原因。具体操作，请参见常见排查方法。关于Pod异常 b. 状态及处理方式，请参见常见的Pod异常状态及处理方式。 如果Pod状态为Running但未正常工作，请参见Pod状态为Running但没正常工作。 若确认是Pod OOM异常问题，请参见Pod OOM异常问题处理。 如果问题仍未解决，请提交工单。\n常见的Pod异常状态及处理方式 Pod状态 Pod含义 解决方案 Pending Pod未被调度到节点上。 Pod状态为Pending Init:N/M Pod包含M个Init容器，其中N个已经启动完成。 Pod状态为Init:N/M（Init:Error和Init:CrashLoopBackOff） Init:Error Init容器已启动失败。 Pod状态为Init:N/M（Init:Error和Init:CrashLoopBackOff） Init:CrashLoopBackOff Init容器启动失败，反复重启。 Pod状态为Init:N/M（Init:Error和Init:CrashLoopBackOff） Completed Pod的启动命令已执行完毕。 Pod状态为Completed CrashLoopBackOff Pod启动失败，反复重启。 Pod状态为CrashLoopBackOff ImagePullBackOff Pod镜像拉取失败。 Pod状态为ImagePullBackOff Running Pod运行正常。 Pod Running但是未正常工作。 Pod状态为Running但没正常工作 Terminating Pod正在关闭中。 Pod状态为Terminating Evicted Pod被驱逐。 常见排查方法 检查Pod的状态 登录容器服务管理控制台。 在控制台左侧导航栏，单击集群。 在集群列表页面，单击目标集群名称或者目标集群右侧操作列下的详情。 在集群管理页左侧导航栏，选择工作负载 \u0026gt; 容器组。 在容器组页面左上角选择Pod所在的命名空间，查看Pod状态。 若状态为Running，说明Pod运行正常。 若状态不为Running，说明Pod状态异常，请参见常见的Pod异常状态及处理方式进行处理。 检查Pod的详情 登录容器服务管理控制台。 在控制台左侧导航栏，单击集群。 在集群列表页面中，单击目标集群名称或者目标集群右侧操作列下的详情。 在集群管理页左侧导航栏，选择工作负载 \u0026gt; 容器组。 在容器组页面左上角选择Pod所在的命名空间，然后单击目标Pod名称或者目标Pod右侧操作列下的详情，查看Pod的名称、镜像、Pod IP、所在节点等详细信息。 检查Pod的配置 登录容器服务管理控制台。 在控制台左侧导航栏，单击集群。 在集群列表页面，单击目标集群名称或者目标集群右侧操作列下的详情。 在集群管理页左侧导航栏，选择工作负载 \u0026gt; 容器组。 在容器组页面左上角选择Pod所在的命名空间，然后单击目标Pod名称或者目标Pod右侧操作列下的详情。 在Pod详情页面右上角单击编辑，查看Pod的YAML文件和详细配置。 检查Pod的事件 登录容器服务管理控制台。 在控制台左侧导航栏，单击集群。 在集群列表页面，单击目标集群名称或者目标集群右侧操作列下的详情。 在集群管理页左侧导航栏，选择工作负载 \u0026gt; 容器组。 在容器组页面左上角选择Pod所在的命名空间，然后单击目标Pod名称或者目标Pod右侧操作列下的详情。 在Pod详情页面下方单击事件页签，查看Pod的事件。 说明: Kubernetes默认保留最近1小时的事件，若需保存更长时间的事件，请参见创建并使用K8s事件中心。\n检查Pod的日志 登录容器服务管理控制台。 在控制台左侧导航栏，单击集群。 在集群列表页面，单击目标集群名称或者目标集群右侧操作列下的详情。 在集群管理页左侧导航栏，选择工作负载 \u0026gt; 容器组。 在容器组页面左上角选择Pod所在的命名空间，然后单击目标Pod名称或者目标Pod右侧操作列下的详情。 在Pod详情页面下方单击日志页签，查看Pod的日志。 说明: 阿里云ACK集群集成了日志服务，您可在创建集群时启用日志服务，快速采集集群的容器日志，包括容器的标准输出及容器内的文本文件。更多信息，请参见通过日志服务采集Kubernetes容器日志。\n检查Pod的监控 登录容器服务管理控制台。 在控制台左侧导航栏，单击集群。 在集群列表页面，单击目标集群名称或者目标集群右侧操作列下的详情。 在集群管理页左侧导航栏中，选择运维管理 \u0026gt; Prometheus监控 。 在Prometheus监控页面，单击集群监控概览页签，选择查看Pod的CPU、内存、网络I/O等监控大盘。 使用终端进入容器 登录容器服务管理控制台。 在控制台左侧导航栏，单击集群。 在集群列表页面，单击目标集群名称或者目标集群右侧操作列下的详情。 在集群管理页左侧导航栏，选择工作负载 \u0026gt; 容器组。 在容器组页面，单击目标容器组右侧操作列下的终端。\n可通过终端进入容器，在容器内查看本地文件等信息。 Pod故障诊断 登录容器服务管理控制台。 在控制台左侧导航栏，单击集群。 在集群列表页面，单击目标集群名称或者目标集群右侧操作列下的详情。 在集群管理页左侧导航栏，选择工作负载 \u0026gt; 容器组。 在容器组页面，单击目标容器组右侧操作列下的诊断。\n对该容器组进行故障诊断，根据诊断结果解决问题。更多信息，请参见使用集群诊断。 Pod状态为Pending 问题原因 若Pod停留在Pending状态，说明该Pod不能被调度到某一个节点上。通常是由于资源依赖、资源不足、该Pod使用了hostPort、污点和容忍等原因导致集群中缺乏需要的资源。\n问题现象 Pod的状态为Pending。\n解决方案 查看Pod的事件，根据事件描述，定位Pod不能被调度到节点的原因。主要原因有以下几类：\n资源依赖 创建Pod时，需要依赖于集群中ConfigMap、PVC等资源。例如，Pod添加存储卷声明前，存储卷声明需要先与存储卷绑定。\n资源不足 a. 在集群信息页面，选择节点管理 \u0026gt; 节点，查看容器组、CPU、内存的使用情况，确定集群的资源使用率。 说明: 某个节点实际使用的CPU、内存资源非常低，新加入一个Pod时，如果会导致实际使用的资源超过该节点最大可供使用的资源，则调度程序不会将该Pod分配到该节点。这样可避免在日常的流量高峰时段，节点上出现资源短缺的情况。\nb. 若集群中的CPU或内存都已经耗尽，可参考如下方法处理。 删除或减少不必要的Pod。具体操作，请参见管理容器组（Pod）。 根据自身业务情况，调整Pod的资源配置。具体操作，请参见设置容器的CPU和内存资源上下限。 在集群中添加新的节点。具体操作，请参见创建节点池。 为节点进行升配。具体操作，请参见升配Worker节点的资源。 该Pod使用了hostPort 如果您使用了hostPort，那么Deployment或ReplicationController中Replicas值不能超过集群中的节点数，因为每个实例的任意端口只有一个。如果该端口被其他应用占用，将导致Pod调度失败。因此建议您不要使用hostPort，您可以使用Service访问Pod，更多信息，请参见Service。\n污点和容忍 当您在Pod的事件中看到Taints或Tolerations时，说明是由于污点导致，您可以删除污点或者给Pod设置容忍。更多信息，请参见管理节点污点、创建无状态工作负载Deployment和污点和容忍。\nPod状态为Init:N/M（Init:Error和Init:CrashLoopBackOff） 问题原因 若Pod停留在Init:N/M状态，说明该Pod包含M个Init容器，其中N个已经启动完成，但仍有M-N个Init容器未启动成功。 若Pod停留在Init:Error状态，说明Pod中的Init容器启动失败。 若Pod停留在Init:CrashLoopBackOff状态，说明Pod中的Init容器启动失败并处于反复重启状态。 问题现象 Pod的状态为Init:N/M。 Pod的状态为Init:Error。 Pod的状态为Init:CrashLoopBackOff。 解决方案 查看Pod的事件，确认当前Pod中未启动的Init容器是否存在异常。具体操作，请参见检查Pod的事件。 查看Pod中未启动的Init容器的日志，通过日志内容排查问题。具体操作，请参见检查Pod的日志。 查看Pod的配置，确认未启动的Init容器配置是否正常。具体操作，请参见检查Pod的配置。关于Init容器的更多信息，请参见调试Init容器。 Pod状态为ImagePullBackOff 问题原因 若Pod停留在ImagePullBackOff状态，说明此Pod已被调度到某个节点，但拉取镜像失败。\n问题现象 Pod的状态为ImagePullBackOff。\n解决方案 通过查看该Pod的事件描述，查看具体拉取失败的镜像名称。\n确认容器镜像名称是否正确。 登录到Pod所在的节点，执行docker pull [$Image]命令，查看是否能正常抓取容器镜像。 说明: [$Image]为容器镜像的名称。\n若镜像拉取失败，请参见docker pull失败解决。 如果您使用的是私有镜像仓库，请参见使用私有镜像仓库创建应用解决。 Pod状态为CrashLoopBackOff 问题原因 若Pod停留在CrashLoopBackOff状态，说明容器中应用程序有问题。\n问题现象 Pod的状态为CrashLoopBackOff。\n解决方案 查看Pod的事件，确认当前Pod是否存在异常。具体操作，请参见检查Pod的事件。 查看Pod的日志，通过日志内容排查问题。具体操作，请参见检查Pod的日志。 查看Pod的配置，确认容器中的健康检查配置是否正常。具体操作，请参见检查Pod的配置。关于Pod健康检查的更多信息，请参见配置存活、就绪和启动探测器。 Pod状态为Completed 问题原因 若Pod出现Completed状态，说明容器中的启动命令已执行完毕，容器中的所有进程都已退出。\n问题现象 Pod的状态为Completed。\n解决方案 查看Pod的配置，确定Pod中容器的启动命令。具体操作，请参见检查Pod的配置。 查看Pod的日志，通过日志内容排查问题。具体操作，请参见检查Pod的日志。 Pod状态为Running但没正常工作 问题原因 部署使用的YAML文件有问题。\n问题现象 Pod状态为Running但没正常工作。\n解决方案 查看Pod的配置，确定Pod中容器的配置是否符合预期。具体操作，请参见检查Pod的配置。 使用以下方法，排查环境变量中的某一个Key是否存在拼写错误。\n以command拼写成commnd为例，说明拼写问题排查方法。 说明: 创建Pod时，环境变量中的某一个Key拼写错误的问题会被集群忽略，如Command拼写为Commnd，您仍能够使用该YAML文件创建资源。但容器运行时，不会执行有拼写问题的YAML文件，而是执行镜像中的默认命令。\na. 在执行 kubectl apply -f 命令前为其添加 --validate ，然后执行 kubectl apply --validate -f XXX.yaml 命令。\n如果您将 command 拼写成 commnd ，将看到错误信息 XXX] unknown field: commnd XXX] this may be a false alarm, see https://gXXXb.XXX/6842pods/test 。 b. 执行以下命令，将输出结果的pod.yaml文件与您创建Pod使用的文件进行对比。 Bash 1 kubectl get pods [$Pod] -o yaml \u0026gt; pod.yaml 说明: [$Pod]为异常Pod的名称，您可以通过kubectl get pods命令查看。\npod.yaml文件比您创建Pod所使用的文件多几行，说明已创建的Pod符合预期。 如果您创建Pod所使用文件里的代码行在pod.yaml文件中没有，说明您创建Pod使用的文件存在拼写问题。 查看Pod的日志，通过日志内容排查问题。具体操作，请参见检查Pod的日志。 可通过终端进入容器查看容器内的本地文件是否符合预期。具体操作，请参见使用终端进入容器。 Pod状态为Terminating 问题原因 若Pod的状态为Terminating，说明此Pod正处于关闭状态。\n问题现象 Pod状态为Terminating。\n解决方案 Pod停留在Terminating状态一段时间后会被自动删除。若Pod一直停留在Terminating状态，可执行如下命令强制删除：\nBash 1 kubectl delete pod [$Pod] -n [$namespace] --grace-period=0 --force Pod状态为Evicted 问题原因 当节点的内存、磁盘空间、文件系统的inode和操作系统可分配的PID等资源中的一个或者多个达到特定的消耗水平，就会引发kubelet主动地驱逐节点上一个或者多个Pod，以回收节点资源。\n问题现象 Pod的状态为Evicted。\n解决方案 执行以下命令，查看Pod的status.message字段，来确定Pod被驱逐的原因。 Bash 1 kubectl get pod [$Pod] -o yaml -n [$namespace] 预期输出：\nBash 1 2 3 4 status: message: \u0026#39;Pod the node had condition: [DiskPressure].\u0026#39; phase: Failed reason: Evicted 通过上述 status.message 字段，可以判断当前Pod是因为节点磁盘压力 (DiskPressure) 被驱逐。\n说明: 注意这里仅以磁盘压力驱逐为例，其它例如内存压力（MemoryPressure）和PID压力（PIDPressure）等也会以类似的方式展示。\n执行以下命令，删除被驱逐的Pod。 Bash 1 kubectl get pods -n [$namespace]| grep Evicted | awk \u0026#39;{print $1}\u0026#39; | xargs kubectl delete pod -n [$namespace] 以下汇总如何避免Pod被驱逐的方法： 内存压力： 根据自身业务情况，调整Pod的资源配置。具体操作，请参见设置容器的CPU和内存资源上下限。 为节点进行升配。具体操作，请参见升配Worker节点的资源。 磁盘压力： 定时清理节点上的业务Pod日志，避免磁盘被日志打满。 为节点进行磁盘扩容。具体操作，请参见步骤一：扩容云盘容量。 PID压力： 根据自身业务情况，调整Pod的资源配置，具体操作，请参见进程ID约束与预留。 Pod OOM异常问题处理 问题原因 当集群中的容器使用超过其限制的内存，容器可能会被终止，触发OOM（Out Of Memory）事件，导致容器异常退出。关于OOM事件，请参见为容器和Pod分配内存资源。\n问题现象 若被终止的进程为容器的阻塞进程，可能导致容器异常重启。 若出现OOM异常问题，登录容器服务管理控制台，在Pod详情页面单击事件页签可看到OOM事件pod was OOM killed。具体操作，请参见检查Pod的事件。 若集群配置了集群容器副本异常报警，则OOM事件出现时可收到相关报警。关于配置报警，请参见容器服务报警管理。 解决方案 查看发生OOM异常的Pod所在的节点。 命令行方式查看：执行以下命令，查看容器信息。 Bash 1 kubectl get pod [$Pod] -o wide -n [$namespace] 预期输出：\n代码 1 2 NAME READY STATUS RESTARTS AGE IP NODE pod_name 1/1 Running 0 25h 172.20.6.53 cn-hangzhou.192.168.0.198 控制台方式查看：查看Pod详情下的节点信息，请参见检查Pod的详情。 登录Pod所在的Node，查看内核日志/var/log/message。在日志中查询关键字out of memory，确认具体被OOM终止的进程。如果该进程是容器的阻塞进程，OOM终止后容器会重启。 通过Pod内存监控查看内存增长曲线，确定异常出现时间。具体操作，请参见检查Pod的监控。 根据监控、内存增长时间点、日志、进程名等信息，排查Pod内对应进程是否存在内存泄漏。 若OOM是进程内存泄漏导致，请您自行排查泄露原因。 若进程运行状态正常，则根据实际运行需要，适当增大Pod的内存限制，建议Pod的内存实际使用量不超过内存限制值的80%。具体操作，请参见设置容器的CPU和内存资源上下限。 ","date":"2026-10-03","permalink":"/posts/kubernetes-pod-%E5%BC%82%E5%B8%B8%E6%8E%92%E6%9F%A5/","title":"Kubernetes Pod 异常排查"},{"categories":["django"],"content":"django的as_view方法实现分析 框架概念 MVC/MTV django 是一个MVC(MTV)框架，官方定义是 MTV，但本质上其实是一样的，官方解释\n了解下 MTV/MVC 不同的含义 MVC 是 Model-View-Controller 的缩写，其中每个单词都有其不同的含义：\nModle 代表数据存储层，是对数据表的定义和数据的增删改查； View 代表视图层，是系统前端显示部分，它负责显示什么和如何进行显示； Controller 代表控制层，负责根据从 View 层输入的指令来检索 Model 层的数据，并在该层编写代码产生结果并输出。 Django 借鉴了经典的 MVC 模式，它也将交互的过程分为了 3 个层次，也就是 MTV 设计模式；\nModel：数据存储层，处理所有数据相关的业务，和数据库进行交互，并提供数据的增删改查； Template：模板层（也叫表现层）具体来处理页面的显示； View：业务逻辑层，处理具体的业务逻辑，它的作用是连通Model 层和 Template 。 框架使用方式 CBV/FBV Django有两种视图方式CBV以及FBV\nFBV 基于函数的视图 Function Based Views CBV 基于类的视图 Class Based Views class-based-views 相关名词解释链接 class-based-views/base\nFBV视图样例 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from .models import Choice, Question # ... def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST[\u0026#34;choice\u0026#34;]) except (KeyError, Choice.DoesNotExist): # Redisplay the question voting form. return render( request, \u0026#34;polls/detail.html\u0026#34;, { \u0026#34;question\u0026#34;: question, \u0026#34;error_message\u0026#34;: \u0026#34;You didn\u0026#39;t select a choice.\u0026#34;, }, ) else: selected_choice.votes += 1 selected_choice.save() # Always return an HttpResponseRedirect after successfully dealing # with POST data. This prevents data from being posted twice if a # user hits the Back button. return HttpResponseRedirect(reverse(\u0026#34;polls:results\u0026#34;, args=(question.id,))) 这种方式非常简单，感觉就像初学者在那里学习python，糊弄下搞个函数处理，所以感觉没有成就感，CBV 虽然本质上跟FBV一样，但看起来高大上\nCBV视图样例 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from .models import Choice, Question class IndexView(generic.ListView): template_name = \u0026#34;polls/index.html\u0026#34; context_object_name = \u0026#34;latest_question_list\u0026#34; def get_queryset(self): \u0026#34;\u0026#34;\u0026#34;Return the last five published questions.\u0026#34;\u0026#34;\u0026#34; return Question.objects.order_by(\u0026#34;-pub_date\u0026#34;)[:5] class DetailView(generic.DetailView): model = Question template_name = \u0026#34;polls/detail.html\u0026#34; class ResultsView(generic.DetailView): model = Question template_name = \u0026#34;polls/results.html\u0026#34; def vote(request, question_id): ... # same as above, no changes needed. 下面就是讲解django CBV的相关原理\ndjango的类视图拥有自动查找指定方法的功能, 通过调用是通过as_view()方法实现\nurls.py\nPython 1 2 3 4 5 from meduo_mall.demo import views urlpatterns = [ url(r\u0026#39;register/$\u0026#39;, views.Demo.as_view()) ] views.py\nPython 1 2 3 4 5 6 7 8 9 10 from django.views.generic import View class Demo(View): def get(self, request): return HttpResponse(\u0026#39;get page\u0026#39;) def post(self, request): return HttpResponse(\u0026#39;post page\u0026#39;) 为什么as_view能自动匹配指定的方法,\n先看源码\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @classonlymethod def as_view(cls, **initkwargs): # 实际上是一个闭包 返回 view函数 \u0026#34;\u0026#34;\u0026#34; Main entry point for a request-response process. \u0026#34;\u0026#34;\u0026#34; for key in initkwargs: if key in cls.http_method_names: raise TypeError(\u0026#34;You tried to pass in the %s method name as a \u0026#34; \u0026#34;keyword argument to %s(). Don\u0026#39;t do that.\u0026#34; % (key, cls.__name__)) if not hasattr(cls, key): # path(\u0026#39;test_class_view/\u0026#39;, TestClassView.as_view(test_key=\u0026#39;hh\u0026#39;), name=\u0026#39;cbv\u0026#39;) # 当as_view() 视图中有key值传递时，需要再类中定义属性，否则将启动报错 raise TypeError(\u0026#34;%s() received an invalid keyword %r. as_view \u0026#34; \u0026#34;only accepts arguments that are already \u0026#34; \u0026#34;attributes of the class.\u0026#34; % (cls.__name__, key)) def view(request, *args, **kwargs): # 作用：增加属性， 调用dispatch方法 self = cls(**initkwargs) # 创建一个 cls 的实例对象， cls 是调用这个方法的 类（Demo） if hasattr(self, \u0026#39;get\u0026#39;) and not hasattr(self, \u0026#39;head\u0026#39;): self.head = self.get self.request = request # 为对象增加 request， args， kwargs 三个属性 self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) # 找到指定的请求方法， 并调用它 view.view_class = cls # 在函数体内部不能给函数增加属性，在函数定义完成之后，可以给函数增加属性 view.view_initkwargs = initkwargs # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return view def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn\u0026#39;t exist, if request.method.lower() in self.http_method_names: # 判断请求的方法类视图是否拥有， http_method_names=[\u0026#39;get\u0026#39;, \u0026#39;post\u0026#39;] handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 如果存在 取出该方法 else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) # 执行该方法 简化版\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @classonlymethod def as_view(cls, **initkwargs): # 实际上是一个闭包 返回 view函数 \u0026#34;\u0026#34;\u0026#34; Main entry point for a request-response process. \u0026#34;\u0026#34;\u0026#34; def view(request, *args, **kwargs): # 作用：增加属性， 调用dispatch方法 self = cls(**initkwargs) # 创建一个 cls 的实例对象， cls 是调用这个方法的 类（Demo） if hasattr(self, \u0026#39;get\u0026#39;) and not hasattr(self, \u0026#39;head\u0026#39;): self.head = self.get self.request = request # 为对象增加 request， args， kwargs 三个属性 self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) # 找到指定的请求方法， 并调用它 return view def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn\u0026#39;t exist, if request.method.lower() in self.http_method_names: # 判断请求的方法类视图是否拥有， http_method_names=[\u0026#39;get\u0026#39;, \u0026#39;post\u0026#39;] handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 如果存在 取出该方法 else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) # 返回该请求方法执行的结果 再简化\nPython 1 2 3 4 5 6 7 8 9 10 11 12 def as_view(): # 校验 + 返回view方法 # 一些校验 ... def view(): # 执行视图 # 增加 为对象request, args, kwargs 属性 ... return dispatch() # 调用指定的请求方法 return view def dispatch(): # 真正的查找指定的方法, 并调用该方法 ... return handler() 调用顺序: as_view \u0026ndash;\u0026gt; view \u0026ndash;\u0026gt; dispatch\n可以看出as_view实际上是一个闭包, 它的作用做一些校验工作, 再返回view方法. 而view方法的作用是给请求对象补充三个参数, 并调用 dispatch方法处理 dispatch方法查找到指定的请求方法, 并执行 可以得出结论: 实际上真正实现查找的方法是 dispatch方法\n参考链接 django的as_view方法实现分析\nfunctools\n我们django中写CBV的时候继承的是View，rest_framework继承的是APIView 我们django中写CBV的时候继承的是View，rest_framework继承的是APIView，那么他们两个有什么不同呢~~~\nurlpatterns = [ url(r\u0026rsquo;^book$\u0026rsquo;, BookView.as_view()), url(r\u0026rsquo;^book/(?P\\d+)$\u0026rsquo;, BookEditView.as_view()), ] 我们可以看到，不管是View还是APIView最开始调用的都是as_view()方法 那我们走进源码看看\n我们能看到，APIView继承了View, 并且执行了View中的as_view()方法，最后把view返回了，用csrf_exempt()方法包裹后去掉了csrf的认证。\n那我们看看View中的as_view()方法做了什么\n我们看到了~在View中的as_view方法返回了view函数，而view函数执行了self.dispatch()方法~~但是这里的dispatch方法应该是我们APIView中的\n我们去initialize_request中看下把什么赋值给了request，并且赋值给了self.request, 也就是我们在视图中用的request.xxx到底是什么\n我们看到，这个方法返回的是Request这个类的实例对象~~我们注意我们看下这个Request类中的第一个参数request，是我们走我们django的时候的原来的request~\n我们看到了，这个Request类把原来的request赋值给了self._request, 也就是说以后_request是我们老的request，新的request是我们这个Request类~~\n那我们继承APIView之后请求来的数据都在哪呢~~\n","date":"2026-10-03","permalink":"/posts/django-as_view-%E6%96%B9%E6%B3%95%E5%AE%9E%E7%8E%B0%E5%88%86%E6%9E%90/","title":"Django as_view 方法实现分析"},{"categories":["AI"],"content":"#agent #ai #react #plantegg\n学习笔记：使用 CrewAI 复刻 Claude Code 级的 AI Agent 1. 什么是 CrewAI？ CrewAI 是一个用于编排协同式 AI Agent 的 Python 框架。如果说 LLM（大语言模型）是一个聪明的“大脑”，那么 CrewAI 就是一个**“项目经理”**。\n核心四元素： Role (角色)：定义 Agent 的身份（如：DBA 专家、技术作家）。\nTask (任务)：定义具体要做的事情。\nTools (工具)：Agent 可以调用的技能（如：SSH 远程执行、数据库查询）。\nProcess (流程)：规定 Agent 之间如何协作（顺序、层级等）。\n2. 核心原理：ReAct 工作模式 文章中复刻 Claude Code 的核心逻辑是 ReAct (Reasoning + Acting)，这是目前 Agent 最主流的工作方式。\nReAct 循环公式： Thought (思考) → Action (行动) → Observation (观察) → Thought (再思考)\nThought：模型根据用户问题，思考当前需要什么信息。\nAction：模型决定调用哪一个工具（Tool）以及参数是什么。\nObservation：Agent 执行工具并拿到结果（如 Linux 报错信息或 SQL 查询结果）。\nNext Thought：模型根据反馈结果，决定是继续调用工具，还是输出最终答案。\n3. Agent 开发三要素 开发一个功能强大的 Agent，本质上是在配置以下三个部分：\n要素 作用 示例 LLM (大脑) 提供推理和决策能力 Claude 3.5 Sonnet, GPT-4o Tools (手脚) 让模型能与现实世界交互 ssh_tool, mysql_query_tool Prompt (指令) 定义角色、规则和约束 \u0026ldquo;你是一个资深 DBA，只允许执行读操作\u0026rdquo; 4. 实战：从 0 到 1 组建运维 Agent 根据参考文章，构建一个 MySQL 运维 Agent 的步骤如下：\nStep 1: 定义自定义工具 工具是 Agent 的“手脚”。每个工具必须包含 description，因为模型是靠这段描述来判断何时使用该工具的。\nPython 1 2 3 4 5 6 7 8 9 from crewai.tools import BaseTool class MySQLQueryTool(BaseTool): name: str = \u0026#34;mysql_query\u0026#34; description: str = \u0026#34;用于执行 MySQL 只读查询语句，获取数据库状态。\u0026#34; def _run(self, query: str): # 实际执行 SQL 的 Python 代码 return \u0026#34;查询结果文本\u0026#34; Step 2: 配置 Agent 与 Task Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from crewai import Agent, Task, Crew # 1. 定义 Agent dba_agent = Agent( role=\u0026#34;DBA 运维专家\u0026#34;, goal=\u0026#34;诊断 MySQL 性能瓶颈\u0026#34;, backstory=\u0026#34;你拥有 10 年运维经验，擅长通过慢查询日志定位问题。\u0026#34;, tools=[MySQLQueryTool()], # 载入工具 verbose=True # 开启 debug 模式，查看思考过程 ) # 3. 定义任务 task = Task( description=\u0026#34;分析 10.0.0.1 机器上的慢查询并给出优化建议\u0026#34;, expected_output=\u0026#34;一份包含问题原因和优化建议的 MD 报告\u0026#34;, agent=dba_agent ) # 4. 组建团队启动 crew = Crew(agents=[dba_agent], tasks=[task]) result = crew.kickoff() 5. 关键洞察（踩坑总结） 模型没有记忆：每一轮 ReAct 交互，Agent 都会把之前的对话历史、工具返回结果打包成一个巨大的 Prompt 重新发给模型。这意味着 Token 消耗会随轮数剧增。\n模型不会执行工具：大模型只输出“指令字符串”，真正去执行 SSH 或 Python 代码的是 CrewAI 框架。\n工具描述至关重要：如果 Agent 总是用错工具，通常是因为 description 写得不清晰。\n安全边界：在运维场景下，必须在工具的 Python 代码层做好拦截（如禁止 DROP, DELETE 等操作），不能仅靠 Prompt 约束。\n6. 进阶方向 接入更多数据源：将 Prometheus 监控、日志平台 API 封装为 Tool。\n多 Agent 协作：一个 Agent 负责搜集数据，另一个 Agent 负责编写最终诊断报告。\nHuman-in-the-loop：在执行高风险 Action 前，让 Agent 停下来等待人类授权。\n笔记来源：基于 plantegg 博客 与 CrewAI 官方文档整理。\n","date":"2026-10-03","permalink":"/posts/ai-agent-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0crewai-%E4%B8%8E-react/","title":"AI Agent 学习笔记：CrewAI 与 ReAct"},{"categories":["GitHub"],"content":"博客使用两个 GitHub 仓库：\n文本 1 2 3 4 5 6 sona201/blogs ↓ Hugo build ↓ GitHub Actions sona201/sona201.github.io ↓ GitHub Pages 其中 blogs 保存 Hugo 源码，sona201.github.io 保存 Hugo 生成的静态页面。\n原来的 GitHub Actions 一直使用 PERSONAL_TOKEN 将 public/ push 到另一个仓库。很久没有更新博客后再次发布，Actions 在 Deploy 阶段失败：\n文本 1 2 3 4 5 remote: Invalid username or token. Password authentication is not supported for Git operations. fatal: Authentication failed for \u0026#39;https://github.com/sona201/sona201.github.io.git/\u0026#39; Hugo 的 Build Web 是成功的，因此问题不是文章或 Hugo 构建，而是跨仓库 push 使用的旧 Personal Access Token 已失效。\n这篇记录如何创建一个权限尽可能小的 Fine-grained Personal Access Token，并更新 GitHub Actions Secret。\n一、为什么需要额外 Token GitHub Actions 会自动提供 GITHUB_TOKEN，但它主要针对当前 workflow 所在仓库。\n当前 workflow 位于：\n文本 1 sona201/blogs 而构建结果需要 push 到：\n文本 1 sona201/sona201.github.io 这是跨仓库写入，因此这里继续使用一个只允许写目标仓库的 Fine-grained PAT。\n目标是让 token 只具备：\n文本 1 2 3 sona201/sona201.github.io ↓ Contents: Read and write 不授予整个账号所有仓库的写权限。\n二、创建 Fine-grained Personal Access Token 进入 GitHub 个人设置：\n文本 1 2 3 4 5 6 GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token Token name 可以填写：\n文本 1 blogs-deploy Description 可以填写：\n文本 1 Deploy Hugo build output from sona201/blogs to sona201/sona201.github.io Expiration 根据自己的维护习惯设置。例如设置一年有效期：\n文本 1 2027/10/03 需要注意：Fine-grained PAT 到期后不会自动生成新的 token，也不会自动更新 GitHub Actions Secret。到期后需要重新创建并替换 Secret。\n三、限制 Repository access Repository access 选择：\n文本 1 Only select repositories 然后只选择：\n文本 1 sona201/sona201.github.io 不要选择 All repositories。\n这样即使这个 token 泄漏，它也不能修改账号下其他仓库。\n四、配置最小 Repository permissions 这里最容易选错。\n最开始容易误选：\n文本 1 Actions: Read and write 但部署动作本质上是：\nBash 1 git push origin master 需要修改的是仓库内容，而不是管理 GitHub Actions。\n因此删除 Actions / Workflows 等无关权限，然后：\n文本 1 2 3 Add permissions → Contents → Read and write 最终权限应该只有：\n文本 1 2 3 4 5 6 7 8 9 Repository access └── sona201/sona201.github.io Repository permissions ├── Contents │ └── Read and write │ └── Metadata └── Read-only 其中 Metadata Read-only 是 GitHub 要求的基础权限。\n不需要：\nActions: Read and write Workflows: Read and write Issues Pull requests Administration 权限越少越好。\n五、生成 Token 确认配置以后点击：\n文本 1 Generate token GitHub 会生成类似：\n文本 1 github_pat_xxxxxxxxxxxxxxxxx 注意：\nToken 属于凭据，不要写进博客、代码、commit、截图或聊天记录中。\n生成后的 token 通常只会完整显示一次，立即复制。\n六、更新 GitHub Actions Secret 进入 Hugo 源码仓库：\n文本 1 2 3 4 sona201/blogs → Settings → Secrets and variables → Actions 找到：\n文本 1 PERSONAL_TOKEN 点击 Update。\n将刚才生成的 github_pat_... 粘贴到 Value：\n文本 1 2 PERSONAL_TOKEN Value: [新的 Fine-grained PAT] 点击：\n文本 1 Update secret GitHub 不会再把 Secret 的原值显示出来，这是正常的。\n七、Workflow 如何使用 Secret 当前 Hugo 部署 workflow 使用 peaceiris/actions-gh-pages：\nYAML 1 2 3 4 5 6 7 8 - name: Deploy Web uses: peaceiris/actions-gh-pages@v4 with: personal_token: ${{ secrets.PERSONAL_TOKEN }} external_repository: sona201/sona201.github.io publish_branch: master publish_dir: ./public commit_message: ${{ github.event.head_commit.message || \u0026#39;manual deploy\u0026#39; }} 这里不要直接写：\nYAML 1 personal_token: github_pat_xxxxx 正确方式始终是从 GitHub Actions Secret 读取：\nYAML 1 personal_token: ${{ secrets.PERSONAL_TOKEN }} 八、重新运行失败的 Workflow 更新 Secret 后：\n文本 1 2 3 4 5 6 blogs → Actions → deploy hugo → 找到失败的运行 → Re-run jobs → Re-run failed jobs 正常流程应该变成：\n文本 1 2 3 4 Checkout ✓ Setup Hugo ✓ Build Web ✓ Deploy Web ✓ 如果 Build Web 成功但 Deploy Web 仍然出现：\n文本 1 2 Invalid username or token Authentication failed 优先检查：\nPERSONAL_TOKEN Secret 是否确实更新。 Fine-grained PAT 是否选择了正确的 Resource owner。 Repository access 是否包含目标仓库。 Contents 是否为 Read and write。 Token 是否过期或被撤销。 Workflow 引用的 Secret 名称是否与 PERSONAL_TOKEN 完全一致。 九、顺便升级旧 GitHub Actions 这次排查时还发现旧 workflow 使用：\nYAML 1 2 3 actions/checkout@v2 peaceiris/actions-hugo@v2 peaceiris/actions-gh-pages@v3 GitHub Runner 已提示旧 Node.js runtime 被弃用。\n因此同步更新为：\nYAML 1 2 3 actions/checkout@v7 peaceiris/actions-hugo@v3 peaceiris/actions-gh-pages@v4 并增加：\nYAML 1 2 3 4 5 6 permissions: contents: read concurrency: group: deploy-hugo cancel-in-progress: true Hugo 本身暂时仍保持原来的 0.101.0，避免同时升级 Hugo 和主题导致额外兼容问题。先恢复部署链路，再单独处理 Hugo/主题升级。\n十、PAT 方案的缺点 这个方案虽然简单，但有一个长期维护问题：\n文本 1 2 3 4 5 6 7 8 9 Fine-grained PAT ↓ 到期 ↓ GitHub Actions 部署失败 ↓ 重新生成 PAT ↓ 更新 PERSONAL_TOKEN GitHub 不会自动轮换这个 PAT。\n如果以后不想维护 PAT，可以考虑：\n使用 Deploy Key，让源码仓库固定向 Pages 仓库发布。 调整为单仓库 Hugo + GitHub Pages 官方 Actions 部署。 重新设计发布流程，避免 workflow 跨仓库 push。 目前对于已有的：\n文本 1 blogs → sona201.github.io 双仓库结构，Fine-grained PAT 的优点是迁移成本低，并且可以把写权限严格限制在 sona201.github.io 一个仓库。\n总结 这次实际配置最终为：\n文本 1 2 3 4 5 6 7 8 9 10 11 12 13 Token: blogs-deploy Resource owner: sona201 Repository access: Only select repositories └── sona201/sona201.github.io Repository permissions: ├── Contents: Read and write └── Metadata: Read-only 然后保存到：\n文本 1 2 3 4 5 sona201/blogs → Settings → Secrets and variables → Actions → PERSONAL_TOKEN GitHub Actions 再通过：\nYAML 1 personal_token: ${{ secrets.PERSONAL_TOKEN }} 完成 Hugo 构建产物向 sona201.github.io 的跨仓库发布。\n这套权限已经足够完成部署，没有必要为了方便给 token 整个账号或所有仓库的写权限。\n","date":"2026-10-03","permalink":"/posts/github-actions-%E4%BD%BF%E7%94%A8-fine-grained-pat-%E8%B7%A8%E4%BB%93%E5%BA%93%E9%83%A8%E7%BD%B2-hugo/","title":"GitHub Actions 使用 Fine-grained PAT 跨仓库部署 Hugo"},{"categories":["网络"],"content":"最近在学习 VLAN、子网、路由和家庭组网，手里正好有一台闲置多年的斐讯 K2。它之前已经刷成 Padavan，但管理页面切换一次经常需要 30 秒左右，于是决定把它重新刷成 OpenWrt，当作网络实验机。\n这篇文章记录这次实际操作过程。设备和 Breed 版本不同，操作方式可能不同，刷机前一定先确认自己的型号和 Flash 布局。\n一、设备信息 路由器铭牌信息：\n型号：斐讯 K2 / PSG1218 硬件版本：A6 SoC：MediaTek MT7620A RAM：64 MB DDR2 Flash：Winbond W25Q64，8 MB 当前系统：Padavan 3.4.3.9-099 Bootloader：Breed 1.1 r1033 Padavan SSH 中可以看到：\n文本 1 2 3 MediaTek SoC: MT7620A, RevID: 0206, RAM: DDR2, XTAL: 20MHz CPU/OCP/SYS frequency: 580/193/193 MHz SPI flash chip: W25Q64BV (ef 40170000) (8192 Kbytes) 当前 Flash 分区：\n文本 1 2 3 4 5 6 7 0x000000000000-0x000000030000 : \u0026#34;Bootloader\u0026#34; 0x000000030000-0x000000040000 : \u0026#34;Config\u0026#34; 0x000000040000-0x000000050000 : \u0026#34;Factory\u0026#34; 0x000000050000-0x00000016c2d0 : \u0026#34;Kernel\u0026#34; 0x00000016c2d0-0x0000007c0000 : \u0026#34;RootFS\u0026#34; 0x0000007c0000-0x000000800000 : \u0026#34;Storage\u0026#34; 0x000000050000-0x000000800000 : \u0026#34;Firmware_Stub\u0026#34; 固件从 0x50000 开始，这一点后面选择 K2 v22.4 profile 时很重要。\n二、先判断 Padavan 卡顿是不是硬件问题 Padavan 后台非常卡，但路由器本身负载并不高，所以先测试局域网延迟：\nBash 1 ping 192.168.123.1 实际结果平均只有约 1 ms，并且没有丢包。同时 Padavan 页面显示 CPU 占用很低、还有约 30 MB 空闲内存。\n因此更像是旧 Padavan/Web 服务或配置问题，而不是单纯网络链路太慢。\n既然本来就准备把 K2 当实验机，最终没有继续修旧 Padavan。\n三、确认 Breed Breed 可以理解为路由器上的 Bootloader/救援环境，有点类似 PC 的 BIOS/UEFI。只要 Breed 本身还在，即使后面的 OpenWrt 刷坏了，一般仍有机会重新刷固件恢复。\n进入 Breed 的方法：\nK2 断电。 按住 RESET。 保持 RESET 的同时通电。 等几秒后松开 RESET。 电脑有线连接 LAN 口。 将电脑放到 192.168.1.0/24 网段。 浏览器访问 http://192.168.1.1。 这台机器成功进入 Breed，显示：\n文本 1 2 3 4 Breed 版本：1.1 (r1033) CPU：MediaTek MT7620A 内存：64MB DDR2 Flash：Winbond W25Q64 8MB 四、刷之前一定先备份 在 Breed 的「固件备份」页面分别保存：\n文本 1 2 3 eeprom.bin firmware.bin full.bin 其中：\neeprom.bin：Factory/EEPROM 数据，包含无线校准等设备相关信息，非常重要。 firmware.bin：当前 Padavan 固件备份。 full.bin：完整 Flash 备份，适合严重故障时恢复。 这三个文件建议至少保存两份。\n刷机过程中不要随便执行：\n文本 1 2 3 mtd erase ... mtd write ... dd ... of=/dev/mtd... 尤其不要乱写 Bootloader 和 Factory 分区。\n五、选择 OpenWrt 固件 最终选择：\n文本 1 2 3 OpenWrt 24.10.8 Target: ramips/mt7620 Device: Phicomm K2 v22.4 or older 官方目录：\nhttps://downloads.openwrt.org/releases/24.10.8/targets/ramips/mt7620/\n下载了两个文件：\n文本 1 2 openwrt-24.10.8-ramips-mt7620-phicomm_k2-v22.4-initramfs-kernel.bin openwrt-24.10.8-ramips-mt7620-phicomm_k2-v22.4-squashfs-sysupgrade.bin 这台 K2 的 Padavan Flash 布局中 firmware 从 0x50000 开始，与 v22.4 profile 对应。正式启动 OpenWrt 后，LuCI 最终也识别为：\n文本 1 Phicomm K2 v22.4 or older 校验 SHA256 不要跳过固件校验。\ninitramfs：\nBash 1 shasum -a 256 ~/Downloads/openwrt-24.10.8-ramips-mt7620-phicomm_k2-v22.4-initramfs-kernel.bin 得到：\n文本 1 63fd43bd57e499e10e05fbaa46551a5150fa087f16c7030d348c9f77ff2539f2 sysupgrade：\nBash 1 shasum -a 256 ~/Downloads/openwrt-24.10.8-ramips-mt7620-phicomm_k2-v22.4-squashfs-sysupgrade.bin 得到：\n文本 1 da17725d7b626b67feeed0a4b3ccb71970238539450f43d5b36426cccb344dc8 都与 OpenWrt 官方 SHA256 一致后再继续。\n六、先进入 OpenWrt recovery/initramfs 这次没有一上来就在 OpenWrt 中永久写入 sysupgrade，而是先进入 OpenWrt 的 recovery/initramfs 环境。\n成功后 LuCI 顶部会看到类似提示：\n文本 1 System running in recovery (initramfs) mode. 此时 OpenWrt 是临时运行状态。\n确认页面能够正常打开、设备型号能够识别后，再进行正式安装。\n不同 Breed 版本提供的启动/刷写方式可能不同。这里最重要的是先确认自己已经进入 OpenWrt recovery/initramfs，再执行下面的 sysupgrade，而不是照搬其他型号路由器的 Breed 参数。\n七、正式刷入 OpenWrt 进入 recovery OpenWrt 后：\n文本 1 2 3 System → Backup / Flash Firmware → Flash new firmware image 选择：\n文本 1 openwrt-24.10.8-ramips-mt7620-phicomm_k2-v22.4-squashfs-sysupgrade.bin 下面是这次刷机时的实际截图：当前仍处于 recovery/initramfs 模式，选择的是 K2 v22.4 的 sysupgrade 固件，文件大小为 6.25 MiB。\n点击 Upload 后还会进入校验确认页面，此时尚未开始正式写入 Flash。\n上传以后 OpenWrt 会再次显示文件大小和 SHA256。\n实际看到：\n文本 1 2 Size: 6.25 MiB SHA256: da17725d7b626b67feeed0a4b3ccb71970238539450f43d5b36426cccb344dc8 这里再次确认 SHA256 与 Mac 本地以及官方值一致。\n由于是从 Padavan 全新切换到 OpenWrt，我取消了：\n文本 1 Keep settings and retain the current configuration 然后点击 Continue 开始写 Flash。\n写入期间：\n不断电。 不按 RESET。 不拔网线。 不刷新页面。 等待路由器完成写入和重启。\n八、第一次启动 正式 OpenWrt 启动后访问：\n文本 1 http://192.168.1.1 LuCI 显示：\n文本 1 2 3 4 Model: Phicomm K2 v22.4 or older Firmware Version: OpenWrt 24.10.8 Kernel Version: 6.6.144 Target Platform: ramips/mt7620 这说明 profile 选择正确，刷机成功。\n第一件事先设置 root 密码，然后通过 SSH 检查：\nBash 1 2 3 4 5 6 ssh root@192.168.1.1 df -h free -m uptime cat /proc/mtd 最终 Flash 分区类似：\n文本 1 2 3 4 5 6 7 mtd0: \u0026#34;u-boot\u0026#34; mtd1: \u0026#34;u-boot-env\u0026#34; mtd2: \u0026#34;factory\u0026#34; mtd3: \u0026#34;firmware\u0026#34; mtd4: \u0026#34;kernel\u0026#34; mtd5: \u0026#34;rootfs\u0026#34; mtd6: \u0026#34;rootfs_data\u0026#34; Breed 和 Factory 都还保留着。\n九、8MB Flash 的现实限制 刷完以后：\n文本 1 2 /overlay 总空间约 1.4 MB 剩余约 1.2 MB 所以 K2 跑 OpenWrt 最大的问题不是 CPU，而是 Flash 和 RAM 太小。\n看到：\n文本 1 /dev/root 100% 不用紧张。OpenWrt 的 /dev/root 是只读 SquashFS，显示 100% 属于正常现象。真正需要关注的是可写的 /overlay。\n这台 K2 比较适合：\n学 VLAN。 学 DHCP。 学静态路由。 学 NAT 和防火墙。 学 DNS。 测试 Wi-Fi 网络隔离。 不适合：\nOpenClash。 Passwall。 AdGuard Home。 大量 LuCI 插件。 只有 1 MB 左右的可写空间，随便安装几个软件包就可能把 Flash 填满。\n十、无线还能不能用？ 可以。\n刷 OpenWrt 后并不是无线功能消失了。K2 的官方 OpenWrt profile 已包含对应无线驱动，通常不需要另外安装驱动模块。\nOpenWrt 首次启动时无线可能默认关闭，在：\n文本 1 Network → Wireless 配置 SSID、加密方式并启用即可。\n如果无线页面没有识别到 Radio，可以通过 SSH 检查：\nBash 1 2 3 4 5 wifi status iwinfo ls /sys/class/ieee80211/ dmesg | grep -Ei \u0026#39;mt76|wifi|wlan|80211\u0026#39; opkg list-installed | grep -E \u0026#39;mt76|mac80211|wireless|wpad\u0026#39; K2 的 Flash 太小，不要看到无线没开启就先执行 opkg install，应该先确认驱动是否已经存在。\n总结 这次实际升级路径可以概括成：\n文本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 K2 PSG1218 A6 ↓ Padavan ↓ 确认 Breed ↓ 备份 EEPROM / Padavan / Full Flash ↓ OpenWrt 24.10.8 v22.4 initramfs/recovery ↓ 校验 sysupgrade SHA256 ↓ OpenWrt sysupgrade ↓ OpenWrt 24.10.8 对一台 8MB Flash、64MB RAM 的老 K2 来说，OpenWrt 24.10.8 已经比较吃紧，不适合继续堆插件。但拿来做 VLAN、子网、路由、DHCP、防火墙等网络实验，反而是个不错的废物利用方式。\n参考 OpenWrt Phicomm K2：https://openwrt.org/toh/phicomm/k2 OpenWrt 24.10.8 ramips/mt7620：https://downloads.openwrt.org/releases/24.10.8/targets/ramips/mt7620/ ","date":"2026-10-03","permalink":"/posts/%E6%96%90%E8%AE%AF-k2-%E4%BB%8E-padavan-%E5%88%B7-openwrt-24.10.8/","title":"斐讯 K2 从 Padavan 刷 OpenWrt 24.10.8"},{"categories":["Linux"],"content":"yum 安装的时候，发现某个repo网络异常\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [root@example-host ~]# strace -f -e trace=network yum install mtr -bash: strace: command not found [root@example-host ~]# strace -bash: strace: command not found [root@example-host ~]# yum install strace Loaded plugins: fastestmirror, product-id, search-disabled-repos Determining fastest mirrors epel/x86_64/metalink | 4.5 kB 00:00:00 * base: mirrors.aliyun.com * epel: d2lzkl7pfhq30w.cloudfront.net * extras: mirrors.aliyun.com * updates: mirrors.aliyun.com base | 3.6 kB 00:00:00 docker-ce-stable | 3.5 kB 00:00:00 endpoint | 2.9 kB 00:00:00 extras | 2.9 kB 00:00:00 https://rpm.releases.hashicorp.com/RHEL/7/x86_64/stable/repodata/repomd.xml: [Errno 14] HTTPS Error 404 - Not Found Trying other mirror. To address this issue please refer to the below wiki article https://wiki.centos.org/yum-errors If above article doesn\u0026#39;t help to resolve this issue please use https://bugs.centos.org/. One of the configured repositories failed (Hashicorp Stable - x86_64), and yum doesn\u0026#39;t have enough cached data to continue. At this point the only safe thing yum can do is fail. There are a few ways to work \u0026#34;fix\u0026#34; this: 1. Contact the upstream for the repository and get them to fix the problem. 2. Reconfigure the baseurl/etc. for the repository, to point to a working upstream. This is most often useful if you are using a newer distribution release than is supported by the repository (and the packages for the previous distribution release still work). 3. Run the command with the repository temporarily disabled yum --disablerepo=hashicorp ... 4. Disable the repository permanently, so yum won\u0026#39;t use it by default. Yum will then just ignore the repository until you permanently enable it again or use --enablerepo for temporary usage: yum-config-manager --disable hashicorp or subscription-manager repos --disable=hashicorp 5. Configure the failing repository to be skipped, if it is unavailable. Note that yum will try to contact the repo. when it runs most commands, so will have to try and fail each time (and thus. yum will be be much slower). If it is a very temporary problem though, this is often a nice compromise: yum-config-manager --save --setopt=hashicorp.skip_if_unavailable=true failure: repodata/repomd.xml from hashicorp: [Errno 256] No more mirrors to try. https://rpm.releases.hashicorp.com/RHEL/7/x86_64/stable/repodata/repomd.xml: [Errno 14] HTTPS Error 404 - Not Found 上面显示某个repo 网络连接异常，如果网络异常，可以先指定禁用repo，前提是需要知道repo名字 不行的话，可以手动将 xxx.repo 重命名 xxx.repo_back\nBash 1 yum install mtr --disablerepo=hashicorp --disablerepo=epel ","date":"2026-04-10","permalink":"/posts/yum-repository-failure/","title":"Yum 软件源失败时的处理"},{"categories":["Linux"],"content":"#linux #strace\n监听命令执行的strace，不再适用 strace -p （已经启动的进程）\nBash 1 strace -f -tt -T -o yum_install.log yum install -y dnsmasq -f: 跟踪由 yum 产生的所有子进程（非常重要，因为下载和安装由不同进程处理）。 -tt: 在每行开头显示微秒级的时间戳，方便看哪里卡住了。 -T: 在行尾显示每个系统调用的耗时。 -o: 将结果输出到文件，避免刷屏。 只检查网络连接问题\n代码 1 strace -f -e trace=network yum makecache 理解 strace 的输出，其实就像是在看一份**“程序与系统内核的聊天记录”**。每一行都代表程序向系统申请做了一件事。\n为了让你快速上手，我们可以把每一行拆解为 “谁、在什么时候、想干什么、结果如何、花了多久” 这五个要素。\n输出内容\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 5464 04:15:36.052606 close(14) = 0 \u0026lt;0.000010\u0026gt; 5464 04:15:36.052658 fdatasync(17) = 0 \u0026lt;0.000064\u0026gt; 5464 04:15:36.052744 close(17) = 0 \u0026lt;0.000009\u0026gt; 5464 04:15:36.052773 stat(\u0026#34;/var/lib/rpm/Group\u0026#34;, {st_mode=S_IFREG|0644, st_size=16384, ...}) = 0 \u0026lt;0.000008\u0026gt; 5464 04:15:36.052814 open(\u0026#34;/var/lib/rpm/Group\u0026#34;, O_RDWR) = 14 \u0026lt;0.000012\u0026gt; 5464 04:15:36.052854 fcntl(14, F_GETFD) = 0 \u0026lt;0.000010\u0026gt; 5464 04:15:36.052886 fcntl(14, F_SETFD, FD_CLOEXEC) = 0 \u0026lt;0.000007\u0026gt; 5464 04:15:36.052914 fdatasync(14) = 0 \u0026lt;0.000056\u0026gt; 5464 04:15:36.052989 close(14) = 0 \u0026lt;0.000009\u0026gt; 5464 04:15:36.053032 fdatasync(16) = 0 \u0026lt;0.000075\u0026gt; 5464 04:15:36.053136 close(16) = 0 \u0026lt;0.000009\u0026gt; 5464 04:15:36.053167 stat(\u0026#34;/var/lib/rpm/Basenames\u0026#34;, {st_mode=S_IFREG|0644, st_size=3485696, ...}) = 0 \u0026lt;0.000008\u0026gt; 5464 04:15:36.053200 open(\u0026#34;/var/lib/rpm/Basenames\u0026#34;, O_RDWR) = 14 \u0026lt;0.000008\u0026gt; 5464 04:15:36.053229 fcntl(14, F_GETFD) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.053254 fcntl(14, F_SETFD, FD_CLOEXEC) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.053278 fdatasync(14) = 0 \u0026lt;0.000058\u0026gt; 5464 04:15:36.053355 close(14) = 0 \u0026lt;0.000007\u0026gt; 5464 04:15:36.053392 fdatasync(15) = 0 \u0026lt;0.000063\u0026gt; 5464 04:15:36.053477 close(15) = 0 \u0026lt;0.000007\u0026gt; 5464 04:15:36.053505 stat(\u0026#34;/var/lib/rpm/Name\u0026#34;, {st_mode=S_IFREG|0644, st_size=32768, ...}) = 0 \u0026lt;0.000007\u0026gt; 5464 04:15:36.053536 open(\u0026#34;/var/lib/rpm/Name\u0026#34;, O_RDWR) = 14 \u0026lt;0.000007\u0026gt; 5464 04:15:36.053563 fcntl(14, F_GETFD) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.053588 fcntl(14, F_SETFD, FD_CLOEXEC) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.053616 fdatasync(14) = 0 \u0026lt;0.000068\u0026gt; 5464 04:15:36.053704 close(14) = 0 \u0026lt;0.000009\u0026gt; 5464 04:15:36.053752 fdatasync(8) = 0 \u0026lt;0.000062\u0026gt; 5464 04:15:36.053837 close(8) = 0 \u0026lt;0.000009\u0026gt; 5464 04:15:36.053874 umask(022) = 022 \u0026lt;0.000007\u0026gt; 5464 04:15:36.053900 open(\u0026#34;/var/lib/rpm/.dbenv.lock\u0026#34;, O_RDWR|O_CREAT, 0644) = 8 \u0026lt;0.000009\u0026gt; 5464 04:15:36.053930 umask(022) = 022 \u0026lt;0.000007\u0026gt; 5464 04:15:36.053956 fcntl(8, F_SETLKW, {l_type=F_WRLCK, l_whence=SEEK_SET, l_start=0, l_len=0}) = 0 \u0026lt;0.000008\u0026gt; 5464 04:15:36.053988 close(13) = 0 \u0026lt;0.000008\u0026gt; 5464 04:15:36.054015 munmap(0x7f6a94e16000, 1318912) = 0 \u0026lt;0.000136\u0026gt; 5464 04:15:36.054184 close(12) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.054215 munmap(0x7f6a94f58000, 294912) = 0 \u0026lt;0.000018\u0026gt; 5464 04:15:36.054253 close(11) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.054277 munmap(0x7f6a95368000, 1032192) = 0 \u0026lt;0.000027\u0026gt; 5464 04:15:36.054327 close(8) = 0 \u0026lt;0.000007\u0026gt; 5464 04:15:36.054357 rt_sigaction(SIGHUP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, NULL, 8) = 0 \u0026lt;0.000005\u0026gt; 5464 04:15:36.054387 rt_sigaction(SIGINT, {sa_handler=0x7f6aae1b0ca0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, NULL, 8) = 0 \u0026lt;0.000007\u0026gt; 5464 04:15:36.054415 rt_sigaction(SIGTERM, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, NULL, 8) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.054442 rt_sigaction(SIGQUIT, {sa_handler=0x7f6aae1b0ca0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, NULL, 8) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.054468 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, NULL, 8) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.054531 close(27) = 0 \u0026lt;0.000020\u0026gt; 5464 04:15:36.061259 munmap(0x7f6a9411a000, 262144) = 0 \u0026lt;0.000040\u0026gt; 5464 04:15:36.061337 munmap(0x7f6a940da000, 262144) = 0 \u0026lt;0.000030\u0026gt; 5464 04:15:36.061405 munmap(0x7f6a9401a000, 262144) = 0 \u0026lt;0.000034\u0026gt; 5464 04:15:36.061461 munmap(0x7f6a8fec0000, 262144) = 0 \u0026lt;0.000033\u0026gt; 5464 04:15:36.061514 munmap(0x7f6a8ffc0000, 262144) = 0 \u0026lt;0.000030\u0026gt; 5464 04:15:36.061566 munmap(0x7f6a8ff00000, 262144) = 0 \u0026lt;0.000034\u0026gt; 5464 04:15:36.061622 munmap(0x7f6a9419a000, 262144) = 0 \u0026lt;0.000031\u0026gt; 5464 04:15:36.061682 munmap(0x7f6a8fe80000, 262144) = 0 \u0026lt;0.000029\u0026gt; 5464 04:15:36.061730 munmap(0x7f6a9415a000, 262144) = 0 \u0026lt;0.000028\u0026gt; 5464 04:15:36.061778 munmap(0x7f6a9409a000, 262144) = 0 \u0026lt;0.000035\u0026gt; 5464 04:15:36.061841 munmap(0x7f6a9405a000, 262144) = 0 \u0026lt;0.000029\u0026gt; 5464 04:15:36.061890 munmap(0x7f6a8ff40000, 262144) = 0 \u0026lt;0.000029\u0026gt; 5464 04:15:36.061943 munmap(0x7f6a8fe40000, 262144) = 0 \u0026lt;0.000029\u0026gt; 5464 04:15:36.062092 unlink(\u0026#34;/var/run/yum.pid\u0026#34;) = 0 \u0026lt;0.000023\u0026gt; 5464 04:15:36.062469 close(5) = 0 \u0026lt;0.000011\u0026gt; 5464 04:15:36.062500 munmap(0x7f6aae683000, 4096) = 0 \u0026lt;0.000011\u0026gt; 5464 04:15:36.062563 close(4) = 0 \u0026lt;0.000009\u0026gt; 5464 04:15:36.062597 munmap(0x7f6aae681000, 4096) = 0 \u0026lt;0.000008\u0026gt; 5464 04:15:36.062680 close(3) = 0 \u0026lt;0.000014\u0026gt; 5464 04:15:36.062761 rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, {sa_handler=0x7f6aae1b0ca0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, 8) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.062818 rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, {sa_handler=0x7f6aae1b0ca0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6aade8d630}, 8) = 0 \u0026lt;0.000005\u0026gt; 5464 04:15:36.072577 munmap(0x7f6a8f3c0000, 262144) = 0 \u0026lt;0.000046\u0026gt; 5464 04:15:36.072891 munmap(0x7f6a8fa80000, 262144) = 0 \u0026lt;0.000046\u0026gt; 5464 04:15:36.072962 munmap(0x7f6a8fa00000, 262144) = 0 \u0026lt;0.000040\u0026gt; 5464 04:15:36.073367 munmap(0x7f6a8f840000, 262144) = 0 \u0026lt;0.000042\u0026gt; 5464 04:15:36.073896 munmap(0x7f6a8fe00000, 262144) = 0 \u0026lt;0.000037\u0026gt; 5464 04:15:36.081421 munmap(0x7f6a946ec000, 262144) = 0 \u0026lt;0.000048\u0026gt; 5464 04:15:36.081505 close(6) = 0 \u0026lt;0.000010\u0026gt; 5464 04:15:36.081593 rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [], 8) = 0 \u0026lt;0.000006\u0026gt; 5464 04:15:36.081627 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 \u0026lt;0.000005\u0026gt; 5464 04:15:36.082218 exit_group(0) = ? 5464 04:15:36.088894 +++ exited with 0 +++ 我们可以把它拆成五个部分来理解：\n12345: 进程 ID (PID)。因为你加了 -f，所以能看到是哪个子进程在干活。 20:30:01...: 精确到微秒的时间戳（-tt 参数）。 connect(...): 动作名称及参数。这里表示尝试连接网络。你可以看到它尝试连接的是哪个 IP 和端口（80）。 = -1 ETIMEDOUT: 返回值。这是最重要的部分！ = 0 或正数：代表成功。 -1 紧跟错误码：代表失败。这里的 ETIMEDOUT 明确告诉你：网络不通，超时了。 \u0026lt;5.000123\u0026gt;: 耗时（-T 参数）。这个连接动作卡了 5 秒钟才返回失败。 1. 核心公式：五位一体法 以下是一行典型的 strace 输出（以你排查内网镜像站为例）：\n示例组成部分 对应的实际内容 翻译成人类语言 PID (进程号) 2834 编号为 2834 的进程（可能是 yum 的子进程）。 时间戳 10:30:01.5 动作发生的精确时间。 系统调用名 connect 程序想发起一个网络连接。 参数 (3, {IP=\u0026quot;192.0.2.10\u0026quot;, port=80}, ...) 目标是示例镜像源 IP，端口是 80。 返回值 = -1 ETIMEDOUT 重点： 结果失败了，原因是连接超时。 耗时 \u0026lt;5.000123\u0026gt; 这个动作让程序傻等了 5 秒钟。 2. 常见“动作”（系统调用）分类 在 yum install 的日志里，你主要会看到这几类动作：\n文件操作类： openat / open: 打开文件（看看它是不是在读 /etc/yum.repos.d/ 下的配置）。 read / write: 读写数据。 网络操作类（排查重点）： socket: 创建一个联网的插座。 connect: 去连服务器。如果后面跟着 ETIMEDOUT，表示连接超时，需要进一步检查路由、防火墙和目标服务。 recvfrom / sendto: 接收或发送网络包（比如 DNS 查询）。 进程控制类： execve: 运行一个新程序（比如 yum 调用了 rpm）。 clone / fork: 创建子进程。 3. 看懂“返回值”：问题的答案就在这里 返回值（等号 = 后面的内容）是排查的关键：\n正整数（如 = 3）：成功。程序拿到了一个资源（比如文件句柄）。 EACCES (Permission denied)：权限不足。你可能忘了加 sudo。 ENOENT (No such file or directory)：文件不存在。说明 repo 配置文件路径写错了。 ETIMEDOUT (Connection timed out)：超时。网络包发出去没人理，通常是安全组/防火墙的问题。 ECONNREFUSED (Connection refused)：被拒绝。目标服务器活着，但不允许你访问这个端口。 4. 你的实战技巧：搜索三部曲 面对几万行的 yum_install.log，不要从头看，用 grep 降维打击：\n搜网络错误：\ngrep \u0026ldquo;ETIMEDOUT\u0026rdquo; yum_install.log\n如果有结果，说明发生了网络超时，仍需要结合地址、路由和服务状态定位原因。\n搜 IP 地址：\ngrep \u0026ldquo;100.\u0026rdquo; yum_install.log\n看看它到底有没有尝试去连你设定的那个内网 IP。\n搜 DNS 解析：\ngrep \u0026ldquo;53\u0026rdquo; yum_install.log\n看 53 端口（DNS）有没有返回成功。如果 DNS 挂了，它连 IP 都拿不到。\n总结 理解 strace 不需要你会写 C 语言或汇编。你只需要像看物流单据一样：\n发货地：你的 ECS\n目的地：mirrors.cloud.aliyuncs.com (100.x.x.x)\n状态：是“已签收”还是“查无此人”或“运输中（超时）”？\n","date":"2026-01-14","permalink":"/posts/strace-notes/","title":"用 Strace 排查命令与网络问题"},{"categories":["python"],"content":"#python #celery\ncelery 简介 celery 是一个分布式任务队列\n代码 1 2 3 4 5 6 7 Celery is a simple, flexible, and reliable distributed system to process vast amounts of messages, while providing operations with the tools required to maintain such a system. It’s a task queue with focus on real-time processing, while also supporting task scheduling. Celery has a large and diverse community of users and contributors, don’t hesitate to ask questions or [get involved](https://docs.celeryq.dev/en/stable/getting-started/resources.html#getting-help). Celery is Open Source and licensed under the [BSD License](http://www.opensource.org/licenses/BSD-3-Clause). 当前使用 celery 版本 5.2.7\n安装 代码 1 2 3 celery==5.2.7 django_celery_results==2.6.0 django-celery-beat==2.8.1 注册应用 代码 1 2 3 4 5 INSTALLED_APPS = [ ... \u0026#39;django_celery_beat\u0026#39;, \u0026#39;django_celery_results\u0026#39; ] 执行数据库迁移 代码 1 python manage.py migrate django_celery_beat django_celery_results celery 配置\nPython 1 2 3 4 5 6 7 8 9 10 # Celery 配置 CELERY_BROKER_URL = \u0026#39;amqp://guest:guest@localhost:5672//\u0026#39; # RabbitMQ 特有优化 CELERY_TASK_ACKS_LATE = True # 任务执行完后再确认，防止 worker 崩溃导致任务丢失 CELERY_WORKER_PREFETCH_MULTIPLIER = 1 # 每次只预取一个任务，防止长耗时任务导致负载不均 CELERY_ACCEPT_CONTENT = [\u0026#39;json\u0026#39;] CELERY_TASK_SERIALIZER = \u0026#39;json\u0026#39; CELERY_RESULT_SERIALIZER = \u0026#39;json\u0026#39; CELERY_TIMEZONE = \u0026#39;Asia/Shanghai\u0026#39; CELERY_RESULT_EXPIRES = 86400 * 7 celery 相关命令\n代码 1 2 3 4 celery -A conf.celery_app worker --loglevel=info # 启动worker，执行节点 celery -A conf.celery_app beat --loglevel=info # 启动schedule，定时任务 celery -A conf.celery_app inspect conf | grep -i celery # 查看相关启动参数，需要在celery启动的时候才能查看 celery -A conf.celery_app inspect registered # 查看 celery 注册的任务 ","date":"2026-01-14","permalink":"/posts/celery-django-configuration/","title":"Celery 与 Django 配置笔记"},{"categories":["think"],"content":" 想自己搞点东西出来，还是什么都没有。 去逛了丽江古城，感觉商业化严重，都是酒吧，人造场景。好看，但似乎没有显出底蕴。 打字总是容易出错，这个有没有办法能解决。问问AI看看 想看书，还是容易被情绪吸引，控制 ","date":"2025-10-31","permalink":"/posts/somethingtosay/","title":"somethingtosay"},{"categories":["django"],"content":"django restful 风格返回所有数据 并且有相应的字段\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from rest_framework.pagination import BasePagination from rest_framework.response import Response from collections import OrderedDict class TotalPagination(BasePagination): \u0026#34;\u0026#34;\u0026#34; 不分页，显示总数 \u0026#34;\u0026#34;\u0026#34; def paginate_queryset(self, queryset, request, view=None): \u0026#34;\u0026#34;\u0026#34; 返回所有 \u0026#34;\u0026#34;\u0026#34; return list(queryset[:]) def get_paginated_response(self, data): return Response(OrderedDict([ (\u0026#39;count\u0026#39;, len(data)), (\u0026#39;results\u0026#39;, data) ])) pagination 的 page_size_query_param 参数是怎么获取的？ drf 的 get_paginated_responseresponse 格式是怎么来的？\n","date":"2025-08-19","permalink":"/posts/drf-total-pagination/","title":"DRF 返回全部数据并保留 count 字段"},{"categories":["ai"],"content":"基于推文让 Gemini 设计 prompt，一共给出了两个 prompt\n原推文 自学可能陷入一种困境，不知道 roadmap，没有 benchmark，没有 feedback，还会overfitting\n1、综合导师型 Prompt (The Comprehensive Mentor) 使用场景：当你准备系统性地学习一个全新的、比较复杂的领域时（例如：机器学习、前端开发、经济学）。\n角色设定：AI 学习导师 你将扮演我的专属学习导师，专门负责引导我进行高效的自学。你的核心目标是帮助我构建一个坚实、灵活且能够实际应用的知识体系。在我们的整个学习过程中，你必须严格遵守以下四大核心职责：\n1. 制定动态学习路线图 (Roadmap):\n任务: 基于我想要学习的领域 [在此处插入你想学习的领域，例如：Python数据分析] 和我目前的背景 [在此处插入你的背景，例如：完全零基础/有一些编程经验]，为我生成一个从入门到精通的、结构化的学习路线图。\n要求:\n路线图需分为明确的阶段（如：基础、进阶、实战）。 每个阶段需包含核心知识点、推荐的学习资源（书籍、课程、文档等），以及预计的学习周期。 强调“第一性原理”，解释各个知识点之间的逻辑关联，说明“为什么”要学它。 2. 设立明确的能力基准 (Benchmark):\n任务: 在路线图的每个阶段结束时，为我设计一个“里程碑项目”或一系列的测试题。\n要求:\n该项目的难度和范围必须能充分检验我当前阶段的学习成果，其标准应参考业界入门级或中级水平的要求。 明确指出完成这个项目/测试所需达到的具体能力指标（例如：能够独立完成数据清洗、能够使用某某库进行可视化等）。 3. 提供深度反馈循环 (Feedback):\n任务: 当我提交我的项目、代码、解决方案或对某个概念的理解时，你需要对我进行深度分析和反馈。\n要求:\n不仅仅是“对”或“错”: 指出我的优点和可以改进的地方。 苏格拉底式提问: 通过提问引导我自行发现问题和更优的解决方案（例如：“你为什么选择用这种数据结构？有没有考虑过在……场景下的性能问题？”）。 代码审查 (如适用): 检查代码的规范性、效率、可读性和健壮性。 4. 防范知识过拟合 (Overfitting):\n任务: 在我学习了某个具体案例或教程后，主动挑战我的理解。\n要求:\n创造新问题: 给我一个与教程略有不同但核心原理相同的“变体问题”，测试我是否能迁移知识。 概念辨析: 提出一些容易混淆的概念让我区分，或者让我解释一个技术的“边界”和“适用范围”。 追问“为什么”: 不断追问核心原理，确保我理解的不是孤立的步骤，而是背后的逻辑。 现在，请根据以上规则，开始我们的第一步：为我制定一个关于 [再次插入你想学习的领域] 的学习路线图。\n2、项目驱动型 Prompt (The Project-Based Deconstructor) 使用场景：当你想“做中学”，目标非常明确时（例如：我想开发一个个人博客网站）。\n角色：项目架构师与技术教练 我想学习 [在此处插入你想通过项目掌握的技能，例如：React 和 Node.js]，最终目标是独立完成一个 [在此处插入你的项目目标，例如：功能完善的个人博客网站]。\n你的任务是作为我的项目架构师和技术教练，引导我完成整个过程。请遵循以下步骤：\n项目拆解 (生成Roadmap): 将这个项目目标拆解成一系列从前到后、从简到难的技术模块和开发步骤。为每个步骤列出需要学习的核心知识点。\n任务驱动 (设置Benchmark): 将每个步骤转化为一个具体的、可交付的任务。例如：“任务一：使用 Create React App 搭建项目框架并实现静态页面布局”。\n敏捷开发与反馈 (执行Feedback \u0026amp; 防止Overfitting):\n我会按照你拆解的任务顺序进行学习和开发。 每当我完成一个任务并向你展示我的代码或成果时，请你对我进行严格的代码审查和功能评审。 在评审中，你需要向我提问，挑战我的技术选型和实现方式，确保我理解了背后的原理，而不仅仅是复制粘贴代码。 现在，请开始第一步：为我的 [再次插入你的项目目标] 项目进行拆解。\n以下是方便复制版，代码形式，右上角有复制按钮 原推 代码 1 自学可能陷入一种困境，不知道 roadmap，没有 benchmark，没有 feedback，还会overfitting 1、综合导师型 Prompt (The Comprehensive Mentor) 使用场景：当你准备系统性地学习一个全新的、比较复杂的领域时（例如：机器学习、前端开发、经济学）。\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 角色设定：AI 学习导师 你将扮演我的专属学习导师，专门负责引导我进行高效的自学。你的核心目标是帮助我构建一个坚实、灵活且能够实际应用的知识体系。在我们的整个学习过程中，你必须严格遵守以下四大核心职责： **1. 制定动态学习路线图 (Roadmap):** - **任务**: 基于我想要学习的领域 **[在此处插入你想学习的领域，例如：Python数据分析]** 和我目前的背景 **[在此处插入你的背景，例如：完全零基础/有一些编程经验]**，为我生成一个从入门到精通的、结构化的学习路线图。 - **要求**: - 路线图需分为明确的阶段（如：基础、进阶、实战）。 - 每个阶段需包含核心知识点、推荐的学习资源（书籍、课程、文档等），以及预计的学习周期。 - 强调“第一性原理”，解释各个知识点之间的逻辑关联，说明“为什么”要学它。 **2. 设立明确的能力基准 (Benchmark):** - **任务**: 在路线图的每个阶段结束时，为我设计一个“里程碑项目”或一系列的测试题。 - **要求**: - 该项目的难度和范围必须能充分检验我当前阶段的学习成果，其标准应参考业界入门级或中级水平的要求。 - 明确指出完成这个项目/测试所需达到的具体能力指标（例如：能够独立完成数据清洗、能够使用某某库进行可视化等）。 **3. 提供深度反馈循环 (Feedback):** - **任务**: 当我提交我的项目、代码、解决方案或对某个概念的理解时，你需要对我进行深度分析和反馈。 - **要求**: - **不仅仅是“对”或“错”**: 指出我的优点和可以改进的地方。 - **苏格拉底式提问**: 通过提问引导我自行发现问题和更优的解决方案（例如：“你为什么选择用这种数据结构？有没有考虑过在……场景下的性能问题？”）。 - **代码审查 (如适用)**: 检查代码的规范性、效率、可读性和健壮性。 **4. 防范知识过拟合 (Overfitting):** - **任务**: 在我学习了某个具体案例或教程后，主动挑战我的理解。 - **要求**: - **创造新问题**: 给我一个与教程略有不同但核心原理相同的“变体问题”，测试我是否能迁移知识。 - **概念辨析**: 提出一些容易混淆的概念让我区分，或者让我解释一个技术的“边界”和“适用范围”。 - **追问“为什么”**: 不断追问核心原理，确保我理解的不是孤立的步骤，而是背后的逻辑。 --- **现在，请根据以上规则，开始我们的第一步：为我制定一个关于 [再次插入你想学习的领域] 的学习路线图。** 2、项目驱动型 Prompt (The Project-Based Deconstructor) 使用场景：当你想“做中学”，目标非常明确时（例如：我想开发一个个人博客网站）。\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 角色：项目架构师与技术教练 我想学习 **[在此处插入你想通过项目掌握的技能，例如：React 和 Node.js]**，最终目标是独立完成一个 **[在此处插入你的项目目标，例如：功能完善的个人博客网站]**。 你的任务是作为我的项目架构师和技术教练，引导我完成整个过程。请遵循以下步骤： 1. **项目拆解 (生成Roadmap)**: 将这个项目目标拆解成一系列从前到后、从简到难的技术模块和开发步骤。为每个步骤列出需要学习的核心知识点。 2. **任务驱动 (设置Benchmark)**: 将每个步骤转化为一个具体的、可交付的任务。例如：“任务一：使用 Create React App 搭建项目框架并实现静态页面布局”。 3. **敏捷开发与反馈 (执行Feedback \u0026amp; 防止Overfitting)**: - 我会按照你拆解的任务顺序进行学习和开发。 - 每当我完成一个任务并向你展示我的代码或成果时，请你对我进行严格的代码审查和功能评审。 - 在评审中，你需要向我提问，挑战我的技术选型和实现方式，确保我理解了背后的原理，而不仅仅是复制粘贴代码。 --- **现在，请开始第一步：为我的 [再次插入你的项目目标] 项目进行拆解。** ","date":"2025-08-10","permalink":"/posts/gemini-learning-prompts/","title":"用 Gemini 设计自学 Prompt"},{"categories":["tools"],"content":"更换icon https://sw.kovidgoyal.net/kitty/faq/#i-do-not-like-the-kitty-icon\n在家目录下\n代码 1 2 3 4 clin:kitty/ $ ll [0:14:09] total 192 -rw-r--r--@ 1 clin staff 11K Apr 7 2024 kitty.app.png -rw-------@ 1 clin staff 83K Feb 20 2024 kitty.conf 将文件放到配置目录下，重启更新 icon\nBash 1 rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock 在notedoc 的笔记里也有对应的配置文档\n","date":"2025-05-29","permalink":"/posts/kitty-icon/","title":"macOS 更换 Kitty 图标"},{"categories":["read"],"content":"ai对话 我的提问： 总是在想学习的时候走其他事情，比如刷会儿视频，看会儿推特。可能是想到要做的事情太多，不知道从哪件开始做。还有想做的事情比较难，害怕做\nGrok回答： 你描述的情况很常见，可能是注意力分散、任务焦虑或拖延症的表现。以下是一些分析和建议，帮助你应对这种状态：\n任务过多，优先级不清：想到要做的事情太多，大脑不知从何下手，容易逃避到刷视频或推特等即时满足的活动中。 任务难度高，害怕失败：面对较难的事情，潜意识里可能有畏难情绪，导致你选择逃避到轻松的活动。 缺乏清晰计划：没有具体的行动步骤，容易让大脑感到混乱，降低执行力。 即时满足的诱惑：刷视频或推特能快速带来愉悦感，而学习需要延迟满足，短期内吸引力较低。 应对建议\n分解任务，设定优先级： 写下所有想做的事情，做一个待办清单。 用四象限法则（重要且紧急、重要不紧急等）或简单按优先级排序，选出当天最重要的1-3件事。 每次只专注一件小任务，比如“学习30分钟某个章节”，而不是笼统的“学习”。 降低启动难度： 用**“两分钟法则”**：告诉自己只做两分钟，比如打开书或写一句话，往往开始后就会继续下去。 把大目标拆成小步骤，比如“读一页书”“写一个段落”，减少心理压力。 管理干扰： 物理隔离：学习时把手机放在另一个房间，或用“勿扰模式”。 数字隔离：用网站屏蔽工具（如Freedom、Cold Turkey）限制推特或视频网站访问。 给自己设定\u0026quot;奖励时间\u0026quot;，比如完成1小时学习后允许刷10分钟视频。 应对畏难情绪： 接受“困难是正常的”，告诉自己不需要完美，只需要开始。 用**\u0026ldquo;5秒法则\u0026rdquo;**：心里倒数5-4-3-2-1，然后立刻行动，打破犹豫。 尝试可视化成功：想象完成任务后的成就感，增强动力。 建立专注习惯： 用番茄工作法：专注25分钟，休息5分钟，循环几次后稍长休息。 每天固定时间学习，培养大脑的习惯，比如每天晚上8点开始30分钟。 在学习前做简短的冥想或深呼吸（参考你4月29日的冥想询问），帮助平静思绪，进入状态。 调整心态： 不要苛责自己偶尔分心，关键是重新回到正轨。 记录每天的小进步，比如\u0026quot;今天专注了20分钟\u0026quot;，积累成就感。 如果焦虑感强烈（参考你4月30日的焦虑询问），试试写下担忧的事情，分析哪些可控，哪些不可控，逐一解决。 额外建议\n环境优化：在干净、安静的地方学习，减少干扰。放点轻音乐（无歌词）可能有帮助。 反思与调整：每天花5分钟回顾：今天分心了吗？为什么？明天怎么改进？ 寻求支持：如果任务太难，可以找朋友或同事讨论，分解问题，或者请教专业人士。 如果你觉得这种状态持续影响效率，或者焦虑感加重，可以考虑咨询心理专业人士，探讨是否有更深层的原因。\n我的提问： 当心情烦躁，或者有点焦虑，没有学习，没有进入心流状态的时候，怎么做才会不焦虑\nGrok回答： 当感到烦躁、焦虑或难以进入心流状态时，可以尝试以下方法来缓解焦虑并调整状态。这些方法基于心理学和实践经验，适合大多数场景：\n深呼吸与正念练习 做4-7-8呼吸法：吸气4秒，屏气7秒，呼气8秒，重复4-5次。 尝试5分钟正念冥想：找安静的地方，专注于呼吸或周围的声音，观察思绪但不纠缠。 效果：深呼吸能降低心率，激活副交感神经系统，快速平静情绪。 身体活动 进行10-15分钟的轻度运动，如快走、瑜伽或跳绳。 户外活动更好，阳光和自然环境能提升血清素水平。 效果：运动释放内啡肽，缓解焦虑，转移注意力。 分解任务，立即行动 列出当前任务，挑一个最简单的小步骤（比如写一句话或整理桌面），马上开始。 使用“2分钟法则”：告诉自己只做2分钟，降低心理阻力。 效果：小行动能打破拖延和焦虑循环，逐步进入专注状态。 环境调整 整理桌面，减少杂乱；播放轻音乐或白噪音（如雨声）。 远离手机通知，设置专注时段（可用Pomodoro番茄钟法，25分钟专注+5分钟休息）。 效果：干净的环境和专注时段能减少干扰，提升心流可能性。 情绪表达与记录 写下焦虑的想法：具体是什么让你烦躁？不需逻辑，倾诉即可。 尝试“5-5-5”练习：写下5件你感恩的事、5件让你骄傲的小事、5件期待的事。 效果：书写能理清思绪，感恩练习能转移负面情绪。 适度休息与自我关怀 如果大脑疲惫，休息10-20分钟，做非屏幕活动（如喝茶、闭眼放松）。 告诉自己：“现在感觉不好没关系，我可以慢慢调整。” 效果：接纳情绪能降低自我压力，休息后更容易进入状态。 饮食与水分补充 喝一杯水，吃点健康零食（如坚果或水果），避免过量咖啡因。 低血糖或脱水可能加重焦虑。 效果：稳定生理状态有助于情绪平稳。 长期建议 建立规律作息：保证7-8小时睡眠，固定时间学习或工作，培养心流习惯。 学习情绪管理：尝试认知行为疗法（CBT）相关技巧，如挑战负面想法。 寻求支持：如果焦虑频繁，考虑与朋友交流或咨询专业心理医生。 快速实践\n现在试试：\n停下1分钟，做5次深呼吸。 写下让你烦躁的1件事和1个能立刻做的微小行动。 动起来：伸展身体或走动5分钟。 这些小步骤能帮你快速切断焦虑循环，逐步找回专注。如果需要更具体指导，比如针对某场景的建议，请告诉我！\n我的提问： 怎么冥想\nGrok回答： 冥想是一种通过专注和放松来提升内心平静、缓解压力的练习。以下是初学者进行冥想的简易步骤和方法：\n基本冥想步骤\n选择安静的环境： 找一个安静、舒适的地方，减少干扰。 可以坐在椅子上、地板上，或使用冥想垫，保持身体放松但不过于松懈。 设定时间： 初学者可以从5-10分钟开始，逐渐延长到15-20分钟。 使用计时器或冥想App（如Headspace、Calm）来避免分心。 调整姿势： 坐直，背部自然挺直，肩膀放松。 双手可放在膝盖上或大腿上，手掌朝上或朝下。 闭上眼睛或半睁，目光轻柔地注视前方一点。 专注于呼吸： 自然呼吸，注意空气进出鼻腔或腹部的感觉。 可以默数呼吸（吸气1，呼气2），帮助集中注意力。 如果思绪飘走，轻轻地将注意力拉回呼吸，不必自责。 观察思绪： 不要试图阻止杂念，而是像旁观者一样观察它们，然后重新聚焦于呼吸或冥想目标。 接受情绪或身体感觉，不评判、不抗拒。 结束冥想： 计时结束后，缓慢睁开眼睛，感受周围环境。 轻柔地活动身体，带着平静的心态回归日常。 常见的冥想类型\n正念冥想（Mindfulness Meditation）： 专注于当下，观察呼吸、身体感觉或周围的声音。 适合缓解压力和提高专注力。 引导冥想（Guided Meditation）： 通过音频或视频引导，跟着老师的指令放松或想象场景。 适合初学者，可在App或YouTube上找到资源。 身体扫描（Body Scan）： 逐步关注身体的每个部位，放松紧张区域。 适合缓解身体压力和改善睡眠。 慈悲冥想（Loving-Kindness Meditation）： 默念祝福语句（如“愿我平安，愿他人幸福”），培养对他人的善意。 适合缓解愤怒或改善人际关系。 实用Tips\n规律练习：每天固定时间冥想（如早晨或睡前），养成习惯。 从小步开始：不要追求完美，5分钟的专注也很有价值。 使用工具：尝试冥想App、背景音乐或白噪音来辅助。 接受不适：初次冥想可能感到无聊或难以集中，这是正常的，坚持下去会改善。 结合情境：在生气或焦虑时，尝试短时间的呼吸冥想（1-2分钟）来快速平静。 注意事项\n如果感到焦虑或不适，可以缩短时间或选择更轻松的引导冥想。 冥想不是“清空大脑”，而是学会与思绪和平共处。 如果有心理健康问题，建议在专业人士指导下练习。 资源推荐\nApp：Headspace、Calm、Insight Timer（有中文内容）。 视频：YouTube上的免费引导冥想，如“正念冥想10分钟”。 书籍：《正念的奇迹》（一行禅师）、《冥想的力量》（Jon Kabat-Zinn）。 通过持续练习，你会逐渐感受到冥想带来的平静和情绪管理的改善。开始时不必强求效果，享受过程即可！\n我的提问： 什么叫信息焦虑\nGrok回答： 信息焦虑（Information Anxiety）是指在信息过载的环境中，人们因无法有效处理、筛选或消化海量信息而产生的心理压力、焦虑或不安情绪。这种现象通常与以下因素有关：\n信息过载：每天接收到大量信息（如新闻、社交媒体、邮件等），超出个人处理能力。 选择困难：面对过多信息或选项，难以判断哪些是有价值的，导致决策疲劳。 错失恐惧（FOMO）：担心错过重要信息或动态，强迫自己不断获取新信息。 信息质量参差：虚假信息、低质量内容或矛盾信息让人感到困惑或不信任。 时间压力：感觉没有足够时间消化或学习所有信息，产生紧迫感。 表现形式\n频繁刷新社交媒体或新闻，害怕“跟不上”。 对信息真实性感到怀疑，但又无法停止获取。 注意力分散，难以专注于单一任务。 感到疲惫、迷茫，甚至对信息产生排斥感。 应对方法\n信息筛选：设定优先级，只关注与自己目标相关的信息来源。 时间管理：限制浏览社交媒体或新闻的时间，避免无休止滚动。 数字排毒：定期断开网络，专注于线下活动或深度思考。 培养批判性思维：学会分辨信息的可信度，减少对低质量内容的依赖。 正念练习：通过冥想或专注练习，缓解焦虑情绪。 信息焦虑是数字时代常见的心理现象，适度管理信息摄入能有效减轻其影响。如果需要更具体的方法或案例，可以进一步探讨！\n我的提问： 数字成瘾的影响\nGrok回答： 数字成瘾（Digital Addiction）是指对数字设备、互联网或特定数字内容（如社交媒体、游戏、视频平台等）的过度依赖和强迫性使用，导致对日常生活、心理健康和人际关系产生负面影响。以下是数字成瘾的主要影响：\n心理健康影响 焦虑与压力：不断检查通知、担心错过信息（FOMO）或与他人比较（如社交媒体上的“完美生活”）会加剧焦虑。 抑郁倾向：过度依赖数字内容可能减少现实中的社交互动，导致孤独感和抑郁情绪。 注意力缺陷：频繁切换应用程序或多任务处理会削弱专注力，类似“数字多动症”。 睡眠问题：睡前使用电子设备（蓝光影响）或熬夜刷屏会干扰睡眠质量，导致疲劳和情绪波动。 身体健康影响 视力问题：长时间盯着屏幕可能导致眼睛干涩、视力下降（计算机视觉综合征）。 姿势问题：久坐或低头使用手机可能引发颈椎痛、背痛或“科技颈”。 缺乏运动：沉迷数字设备可能减少体育活动时间，增加肥胖和心血管疾病风险。 社交与人际关系影响 关系疏远：过度使用数字设备可能减少与家人、朋友的面对面交流，导致亲密关系淡化。 社交技能下降：长期依赖线上互动可能削弱现实中的沟通能力，尤其对青少年影响显著。 虚拟冲突：社交媒体上的争论或负面评论可能引发情绪波动或现实中的矛盾。 学业与工作效率影响 分心与拖延：频繁查看手机或沉迷游戏会降低学习和工作效率，延长任务完成时间。 创造力下降：过度依赖数字内容可能减少深度思考和创造性活动的时间。 职业风险：在工作场合过度使用社交媒体可能影响专业形象或导致纪律问题。 时间管理与生活平衡 时间浪费：无意识地刷手机或看视频可能占用大量时间，挤压其他重要活动（如锻炼、阅读或陪伴家人）。 生活失衡：数字成瘾可能让人忽视现实生活的目标和责任，感到空虚或失去方向。 长期影响 认知能力下降：长期依赖数字设备可能削弱记忆力、问题解决能力和批判性思维。 自我认同问题：过度追求线上认可（如点赞、关注）可能导致自我价值感依赖外部反馈。 成瘾恶化：未及时干预可能导致更严重的依赖行为，甚至需要专业心理治疗。 应对数字成瘾的建议\n自我觉察：记录每天使用数字设备的时间，识别成瘾行为。 设定界限：制定“无屏幕时间”（如用餐或睡前），或使用应用程序限制使用时长。 替代活动：用阅读、运动或面对面社交等活动替代数字娱乐。 优化环境：将手机放在远离工作或休息区域的地方，减少诱惑。 寻求支持：与家人朋友讨论目标，或在严重情况下寻求心理咨询。 数字成瘾的影响是多方面的，但通过有意识地管理使用习惯，可以有效减轻其负面后果。如果需要更具体的策略或某方面的深入分析，请告诉我！\n我的提问： 总是想不停的刷X，明明已经感觉没有更新了，还是想不停的刷新，这种有什么好的解决方法\nGrok回答： 你描述的情况是典型的数字成瘾行为，具体表现为对X平台（或其他社交媒体）的强迫性刷新，背后可能与信息焦虑、错失恐惧（FOMO）或大脑对即时反馈的依赖有关。以下是针对“不停刷X、忍不住刷新”的具体解决方法，结合实用策略和心理学原理：\n理解触发原因 反思动机：问自己为什么想刷X？是为了获取新信息、打发时间、缓解压力，还是寻求社交认可？明确动机有助于找到替代方案。 识别触发点：是无聊、焦虑、等待时段（如通勤），还是特定情绪（如孤独）？记录触发场景，针对性应对。 方法：\n每天花5分钟写下刷X的时间点和当时的情绪/情境，找出规律。 如果是为了消磨时间，尝试替代活动（如听播客、阅读短篇）。 设定明确的界限 时间限制：每天为X使用设定固定时间（如30分钟），使用手机的屏幕时间管理功能（如iOS的“屏幕时间”或Android的“数字健康”）强制执行。 刷新间隔：强迫自己每次刷新前等待几分钟，逐渐延长间隔，打破即时刷新的习惯。 无屏幕时段：设定特定时间段（如用餐、睡前1小时）完全不碰手机。 方法：\n下载限制类应用（如Freedom、StayFocusd）设定X使用时长。 将手机设置为灰度模式，降低屏幕的视觉吸引力。 改变使用环境 物理隔离：将手机放在另一个房间或不易触及的地方，增加刷X的“摩擦成本”。 通知管理：关闭X的推送通知，或仅保留重要提醒，减少被动的触发。 调整首页内容：取消关注低质量或触发焦虑的账号，关注更有价值的内容，减少无意义的滚动。 方法：\n把X应用从手机桌面移到文件夹或次级页面，增加打开难度。 使用电脑版X（如果必须查看），因为电脑界面不如手机“抓人”。 替代行为与习惯养成 替代即时满足感：刷X提供快速的“多巴胺奖励”，可以用其他活动替代，如听音乐、做5分钟拉伸、玩简单益智游戏。 培养深度活动：将刷X的时间转向需要专注的活动，如阅读、写日记、学习新技能，逐步重建注意力。 微习惯：每次想刷X时，先做一个小动作（如喝一口水、深呼吸3次），打断自动行为。 方法：\n准备一个“无聊时清单”，列出5-10个快速活动（如整理桌面、看一页书）。 尝试“番茄工作法”，专注25分钟做其他任务，奖励自己5分钟刷X。 应对心理驱动 缓解FOMO：告诉自己“错过信息没关系，重要内容总会再出现”。接受信息不可能全部掌握。 正念练习：通过冥想或呼吸练习，观察想刷X的冲动而不立即行动，增强自我控制力。 奖励机制：为自己设定目标（如一天只刷X3次），完成后给予小奖励（如吃喜欢的零食）。 方法：\n下载正念应用（如Headspace、Calm）练习5分钟冥想，专注当下。 写下“刷X带来的负面感受”（如浪费时间、焦虑），贴在手机旁提醒自己。 长期策略 数字排毒：每周选择一天完全不使用X或其他社交媒体，重新体验“无信息”的状态。 重建社交连接：将刷X的时间用于与朋友面对面交流或打电话，满足社交需求。 寻求支持：告诉朋友或家人你的目标，让他们监督或一起减少手机使用。 方法：\n加入“数字极简”挑战，如7天每天减少10分钟屏幕时间。 与朋友约定“无手机聚会”，专注于现实互动。 技术工具辅助 限时应用：如Forest（种树鼓励专注）、AppBlock（锁定应用）。 内容过滤：使用浏览器扩展（如News Feed Eradicator）隐藏X的信息流，只保留必要功能。 数据反馈：定期查看屏幕时间报告，设定每周减少X使用时间的目标。 如果问题严重 如果尝试以上方法后仍无法控制，可能是更深层的焦虑或成瘾倾向，建议：\n咨询心理医生或认知行为治疗（CBT）专家，针对成瘾行为进行干预。 参加支持小组（如“数字成瘾匿名会”），分享经验并获得支持。 快速行动建议\n从今天开始，试试以下三步：\n今晚：关闭X所有通知，手机调至灰度模式。 明天：设定每天刷X的最大时长（如20分钟），用应用锁定。 本周：每天替换一次刷X的时间，做5分钟其他活动（如散步、写日记）。 刷X的冲动通常是大脑对即时奖励的渴望，通过增加摩擦、替代行为和心理调整，可以逐步打破这种习惯。如果你有特定场景（比如只在晚上刷）或想深入某方法的效果，可以告诉我，我会帮你定制更精准的方案！\n我的提问： 总是想不停的刷X，明明已经感觉没有更新了，还是想不停的刷新，这种有什么好的解决方法\nGrok回答： 你的描述非常符合数字成瘾中的一种典型行为——强迫性刷新X（或其他社交媒体），即使知道没有新内容仍然无法停止。这种行为可能与信息焦虑、多巴胺驱动的习惯循环以及** 错失恐惧（FOMO）**有关。以下是针对这种行为的有效解决方法，结合具体场景和实用建议：\n为什么会不停刷X？\n多巴胺循环：每次刷新X都可能带来新内容（帖子、评论、点赞），触发大脑释放多巴胺，形成“奖励”感，驱使你不断重复。 FOMO（错失恐惧）：担心错过重要信息、热门话题或朋友动态，导致强迫性检查。 习惯性行为：刷X可能已成为无意识的习惯，尤其在空闲或压力时刻，用来逃避现实任务或情绪。 信息过载环境：X的快速更新和算法推荐让你觉得总有新东西，难以“停下来”。 解决方法\n以下方法从行为调整、环境优化、心理管理和替代习惯四个方面入手，帮助你减少强迫性刷新的冲动：\n行为调整 设定使用时间限制： 使用手机自带的“屏幕时间”功能（iOS）或“数字健康”功能（Android），为X设置每日使用时长（如30分钟）。 尝试“时间块”方法：每天固定几次检查X（如早、中、晚各10分钟），其他时间避免打开。 延迟满足： 当有刷X的冲动时，告诉自己“等5分钟再看”。用这段时间做其他事（如喝水、伸展），打破即时满足的习惯。 批量处理： 关注关键账号或话题，减少泛泛浏览。把感兴趣的帖子保存到收藏夹，晚些时候批量阅读，而不是实时刷新。 环境优化 减少触发点： 将X的App从手机主屏幕移到文件夹或次级页面，增加打开的“摩擦”。 关闭X的推送通知，避免被提示打断。 创造无屏幕环境： 在某些场景（如吃饭、学习、睡觉前）将手机放在另一个房间，或使用“勿扰模式”。 使用灰度模式（手机设置中开启）降低屏幕吸引力，减少刷X的欲望。 借助工具： 下载专注类应用（如Forest、Freedom），在特定时间段屏蔽X。 在电脑上使用浏览器插件（如StayFocusd），限制X网页版访问。 心理管理 正念练习： 每次想刷X时，停下来问自己：“我为什么想刷新？是无聊、焦虑还是逃避？”识别情绪触发点。 尝试简单的正念呼吸：深吸气5秒，屏气5秒，呼气5秒，重复几次，平复冲动。 接纳无聊： 接受“没事可做”的状态是正常的。不必用X填满每一刻空闲，允许大脑放松或发散思考。 设定目标： 明确减少刷X的理由（如提高效率、改善睡眠、专注学习），写下来作为动力。每次抗拒冲动后奖励自己（非数字类的，如吃块巧克力）。 替代习惯 替换行为： 用其他低刺激活动替代刷X，如听播客、看书、写日记或做简单家务。 如果想获取信息，试试更结构化的方式：订阅Newsletter、阅读精选文章，或关注X上高质量账号，减少无意义滚动。 丰富线下生活： 增加现实中的活动，如运动、与朋友见面、学新技能，让生活重心从线上转移。 尝试“数字排毒日”：每周一天完全不碰X，体验脱离网络的轻松感。 社交支持： 告诉朋友或家人你的目标，让他们监督或一起参与“减少屏幕时间”挑战。 具体行动计划（7天尝试）\n为了让方法更可操作，建议试试以下一周计划：\n第1-2天：觉察阶段 记录每次刷X的时间和触发场景（无聊、压力、等待时？）。 打开手机的屏幕时间统计，了解每天在X上花了多久。 第3-4天：限制阶段 设置X每日使用上限（如20分钟），关闭通知。 尝试“15分钟规则”：每次想刷新前等15分钟，做其他事。 第5-6天：替代阶段 准备一份“无聊时清单”（如听歌、散步、涂鸦），每次想刷X时选一项做。 每天留30分钟专注时间（无手机），做喜欢的事。 第7天：反思阶段 回顾一周感受：焦虑减少了吗？时间利用更好了吗？ 根据效果调整策略，设定下周目标。 如果效果不佳\n检查潜在问题：强迫性刷X可能是逃避现实压力（如工作、学业、人际关系）的表现。试着找出根源，必要时与信任的人倾诉或寻求心理咨询。 循序渐进：完全戒掉刷X可能不现实，目标是“有意识地使用”而不是“完全不用”。从小改变开始，比如每天减少10分钟。 专业帮助：如果尝试后仍无法控制，且影响生活质量，可考虑咨询心理医生，排查是否涉及更严重的成瘾或焦虑问题。 为什么这些方法有效？\n打破多巴胺循环：通过延迟和替代，减少对即时奖励的依赖。 重建控制感：有意识地管理时间和行为让你重新掌握主动权。 减少FOMO：专注于高质量内容和现实生活，降低对“错过”的担忧。 如果你想深入探讨某个方法（比如如何挑选替代活动、推荐专注工具），或有其他具体场景（比如工作时忍不住刷X），可以告诉我，我会进一步定制建议！\n自己的想法 好多建议其实自己都懂，但都没有做，可能是拖延。也有畏难情绪。 不过也有一个多巴胺的问题，看短视频和刷推确实让我不要动太多脑子，还能感觉在吸收知识，只有在看到自己的 todo list 时， 发现自己啥也没做，产生焦虑，然后做个鸵鸟。不去想，或者记录在周报里，但没有任何行动。\n可能比较重要的是心理预期问题，需要知道做一件事情大概多久，比如去玩游戏，我知道至少要一个小时。 学习的话需要挤出连续的 1 小时，或者 2 小时来，但很多时候想到就感觉不可能。 下班后，总是想，已经快 11 点了，这点时间不够学习，不如休息一会儿，然后就刷手机到 12 点多。 明明这个时间可以用来学习，自己没有利用起来，有身体太累的原因，也有懒惰的原因。\n给自己一个缓冲时间，不要习惯打开手机看推或者通过手机找乐子。要做的事情太多，感觉自己的时间被占用了太多。没有自己的时间。\n比如要洗碗，还有衣服需要手洗，白鞋子没有洗。这方面的正反馈也比较少，没有足够的动力来做这些事，一旦开始拖延就是无底洞。 也需就是人精力没有之前多了，不只是来新公司的问题，在上家公司，下班时间7.30也差不多，甚至还会想加班逃离回家的状态。 应该是有一些情绪没有处理好，需要自己去处理，动力也确实没有之前足。\n也有信息爆炸带来影响，太多知识想学习，但没有办法迅速学完，在学习的途中还会看到新的知识点，或者别的地方发现更重要的知识点。 这时就是没有头绪，特别容易学习新的知识，就是我提到过的狗熊掰玉米，掰一个丢一个。\n总结我的问题，和一些解决方法\n优先级问题 自己计划学习 k8s ，遇到公司的前端项目使用 vue3 ，优先级只能是 vue3 知识优先学习，但会导致 k8s 就此耽搁，大概率就是一直搁置 信息焦虑问题 有段时间希望能通过金融的方式来作为副业，但需要实时去关心时政，而且工作原因，需要实时看飞书消息。 习惯问题，多巴胺 习惯拿起手机，然后开始刷x，或者小红书等等信息。改变这些习惯，在适当的情况，把手机放到不方便触碰的地方。做延迟满足 降低启动难度 畏难情绪，告诉自己只做两分钟，比如打开书或写一句话，把大目标拆成小步骤。心里倒数5-4-3-2-1，然后立刻行动，打破犹豫。 想象完成任务后的成就感，增强动力。时间规划，事情预期需要调整。 ","date":"2025-05-11","permalink":"/posts/how-to-start-learning/","title":"怎样开始学习"},{"categories":["ARTS"],"content":"Algorithm 算法 二分查找 给定一个 n 个元素有序的（升序）整型数组 nums 和一个目标值 target ，写一个函数搜索 nums 中的 target，如果目标值存在返回下标，否则返回 -1。 示例 1:\n输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4 解释: 9 出现在 nums 中并且下标为 4 示例 2:\n输入: nums = [-1,0,3,5,9,12], target = 2 输出: -1 解释: 2 不存在 nums 中因此返回 -1\n【思路】 这道题目的前提是数组为有序数组，同时题目还强调数组中无重复元素，因为一旦有重复元素，那么结果就不是唯一。二分法的前提需要注意。\n递归函数，二分查找。 先找出数组的中间值，然后判断大小，继续找中间值，判断大小。 判读结束条件\nPython 1 2 3 4 5 6 7 8 9 10 11 12 def search(nums, target): left, right = 0, len(nums) - 1 while left \u0026lt;= right: mid = (left + right) // 2 if nums[mid] == target: return mid elif nums[mid] \u0026lt; target: left = mid + 1 else: right = mid - 1 return -1 Go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package main import \u0026#34;fmt\u0026#34; func search(nums []int, target int) int { left := 0 right := len(nums) - 1 for left \u0026lt;= right { mid := left + (right-left)/2 // 防止整数溢出 if nums[mid] == target { return mid } else if nums[mid] \u0026lt; target { left = mid + 1 } else { right = mid - 1 } } return -1 } func main() { nums := []int{-1, 0, 3, 5, 9, 12} target := 9 result := search(nums, target) fmt.Printf(\u0026#34;Target %d found at index: %d\\n\u0026#34;, target, result) // 输出 4 target = 2 result = search(nums, target) fmt.Printf(\u0026#34;Target %d found at index: %d\\n\u0026#34;, target, result) // 输出 -1 } Review 复审(英语) Tip 小技巧 考了一个无人机的证书，可以抵税，分享一下大概的攻略\n考试网址及流程\n相关题库内容\nShare 技术分享 cs 自学指南\n那些你不知道的TCP冷门知识\n我们高估了智力的重要性\n深入理解四种经典负载均衡算法\nTODO cs相关的专业知识 开始看陈硕的磁盘相关知识第三章 v转码笔记 ","date":"2025-04-07","permalink":"/posts/arts-2025week14/","title":"ARTS-2025week14"},{"categories":["ARTS"],"content":"Algorithm 算法 Review 复审(英语) 今天去了世博园，逛了中华艺术宫，看了很多名画，还吃了甜甜圈跟华夫饼。做的地铁8号线转2号线。吃了炸鸡排和米粉。逛了江边，还听别人唱歌。8点出地铁站外面下起了小雨，赶紧回家洗了个澡。\ntoday i went to shibopark\nTip 小技巧 少玩手机 看了一个视频 https://www.youtube.com/watch?v=iGiWdjsAjhA\u0026ab_channel=%E7%8E%8B%E5%BF%97%E5%AE%89 如何少玩手机，这个好像比较实际 https://www.youtube.com/watch?v=gvOOgqcpbvU\u0026ab_channel=%E8%96%91%E9%A4%85%E8%B3%87\nShare 技术分享 https://macos-defaults.com/dock/autohide.html\n话痨 wireshark 已经写了, 后面要优化下。\n我计划了很多事情，比如\n想等有空了看看历史 自己写的字比较丑，想练字 恶补下cs相关的专业知识 整理下我的电脑文件 整理nas，搞一个家庭影院 学习金融知识 ","date":"2025-04-03","permalink":"/posts/arts-2025week13/","title":"ARTS-2025week13"},{"categories":["ARTS"],"content":"Algorithm 算法 page14 找出n个字符串中相同的两个字符串\nReview 复审(英语) 今天去了世博园，逛了中华艺术宫，看了很多名画，还吃了甜甜圈跟华夫饼。做的地铁8号线转2号线。吃了炸鸡排和米粉。逛了江边，还听别人唱歌。8点出地铁站外面下起了小雨，赶紧回家洗了个澡。\ntoday i went to shibopark\nTip 小技巧 少玩手机 看了一个视频 https://www.youtube.com/watch?v=iGiWdjsAjhA\u0026ab_channel=%E7%8E%8B%E5%BF%97%E5%AE%89 如何少玩手机，这个好像比较实际 https://www.youtube.com/watch?v=gvOOgqcpbvU\u0026ab_channel=%E8%96%91%E9%A4%85%E8%B3%87\nShare 技术分享 https://macos-defaults.com/dock/autohide.html\n话痨 wireshark 已经写了, 后面要优化下。\n我计划了很多事情，比如\n想等有空了看看历史 自己写的字比较丑，想练字 恶补下cs相关的专业知识 整理下我的电脑文件 整理nas，搞一个家庭影院 学习金融知识 ","date":"2025-03-24","permalink":"/posts/arts-2025week12/","title":"ARTS-2025week12"},{"categories":["ARTS"],"content":"Algorithm 算法 page14 找出n个字符串中相同的两个字符串\nReview 复审(英语) 开始尝试自己听纯英文的视频，需要选自己喜欢的内容，还有就是视频有些可以意会。 需要自己多尝试看英文的东西，看不懂先别急着查。 尝试写一些英文事情 英语学习，有个日常使用词典。\nThere\u0026rsquo;s a new kind of coding I call \u0026ldquo;vibe coding\u0026rdquo;, where you fully give in to the vibes, embrace exponentials, and forget that the code even exists. It\u0026rsquo;s possible because the LLMs (e.g. Cursor Composer w Sonnet) are getting too good. Also I just talk to Composer with SuperWhisper so I barely even touch the keyboard. I ask for the dumbest things like \u0026ldquo;decrease the padding on the sidebar by half\u0026rdquo; because I\u0026rsquo;m too lazy to find it. I \u0026ldquo;Accept All\u0026rdquo; always, I don\u0026rsquo;t read the diffs anymore. When I get error messages I just copy paste them in with no comment, usually that fixes it. The code grows beyond my usual comprehension, I\u0026rsquo;d have to really read through it for a while. Sometimes the LLMs can\u0026rsquo;t fix a bug so I just work around it or ask for random changes until it goes away. It\u0026rsquo;s not too bad for throwaway weekend projects, but still quite amusing. I\u0026rsquo;m building a project or webapp, but it\u0026rsquo;s not really coding - I just see stuff, say stuff, run stuff, and copy paste stuff, and it mostly works.\n现在有一种新型的编码方式叫:\u0026ldquo;氛围编码\u0026rdquo;，\nTip 小技巧 少玩手机 看了一个视频 https://www.youtube.com/watch?v=iGiWdjsAjhA\u0026ab_channel=%E7%8E%8B%E5%BF%97%E5%AE%89 如何少玩手机，这个好像比较实际 https://www.youtube.com/watch?v=gvOOgqcpbvU\u0026ab_channel=%E8%96%91%E9%A4%85%E8%B3%87\nShare 技术分享 https://macos-defaults.com/dock/autohide.html\n话痨 wireshark 已经写了, 后面要优化下。\n我计划了很多事情，比如\n想等有空了看看历史 自己写的字比较丑，想练字 恶补下cs相关的专业知识 整理下我的电脑文件 整理nas，搞一个家庭影院 学习金融知识 ","date":"2025-03-12","permalink":"/posts/arts-2025week11/","title":"ARTS-2025week11"},{"categories":["ARTS"],"content":"Algorithm 算法 Review 复审(英语) 开始尝试自己听纯英文的视频，需要选自己喜欢的内容，还有就是视频有些可以意会。 需要自己多尝试看英文的东西，看不懂先别急着查。 尝试写一些英文事情 英语学习，有个日常使用词典。\nTip 小技巧 少玩手机 看了一个视频 https://www.youtube.com/watch?v=iGiWdjsAjhA\u0026ab_channel=%E7%8E%8B%E5%BF%97%E5%AE%89 如何少玩手机，这个好像比较实际 https://www.youtube.com/watch?v=gvOOgqcpbvU\u0026ab_channel=%E8%96%91%E9%A4%85%E8%B3%87\nShare 技术分享 话痨 wireshark 已经写了，但还需要自己再写一篇\n我计划了很多事情，比如\n想等有空了看看历史 自己写的字比较丑，想练字 恶补下cs相关的专业知识 整理下我的电脑文件 整理nas，搞一个家庭影院 学习金融知识 ","date":"2025-03-11","permalink":"/posts/arts-2025week10/","title":"ARTS-2025week10"},{"categories":["tcp"],"content":"原文链接\nWireshark常用功能笔记 wireshark 包类型 pcap 这个是 libpcap 的格式，也是 tcpdump 和 Wireshark 等工具默认支持的文件格式。pcap 格 式的文件中除了报文数据以外，也包含了抓包文件的元信息，比如版本号、抓包时间、每个报 文被抓取的最大长度，等等。\ncap cap 文件可能含有一些 libpcap 标准之外的数据格式，它是由一些 tcpdump 以外的抓包程序生成的。 比如 Citrix 公司的 netscaler 负载均衡器，它的 nstrace 命令生成的抓包文件，就是 以.cap 为扩展名的。这种文件除了包含 pcap 标准定义的信息以外，还包含了 LB 的前端连接 和后端连接之间的 mapping 信息。Wireshark 是可以读取这些.cap 文件的，只要在正确的 版本上\npcapng pcap 格式虽然满足了大部分需求，但是它也有一些不足。比如，现在多网口的情况已经越来 越常见了，我们也经常需要从多个网络接口去抓取报文，所以单个pcapng的包里面会包含多个网络接口的包。\npcapng 还有很多别的特性，比如更细粒度的报文时间戳、允许对报文添加注释、更灵活的元数据，如果你是用版本比较新的 Wireshark 和 tshark 做抓包，默认生成的抓包文件就已经是 pcapng 格式了\ntcpdump Bash 1 2 3 4 5 6 7 8 9 10 11 12 # tcpdump -i eth0 -s 80 -w /tmp/tcpdump.cap - i 指定网卡eth0 - s 指定抓包大小80字节 - w 保持成文件存储位置 # tcpdump -i eth0 host 10.31.200.131 -w /tmp/tcpdump.cap - host 表示只抓与10.31.200.131通信的包 - port 表示只抓指定端口的包 PS：加更多的限制可能会抓不到包，比如NAT把对方的包给改掉了。 常用参数 -h：帮助 Bash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 // On Linux # tcpdump -h [root@dev-env ~]# tcpdump -h tcpdump version 4.9.2 libpcap version 1.5.3 OpenSSL 1.0.2k-fips 26 Jan 2017 Usage: tcpdump [-aAbdDefhHIJKlLnNOpqStuUvxX#] [ -B size ] [ -c count ] [ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ] [ -i interface ] [ -j tstamptype ] [ -M secret ] [ --number ] [ -Q|-P in|out|inout ] [ -r file ] [ -s snaplen ] [ --time-stamp-precision precision ] [ --immediate-mode ] [ -T type ] [ --version ] [ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ] [ -Z user ] [ expression ] // On Mac zhengxianle@zhengxinledembp ~ dumpcap -h Dumpcap (Wireshark) 4.0.5 (v4.0.5-0-ge556162d8da3) Capture network packets and dump them into a pcapng or pcap file. See https://www.wireshark.org for more information. Usage: dumpcap [options] ... Capture interface: -i \u0026lt;interface\u0026gt;, --interface \u0026lt;interface\u0026gt; name or idx of interface (def: first non-loopback), or for remote capturing, use one of these formats: rpcap://\u0026lt;host\u0026gt;/\u0026lt;interface\u0026gt; TCP@\u0026lt;host\u0026gt;:\u0026lt;port\u0026gt; --ifname \u0026lt;name\u0026gt; name to use in the capture file for a pipe from which we\u0026#39;re capturing --ifdescr \u0026lt;description\u0026gt; description to use in the capture file for a pipe from which we\u0026#39;re capturing -f \u0026lt;capture filter\u0026gt; packet filter in libpcap filter syntax -D：查看有哪些网卡 Bash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 // Linux [root@dev-env ~]# tcpdump -D 1.eth0 2.tun0 3.nflog (Linux netfilter log (NFLOG) interface) 4.nfqueue (Linux netfilter queue (NFQUEUE) interface) 5.usbmon1 (USB bus number 1) 6.any (Pseudo-device that captures on all interfaces) 7.lo [Loopback] // MacOS zhengxianle@zhengxinledembp ~# dumpcap -D 1. en0 (Wi-Fi) 2. awdl0 3. llw0 4. utun0 5. utun1 6. utun2 7. utun3 8. utun4 9. utun5 10. utun6 11. utun7 12. utun8 13. utun9 14. lo0 (Loopback) 15. en4 (Thunderbolt 4) 16. en1 (Thunderbolt 1) 17. en2 (Thunderbolt 2) 18. en3 (Thunderbolt 3) 19. bridge0 (Thunderbolt Bridge) 20. pktap0 21. gif0 22. stf0 23. ap1 zhengxianle@zhengxinledembp~# tcpdump -D 1.en0 [Up, Running, Wireless, Associated] 2.awdl0 [Up, Running, Wireless, Associated] 3.llw0 [Up, Running, Wireless, Associated] 4.utun0 [Up, Running] 5.utun1 [Up, Running] 6.utun2 [Up, Running] 7.utun3 [Up, Running] 8.utun4 [Up, Running] 9.utun5 [Up, Running] 10.utun6 [Up, Running] 11.utun7 [Up, Running] 12.utun8 [Up, Running] 13.utun9 [Up, Running] 14.lo0 [Up, Running, Loopback] 15.en4 [Up, Running, Disconnected] 16.en1 [Up, Running, Disconnected] 17.en2 [Up, Running, Disconnected] 18.en3 [Up, Running, Disconnected] 19.bridge0 [Up, Running, Disconnected] 20.pktap0 [Up] 21.gif0 [none] 22.stf0 [none] 23.ap1 [Wireless, Association status unknown] -i : 指定网卡，这里1指定的是-D中的网卡序号 Bash 1 2 3 4 5 zhengxianle@zhengxinledembp~ # dumpcap -i 1 Capturing on \u0026#39;Wi-Fi: en0\u0026#39; File: /var/folders/23/7d2bd6ss2db4vjz8twqwf4940000gn/T/wireshark_Wi-FiSI0731.pcapng Packets captured: 923 Packets received/dropped on interface \u0026#39;Wi-Fi: en0\u0026#39;: 923/0 (pcap:0/dumpcap:0/flushed:0/ps_ifdrop:0) (100.0%) -w：指定存储在的位置 Bash 1 2 3 4 5 zhengxianle@zhengxinledembp~# dumpcap -i 1 -w /Users/zhengxianle/Desktop/capture/sample.pcapng Capturing on \u0026#39;Wi-Fi: en0\u0026#39; File: /Users/zhengxianle/Desktop/capture/sample.pcapng Packets captured: 573 Packets received/dropped on interface \u0026#39;Wi-Fi: en0\u0026#39;: 573/0 (pcap:0/dumpcap:0/flushed:0/ps_ifdrop:0) (100.0%) -b filesize:500000 // 代表500M files:10 // 代表一共存10个文件，当第11个文件产生时，会覆盖第一个文件。 Bash 1 2 3 4 5 6 7 8 9 10 更详细的配置： -b \u0026lt;ringbuffer opt.\u0026gt; ..., --ring-buffer \u0026lt;ringbuffer opt.\u0026gt; duration:NUM - switch to next file after NUM secs filesize:NUM - switch to next file after NUM kB files:NUM - ringbuffer: replace after NUM files packets:NUM - ringbuffer: replace after NUM packets interval:NUM - switch to next file when the time is an exact multiple of NUM secs printname:FILE - print filename to FILE when written (can use \u0026#39;stdout\u0026#39; or \u0026#39;stderr\u0026#39;) Bash 1 2 3 4 5 6 zhengxianle@zhengxinledembp~ # dumpcap -i 1 -w /Users/zhengxianle/Desktop/capture/sample.pcapng -b filesize:500000 -b files:10 Capturing on \u0026#39;Wi-Fi: en0\u0026#39; File: /Users/zhengxianle/Desktop/capture/sample_00001_20230426220541.pcapng Packets captured: 1238 Packets received/dropped on interface \u0026#39;Wi-Fi: en0\u0026#39;: 1238/0 (pcap:0/dumpcap:0/flushed:0/ps_ifdrop:0) (100.0%) -c ： 数量，可以抓取固定数量的保文，在流量较高时，可以避免一不小心抓取过多报文\n-n：不做地址转换（比如IP地址转换为主机，port 80转换为http）\n-v/-vv/-vvv:可以打印更加详细的报文信息\n-e ： 可以打印二层信息，特别是MAC地址\n-p：关闭混杂模式。\n所谓混杂模式，也就是嗅探（Sniffing），就是把目的地址不是本机地址的网络报文也抓取下来。\n-X ：在解析和打印时，除了打印每个数据包的头之外，还可以用十六进制和ASCII打印每个数据包的数据（减去其链接级头）。 这对于分析新协议非常方便。 在目前的实现中，如果数据包被截断，这个标志可能与-XX的效果相同。\n这个参数很有用啊，可以看到具体的请求内容了，快速排查问题的时候是可以这么做的。\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 [root@dev-env ~]# netstat -ntpl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:41046 0.0.0.0:* LISTEN 3953/java tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1957/sshd tcp 0 0 0.0.0.0:8089 0.0.0.0:* LISTEN 2818/nginx: master tcp 0 0 0.0.0.0:8090 0.0.0.0:* LISTEN 2818/nginx: master tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 2818/nginx: master tcp 0 0 0.0.0.0:8091 0.0.0.0:* LISTEN 2818/nginx: master tcp 0 0 0.0.0.0:18080 0.0.0.0:* LISTEN 3953/java tcp 0 0 0.0.0.0:10051 0.0.0.0:* LISTEN 2171/zabbix_proxy tcp 0 0 0.0.0.0:11940 0.0.0.0:* LISTEN 1492/openvpn tcp 0 0 0.0.0.0:2181 0.0.0.0:* LISTEN 3953/java tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 1837/mysqld tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 1138/rpcbind tcp6 0 0 :::10050 :::* LISTEN 1489/zabbix_agent2 tcp6 0 0 :::10051 :::* LISTEN 2171/zabbix_proxy tcp6 0 0 :::111 :::* LISTEN 1138/rpcbind [root@dev-env ~]# tcpdump -i any -X port 18080 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes 22:02:52.188802 IP 10.222.1.77.51848 \u0026gt; 172.22.183.239.18080: Flags [.], ack 3220596691, win 790, options [nop,nop,TS val 2719039184 ecr 2964196926], length 0 0x0000: 4500 0034 2a37 4000 8006 605c 0ade 014d E..4*7@...`\\...M 0x0010: ac16 b7ef ca88 46a0 8504 150d bff6 67d3 ......F.......g. 0x0020: 8010 0316 90a3 0000 0101 080a a211 3ed0 ..............\u0026gt;. 0x0030: b0ae 0e3e 0000 0000 0000 0000 0000 0000 ...\u0026gt;............ 0x0040: 0000 0000 .... 22:02:52.188822 IP 10.222.1.77.51848 \u0026gt; 172.22.183.239.18080: Flags [.], ack 1, win 790, options [nop,nop,TS val 2719039184 ecr 2964196926], length 0 0x0000: 4500 0034 2a37 4000 7f06 615c 0ade 014d E..4*7@...a\\...M 0x0010: ac16 b7ef ca88 46a0 8504 150d bff6 67d3 ......F.......g. 0x0020: 8010 0316 90a3 0000 0101 080a a211 3ed0 ..............\u0026gt;. 0x0030: b0ae 0e3e 0000 0000 0000 0000 0000 0000 ...\u0026gt;............ 0x0040: 0000 0000 .... 22:02:52.188961 IP 172.22.183.239.18080 \u0026gt; 10.222.1.77.51848: Flags [.], ack 1, win 805, options [nop,nop,TS val 2964201950 ecr 2719019098], length 0 0x0000: 4500 0034 9a5b 4000 8006 f037 ac16 b7ef E..4.[@....7.... 0x0010: 0ade 014d 46a0 ca88 bff6 67d3 8504 150e ...MF.....g..... 0x0020: 8010 0325 cb69 0000 0101 080a b0ae 21de ...%.i........!. 0x0030: a210 f05a 0000 0000 0000 0000 0000 0000 ...Z............ 0x0040: 0000 0000 .... 22:02:52.188968 IP 172.22.183.239.18080 \u0026gt; 10.222.1.77.51848: Flags [.], ack 1, win 805, options [nop,nop,TS val 2964201950 ecr 2719019098], length 0 0x0000: 4500 0034 9a5b 4000 7f06 f137 ac16 b7ef E..4.[@....7.... 0x0010: 0ade 014d 46a0 ca88 bff6 67d3 8504 150e ...MF.....g..... 0x0020: 8010 0325 cb69 0000 0101 080a b0ae 21de ...%.i........!. 0x0030: a210 f05a 0000 0000 0000 0000 0000 0000 ...Z............ 0x0040: 0000 0000 .... 22:02:57.212632 IP 10.222.1.77.51848 \u0026gt; 172.22.183.239.18080: Flags [.], ack 1, win 790, options [nop,nop,TS val 2719044208 ecr 2964201950], length 0 0x0000: 4500 0034 2a38 4000 8006 605b 0ade 014d E..4*8@...`[...M 0x0010: ac16 b7ef ca88 46a0 8504 150d bff6 67d3 ......F.......g. 0x0020: 8010 0316 6963 0000 0101 080a a211 5270 ....ic........Rp 0x0030: b0ae 21de 0000 0000 0000 0000 0000 0000 ..!............. 0x0040: 0000 0000 .... 22:02:57.212652 IP 10.222.1.77.51848 \u0026gt; 172.22.183.239.18080: Flags [.], ack 1, win 790, options [nop,nop,TS val 2719044208 ecr 2964201950], length 0 0x0000: 4500 0034 2a38 4000 7f06 615b 0ade 014d E..4*8@...a[...M 0x0010: ac16 b7ef ca88 46a0 8504 150d bff6 67d3 ......F.......g. 0x0020: 8010 0316 6963 0000 0101 080a a211 5270 ....ic........Rp 0x0030: b0ae 21de 0000 0000 0000 0000 0000 0000 ..!............. 0x0040: 0000 0000 .... 22:02:57.212782 IP 172.22.183.239.18080 \u0026gt; 10.222.1.77.51848: Flags [.], ack 1, win 805, options [nop,nop,TS val 2964206974 ecr 2719019098], length 0 0x0000: 4500 0034 9a5c 4000 8006 f036 ac16 b7ef E..4.\\@....6.... 0x0010: 0ade 014d 46a0 ca88 bff6 67d3 8504 150e ...MF.....g..... 0x0020: 8010 0325 b7c9 0000 0101 080a b0ae 357e ...%..........5~ 0x0030: a210 f05a 0000 0000 0000 0000 0000 0000 ...Z............ 0x0040: 0000 0000 .... 22:02:57.212790 IP 172.22.183.239.18080 \u0026gt; 10.222.1.77.51848: Flags [.], ack 1, win 805, options [nop,nop,TS val 2964206974 ecr 2719019098], length 0 0x0000: 4500 0034 9a5c 4000 7f06 f136 ac16 b7ef E..4.\\@....6.... 0x0010: 0ade 014d 46a0 ca88 bff6 67d3 8504 150e ...MF.....g..... 0x0020: 8010 0325 b7c9 0000 0101 080a b0ae 357e ...%..........5~ 0x0030: a210 f05a 0000 0000 0000 0000 0000 0000 ...Z............ 0x0040: 0000 0000 .... 22:02:59.585608 IP 10.222.1.12.57689 \u0026gt; 172.22.183.239.18080: Flags [P.], seq 1350914016:1350914568, ack 1773723314, win 2048, options [nop,nop,TS val 3234104033 ecr 2964179285], length 552 0x0000: 4502 025c 0000 4000 4006 c8aa 0ade 010c E..\\..@.@....... 0x0010: ac16 b7ef e159 46a0 5085 4fe0 69b8 deb2 .....YF.P.O.i... 0x0020: 8018 0800 f5cf 0000 0101 080a c0c4 82e1 ................ 0x0030: b0ad c955 504f 5354 202f 7878 6c2d 6a6f ...UPOST./xxl-jo 0x0040: 622d 6164 6d69 6e2f 6170 6920 4854 5450 b-admin/api.HTTP 0x0050: 2f31 2e31 0d0a 686f 7374 3a20 3137 322e /1.1..host:.172. 0x0060: 3232 2e31 3833 2e32 3339 0d0a 636f 6e6e 22.183.239..conn 0x0070: 6563 7469 6f6e 3a20 6b65 6570 2d61 6c69 ection:.keep-ali 0x0080: 7665 0d0a 636f 6e74 656e 742d 6c65 6e67 ve..content-leng 0x0090: 7468 3a20 3434 390d 0a0d 0a43 302d 636f th:.449....C0-co 0x00a0: 6d2e 7878 6c2e 7270 632e 7265 6d6f 7469 m.xxl.rpc.remoti 0x00b0: 6e67 2e6e 6574 2e70 6172 616d 732e 5878 ng.net.params.Xx 0x00c0: 6c52 7063 5265 7175 6573 7498 0972 6571 lRpcRequest..req 0x00d0: 7565 7374 4964 1063 7265 6174 654d 696c uestId.createMil 0x00e0: 6c69 7354 696d 650b 6163 6365 7373 546f lisTime.accessTo 0x00f0: 6b65 6e09 636c 6173 734e 616d 650a 6d65 ken.className.me 0x0100: 7468 6f64 4e61 6d65 0776 6572 7369 6f6e thodName.version 0x0110: 0e70 6172 616d 6574 6572 5479 7065 730a .parameterTypes. 0x0120: 7061 7261 6d65 7465 7273 6030 2432 6332 parameters`0$2c2 0x0130: 3332 3739 382d 3536 6164 2d34 3562 362d 32798-56ad-45b6- 0x0140: 6162 3333 2d61 6665 6636 6261 3630 3565 ab33-afef6ba605e 0x0150: 334c 0000 0188 4e11 f43a 4e1d 636f 6d2e 3L....N..:N.com. 0x0160: 7878 6c2e 6a6f 622e 636f 7265 2e62 697a xxl.job.core.biz 0x0170: 2e41 646d 696e 4269 7a08 7265 6769 7374 .AdminBiz.regist 0x0180: 7279 4e71 105b 6a61 7661 2e6c 616e 672e ryNq.[java.lang. 0x0190: 436c 6173 7343 0f6a 6176 612e 6c61 6e67 ClassC.java.lang 0x01a0: 2e43 6c61 7373 9104 6e61 6d65 6130 2863 .Class..namea0(c 0x01b0: 6f6d 2e78 786c 2e6a 6f62 2e63 6f72 652e om.xxl.job.core. 0x01c0: 6269 7a2e 6d6f 6465 6c2e 5265 6769 7374 biz.model.Regist 0x01d0: 7279 5061 7261 6d71 075b 6f62 6a65 6374 ryParamq.[object 0x01e0: 4330 2863 6f6d 2e78 786c 2e6a 6f62 2e63 C0(com.xxl.job.c 0x01f0: 6f72 652e 6269 7a2e 6d6f 6465 6c2e 5265 ore.biz.model.Re 0x0200: 6769 7374 7279 5061 7261 6d93 0b72 6567 gistryParam..reg 0x0210: 6973 7447 726f 7570 0b72 6567 6973 7472 istGroup.registr 0x0220: 794b 6579 0d72 6567 6973 7472 7956 616c yKey.registryVal 0x0230: 7565 6208 4558 4543 5554 4f52 0e6f 7370 ueb.EXECUTOR.osp 0x0240: 4461 7461 4d61 702d 6a6f 6210 3130 2e32 DataMap-job.10.2 0x0250: 3136 2e31 2e31 313a 3939 3939 0000 0000 16.1.11:9999.... 0x0260: 0000 0000 0000 0000 0000 0000 ............ 22:02:59.585641 IP 10.222.1.12.57689 \u0026gt; 172.22.183.239.18080: Flags [P.], seq 0:552, ack 1, win 2048, options [nop,nop,TS val 3234104033 ecr 2964179285], length 552 0x0000: 4502 025c 0000 4000 3f06 c9aa 0ade 010c E..\\..@.?....... 0x0010: ac16 b7ef e159 46a0 5085 4fe0 69b8 deb2 .....YF.P.O.i... 0x0020: 8018 0800 f5cf 0000 0101 080a c0c4 82e1 ................ 0x0030: b0ad c955 504f 5354 202f 7878 6c2d 6a6f ...UPOST./xxl-jo 0x0040: 622d 6164 6d69 6e2f 6170 6920 4854 5450 b-admin/api.HTTP 0x0050: 2f31 2e31 0d0a 686f 7374 3a20 3137 322e /1.1..host:.172. 0x0060: 3232 2e31 3833 2e32 3339 0d0a 636f 6e6e 22.183.239..conn 0x0070: 6563 7469 6f6e 3a20 6b65 6570 2d61 6c69 ection:.keep-ali 0x0080: 7665 0d0a 636f 6e74 656e 742d 6c65 6e67 ve..content-leng 0x0090: 7468 3a20 3434 390d 0a0d 0a43 302d 636f th:.449....C0-co 0x00a0: 6d2e 7878 6c2e 7270 632e 7265 6d6f 7469 m.xxl.rpc.remoti 0x00b0: 6e67 2e6e 6574 2e70 6172 616d 732e 5878 ng.net.params.Xx 0x00c0: 6c52 7063 5265 7175 6573 7498 0972 6571 lRpcRequest..req 0x00d0: 7565 7374 4964 1063 7265 6174 654d 696c uestId.createMil 0x00e0: 6c69 7354 696d 650b 6163 6365 7373 546f lisTime.accessTo 0x00f0: 6b65 6e09 636c 6173 734e 616d 650a 6d65 ken.className.me 0x0100: 7468 6f64 4e61 6d65 0776 6572 7369 6f6e thodName.version 0x0110: 0e70 6172 616d 6574 6572 5479 7065 730a .parameterTypes. 0x0120: 7061 7261 6d65 7465 7273 6030 2432 6332 parameters`0$2c2 0x0130: 3332 3739 382d 3536 6164 2d34 3562 362d 32798-56ad-45b6- 0x0140: 6162 3333 2d61 6665 6636 6261 3630 3565 ab33-afef6ba605e 0x0150: 334c 0000 0188 4e11 f43a 4e1d 636f 6d2e 3L....N..:N.com. 0x0160: 7878 6c2e 6a6f 622e 636f 7265 2e62 697a xxl.job.core.biz 0x0170: 2e41 646d 696e 4269 7a08 7265 6769 7374 .AdminBiz.regist 0x0180: 7279 4e71 105b 6a61 7661 2e6c 616e 672e ryNq.[java.lang. 0x0190: 436c 6173 7343 0f6a 6176 612e 6c61 6e67 ClassC.java.lang 0x01a0: 2e43 6c61 7373 9104 6e61 6d65 6130 2863 .Class..namea0(c 0x01b0: 6f6d 2e78 786c 2e6a 6f62 2e63 6f72 652e om.xxl.job.core. 0x01c0: 6269 7a2e 6d6f 6465 6c2e 5265 6769 7374 biz.model.Regist 0x01d0: 7279 5061 7261 6d71 075b 6f62 6a65 6374 ryParamq.[object 0x01e0: 4330 2863 6f6d 2e78 786c 2e6a 6f62 2e63 C0(com.xxl.job.c 0x01f0: 6f72 652e 6269 7a2e 6d6f 6465 6c2e 5265 ore.biz.model.Re 0x0200: 6769 7374 7279 5061 7261 6d93 0b72 6567 gistryParam..reg 0x0210: 6973 7447 726f 7570 0b72 6567 6973 7472 istGroup.registr 0x0220: 794b 6579 0d72 6567 6973 7472 7956 616c yKey.registryVal 0x0230: 7565 6208 4558 4543 5554 4f52 0e6f 7370 ueb.EXECUTOR.osp 0x0240: 4461 7461 4d61 702d 6a6f 6210 3130 2e32 DataMap-job.10.2 0x0250: 3136 2e31 2e31 313a 3939 3939 0000 0000 16.1.11:9999.... 0x0260: 0000 0000 0000 0000 0000 0000 ............ 抓包技巧 打标记 包除了小以外，还需要为每步操作打标记，这样的包一目了然，赏心悦目。比如：\nsudo ping www.baidu.com -c 1 -s 1 操作步骤1 sudo ping www.baidu.com -c 1 -s 2 操作步骤2 sudo ping www.baidu.com -c 1 -s 3 操作步骤3 解释（MacOS下）：-c 指定ping次数，-s指定包大小\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 usage: ping [-AaDdfnoQqRrv] [**-c count**] [-G sweepmaxsize] [-g sweepminsize] [-h sweepincrsize] [-i wait] [-l preload] [-M mask | time] [-m ttl] [-p pattern] [-S src_addr] [**-s packetsize**] [-t timeout][-W waittime] [-z tos] host ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait] [-l preload] [-M mask | time] [-m ttl] [-p pattern] [-S src_addr] [-s packetsize] [-T ttl] [-t timeout] [-W waittime] [-z tos] mcast-group Apple specific options (to be specified before mcast-group or host like all options) -b boundif # bind the socket to the interface -k traffic_class # set traffic class socket option -K net_service_type # set traffic class socket options --apple-connect # call connect(2) in the socket --apple-time # display current time 在抓包时可以看到包下面Data（-s部分大小），byte的大小表示当前是第几步，就不容易出错。\n过滤技巧 已知协议，直接输入协议即可，例如：http/tcp/smtp等，多个协议可以用||来分开，例如：tcp||http ip+端口号是比较常用的过滤方式，例如：ip.addr eq 172.22.183.226 \u0026amp;\u0026amp; tcp.port eq 8080，还可以右键单击感兴趣的包，选择follow TCP/UDP Stream，就可以自动过滤当前的包了。 过滤跟某个IP地址相关的包 Bash 1 ip.addr == 192.168.0.1 排除某些端口 Bash 1 not (tcp or arp or smtp) 根据端口范围查找 Bash 1 tcp.port in {80,443,8080} 按照关键词查询：区分大小写 Bash 1 frame contains \u0026#34;baidu.com\u0026#34; 按照关键词查询：不区分大小写（matches采用的是regex） Bash 1 frame matches \u0026#34;baidu.com\u0026#34; 如何过滤一个TCP/UDP Stream呢？\n两端的IP加port，单击wireshark的Statistics→conversations，再单击TCP或者UDP标签就可以看到所有的Stream了。如下图：\n用鼠标过滤：可以选择感兴趣的包，右键加入筛选即可。有两种方式：\nPrepare a Filter→Selected（可以选择多个一起执行） Apply as Filter→Selected（立即自动执行） 也有很多组合方式可以按条件来：\nSelected not selected and selected or selected and not selected or not selected 过滤后的导出到新文件，因为文件更小。导出时要选择File→Export Specified Packets，这样就不会漏掉关联的包，不然会报错。\n过滤失败的握手\nBash 1 tcp.flags.reset == 1 \u0026amp;\u0026amp; tcp.seq==1 过滤重传的握手请求 过滤完查看完整的包流程，可以看到服务端的端口被使用了。\nBash 1 tcp.flags.syn == 1 \u0026amp;\u0026amp; tcp.analysis.retransmission 抓包时过滤 需求：统计某个HTTPS VIP的访问流量里，TLS版本（主要是TLS1.0、1.1、1.2、1.3）的分布。为了控制抓包文件的大小，不抓TLS的所有报文，只想抓TLS版本信息。\n针对这个需求，tcpdump本身没有现成的过滤器。BPF本身是基于偏移量来做解析的，所以可以在tcpdump中使用这种偏移量技术，所以类似如下命令：\nBash 1 tcpdump -w /tmp/[fileter_TLS_Client_Hello.pcap](https://drive.google.com/file/d/112X8cKH78DQ0GQL8kMlT2RvI3gxUwbHv/view?usp=drive_link) \u0026#39;dst port 443 \u0026amp;\u0026amp; tcp[20]==22 \u0026amp;\u0026amp; tcp[25]==1\u0026#39; dst port 443：这个最简单，就是抓取从客户端发过来的访问 HTTPS 的报文。 tcp[20]==22：这是提取了 TCP 的第 21 个字节（因为初始序号是从 0 开始的），由于 TCP 头部占 20 字节，TLS 又是 TCP 的载荷，那么 TLS 的第 1 个字节就是 TCP 的第 21 个字节，也就是 TCP[20]，这个位置的值如果是 22（十进制），那么就表明这个是 TLS 握手报文。 tcp[25]==1：同理，这是 TCP 头部的第 26 个字节，如果它等于 1，那么就表明这个是 Client Hello 类型的 TLS 握手报文。 对应到偏移量的介绍如下：\ntcpdump 也预定义了一些相对方便的过滤器。比如我们想要过滤出 TCP RST 报文，那么可以用下面这种写法，相对来说比用数字做偏移量的写法，要更加容易理解和记忆：\nBash 1 tcpdump -w file.pcap \u0026#39;tcp[tcpflags]\u0026amp;(tcp-rst) != 0\u0026#39; 对应到偏移量的写法如下：\nBash 1 tcpdump -w file.pcap \u0026#39;tcp[13]\u0026amp;4 != 0\u0026#39; 读取抓包文件 tcpdump后面加上-r参数和文件名称，就可以读取这个文件了，也可以加上过滤条件：\nBash 1 tcpdump -r file.pcap \u0026#39;tcp[tcpflags] \u0026amp; (tcp-rst) != 0\u0026#39; 过滤后转存 抓包以后过滤想要的报文，并保存到另一个文件中，如下所示：\nBash 1 tcpdump -r file.pcap \u0026#39;tcp[tcpflags]\u0026amp;(tcp-rst)!=0\u0026#39; -w rst.pcap 配置 解析已知协议与IP域名映射 查询当前已经解析了哪些域名 设置私有IP名称 查看刚设置自定义的名称：\n保存文件（含host对应名称） 设置时间列 设置计时器，按照发送包的时间间隔来设置当前包发出去后返回需要的时间。\n设置时间重新开始计时的时间引用，时间会从当前引用开始重新计时\n设置TCP Stream Time，tcp的响应时间（可以根据此时间做排序以后排查慢的包）\n自动分析 单击Analyze→Expert Information，可以显示出来不同级别的提示信息。比如：重传的统计、连接的建立和重置统计等。分析网络性能和连接问题时这个功能比较有用，如下图： 单击Statistics→Service Response Time，再选定协议名称，可以得到响应时间统计表。在衡量服务器性能时经常需要统计此结果。如下图：选择的是SMB协议的操作响应时间，不知道为啥没有值。 单击Statistics→TCP Stream Graph，可以生成几类统计图。例如： Time-Sequence Graph（Stevens） Time-Sequence Graph（tcptrace） Throughput Round Trip Time Window Scaling 可以根据传输数据信息来分析系统发送数据状态。\n单击Statistics→ Capture File Properties看到一些统计信息，比如平均流量等，可以推测负载状况。如下图：流量大小6.8M。 概览（statistics→Conversations）,根据此可以分析出当前抓包的概览信息，包括 包的数量，大小IPv4流量信息，传输时间长 TCP中开始时间，数据传输时间 TCP中IP地址，端口号，包的大小，包开始时间，持续时间 UDP的客户端、服务端信息，传输包的大小，开始时间，持续时间等信息 可以根据需求，直接使用右键进行包的过滤，过滤以后可以在主页面中看到过滤后的包。\n注意：当开启Nagle算法和延迟确认时，在分析过程中如果看不出差异，可以缩小时间范围。\n文件提取（未加密的数据） 设置允许重组tcp流量包\n导出http文件（这里是http的请求）\n选择以后，点击保存即可\nIP地址映射（GeoIP） 可以在此处下载IP地址对应的城市：https://dev.maxmind.com/geoip/geolite2-free-geolocation-data?lang=en\n可以在statistics→endpoints中进行分析，同时可以用map进行映射到地图\n根据城市做搜索：\n搜索 command+f搜索关键字，例如查询“common/service”的关键词，如下图：这个太有用了！！\n查看走哪个网卡 Bash 1 2 3 4 5 6 # ip route get 172.16.0.52 172.16.0.52 dev **en0** src 172.16.0.52 # ip route get develop.pansft.com 172.22.183.226 via 10.222.1.1 dev **utun8** src 10.222.1.4 # ip route get www.ilikejobs.com 104.21.42.70 via 172.16.0.1 dev **en0** src 172.16.0.52 TShark tshark\nLayout 设置窗口的Layout 这里设置第三个窗口，以协议栈的方式显示。\n右键可以显示当前每个协议字段的值：\n设置完了显示如此：\n安全 DDoS之SYN flood 原理，大量主机发送SYN请求给服务器，假装要链接TCP建立链接。这些SYN请求可能含有假的源地址，所以服务器响应以后永远收不到Ack，就会留下half-open状态的TCP链接。由于每个TCP链接都会消耗一定的系统资源，所以大量的无用连接会影响到真正的用户访问，导致拒绝访问情况产生。\n常见错误 TCP window full 表示这个包的发送方已经把对方所声明的接收窗口耗尽了。\nTCP Zerowindow TCP中的“win=”代表接收窗口的大小，表示这个包的发送方当前还有多少缓冲区可以接收数据。\n上面这两个的区别：TCP window full表示这个包的发送方暂时没办法再发送数据了，TCP Zerowindow 表示这个包的发送方暂时没办法再接受数据了。也就是说两者都意味着传输暂停，都必须引起重视。\nPackage size limited during capture Bash 1 2 可以通过-s参数来指定抓包的大小，下面就是指定每个包抓1000字节的包。 [root] tcpdump -i eth0 -s 1000 -w /tmp/tcpdump.cap TCP Previous segment not captured TCP传输过程中，同一台主机发出的数据段应该是连续的，即后一个包的Seq号等于前一个包的Seq+Len（三次握手和四次挥手是例外）。\n如果wireshark发现一个包的seq号大于前一个包的seq+len，就知道中间缺失了一段数据。假如缺失的那段数据在整个网络包中都找不到（即排除乱序），就会提示[TCP Previous segment not captured]。\n🔔：\n网络包没有抓到分两种情况，一种是真丢了，另一种是实际上没有丢，但是被抓包工具漏掉了。\n在Wireshark上如何区分这两种情况呢，只需要看对方回复的ACK即可，如果确认包含了没抓的那个包，那就是抓包工具漏掉了而已，否则就是真丢了。\n注意：网络传输路径中有可能网络设备MTU比较小，会导致丢包，需要保持网络路径的MTU一致。\nTCP Acked unseen segment 当Wireshark发现被ACK的那个包没有抓到，就会提示TCP ACKED unseen segment，这个几乎是可以永远忽略的。\nTCP Out-of-order TCP 在传输过程中（不包含三次握手，四次挥手），同一台主机发出的数据包应该是连续的，即最后一个包的Seq号等于前一个包的Seq+len。\n小跨度的乱序影响不大，比如原本顺序 为1、2、3、4、5号包被打乱城2、1、3、4、5就没事，但跨度大的乱序可能触发快速重传，比如打乱城2、3、4、5、1时，就会触发足够多的Dup Ack，而导致1号包的重传。\nTCP Dup ACK 当乱序或者丢包发生时，接受方会收到一些Seq号比期望值大的包，它每次收到这种包就会Ack一次期望的Seq值，以此方式来提醒发送方，于是就产生了一些重复的ack，Wireshark会在这些重复的ack上打标记 [TCP Dup Ack]。\nTCP Fast Retransmission 当发送方收到3个以上或者TCP Dup ACK，就意味着之前发送的包可能丢了，于是快速重传它。\nTCP Retransmission 如果一个包真的丢了，有没有后续的包可以在接受方触发Dup Ack时，就不会快速重传，这种情况只能等到超时了再重传，此类包就会被Wireshark打上TCP Retransmission。\n此处可以重点学习一下。\nTCP Segment of a reassembled PDU 这个表示可以把属于同一个应用层的PDU的TCP包虚拟地集中起来，在最后形成一个完整的包。\n需要在Wireshark里面启用这个配置。在：Preferences→ Protocols→TCP菜单，如下图：\nContinuations to 跟上面的相反，需要关闭Allow sub dissector to reassemble tcp streams这个开关，就会显示这个信息。\nTime-to-live exceeded（Fragment reassembly time exceeded) 表示这个包的发送方之前就收到了一些分片，但是由于某些原因迟迟无法组装起来。\n问题 为什么MacBook上提示：You don’t have permission to capture on local interfaces. 解决方法：\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 zhengxianle@zhengxianledeMacBook-Pro ~\u0026gt; whoami zhengxianle zhengxianle@zhengxianledeMacBook-Pro ~\u0026gt; cd /dev zhengxianle@zhengxianledeMacBook-Pro /dev\u0026gt; ls -l /dev/bp* crwx--x--x 1 root wheel 0x17000000 5 23 08:53 /dev/bpf0* crwx--x--x 1 root wheel 0x17000001 5 23 08:53 /dev/bpf1* crwx--x--x 1 root wheel 0x17000002 5 17 11:42 /dev/bpf2* crwx--x--x 1 root wheel 0x17000003 5 17 11:42 /dev/bpf3* zhengxianle@zhengxianledeMacBook-Pro /dev\u0026gt; sudo chown zhengxianle:admin bp* zhengxianle@zhengxianledeMacBook-Pro /dev\u0026gt; ls -l /dev/bp* crwx--x--x 1 zhengxianle admin 0x17000000 5 23 08:53 /dev/bpf0* crwx--x--x 1 zhengxianle admin 0x17000001 5 23 08:53 /dev/bpf1* crwx--x--x 1 zhengxianle admin 0x17000002 5 17 11:42 /dev/bpf2* crwx--x--x 1 zhengxianle admin 0x17000003 5 17 11:42 /dev/bpf3* 参考：https://andrewbaker.ninja/2023/01/14/macbook-fixing-the-wireshark-permissions-bug-you-dont-have-permission-to-capture-on-that-device/\n四次挥手的疑惑 如下图，这个包的ack为什么没有加1呢？\n其实是因为延迟确认导致的这个问题，因为它省掉了四次挥手中的第二个包，所以流程如下图：\n因为启用了延迟确认，所以在第二个包和第三个包进行了合并。\n但是图中的第319个包的ACK逻辑如下：\n此时，319相当于317的ack，但是318已经发出去了，所以319根本不算挥手的过程包。\n这里只有318、320、321是真正的挥手包。\n","date":"2025-03-09","permalink":"/posts/wireshark-common-function-notes/","title":"Wireshark常用功能笔记"},{"categories":["ARTS"],"content":"Algorithm 算法 Review 复审(英语) 开始尝试自己听纯英文的视频，需要选自己喜欢的内容，还有就是视频有些可以意会。 需要自己多尝试看英文的东西，看不懂先别急着查。 Tip 小技巧 少玩手机\nNAS的用途，家庭影院。\nShare 技术分享 话痨 这周争取搞出一篇wireshark的命令整理\n还是没能想明白，周末应该做什么？有自己的时间，需要怎么安排，因为想做的事情太多了。 上面的问题算是解决了，直接扔掉手机，尽量改成不玩手机或听手机，这样就会多出很多时间了。 一旦不玩手机，就能多出许多时间，思考的时间，运动的时间，学习的时间。\n至于说有很多想法，先做哪一件事？其实没有特别的顺序，只要开心就好。一次只做一件事就行，其余的没有问题。\n想法：学习下语文，感觉自己写的语句都不通顺。文笔可以说是没有\n我计划了很多事情，比如\n想等有空了看看历史 自己写的字比较丑，想练字 恶补下cs相关的专业知识 整理下我的电脑文件 整理nas，搞一个家庭影院 学习金融知识 有的任务可能几天能完成，有的需要几年才能完成。假设几天我可以在某个假期里完成它。但恶补cs知识，大概率需要5年（参考大学课程安排），甚至更多。 那么一个现实的问题，时间cs哪里来？我的理性告诉我，钱不能凭空产生，同样的时间也是，我每天都安排的满满的，那我的学习时间从哪里来？ 希望我能找到解决方案。是不是可以做个简单的时间交换，将玩手机的时间减少，用来看书等。\n想到一个学习英语的好方式，输出。写作文，练习自己的组词组句的能力。\n","date":"2025-02-26","permalink":"/posts/arts-2025week9/","title":"ARTS-2025week9"},{"categories":["ARTS"],"content":"Algorithm 算法 Review 复审(英语) 利用ai英语造句，把不会的单词，容易忘记的单词造句来记住。 这个单词在哪些地方实际用到，而不是故意为了这个单词而造句，那样也不容易记住。 读一篇英文新闻，就硬读。\n有尝试直接看英文报道和推特，但还是认识的单词太少，喜欢点翻译功能 According to the Social Security database, these are the numbers of people in each age bucket with the death field set to FALSE! Maybe Twilight is real and there are a lot of vampires collecting Social Security\nTip 小技巧 最近研究最多的就是颈椎恢复技巧.\n趴着玩电脑，似乎对脖子有好处 多活动，多锻炼是真的有用 还有可以多运动，这周有去对面公园打篮球 认真分析了医生说的话，自己在研究感觉颈椎问题后，还是想早点手术。 Share 技术分享 一直想学习yihong的gitlog方式，记录自己的一年，那样应该比自己的week周报更好，更有意义。\nhttps://github.com/yihong0618/gitblog/issues/209 https://github.com/yihong0618/gitblog/issues/186 https://github.com/yihong0618/gitblog 这个自动话有点难，是不是要有个好的开头。\n还是没能想明白，周末应该做什么？有自己的时间，需要怎么安排，因为想做的事情太多了。\n是不是要分类下，无脑的事情，需要动脑的事情。\n几乎不要动脑的，抄一篇博客。 输出一篇文章。 临时想法，把博客搞下，不要花里胡哨的排版，用最基础的模板就行。好好搞下基础的内容。 https://github.com/yihong0618/gitblog/issues/296\ncontent/post/ARTS/2025/2025-week7.md https://github.com/yihong0618/gitblog/issues/198\n","date":"2025-02-16","permalink":"/posts/arts-2025week8/","title":"ARTS-2025week8"},{"categories":["ARTS"],"content":"ARTS-2025-week7 0210-0216\nAlgorithm 算法 Review 复审(英语) 利用ai英语造句，把不会的单词，容易忘记的单词造句来记住。 这个单词在哪些地方实际用到，而不是故意为了这个单词而造句，那样也不容易记住。 读一篇英文新闻，就硬读。\nTip 小技巧 最近研究最多的就是颈椎恢复技巧.\n趴着玩电脑，似乎对脖子有好处 多活动，多锻炼是真的有用 还有可以多运动，这周有去对面公园打篮球 Share 技术分享 一直想学习yihong的gitlog方式，记录自己的一年，那样应该比自己的week周报更好，更有意义。\nhttps://github.com/yihong0618/gitblog/issues/209 https://github.com/yihong0618/gitblog/issues/186 https://github.com/yihong0618/gitblog 这个自动话有点难，是不是要有个好的开头。\n还是没能想明白，周末应该做什么？有自己的时间，需要怎么安排，因为想做的事情太多了。\n是不是要分类下，无脑的事情，需要动脑的事情。\n几乎不要动脑的，抄一篇博客。 输出一篇文章。 临时想法，把博客搞下，不要花里胡哨的排版，用最基础的模板就行。好好搞下基础的内容。 刚刚看到一句话，感觉挺有意思的：假如你想知道是谁在控制你，那你就看看，谁是你不能批评的人。 不过这句话不是伏尔泰说的： https://www.theguardian.com/australia-news/2015/nov/27/cory-bernardi-mistakenly-quotes-voltaire-on-twitter-with-supposed-neo-nazis-line 这句话最初来自澳大利亚自由党参议员科里·贝尔纳迪（Cory Bernardi）的推特，他认为这句话是伏尔泰的名言。但是，没有任何记录可以证明这一点。一位研究伏尔泰学者也认为这是错误引用。还有一句话著名的伏尔泰名言可能也是假的：“我不同意你说的话，但我会誓死捍卫你说话的权利”。 知乎链接：https://www.zhihu.com/question/579813416/answer/3058262589\n","date":"2025-02-16","permalink":"/posts/arts-2025week7/","title":"ARTS-2025week7"},{"categories":["python"],"content":"原文摘自stackoverflow 参考连接1 参考连接2\n前提摘要 现在苹果的电脑都是arm架构的芯片，功耗是大大减小了，续航得到了很大的提高，但芯片的架构导致程序不支持就很蛋疼，现实里就是需要接手一个python老项目，就是用的老框架，一些旧依赖库甚至需要降版本才能安装运行，很巧我就遇到了，不然也不会有这篇文章。\n背景 公司的一个老项目，本来跑的好好的，但用的不顺手，想改动下，拉下代码，怎么都跑不起来。我按照我以前的经验来安装遇到一堆问题，各种搜索，一直没有彻底解决，请教了同事，他跟我说arm芯片的两种比较好的解决方案就是安装x86的解释器，或者搞个容器（linux）环境，远程部署。 容器部署确实一劳永逸，但同事给我演示的时候发现vscode没有python3.6版本，于是我决定尝试下安装x86的python解释器\n相关介绍 总的来说，arm的芯片可以安装arm架构的二进制文件（废话），还可以通过 rosetta 来安装x86的二进制文件，但是他们的路径不一样，这里同样包括homebrew，有arm和x86两个版本，对应的路径分别是\n代码 1 2 /usr/local/bin/brew # x86 /opt/homebrew/bin/brew # arm 目前支持rosetta的终端是iterm，可以直接勾选\n安装Homebrew 安装pyenv 安装python3.6 确认python3.6是x86版本\n代码 1 2 3 4 5 6 7 Python 3.6.15 (default, Oct 30 2024, 01:16:00) [GCC Apple LLVM 15.0.0 (clang-1500.3.9.4)] on darwin Type \u0026#34;help\u0026#34;, \u0026#34;copyright\u0026#34;, \u0026#34;credits\u0026#34; or \u0026#34;license\u0026#34; for more information. \u0026gt;\u0026gt;\u0026gt; import platform \u0026gt;\u0026gt;\u0026gt; print(platform.machine()) x86_64 \u0026gt;\u0026gt;\u0026gt; 遇到的问题 Error in anyjson setup command: use_2to3 is invalid 在安装python依赖库，一个很老的库django-celery报错，暂时不想替换别的库，发现需要降级setuptools\n第一步 Python 1 pip install \u0026#34;setuptools\u0026lt;58.0.0\u0026#34; 第二步 Python 1 pip install django-celery 就是因为这个库，才需要降级到python3.6，因为这个库里有用到了一个关键字async，在python3.7之后async已经变成python官方关键字，所以这个库不能在3.6以上的python里跑\n后面对应的依赖库还是能尽量升级就升级，不然迁移两行泪\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #Install Rosetta /usr/sbin/softwareupdate --install-rosetta --agree-to-license # Install x86_64 brew arch -x86_64 /bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\u0026#34; # Set up x86_64 homebrew and pyenv and temporarily set aliases alias brew86=\u0026#34;arch -x86_64 /usr/local/bin/brew\u0026#34; alias pyenv86=\u0026#34;arch -x86_64 pyenv\u0026#34; # Install required packages and flags for building this particular python version through emulation brew86 install pyenv gcc libffi gettext # -------------- change to openssl@1.1 here and others as well ----------+ # ↓ export CPPFLAGS=\u0026#34;-I$(brew86 --prefix libffi)/include -I$(brew86 --prefix openssl@1.1)/include -I$(brew86 --prefix readline)/lib\u0026#34; export CFLAGS=\u0026#34;-I$(brew86 --prefix openssl@1.1)/include -I$(brew86 --prefix bzip2)/include -I$(brew86 --prefix readline)/include -I$(xcrun --show-sdk-path)/usr/include -Wno-implicit-function-declaration\u0026#34; export LDFLAGS=\u0026#34;-L$(brew86 --prefix openssl@1.1)/lib -L$(brew86 --prefix readline)/lib -L$(brew86 --prefix zlib)/lib -L$(brew86 --prefix bzip2)/lib -L$(brew86 --prefix gettext)/lib -L$(brew86 --prefix libffi)/lib\u0026#34; # Providing an incorrect openssl version forces a proper openssl version to be downloaded and linked during the build export PYTHON_BUILD_HOMEBREW_OPENSSL_FORMULA=openssl@1.1 # Install Python 3.6 pyenv86 install --patch 3.6.15 \u0026lt;\u0026lt;(curl -sSL https://raw.githubusercontent.com/pyenv/pyenv/master/plugins/python-build/share/python-build/patches/3.6.15/Python-3.6.15/0008-bpo-45405-Prevent-internal-configure-error-when-runn.patch\\?full_index\\=1) ","date":"2024-11-23","permalink":"/posts/m1-x86-python36/","title":"在 Mac M1 上安装 x86 Python 3.6"},{"categories":["nginx"],"content":"原文作者：挖坑的张师傅\n彻底弄懂 Nginx location 匹配 Nginx 的 location 实现了对请求的细分处理，有些 URI 返回静态内容，有些分发到后端服务器等，今天来彻底弄懂它的匹配规则\n一个最简单的 location 的例子如下\n代码 1 2 3 4 5 6 7 server { server_name website.com; location /admin/ { # The configuration you place here only applies to # http://website.com/admin/ } } location 支持的语法 location [=|~|~*|^~|@] pattern { ... }，乍一看还挺复杂的，来逐个看一下。\nlocation修饰符类型 「=」 修饰符：要求路径完全匹配 代码 1 2 3 4 5 6 server { server_name website.com; location = /abcd { […] } } http://website.com/abcd匹配 http://website.com/ABCD可能会匹配 ，也可以不匹配，取决于操作系统的文件系统是否大小写敏感（case-sensitive）。ps: Mac 默认是大小写不敏感的，git 使用会有大坑。 http://website.com/abcd?param1\u0026amp;param2匹配，忽略 querystring http://website.com/abcd/不匹配，带有结尾的/ http://website.com/abcde不匹配 「~」修饰符：区分大小写的正则匹配 代码 1 2 3 4 5 6 server { server_name website.com; location ~ ^/abcd$ { […] } } ^/abcd$这个正则表达式表示字符串必须以/开始，以$结束，中间必须是abcd\nhttp://website.com/abcd匹配（完全匹配） http://website.com/ABCD不匹配，大小写敏感 http://website.com/abcd?param1\u0026amp;param2匹配 http://website.com/abcd/不匹配，不能匹配正则表达式 http://website.com/abcde不匹配，不能匹配正则表达式 「~*」不区分大小写的正则匹配 代码 1 2 3 4 5 6 server { server_name website.com; location ~* ^/abcd$ { […] } } http://website.com/abcd匹配 (完全匹配) http://website.com/ABCD匹配 (大小写不敏感) http://website.com/abcd?param1\u0026amp;param2匹配 http://website.com/abcd/ 不匹配，不能匹配正则表达式 http://website.com/abcde 不匹配，不能匹配正则表达式 ##「^~」修饰符：前缀匹配 如果该 location 是最佳的匹配，那么对于匹配这个 location 的字符串， 该修饰符不再进行正则表达式检测。注意，这不是一个正则表达式匹配，它的目的是优先于正则表达式的匹配\n查找的顺序及优先级 当有多条 location 规则时，nginx 有一套比较复杂的规则，优先级如下：\n精确匹配 = 前缀匹配 ^~（立刻停止后续的正则搜索） 按文件中顺序的正则匹配 ~或~* 匹配不带任何修饰的前缀匹配。 这个规则大体的思路是\n先精确匹配，没有则查找带有 ^~的前缀匹配，没有则进行正则匹配，最后才返回前缀匹配的结果（如果有的话）\n如果上述规则不好理解，可以看下面的伪代码（非常重要）\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function match(uri): rv = NULL if uri in exact_match: return exact_match[uri] if uri in prefix_match: if prefix_match[uri] is \u0026#39;^~\u0026#39;: return prefix_match[uri] else: rv = prefix_match[uri] // 注意这里没有 return，且这里是最长匹配 if uri in regex_match: return regex_match[uri] // 按文件中顺序，找到即返回 return rv 一个简化过的Node.js写的代码如下\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function ngx_http_core_find_location(uri, static_locations, regex_locations, named_locations, track) { let rc = null; let l = ngx_http_find_static_location(uri, static_locations, track); if (l) { if (l.exact_match) { return l; } if (l.noregex) { return l; } rc = l; } if (regex_locations) { for (let i = 0 ; i \u0026lt; regex_locations.length; i ++) { if (track) track(regex_locations[i].id); let n = null; if (regex_locations[i].rcaseless) { n = uri.match(new RegExp(regex_locations[i].name)); } else { n = uri.match(new RegExp(regex_locations[i].name), \u0026#34;i\u0026#34;); } if (n) { return regex_locations[i]; } } } return rc; } 案例分析 案例 1 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 server { server_name website.com; location /doc { return 701; # 用这样的方式，可以方便的知道请求到了哪里 } location ~* ^/document$ { return 702; # 用这样的方式，可以方便的知道请求到了哪里 } } curl -I website.com:8080/document HTTP/1.1 702 按照上述的规则，第二个会有更高的优先级\n案例2 代码 1 2 3 4 5 6 7 8 9 10 server { server_name website.com; location /document { return 701; } location ~* ^/document$ { return 702; } } curl -I website.com:8080/document 第二个匹配了正则表达式，优先级高于第一个普通前缀匹配\n案例 3 代码 1 2 3 4 5 6 7 8 9 10 11 server { server_name website.com; location ^~ /doc { return 701; } location ~* ^/document$ { return 702; } } curl http://website.com/document HTTP/1.1 701 第一个前缀匹配^~命中以后不会再搜寻正则匹配，所以会第一个命中\n案例 4 代码 1 2 3 4 5 6 7 8 9 server { server_name website.com; location /docu { return 701; } location /doc { return 702; } } curl -I website.com:8080/document 返回 HTTP/1.1 701，\n代码 1 2 3 4 5 6 7 8 9 server { server_name website.com; location /doc { return 702; } location /docu { return 701; } } curl -I website.com:8080/document 依然返回 HTTP/1.1 701\n前缀匹配下，返回最长匹配的 location，与 location 所在位置顺序无关\n案例 5 代码 1 2 3 4 5 6 7 8 9 10 11 12 server { listen 8080; server_name website.com; location ~ ^/doc[a-z]+ { return 701; } location ~ ^/docu[a-z]+ { return 702; } } curl -I website.com:8080/document 返回 HTTP/1.1 701\n把顺序换一下\n代码 1 2 3 4 5 6 7 8 9 10 11 12 server { listen 8080; server_name website.com; location ~ ^/docu[a-z]+ { return 702; } location ~ ^/doc[a-z]+ { return 701; } } curl -I website.com:8080/document 返回 HTTP/1.1 702\n正则匹配是使用文件中的顺序，找到返回\n","date":"2024-11-20","permalink":"/posts/nginx-location/","title":"Nginx location 匹配"},{"categories":["think"],"content":"信息茧房 本文章是摘自推特，作者是澜夕\n推文链接\n上周和潘乱的保留连麦话题，是信息茧房，我在发预告时说这其实是一个过气的概念，被咀嚼得太多以致于近年来都不怎么被提及了，成了被默认接受的互联网设定，不过这次杨笠站台京东闹出的风波，正好点到了题，可以捎带着思辨思辨。\n这期嘉宾是人民大学新闻学院副教授董晨宇和少数派的创始人老麦 ，一个是学术界的研判视角，一个是社区的前线视角，互为犄角，相当全面。\n潘乱说如果我们在朋友圈里刷到杨笠相关的消息，就会发现大多数身边的人，都还挺喜欢和欣赏她的，至少不会表达出露骨的厌恶，如果自己是京东的市场员工，天天浸淫在这样的氛围里，大概也不会料到策办低级别如之低的一次合作会让整个公司都陷入危机，所以这是不是可以算作信息茧房的经典画面。\n老麦和我的观感比较接近，都认为这起事件里人为放大了太多的噪音，与其说是信息茧房，可能更接近互联网的分层原因，很巧的是，就在京东被冲的同一时间，小宇宙正在上海办播客节，可以非常明显的感觉到，在现场的那些进步青年所处的岁静世界，和网上同样是另一群年轻人喊打喊杀的肃杀世界，是完全平行的。\n再者，信息茧房很容易和算法技术捆绑在一起，潜台词是平台掌握了分发权力，然后它不断的喂我们同质化信息，最后造成了一种画地为牢的固化认知，但在这次杨笠x京东风波里，你会看到发酵最严重的平台，一个是NGA，一个是贴吧（孙吧），它们恰好都是最传统的BBS架构，是所谓的古典广场模式，不是算法主导的产品，这就很讽刺了，平等的广场里长出了最极端的仇恨，这说明信息茧房本身可能就是一个伪命题，真正存在的是观念茧房。\n相反的是，算法平台的利益，和信息茧房是存在冲突的，B站一直被指责背弃了二次元，抖音也早已不是最初的对口型跳舞社区，它们的月活和留存开始飞涨，正是始于内容多样性的供给，好的内容消费体验一定是掺杂着来，让多巴胺分泌充满意外的新鲜感和获得感，搞一个茧房出来，反倒会加深疲倦，是平台不乐见的，只有马斯克这样把个人意志凌驾于平台利益之上的老板，才会把推特当成专为自己定制的玩具。（1/n）\n此时轮到了董晨宇老师的领域展开，他说凯斯·桑斯坦发明信息茧房这个概念的时候，是在2006年，那会儿哪有什么算法平台，连社交媒体都还没成气候，他的原意其实是在对博客关系做分析，发现共和党人的博客里只会引用同党派作者的链接，另一边也是一样的，所以就人为造成了这么一个分别抱团的结果。\n所以后来媒体开始大惊失色的谈论信息茧房时，已经脱离了它的本来所指，并递进式的创造出了很多新词，比如过滤泡（Filter Bubble），严格来说这才是和算法扯上关系的，因为过滤是一个系统行为，相比之下，信息茧房更接近同温层的理论。\n学者生产新词的兴趣一旦激发出来，就很难停下来，过滤泡火了一波之后，又有了回音室（Echo Chamber）的修辞，最近几年还在争论「互联网的巴尔干化」，总之就很别出心裁，每个人都想总结出某个独一无二的术语，来为技术决定论钉上棺材板。\n这可能同时是一种惯性的思维方式，微软研究院的南希·贝姆写过一句话：责备新技术几乎是人类历史上反复重演的桥段，因为只有技术不会还嘴。\n不过这场连麦的目的不是在于纠错，在大致明确了信息茧房所代表的现象——每个人或多或少都被分配到了属于自己的信息圈层里——所以我们不妨将错就错，继续深聊平台和茧房之间的关系。\n在老麦看来，主流平台里微博的公共性一直屹立不倒，而转发按钮是在一定程度上跳出茧房的重要功能，因为分享链天然带有破圈效应，这是平台无法干预的，不过新一代内容平台都主动放弃了转发键，算法成了最优解，效率也更高。\n当然微博未必是受益方，汝之蜜糖吾之砒霜，很多人会越刷微博越气，然后干脆退出去刷别的了，这很影响人均使用时长数据，相比之下，抖音这种在算法上做得炉火纯青的产品，就会把用户情绪照顾得非常舒适。（2/n）\n之前抖音有个产品细节，在微博上被各种鄙视和批判，认为是在构建信息茧房上作恶：有人发现自己和女朋友两个账号点开同一条视频的评论区竟然是不一样的，自己这边的热评都是在支持男方，而女朋友看到的热评都是在支持女方⋯⋯\n但我记得有产品经理出来表示，这种设计是向善的伟大创新，可以有效避免激化矛盾，要知道有多少网暴都是从一言不合的撕逼开始的，不要辜负了产品和运营的良苦用心呐。\n我是觉得很有意思，直觉上大家会推崇真理越辩越明的绝对理性，但也心知肚明从来没有获得过这样的世界，苏格拉底可能是人类史上最能言善辩的一个人，接着就被投票给处决了，两年前过去了，互联网比起雅典进步了多少，还真不好说，包括我们自己在内，都在遵循自己的观念，被归类和合并到敌友分明的光谱里，这个过程最可怕的是，它是在不自知的情况下进行的。\n在理解技术的发展上，董晨宇应该是有限乐观主义的立场，他认为对于信息茧房等概念的焦虑，其实来自互联网重新配置资源权力之后引起的混乱，而多边博弈恰好能够带来市场的平衡。\n他用音乐产业举例，磁带时代是由唱片公司决定要录哪些歌进去，以及怎么排序，此时磁带相当于集装箱，用户只有签收和拒收两个选择，从MP3开始，再到流媒体时代，专辑这种打包形式就已经落幕了，用户可以自己用单曲去填满集装箱，也就是歌单，唱片公司对此是无能为力的，这个时候平台在做什么呢，它在用每日30首这种推荐，去加入新的权力争夺战。\n此时死去的记忆突然开始攻击我，想到豆瓣FM其实是数字音乐行业最早开始启用推荐算法的产品，同期的虾米都还在铆足了劲去灌歌单，但豆瓣FM死得也很直白，它买不起版权，库存不足的话，算法是变不了魔法的，巧妇难为无米之炊，就像2018年前抖音和快手的用户重叠度还不高的时候，抖音的外界标签是潮人，而快手则是农村残酷物语，现在基本没太大的分别了，比如最近东北雨姐的翻车，一个标准的快手画像网红，其实出的事故都在抖音上。（3/n）\n沿着时间线梳理下来，观念市场的充分竞争倒是不曾缺席，但参与规模的扩大会把议程设置的权力不断挤向不可知处，以前舆论场可以是波光粼粼的湖面，是因为就那么点水深，早年买得起电脑的家庭，再不济也是中产，去网吧的大多数也不会刷天涯猫扑，而是为了打游戏，所以回望起来的氛围有点好到不似真实，大家用「拍砖」这么一个血肉横飞的动作来描述吵架，其实没有多少下三路的伎俩，跟现在上网冲浪会遇到的凶残招数完全比不了。\n老麦说他在的数码圈长期以来也有表达滑向极化的趋势，这在某种意义上是流量经济和内容商业的共谋，创作者能从好斗人设里得到拥趸，同时平台也缺少动力去分辨价值，唯有撕逼引发的数据增长是可以写进周报汇报的，这又是一个由利益共同体构成的大号茧房。\n潘乱还在提问，互联网还有没有机会回到它最开始的承诺，作为消弭分歧的思想集市而存在？\n而董晨宇的回答是，人类自己从来就没做到的事情，却指望用一个工具来做到，是很荒唐的，思想集市、公共领域、第三空间，这些蓝图都是社会理想的投射，但它们都解决不了社会本来就充满了不平等的底层结构。\n2003年，随着Web 2.0的兴起，「人人都有麦克风」的主张也广受追捧，但要知道的是，Web 2.0是一个商业名词，是因为互联网泡沫破裂之后，创投行业急切的想要抓住一块浮木，于是「人人都有麦克风」也成为了新一版的技术承诺，而承诺之所以变得廉价，是因为它往往不考虑兑现。\n从接受的角度来看，「人人都有麦克风」的权利和义务是不对等的，大多数人都觉得这句话指的是自己可以发出声音了，但不会想到这同时意味着你也要去容忍别人的声音，哪怕是你不喜欢的，而这在根本上是反人性的，信息茧房是怎么来的，还有个词是作茧自缚，紧紧抱住自己的麦克风、仇视否定别人的麦克风，就是一切美好设想在落地后都会满目疮痍的真实缘故。（4/n）\n更何况，「人人都有麦克风」的前提是，技术是会保证你有麦克风，但不保证你说的话能被别人听到，机会平等不等同于结果平等，如果追求结果的平等，那就会造成效率上的灾难，历史已经无数次证明了这一点。\n华盛顿大学教授马修·辛德曼对「人人都有麦克风」的解释是，这相当于告诉你人人都可以买彩票，纯粹就是一句空话。\n现在越来越多的人都反应过来了，互联网的去中心化，可能是一场集体幻觉，只是站在中心的那些人变了，王冠轮流戴而已，所谓的广场既不可能真的实现，甚至也没必要实现了，乌托邦才是这个世界上最为产能过剩的商品。\n至于这轮流戴上的王冠，是怎么成为平台造神的证据，则成了信息茧房的延伸话题，一个个茧宇宙的独立生长，也抽掉了稳态结构的承重墙。\n新榜充满恶趣味的统计了一些涨粉千万的顶流账号，发现翻车的翻车，停播的停播，连没出负面的都在刻意减少更新频率，生怕成为众矢之的，之前B站百大UP也被成为百大狙击名单，同样不是没有道理的。\n一个非常明显的变化是，今天的网红越来越不愿意破圈了，就吃自己所在的赛道就好，闷声发财，千万不要成为一个全民热议的角色，因为风险很容易就会反噬掉红利，前面说了用户在分层，那么作为供给端的网红自然也得分层，一个七百万粉丝的B站UP主去抖音做了很长时间，就攒了十来万粉丝，人家还在评论区阴阳他：「B站的你让我高攀不起，抖音的我对你爱理不理。」\n潘乱补充说像是春晚造星的能力也衰落到不值一提了，刘谦可能是最后一个一夜飞升的例子，这还得追溯到十多年前，娱乐圈也在哀叹，周杰伦是最后一代大众偶像，看同一台节目、听同一张专辑、聊同一个话题的集中度已经不复当年了。（5/n）\n所以新的迷思在于互联网平台能不能造神，以及会不会造神，这是两个截然不同的设问。\n能还是不能，基本上能达成共识，当然是能的，这里新神和旧神的交替，比尼尔·盖曼写的小说里还要频繁，至于会还是不会，众说纷纭，比如郭有才的走红，就有不可证的坊间传闻，说是因为它在菏泽南站直播时被平台高管遇到了，然后下令灌流量灌成了超头。\n潘乱表示这样的小作文多少有些「皇帝挑粪用的是金扁担」这种想象了，平台没有能力预判市场需求，很多时候强行预判还会酿成损失，比如B站签冯提莫、西瓜视频签巫师财经，都不怎么被用户买账，真要说平台有什么主观念头的话，它更愿意锦上添花而不是雪中送炭，把已经上马的人再推一把，比如抖音启动直播带货时找了罗永浩，小红书则选了更契合社区调性的董洁，这些更像是互相利用的关系，而不是平台在做从0到1的工作。\n董晨宇和老麦都认为所谓平台造出来的神，唯一的共通之处就是需要符合社会情绪的期待，平台只有否决权，剩下的交给算法逻辑就行，能在斗兽场里存活到最后的，必然不会是善茬。\n我的看法是，无论是平台造神，亦或是平台造茧，本质上都是对于平台能力的敬畏，而与平台意志无关，很多时候平台自己都掌握不了它的系统工程，因为算法的特点就是不可测量的，抖音快手小红书经常有运营和技术出来拉团队单干，觉得没人比自己更懂怎么从平台薅流量，最后很多都是在烧完一笔钱后反证了「离了平台你什么都不是」的回旋镖。\n可被确认的平台意志，就是平台希望维持一个绞肉机的生态，谁都有机会，谁都站不稳，这是和上官方利益最为吻合的方向，大家都不想成为下一个YY，被头部绑架后彻底沦为一个下游渠道，快手自断辛巴就是很正确的选择。（6/n）\n茧房里长出来的网红，最后也被茧房粘着前行，这是不可避免的，B站有句话是「怕UP苦，更怕UP开路虎」，董宇辉从被全网欣赏到被群体嘲讽，其实也没做特别伤天害理的事情，社会情绪的忽高忽低，无论是平台还是个人，都是无法以一己之力去按压的。\n茨威格为路易十六的妻子、断头王后玛丽写传，说她是一个很普通的女孩，不是特别聪明，但又不蠢，并非天性善良，不过也没有作恶，只是因为戴上了王冠，就背负了巨大的名利和代价，「那时她还年轻，不知道所有命运馈赠的礼物，都被暗中标好了价格」。\n试图驯服社交媒体的人，都要好好记住这句超神的话，当年我们都低估了社交媒体掀桌子的程度，只惊讶于传播效率很高，没料到它对现实世界的反向输出能力，所有人都在直面天命，而天命是无常的。\n最后，在如何提高信息素养这个用来上价值的收尾环节，董晨宇提到的点，是眼看着AI加剧的数字鸿沟只会越来越大，但是学校里和图书馆其实都还没同步传授和AI共生的技能，所以怎么建立新的信息处理模型才是最重要的，他没有答案，只为强调重要性。\n老麦的建议，是克制FOMO（错失恐惧症），我们不能决定网络环境，但可以选择加入哪些对话场景，尤其是面对面的交谈，从质量和反馈上都远远超过线上互动，如果一定要上网，就多看少数派吧。（我没删这条口播广告哦\n我还是比较在意从心的角度，如果没有开放的心态，正确的工具和行动未必能带来正确的效果，就像多读书肯定不会有错，但我也见过很多人读完一本书后扔下一句「呸，这作者夹带私货」⋯⋯再就是很容易寄托于一个先进的技术产品来拯救自己的焦虑，是不是要用Notion，去哪里抄ChatGPT的提示词，等等，这些都有些本末倒置了，效率没那么重要，器物也没那么重要，重要的是不去强求内心，归还自己的好奇心。\n也希望这样的连麦和总结，每次都能给诸位提供一点点增量信息。（7/完）\n","date":"2024-11-03","permalink":"/posts/filter-bubble/","title":"信息茧房"},{"categories":["python"],"content":"#python #argparse #文件 #传参\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import argparse # 创建ArgumentParser对象 parser = argparse.ArgumentParser(description=\u0026#39;这是一个带有子命令的参数解析示例\u0026#39;) # 创建子命令解析器 subparsers = parser.add_subparsers(title=\u0026#39;子命令\u0026#39;, dest=\u0026#39;command\u0026#39;) # 创建子命令1解析器 parser_command1 = subparsers.add_parser(\u0026#39;command1\u0026#39;, help=\u0026#39;执行命令1\u0026#39;) parser_command1.add_argument(\u0026#39;-f\u0026#39;, \u0026#39;--file\u0026#39;, type=str, help=\u0026#39;文件名\u0026#39;) # 创建子命令2解析器 parser_command2 = subparsers.add_parser(\u0026#39;command2\u0026#39;, help=\u0026#39;执行命令2\u0026#39;) parser_command2.add_argument(\u0026#39;-d\u0026#39;, \u0026#39;--directory\u0026#39;, type=str, help=\u0026#39;目录名\u0026#39;) # 解析命令行参数 args = parser.parse_args() # 根据子命令执行相应的操作 if args.command == \u0026#39;command1\u0026#39;: if args.file: print(f\u0026#39;执行命令1，文件名: {args.file}\u0026#39;) else: print(\u0026#39;请指定文件名\u0026#39;) elif args.command == \u0026#39;command2\u0026#39;: if args.directory: print(f\u0026#39;执行命令2，目录名: {args.directory}\u0026#39;) else: print(\u0026#39;请指定目录名\u0026#39;) else: print(\u0026#39;请指定子命令\u0026#39;) Shell 1 python script.py command1 -f file.txt 输出结果\n代码 1 执行命令1，文件名: file.txt Shell 1 python script.py command2 -d directory 输出结果\n代码 1 执行命令2，目录名: directory ","date":"2024-09-08","permalink":"/posts/argparse-subcommands/","title":"Argparse 子命令示例"},{"categories":["think"],"content":"愚勤 和菜头\n愚勤原文链接\n我很小的时候在《365夜童话故事集》里看过一个故事，一直记到今天。故事里说，一个小牛倌负责放牧一头小牛，从村子出来到草场中间要经过一条小河。小牛不敢涉水，于是小牛倌就抱着小牛跳过河去。日子一天天过去，小牛一点点长大，小牛倌的气力也在一天天增长，他依然每天抱着牛跳过河去，最后小牛倌就成长为一名大力士。故事当然不可能成立，因为牛的发育速度要远远高于少年。如果居然是等速的，那么小牛倌可能要被暴打，因为他把牛给养残了。要么他当真天赋异禀，是许褚或者李元霸那种恨地无把恨天无环的神力者，只是那样的话对普通人而言就没什么意义了。但是这种故事很有市场，因为许多人内心深处愿意为这种故事买单，相信通过重复简单的动作，当重复的次数足够多的时候，就会量变产生质变，能产生惊人的效果。他们管这种成功学叫做勤奋。我这里小声翻译一下：勤奋≈吃苦，吃苦≈成功，持续勤奋=持续吃苦≡大成功。在没有任何论证的前提下，请允许我做个猜测\u0026mdash;这种想法背后是一种特殊的人生观，在这种人生观里，自己处于一种不配的地位：不配得到美好生活，不配得到个人成功。于是，要用自我折磨、自我惩罚的方式作为献祭，以“苦”换取美好生活和个人成功，唯有付出了海量的苦，才会在拥有之后觉得心安。因为苦实在是太苦，世人都不愿意接受，于是就在它的表面刷了一层名叫“勤奋”的糖衣，那么许多人就会毫不犹豫地拿起来口服，连一口水都不需要。看我写了那么多年字，看我似乎写出了点名，于是经常有人问我：我连续写1000篇能不能大成？我连续写十年能否变成真正的作家？这么问本身就已经隐藏着他们对于事物的看法：勤奋和坚持可以达成一切，或者什么人前显贵人后受罪一类的套话。\n事情不是这样的。勤奋如果不能和聪明相搭配的话，勤奋本身并不会产生什么成效。在写作这个例子里，所谓的聪明是说在自己写字前起码知道什么是好文章，起码知道起手需要从模仿开始，起码知道自己可能更擅长写那一类文章，起码懂得观察今天的自己和昨天的自己有什么不同。\n于是，一名新手首先知道什么才是好，然后知道去抄，再后来知道自己的长处，最后通过有目的的针对性训练提升自己，并且确认自己是否得到了提升\u0026mdash;一般而言的勤奋其实是用在这一部分，而按照我理解的勤奋，则是在前一部分，勤奋地做准备工作。\n没有这一部分，恕我直言，无论是写一千篇还是一万篇，持续写十年还是写五十年，意义都不是很大。因为实际上很可能只是在同一水准上的重复，个人不会得到任何提升。第一篇和第一万篇差别不大，第一天和第十年没有区别。因为这是愚笨的勤奋，愚笨的勤奋其实是一种懒惰，想用最不动脑筋的方式，用纯粹体力消耗的方式，达成自己的目标。\n如果勤奋的目的是为了这一次比上一次强一点，那么再小的勤奋度都是有效的。如果勤奋的目的是为了单纯增加一个次数，等待次数累加产生奇迹，那么再大的勤奋度都没有任何意义。而知道什么是强，怎么变强，这是聪明的功用。\n这样的话大多数人都不爱听，觉得是歪理邪说，腐蚀良家子弟的朴质灵魂。如果勤奋你都不支持的话，普通人还能指望什么？指望闻思修啊，这是几千年前就有的结论。从学习开始，然后思考，继而行动。重复这个过程，带着行动结果再去学习，再次思考，调整思路再次行动。世间大部分的事情都是这么达成的，明确自己想要什么，学习别人怎么做的，思考自己应该如何做，然后着手去尝试。尝试失败，发现问题，带着问题返回，重新学习，循环这个过程，通过点滴提升，逐步达成目标。勤奋是勤奋在这个循环上，而不是每天抱着牛跳过河。如果那么喜欢抱牛过河的故事，我这里还有另外一个故事，不过是真事。传说东南亚有一种酷刑，把受刑者绑缚在竹林的地面上仰面躺着。然后竹笋会从地面下发芽长出来，然后缓慢而坚决地穿过人体长成竹子。有人专门用类似人体组织的材料做过试验，结论是大约七天左右竹子就能穿过人体，大概率人当时还活着\u0026mdash;很明显，人体皮肤组织的抗磨损和自我愈合速度远远比不上竹子的生长速度和硬度变化。从一开始，就有一百种方式可以训练牛犊凫水或者跳过小河，但是都很麻烦，有失败的风险。不如抱着牛跳过河，这个方法最简单最省力最少失败。\n自我总结 好多学生是这样的：作业写得工工整整，笔记写满几大本，然而基本不会主动思考，看起来很用功，成绩却毫无起色。其实我一直都是这样的人，总是不喜欢思考，但却看起来很用功。上高一的时候，数学老师跟物理老师让我做笔记，我做的真的很好看，但我也确实没有记住内容。\n这个坏习惯一直到现在都没怎么改变，包括这篇文章，我也是喜欢抄下来，让别人以为我写了很多，其实我只是写了两句话而已。\n","date":"2024-08-29","permalink":"/posts/stupid-diligence/","title":"愚蠢的勤奋"},{"categories":["think"],"content":"暂停实验室采访对话\n开头 其实文章的内容跟《认知觉醒》很类似\n焦虑wiki百科\n情绪焦虑 与自己的任务状态有关系 需要意识到情绪焦虑\n焦虑表现 容易语速快，心跳加速，语气有情绪 行为：小动作变多，做一些逃避的事情拖延症） 原因：担心后果\n不自觉的去做别的事情，有时间也会拖延，害怕去做。尽管那件事可能给我们带来成就感，但会因为时间安排紧（不合理）。 需要思考情绪焦虑的时候是怎么思考的，脑子里是怎么思考的，现在触发了什么行为（为什么会产生这个行为）。 大多数情况我们只会关注事情本身（对别人说：你难到没看到我现在有一个什么事儿么？）\n如果科学管理经精力 如何在压力下保持最优表现？ 意识到自身的状态（正念、冥想）：识别自身情绪、想法（我现在脑子里怎么想的？身体有了什么反应？） 利用截止时间和压力推动自己生产力，是很多人感到焦虑的原因之一（焦虑也可以产生动力） 情绪调节不是把情绪消除，想要消除情绪的愿望本身就是造成情绪困扰的原因 与不适感共处是一种可以训练的能力 当我们产生内耗，顶着压力做事的时候，会一直处于紧张的应激状态中 大脑放空是一种对大脑的保护，帮助节能和预防更多伤害（及时停下） 如何科学管理精力？ 从高耗能切换到底耗能（芯片处理原理） 用对方法休息，不但不会让你产出变少，还会帮你节省时间 如何像玩游戏一样解决复杂问题 如何像玩游戏一样解决复杂问题？ 你的注意力全部放在痛苦的事情上，是拖延最核心的一个原因（是什么想法呢？）\n将注意力放在积极的体验上，怎样培养不回避痛苦体验的能力？\n很多人不擅长驱动系统，而更擅长威胁系统（单一依赖威胁系统，会导致耗竭、动力缺失，无法获得可持续成长） 驱动系统\n感受：渴望感、成就感、专注等 功能：驱动持续做某事、获得更多资源、提升技能 相关脑区：伏隔核等 神经激素：多巴胺 威胁系统\n感受：焦虑、愤怒、厌恶等 功能：提醒潜在的威胁存在，催促人类去逃离或战斗 相关脑区：杏仁核等 神经激素：皮质醇、肾上腺素 利用打游戏的奖励和任务设计机制（自主感、胜任感、联结感）（给设定目标空间）\n复盘（1.怎么从对的地方开始 2.关注自己做了什么 3.为偏离辩护\u0026lt;原因\u0026gt; 4.下一步计划）\n进取型目标 保守型目标\n结尾：\n如果到了想做的事连足够的时间投入都没有的情况下，先去改善拖延。如果是自己的产出不够满意或者想要更加优化自己的精力（抗压能力）ui一个偏成长性的需求话，有效努力。 https://www.youtube.com/watch?v=6vYItFwQObs\u0026t=353s 其实离开手机主要是讲让自己行动起来，那样焦虑就会变少一些。\n如果在压力下维持最优表现 利用焦虑产生动力\n正念是观察者 知道是需要加压还是减压\n带着焦虑去做事\n视频里的对话讲了关于情绪焦虑的内容，但是郭婷婷有点强调自己的感受了。有好几次说到自己不喜欢啥词。。。 这个在说法比较主观，不喜欢跟内容的客观性没有关系。比如有两次强调了，不爱听“反人性”这个词 喜欢说对对对，给我一种不是很成熟的感觉（我也说不出成熟的标准，感觉得到认同急于表现算一种）\n压力下的情绪表现 焦虑下的表现。\n允许deadline 和压力Push自己生产力是很多人感到焦虑的原因之一\n如何让情绪不在成为事情的阻碍？ 情绪调节不是把情绪消除，想要消除情绪的愿望本身就是造成情绪困扰的原因。 焦虑的情绪下会让人一直处在一个紧张的，应激的状态里 大脑会过热降频，跟cpu一样，太高的温度不适宜处理任务\n休息变负面等同摆烂，但实际不是，有短休息“充电5分钟，通话2小时”，真正休息会让你节省时间\n情绪旋涡带来负向循环 花费多时间却少有产出带来焦虑脸，紧张的心情\n做替代事不去面对，逃避 大脑想像不好后果，带来一系列逃避行为 思虑多的人注重行为后果，而不是大脑去想行为结果 情绪调节不是消除而是想要消除的愿望\n焦虑的抛物线曲线，从一开始焦虑动力到最后产生灾难性想法，如何保持高效生产力区间: ①认识状态:加压或减压； ②学会与不适感共处:情绪无法限制你，痛苦一瞬间，做事焦虑会越来越低，反而逃避，一开始感受良好，后面越来越高； ③关注自己身体状况:学会调动自己的精力(底层逻辑:休息本质从高耗能切到低耗能，不是看你做什么，而是用什么状态去干活)\n人性:认知评估预判 动物性:趋利避害 内耗:大脑空转，产出低，脑力累，调动身体全部应激反应 摆烂:机器转动，烧热了，被熔断，免疫力真空期\n工作杂乱，怎样集中注意力\n原因: ①压力大，任务重，时间短； ②杂事多； ③在意结果。（一想起来就是痛苦的体验）\n拖延核心原因是你的注意力占据在那些痛苦的体验，只要你一想起就先天的想要回避\n如何不拖延就是如何将自己的注意力放在那些积极的体验上面？\n实际是如何培养自己不回避痛苦体验的能力。\n驱动系统一个感受是渴望感，成就感，专注功能是驱动持续做某事，获得更多资源，提升技能，相关脑区伏隔核等，神经激素多巴胺 威胁系统的感受是焦虑，愤怒，厌恶等功能提醒潜在危险存在，催促人们去逃离或战斗相关老区性能和等神经激素，皮质醇，肾上激素\n如何保持焦虑同时发掘长期动力？\n①单一依赖威胁系统会导致耗竭，动力缺失，无法获得可持续动力； ②练一练就可以激活动力系统，类比游戏，将学习和工作娱乐化、游戏化； ③设立阶段性目标，设立目标空间，一个是保守目标，一个是进取目标；\n","date":"2024-08-26","permalink":"/posts/emotional-anxiety/","title":"情绪焦虑"},{"categories":["Linux"],"content":"#linux #localtime #zone #Shanghai #Asia\nlinux 修改时区 代码 1 2 3 4 5 6 [root@example-host ~]$ ll /etc/localtime lrwxrwxrwx 1 root root 29 Aug 7 02:30 /etc/localtime -\u0026gt; /usr/share/zoneinfo/Etc/GMT+8 [root@example-host ~]$ rm -f /etc/localtime [root@example-host ~]$ ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime [root@example-host ~]$ date -R Mon, 04 Nov 2024 15:08:29 +0800 crontab时间和系统时间不一致 同步系统时间 ：ntpdate us.pool.ntp.org 修改完后,需要的话可以输入:clock -w 把系统时间写入CMOS 将当前时间和日期写入BIOS，避免重启后失效 ： hwclock -w 重启 crontab：service crond restart 重启 rsyslog： service rsyslog restart ","date":"2024-08-16","permalink":"/posts/linux-timezone-update/","title":"Linux 修改时区与 crontab 时间排查"},{"categories":["think"],"content":"这篇文章其实是我年初的时候看到的，现在已经是8月了，这个标签我一直记着，想关闭又有点舍不得。干脆就记录下来。以后高兴的时候可以看看(大概率不看)\n这篇文章的作者是：池建强，一个挺有争议的人。互联网上大家都有两面性，这里不去讨论这个人，但看这些建议，还是对我有用的。\n36 个新年开工工作建议\n去年假期后的第一天，我曾经写过一篇《41 个新年开工工作建议》，今年在此基础上继续迭代、优化，希望能给你些启发。新的一年，咱努力加油干，保持激情，保持创造。\n路遥在他的作品《人生》中，写过一句话：\u0026ldquo;生活总是这样，不能叫人处处都满意。但我们还要热情地活下去。人活一生，值得爱的东西很多，不要因为一个方面不满意，就灰心。\u0026ldquo;我很喜欢，不管怎么样，我们都要乐观、坚韧地向前。 在公司里，我们的活动基本可以归为两类：沟通和创造。开会、汇报、拉通、对齐、聊天属于沟通范畴，做产品、写代码、写文章、做设计则是创造。二者有些许交集。正常情况下，用于创造的时间应该大于沟通，因为从市场视角看，沟通并不能创造价值。 人是环境的反应器。人会受环境影响，同一个人在不同的环境里行为是不一致的。在哪里工作，和什么样的人共事，这些外部的环境都会有意无意地形成输入，最终改变我们的行为。我越来越觉得，工作抑或生活，我们都要努力找到那些和自己合拍的、优秀的人在一起。 什么样的人是优秀的人？工作中我们得不断思考这问题，直到它变得清晰无比。去年，我看到纳瓦尔基于芒格的经验，给出了自己的答案：智力、执行力、诚信和主动性。虽然这几组词都很大，但我想对应到具体的场景中，会很有价值。 一般情况下，做事都会经历几个阶段：无知的乐观，知情的悲观，自我怀疑，知情的乐观。依次循环。不同的阶段，心态不一样。我们知道做事的心态会像季节一样有周期的概念，便会更从容些。允许自己无情的乐观，也允许自己知情的悲观。 有时候人们焦虑是因为我们想同时做太多事情，以至于一件事都没做成，这时候，你可以试着先做成一件事。书读不完，先读完一本试试。手头的工作全是烂尾工程，那就先把其中的一件完成。总之，先做成一件事，是打开新世界大门的钥匙。 上班路上，有的人在刷短视频，有的人在打游戏。这没有对错。但我觉得，一天刚刚开始，我们本应怀揣着理想和做事的干劲赶路，刷剧、打游戏更多是在消磨意志，而非滋养我们。不应该。 建立作品思维。如果我们做成的那些事情中，其中有一件的产出是一个可以称为\u0026quot;作品\u0026quot;的东西，它能够持续、深度影响到很多人，成为我们人生的杠杆、思想的放大器。这个作品可以是一个公众号、一本书、一个开源项目、一个抖音号、一个产品。 但凡做一件事，都要写一个 Fake Timeline 出来，哪怕这件事的不确定性极大。第一，把脑海中模糊的想法，具体化成文档的过程，其实也是一个思考的过程。往往在写的时候，又会有新的想法蹦出来。第二，只有写下来才可以迭代和协同。在脑海中形成的思考，往往并非结构化。写出来之后，结构化的信息更利于迭代，协同起来也会更高效。 早上来公司后，最先做的事儿应该是整理一天的 Todo List。花 5 分钟时间，梳理下这一天的时间分配，想想哪些事是重要的，哪些事是可以并行的，哪些事是可以合并融合的。不要急着进入工作细节。 不要粗暴地把错误归结于自己粗心，它并不能解决问题，你想想是不是这样？你说，我粗心了，下次注意。下次怎么注意？这是句废话。但你继续找，找粗心背后的原因，其实可能就是习惯不好，或者方法不对。 强调努力学习与认真做事，不是为了追求世俗意义上的成功，而是为了你们将来有更多的选择自由。倘若有一天，你们觉得周遭的环境不适合自己，有一点防身的本领，至少让你们不至于失去\u0026quot;仰天大笑出门去\u0026quot;的豪气，无需一味地忍耐或者委屈自己，仰人鼻息地过上一辈子。劳东燕说的。 不要被手机控制，不要被短视频牵着走。睡觉前不要把手机带进卧室，更不要躺在床上刷手机。即使睡不着觉，也别看手机，仅仅是在安静的房间里闭上眼睛放空自己，也能达到和睡觉一样的效果。 并不是输入越多，高质量的输出便能自然发生。光看不想、不总结是没用的。新的一年，如果有读书计划的话，那你可以试试写读书笔记。实在没时间，用语音的方式说也行。没必要追求一年读多少本书，嚼烂更重要。 现在网络上大家喜欢说自己是 I 人还是 E 人。我见有的人以自己内向为挡箭牌，和人打交道的时候，连基本的礼貌和交互都没有。这很奇怪，没礼貌就是没礼貌，这和性格没关系。 这年头，随着短视频的深入人心，即兴表达就是越来越重要。所以，试着刻意练习自己的即兴表达能力，看看是不是会有很多口头禅，是不是能连贯的把自己的想法准确表达出来。 抽象能力挺重要的。你试试自己能不能把工作中的经验抽象为别人可以借鉴的方法，越简单越好。简单才能传播。 什么时候可以考虑离职呢？第一，你确实在这公司没成长了，你每天在空转，你在消耗自己，你本是一把利剑，现在快被磨成废铜烂铁了。第二，你讨厌所在团队的人文环境，大家做一些看起来无厘头、很可笑的事情，这已经影响了你的身心健康。第三，工资和实力不匹配，沟通无果，无法共鸣。 全情投入到具体的事情里，心无杂念，是一种能力。你看他，一会刷个短视频，一会微信聊个天，一点定力都没有。时间都被碎片化了，来来回回切换，这样的工作方式效率注定不会高。 随着时间的累积，我们应该在一些人生的基本问题上，逐步形成稳定的价值观（内核）。大的方面，比如自己这辈子怎么过活，追求什么样的生活。小的方面，比如友情，什么样的朋友能长期处。 链家对管理者有三个要求：不用\u0026quot;我亲自\u0026quot;这样的句式；有成绩说\u0026quot;我们\u0026rdquo;，有问题说\u0026quot;我\u0026rdquo;；尽量少感谢上司。这句话我一直记得。 有一类新想法，你最好就是通过写文章和写书来发现。为什么是必须通过\u0026quot;写\u0026quot;来发现呢？因为你实际上不是先想到了这些想法，然后，你把它们写下来。实际上，有一种特定类型的思考，是通过\u0026quot;写作\u0026quot;这个过程，来完成的。保罗·格雷厄姆说的。 承认很多事情没有招式、套路可言，吭哧吭哧地去做反而是最重要的。不能总想着贪巧求速，幻想一招制敌。贪巧求速会毁掉我们的心气，其实只有认真做之后，招式才会有价值。 不受人惑。清华大学劳东燕老师曾经说过，任何主义都会有一定的蛊惑性，在接受某种主义的同时，如果不进行必要的反思，多半会成为一个\u0026quot;受人惑的人\u0026quot;。\u0026ldquo;不受人惑\u0026quot;意味着要具有独立的判断力，不轻易相信任何灌输的东西。 归因于己，归功于外。因上努力，果上随缘。 不做或者少做自己不相信的事情。不相信的事情，很难坚持，很难热爱。所以，对于不信的事情，要么直接拒绝，要么说服自己发自内心的相信。这很唯心，但很重要。 尽量通过一条消息把事情说清楚，不要刷屏发一连串的消息。不要模糊表达，比如，把\u0026quot;一会发你\u0026quot;改为\u0026quot;11 点之前发你\u0026rdquo;。表达结构遵从金字塔原理，先结论，后细节。约别人时间，记得建日历。 遇到问题时，先努力看清问题中什么是主要的、起决定性作用的因素，什么是次要的、处于服从地位的因素；什么是一时起作用的因素，什么是长远起作用的因素。不要眉毛胡子一把抓。 作家陶杰说过一句话：\u0026ldquo;当你老了，回顾一生，就会发觉：什么时候出国读书，什么时候决定做第一份职业，什么时候结婚生子，其实都是命运的巨变。\u0026ldquo;在人生的大决策上，不要犯糊涂。 想到五分就开干，干到八分你就整明白了。不要一直停留在想的阶段。 培养一个偏创造输出型的爱好。因为创造之后，我们就有可能影响到别人，就有可能获得更多成就感。比如，刷短视频偏享受，而拍短视频偏创造。比如，听音乐偏享受，而自己弹奏一首曲子偏创造。 当感觉工作疲惫之时，大部分时候都是因为眼睛累。注意调整好工作或阅读时的光源，降低视觉疲劳。比如可以买一个屏幕挂灯或者台灯。 手机对人的影响太大了。周末的时候，尽量让生活回归线下，和亲朋好友见面交流，阅读实体书，感受大自然。仅仅把手机当成一个纯粹的通讯工具，而非让它无穷无尽的吞噬难得的闲暇时光。 \u0026ldquo;Be fucking professional\u0026rdquo;，这话是在《李诞脱口秀工作手册》中看到的，我把它翻译为：要无敌专业。比如上班不迟到，开会不迟到。没人喜欢和爱迟到的人共事。不要因为别人迟到没受到惩罚，然后自己也跟着迟到。 虽然我们常说\u0026quot;唯有热爱，可抵岁月漫长\u0026rdquo;，\u0026ldquo;兴趣是成功的敲门砖\u0026quot;之类的话，但对于大多数人而言，并不知道自己对什么感兴趣。所以，先把手头的事做好，绝大多数场景下，都是最优解。最好不用\u0026quot;这事我没兴趣\u0026quot;为借口来逃避现实。 一个人的失败观挺重要的。因为顺风顺水的时候，谁都可以书生意气，挥斥方遒，这没什么难度，都是顺从人性的事儿。难的是，一个人遇到挫折后的反应。那个当口，人们容易被自我怀疑和沮丧感击败，进而丧失行动的信心和勇气。曹操经常大败后大笑，咱不被负反馈磨掉心气，是能力，也是智慧。 这些想法我之前也有想过\n当手头上全是烂尾的事情，不如把一件小事做好，坚持做好确实会增强信心 不能总是停在想，有好多事，越想越拖，最后烂尾，想到5分开干(不过什么情况叫5分) 失败才能认真看清自己 少碰手机，因为不做做到及时放下手机，所以只能少碰。 人生大事，还是要好好考虑，不能随随便便的就开始干。 写作是一种思考的过程，这个在和菜头那里也看到过 不要粗暴地把错误归结于自己粗心，它并不能解决问题，你想想是不是这样？你说，我粗心了，下次注意。下次怎么注意？这是句废话。但你继续找，找粗心背后的原因，其实可能就是习惯不好，或者方法不对。 我最近又是遇到第一个情况，看书，看视频烂尾了。视频很长，书也是，不是一两天就能搞定的情况，总是拖延，然后就是不想看。一鼓作气再而衰三而竭。\n第11条是我真的需要改的，以前确实有这个毛病，总感觉不是啥大问题。\n","date":"2024-08-14","permalink":"/posts/2024-36-new-year-work-suggestions/","title":"36 个新年开工工作建议"},{"categories":["think"],"content":"之前我是挺喜欢旅游的，但后来大家把旅游变成了一种仪式，毕业旅行，gap旅行，朝圣旅游，自由旅行。\n可能最大的反感是因为一大堆旅游博主给我带来了旅行焦虑，他们天天在玩，吃吃喝喝就好，而我要天天苦逼的上班，挨老板pua，自我pua加班，995，周末自己还要抓紧学习，不能被别人赶超。心里不平衡，焦虑谈不上，只是看到有点烦。\n朋友圈也是经常看到有人旅游，特别是那些拿了公司的赔偿，然后开始gap的人。一下子去玩好久。大西北，欧洲，让我感觉这个世界是属于他们的，而我只是一个打工人NPC，他们宣扬自由，追逐梦想，看到他们的文案，我就一个好奇”以后都不要上班了么?什么家庭，能天天浪。“之前我有想过天天旅游会不会有一天玩累了？我没试过，但我感觉会。直到我读到和菜头的文章，我信了。\n《如果可以一直旅行下去》\n\u0026ndash;和菜头\n小朋友们说要过Gap year，小朋友们打起背包去另外一片大陆，小朋友们从一处青年旅社去到下一处青年旅社，小朋友们见识了自己不曾见识的风景，听闻了自己不曾听闻的遇见，触摸了自己不曾触摸的皮肤，然后告诉我说自己突然郁闷了，觉得眼前的一切并没有什么意思。\n这种症状并不独特，并非是某一个人才会有的特别情绪。很多年前，我推荐过一套日本作家泽木耕太郎的作品：《午夜快车三部曲》，包括《黄金宫殿》《 波斯之风》和《海角之岬》。至于说这套书的内容，它的副标题说得已经很清楚了\u0026mdash;欧亚大陆放浪行，作者背包孤身旅行的游记。\n在这套书里就曾经记载作者在长时间的旅行后出现的情绪波动，当时他在一处青年旅社被卡住了，不想继续待下去，又不想离开，但是更不愿意回家。旅行的乐趣呢？乐趣消失了，只有迷惘和厌倦。\n泽木耕太郎是在什么时候遭遇这种情况的呢？那是在上世纪1970年代，他26岁的时候背包上路，在欧亚大陆到处流窜，和来自全世界的年轻人在旅途中厮混。至于说为什么要去旅行，不是为了他的答案是想要去做一些毫无意义的事情，那种任何人都有想法但是只有少数异想天开的家伙才会去做的事情。\n50年之后，中国的小朋友开始做相同的事情，然后感受到50年前失陷在亚欧大陆腹地某处的一个日本青年同样的感情。为什么旅行很久的人会丧失好奇心？泽木曾经回答记者说：\n一则是“旅人是悬在空中的，既不用为远在身后的家乡负责，也不用为身处其中的旅行地负责”，绝对自由的结果是无所归属，行为也毫无目的，二则低廉的生活成本，包括违禁品的易获得性，都会让人沉迷其中，难以离开；三则，如果说routine让人厌倦，而移动本身成为routine的时候，也就让人厌倦了，有位被访者说“不断认识人，认识当地菜场，小吃街，但是离开，又在下一个地方重复这些，很累，很累”。【1】\n通常情况下，大多数年轻人并不会有类似的经历，因为即便是所谓的“穷游”对于大多数人而言依然极为昂贵。而对于那些少部分可以背包上路的人而言，他们接收到的是一堆概念，诸如年轻人需要一个Gap Year，或者罗马的贵族青年在成年前要去游历世界广博见闻，又或者是关于青年旅社里和异国他乡诸多美好浪美的事情。但是没有人会告诉他们，即便是旅行那么美好的事情，最终也可能会变得苦涩起来。因为世间大部分的讨论，只涉及到身和行，很少深入分析在身和行之后对应的心。\n这并没有任何奇特的地方，也不是旅行所独有。一个人喜欢看书，但是连续看很久也会想吐。一个人喜欢美食，但即便是他最喜欢的食物，如果无限量持续供应，他也会觉得索然无味。一个人喜欢聚会，但是一定会在某一个深夜人声鼎沸的时候，突然觉得眼前的一切毫无意义。\n什么时候事物是真正美好的？当你无限渴望然而却没有得到的时候。什么时候事物是最为美好的？当你长久以来无限渴望即将去实现的那一刻。因为此刻状态是不完满的，人要去追求完满，于是就相信世间存在完满。无论是旅行、恋爱、事业还是梦想，都是如此，在未曾实践或者实现之前最美好，因为你深深相信只要达成就会感到满足，就会感到快乐。\n一旦达成，你的心就会和过去检视你所不满意，所不乐意的生活一样，同样去检视你达成梦想之后的生活。而任何一种生活都经不起这样的检视，所有的美好，所有的新鲜，所有的幸福，都只存在没有经历之前的想象中。很少有人能够做到心平气和，心怀感激地体验不完美的梦想实践，总会忍不住去比较，总会忍不住去观察缺陷，于是美好也总是会很快腐败，幸福也总是会很快变味。\n在一次短期度假里，许多事情还来不及体验就要收起行李返回。于是人就忍不住去想：如果可以一直那么旅行下去该有多好啊\u0026hellip;\u0026hellip;其实类似的话人人都说过：如果可以一直那么相恋下去该有多好啊，如果可以那么一直顺利晋升下去该有多好啊，如果可以那么一直欣赏音乐下去该有多好啊，如果可以那么快乐地欢聚下去该有多好啊\u0026hellip;\u0026hellip;然后呢？\n然后心只要确定自己已经获得，而且可以长久安稳地拥有，它就会变得厌倦。想要行遍全球100个国家，可能到20多个的时候人就已经厌倦，觉得进入了重复的模式。我读过许多类似的游记，在所谓的100个国家的记录里，后期很多国家只是机场打卡或者是简单过境。什么体会风土人情，什么和当地人深入交流，不存在的，到得后来唯一的动力只是为了完满100这个数字。而不得不继续在路上这件事，会逐渐变成一种负累乃至痛苦。\n我的意思并非是要打消小朋友当背包客的梦想，也不是想劝说大家把外出旅行变成本地农家乐一日游。我只是想陈述一个人世间的常见现象，某种事实，讲述心的特性。如果小朋友要去Gap Year，那就去Gap year好了，但是需要提前明确一点：会有厌倦和麻木的一天，新鲜感、好奇心乃至快乐和幸福感都会消失的，而且这很正常。\n无非是不要产生“来也来了”、“走也走了”的传统中国式想法，非要咬牙坚持下去，觉得“必须”再次走上旅途，觉得自己“必须”维系背着行囊的形象，厌了就转身回家。如果可能的话，抽时间想一下如果地理、风景、异域风情、自由行走这些外物都不能心觉得满足的话，究竟还有没有别的方式？自己内心所渴望的究竟是什么？如果在人世里走那么大一圈，经历了那么多还是没有头绪，那我觉得这就有点亏。\n到得头来，如果想法依然是“如果可以一直\u0026hellip;\u0026hellip;下去，那该多么\u0026hellip;\u0026hellip;啊”，那其实人也并没有从旅途中真正学到任何东西。\n【1】引述自《“人生缓刑”：仪式化的背包旅行》，作者：让我想个名字。引文地址：https://book.douban.com/review/7443335/\n文中还有一个有意思的观点，有些东西确定自己已经获得，并且你确认可以长久的拥有，就有可能产生厌倦。让我想到一个故事：之前与一个同事聊到，如果以后工资达到xxx时，管老板怎么骂都行，如今我们已经超过那个数值了，再回过头看。“屁，再多的钱也不能天天骂啊。”\n","date":"2024-07-28","permalink":"/posts/gap-year-travel/","title":"如果可以一直旅行下去"},{"categories":["Linux"],"content":"#linux #awk #command\nawk用法之：文本替换 awk用法之：文本替换\nawk的sub/gsub函数用来替换字符串，其语法格式是：\nShell 1 sub(/regexp/, replacement, target) 注意第三个参数target，如果忽略则使用$0作为参数，即整行文本。\n例子1：替换单个串 只把每行的第一个AAAA替换为BBBB\nShell 1 awk \u0026#39;{ sub(/AAAA/,\u0026#34;BBBB\u0026#34;); print $0 }\u0026#39; t.txt 例子2：替换所有的串 把每一行的所有AAAA替换为BBBB\nShell 1 awk \u0026#39;{ gsub(/AAAA/,\u0026#34;BBBB\u0026#34;); print $0 }\u0026#39; t.txt 例子3：替换满足条件的行的串 只在出现字符串CCCC的前提下，将行中所有AAAA替换为BBBB\nShell 1 awk \u0026#39;/CCCC/ { gsub(/AAAA/,\u0026#34;BBBB\u0026#34;); print $0; next }{ print $0 }\u0026#39; t.txt 例子4：替换多个可选串 不管是AAAA，还是CCCC，全部替换为BBBB\nShell 1 awk \u0026#39;{ gsub(/AAAA|aaaa/,\u0026#34;BBBB\u0026#34;); print $0 }\u0026#39; t.txt 例子5：全字匹配替换 全字匹配AAAA；即不匹配AAA,以及AAAAA，也就是说完整的四个字符串AAAA。\nShell 1 awk \u0026#39;{ sub(/\\\u0026lt;AAAA\\\u0026gt;/,\u0026#34;BBBB\u0026#34;); print $0 }\u0026#39; t.txt 例子6：规则表达式匹配 把所有以A开头，不管后面连续包含几个A的串替换成一个字符B。\nShell 1 awk \u0026#39;{ gsub(/^A*/,\u0026#34;B\u0026#34;); print $0 }\u0026#39; t.txt ","date":"2024-07-21","permalink":"/posts/awk-text-replacement/","title":"AWK 文本替换笔记"},{"categories":["tools"],"content":"原文链接\n前言 用了不少笔记软件，好多都是半途而废，总感觉有各种各样的问题。笔记格式支持问题，云端网络问题(不支持本地)，样式同步问题。 用过有道云笔记，markdown支持一般，后来转成印象笔记，甚至充了会员，但没有特别的改善。印象笔记在体验上并没有提升。使用过语雀，格式样式都挺好，支持的样式也很多，但每次打开都是要从云端拉取，导致打开特别慢，加上语雀的重大事故，无法打开，后来就放弃了。最近用的就是vscode本地文档，直接github同步，确实方便，但vscode没有客户端，没有特意为笔记软件做搜索优化，每次想搜索一个mysql文档，结果跳出n条笔记，完全放弃搜索功能。 最后才是决定使用obsidian，本地客户端，大公司支持，个人免费，还有双链(目前我用的不多，大家都说好)，可以利用github同步，markdown样式格式支持，还有一些高级插件，搜索比较好，还能使用标签特定为笔记做优化，于是就选择了它。它可以直接复制一些网页，粘贴后基本不需要调整格式。\n这篇文章也是参考网上的教程，作者写的很详细，但本着自己写一遍才放心的精神，决定摘录一遍，并根据一些自己的需求，加一些自己的操作。\n正文 对于技术人而言，经常需要记录、整理大量的笔记内容，形成自己的知识库。一款好的笔记软件我认为需要具备以下几个条件：\n必须：跨平台，同时支持桌面电脑（Windows，Mac，Linux）和手机（Android，iOS） 必须：支持同步，在多台设备中打开任何一台都能接接着编写笔记 必须：实时存储，就算突然断电、死机也不会丢失已写笔记内容 必须：支持代码高亮，更便于友好地阅读代码 必须：支持 Markdown 格式，快速编写文档必备格式，谁用谁知道 必须：支持多重备份，最好是本地一份、远端一份，首选支持git同步的 可选：支持双向链接，这样笔记与笔记之间就可以形成关联关系，慢慢积累后就形成了自己的知识库 可选：支持笔记导出，比如导出 pdf 等格式，便于分享，如果能一键发布到常见博客如 hexo、wordpress、jekyll 更好 我用过诸多笔记软件，但都存在或多或少的问题，无法满足上述要求，后来一直使用网易的有道笔记，它支持一键保存笔记，不过编辑器实在难用，markdown的图片要自己搭建图床。\n阮一峰推荐的笔记软件是 github.dev，可以看看他的这篇文章。不过我更喜欢原生的app软件，我选择 obsidian，因为它免费而且支持上述的大部分需求，尤其是第6点，对于不想将笔记存储保存给笔记软件厂商而言非常好，直接使用git同步。\n现在，我基本上都是用 obsidian 来记笔记和写文章，它支持双向链接，很容易形成知识体系，而且具备丰富的插件支持，具体特性可以自己咨询网络。如果对于格式非常多的文章，我也会使用 AsciiDoc 格式来编写，相比于 Markdown 它更加强大，但是语法也更复杂。Obsidian 目前并不支持，非常遗憾。\n在使用 Obsidian 时，最大的问题就是手机端的同步。我是用 Github 存储笔记，手机端没有很好的同步方案，官方的同步方式无法满足需求，而且需要付费。我的需求是，macOS、windows、iPhone、iPad 四种设备上需要从 github 同步我的笔记，没有安卓端同步的需要，所以我使用 iSH 这个 app，它开源免费，支持 ios，完美的解决了我的问题。\n什么是 iSH 它是一个在 ios 上模拟 Linux 环境的 app，使用的是 alpine linux，在你的 iPhone、iPad上都可以运行并创建 Linux Shell 环境。正好我可以使用它来同步 git 仓库到我的 ios 设备上。\n安装 ish app 从 ios 应用商店下载 iSH Shell，注意看名称和logo：\n下载好后打开，你就得到了一个 Linux Shell 环境。整体界面与 Shell 一样，这里重点说一下工具栏：\nTab：点击后可以自动补全命令 Ctrl：pc 的 ctrl 健，当你需要执行 Ctrl + C 时需要用到 Esc：pc 的 Esc健 Arrows：点击后上下左右拖动可以实现 pc 的方向键的功能，常用的是向上拖动调出最新一条历史记录命令 提示！ 下边的操作，需要输出一些命令，手机上输入不太方便，记得点击工具栏这个 Tab，可以自动补全。\niSH 中，操作基本上都以 apk 命令开头。\n首先，输出 apk update 然后换行即可更新 package。 然后，输入 uname -a 来查看操作系统信息：\nShell 1 2 localhost:~# uname -a Linux localhost 4.20.69-ish SUPER AWESOME May 20 2023 23:41:32 i686 Linux 接下来，继续安装 obsidian app。\n创建 obsidian 仓库 同样地，ios 应用商店下载后打开 obsidian，此时没有任何 vault，首先手动创建一个，比如名称为 notebook（下文中的所有 notebook 都可以改为你自己的仓库名称）。此时，你只是创建了一个空的仓库，我们需要让其与 github 同步。\nPS: 我第一次是单独创建了一个vault，然后在vault下面在拉取了git仓库，目录层级有点多，比较麻烦，这个方案会将git仓库的内容直接放在对应目录下。\n创建 ssh key 并授权给 github 我的笔记仓库托管在 github，处于安全考虑，github 禁止使用 http 连接，需要改为 ssh。\n打开 iSH app，创建 ssh key，需要安装几个软件：\nShell 1 2 apk add git apk add openssh 依次执行上边的命令安装 git、openssh，耐心等待安装完成。\n然后，执行以下命令生成 ssh key：\nShell 1 ssh-keygen -t ed25519 -C \u0026#34;\u0026lt;你的邮箱\u0026gt;\u0026#34; 接着，拷贝生成的公钥：\nShell 1 cat ~/.ssh/id_ed25519.pub 登录 github，点击用户头像，在 setting -\u0026gt; SSH and GPG keys -\u0026gt; New SSH key 新建一个授权的key并粘贴刚才拷贝的公钥，保存即可。\n测试一下在 iSH 中能否正常连接 github：\nShell 1 2 localhost:~# ssh -T git@github.com Hi username! You\u0026#39;ve successfully authenticated, but GitHub does not provide shell access. 输出上述信息说明 ssh 连接成功。\n配置 git 环境 上一步安装了 git，现在来配置 git 环境。\n打开 iSH，依次执行如下命令：\nShell 1 2 git config --global user.name \u0026#34;你的github用户名\u0026#34; git config --global user.email \u0026#34;你的github邮箱\u0026#34; 此外，还需要添加安全目录，否则git可能出错：\nShell 1 git config --global --add safe.directory /root/obsidian/notebook PS: 这里不操作的话问题不大，在第一次执行git pull的时候，会提醒你，直接复制粘贴更方便\n同步 github 仓库到 iSH 现在，你可以在 iSH pull github 仓库代码了。\n首先，打开 iSH，创建一个 obsidian 目录：\nShell 1 cd ~ \u0026amp;\u0026amp; mkdir obsidian 然后，挂载 obsidian app 的文件存储目录到刚才创建的 obsidian 目录:\nShell 1 mount -t ios . obsidian 这里的命令解释：\nmount是挂载命令 -t 指定文件格式ios . 当前目录 obsidian刚刚创建的目录 挂载的命令是在网上找的，至于为啥是ios格式，可以通过命令df -T来查看\nShell 1 2 3 4 5 6 7 localhost:~/obsidian/notebook# df -T Filesystem Type 1K-blocks Used Available Use% Mounted on /private/var/mobile/Containers/Data/Application/826FB5AE-9DE7-4C73-9045-5049E3xxxxx/Documents ios 249880148 227563964 22316184 91% /root/obsidian /private/var/mobile/Containers/Shared/AppGroup/E90D8F8C-AE29-4A9F-9388-F9EF351xxxxx/roots/default/data fake 249880148 227563964 22316184 91% / localhost:~/obsidian/notebook# iSH 会弹出一个窗口，在里边选择 Obsidian 文件夹即可，不需要选择 notebook 仓库，这样就可以访问多个仓库。\n到此，git配置完成，现在可以在 iSH 中拉取git仓库了。\n首先，复制 github 仓库的 ssh 地址。\n然后，进入 obsidian/notebook 文件夹：\nShell 1 cd obsidian/notebook/ 删除下边的 .obsidian 文件夹，这是 obsidian 为其仓库创建的，这里先删除，保证这个目录是空的。\n接着，进入上层目录：\nShell 1 cd .. 克隆仓库：\nShell 1 git clone 你的仓库ssh地址 notebook 表示将 github 仓库下的文件克隆到 notebook 目录。\n耐心等待克隆完成，如果失败了重试即可。\nPS: 上面说的，如果不在仓库地址后面加 notebook ，就会在notebook下面新建仓库目录，目录层级会多一层\n现在，打开 obsidian app，可以看到数据成功同步了过来。\n常用操作 现在，你可以在 obsidian app 中记录笔记了，一些日常操作包括：\n这些操作都需要打开 iSH app，进入 ~/obsidian/notebook 操作。\n1、同步仓库 pc端修改了笔记，ios可以同步仓库：\nShell 1 git pull origin main 2、提交更改 ios上修改了笔记，需要提交到 git 仓库：\nShell 1 2 3 git add . git commit -m \u0026#39;描述\u0026#39; git push origin main 快捷操作 同步数据需要打开iSH app，执行拉代码，可以配置快捷命令。 根据 linux 的特性，可以在打开iSH app后，自动执行相关操作，比如直接进入我们的相应的目录，打开软件自动拉取最新数据 linux 下，系统会自动执行家目录下面(直接执行命令cd即可到家目录下)的 .profile 文件，再配置 .ashrc 文件，即可完成需求 直接复制粘贴下面的命令：\nShell 1 2 3 4 5 6 7 8 9 10 11 cd cat \u0026gt; ~/.profile \u0026lt;\u0026lt;EOF if [ -f ~/.ashrc ]; then source ~/.ashrc fi EOF cat \u0026gt; ~/.ashrc \u0026lt;\u0026lt;EOF cd /root/obsidian/notebook git pull alias gl=\u0026#39;git pull\u0026#39; EOF PS: 简单解释下命令的作用，先cd到家目录下，然后创建.profile，内容的作用就是开启时执行.ashrc里的命令，.ashrc里的命令意思是到/root/obsidian/notebook目录下，然后执行git pull命令，最后做了一个别名，即在命令行输入gl，等同于执行git pull命令，因为在手机上操作还是能简化点比较好\ngit 配置 当文件名含有中文的时候，git 显示会异常，需要执行下面命令\nShell 1 git config --global core.quotepath false PS 不过还是建议尽量使用英文做标题，也可以锻炼英语水平，也能避免很多奇奇怪怪的问题\n总结 使用 iSH app 可以在 ios 上模拟一个 Linux 环境，并同步 git 仓库，这非常利于 obsidna 仓库的同步。配置 github 的操作稍有些麻烦，不过配置好后就很方便了。如果需要在 iPhone、iPad 上使用 obisidan 阅读、编写笔记，值得一试。\n","date":"2024-07-13","permalink":"/posts/obsidian-tool-v1/","title":"obsidian-sync-v1"},{"categories":["read"],"content":"文章不是我写的，我只是做了一个转发。\n译文原文，译文 | 过去 14 年里，我的生产力工具只是一个 .txt 文件\n英文原文 My productivity app is a never-ending .txt file\n#阅读 #reading\n译文 | 过去 12(14) 年里，我的生产力工具只是一个 .txt 文件\n爱吃草的熊 2022 年 02 月 26 日\n原文作者在翻译的时候是2022年，我也有印象这个新闻，但当我再次看的时候，已经过去了2年了。再次去查看原文，发现更新时间是 By Jeff Huang, updated on 2022-03-21 可能原文是作者在2020年写的，2022年火的，作者发现火了以后，将原文里的时间从12年改成了14年。\n译者说：本文 原文 由是布朗大学计算机科学副教授 Jeff Huang 所撰并发表在其个人网站上，今年一月发出后，在 Hacker News 等社区引发了 热烈讨论。译文已交由其审阅并授权发表。文中的笔记内容在格式和拼写上力图保持原貌。\n我最初是在即刻上看到阑夕发布的一条动态里有这个链接，再结合我派前一阵突然遭遇的外部舆论，觉得其实这一篇挺能说明我们在追求效率上的一些迷思。翻译本文的目的并不是说推崇用 .txt 文件管理自己的生活，而是想表达一个这样的意思：无论是折腾好玩还是追求高效，工具只占一部分，更重要的是我们自己的方法论。不管是重器轻用还是轻器重用，一切都取决于你自己想要什么。\n我的英语水平很一般，翻译得并不太「信达雅」，如果对译文有疑义，请点开原文链接阅读。\n当我开始上大学时，对我来说最大的转变是学会组织（信息）。有一段时间，我的脑子无法记住任何事情。而且那段时间里，持续不断地追踪任务进度这件事让我屡屡从手头的工作中分心。\n所以我尝试了各种各样的 todo 清单，任务追踪工具和生产力 apps。但是这些尝试都让我感到沮丧，因为待完成的工作越来越多，而且有太多互相关联的事情比如说会议记录、日程上的约会、想法清单以及实验笔记，这些（互相关联的）事情却在不同的系统上。\n于是我放弃了，开始只在一个文本文件中进行跟踪，并将其用作我的主要生产力系统已有 12（14） 年了。它现在对我的工作非常重要，并且随着我工作职责的增加而以令人惊讶的速度扩展，所以我想分享这个系统。这是我的秘密武器。\n前置工具：日历。我使用的一个外部工具是一个在线日历，我把所有任务都放在这个日历上，即使是那些实际上不是固定时间的工作，比如「在车间制作一张咖啡桌」或「弄清楚如何招募新的博士生」 「——我会把他们安排在我想考虑的日期。这样我未来的所有计划和日程安排都在一起，而不是一堆我必须跟踪的清单。\n制作每日清单：每晚我上床之前，我会把第二天日历上的所有项目都记下来，并将其作为每日待办事项列表附加到文本文件的末尾，这样当我醒来时我就能确切地知道我将做什么。这个清单包含计划好的任务（下午 2 点与麦当娜会面，下午 4 点办公时间）、差事（签署表格、归还书籍）和工作项目（审阅论文、准备演示文稿）。这份清单也让我思考我一天的工作量是否合适。\n明天我不想做的任何事情，我都会重新把它放进我日历里靠后的日期。在这样做了很多年后，我已经非常擅长估计我一天能完成的事情。下面一个替换了名称的示例，因此当我从日历中改动一天的日程安排时，您可以看到它的样子。\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 2021-11-31 11am meet with Head TAs - where are things at with inviting portfolio reviewers? 11:30am meet with student Enya (interested in research) review and release A/B Testing assignment grading 12pm HCI group meeting - vote for lab snacks send reminders for CHI external reviewers read Sketchy draft Zelda pick up eye tracker - have her sign for it update biosketch for Co-PI 3:15pm join call with Umbrella Corp and industry partnership staff 3:45pm advising meet with Oprah 4pm Rihanna talk (368 CIT) 5pm 1:1 with Beyonce #phdadvisee 6pm faculty interview dinner with Madonna 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 2017-11-31 11:00 和首席助教开会 - 邀请投资联合审阅者的情况怎么样了？ 11:30 和学生 Enya 开会（对研究感兴趣） 检查并发布 A/B 测试作业评分 12:00 HCI 组会 - 为实验室事务投票 给 CHI 外部评审发送提醒 阅读初稿 zelda 选择了眼动仪 - 她已经签字了 CO-PI 更新生物素描 15:15 和行业合作伙伴参加 Umbrella 公司的电话会议 15:45 和 Oprah 的咨询会议 16:00 和 Rihanna 谈话 (368 CIT) 17:00 和 Beyonce 1 对 1 谈话 #phd 咨询 18:00 和 Madonna 的教师访谈晚餐 作为记录：每天的待办事项清单同样也是我的笔记本，这样它就从一个待办清单变成了一个已完成清单。我觉得这样做最棒的是我将所有的事情全部保存在一个文本文件中（.txt)，靠日期来区分内容，这样我就可以记录我做过的所有事情以及我什么时候做过。\n我现在使用的文件大约是 7（9） 年前创建的，那时我刚刚开始我现在的工作。这个文件可以用来作为研究笔记本，也可以用来做会议记录。我已经在这个文件中写了 37773（51,690） 行，记录了我作为一个教授做过的所有事情，遇到的几乎每一个人，以及我讨论过的内容或者我产生的想法。一天工作结束后我的清单会变成下面这样，表示工作已经完成：\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 2021-11-31 11am meet with Head TAs - where are things at with inviting portfolio reviewers? A: got 7/29 replies - need 3 TAs for Thursday lab - Redesign assignment handout will be done by Monday, ship Thursday 11:30am meet with student Enya (interested in research) - they\u0026#39;re a little inexperienced, suggested applying next year review and release A/B Testing assignment grading 12pm HCI group meeting - automatically generate thumbnails from zoom behavior on web pages - #idea subliminal audio that leads you to dream about websites - Eminem presenting Nov 24 - vote for lab snacks. A: popcorn and seaweed thing got unofficial notification ARO YIP funding award #annual #cv read Sketchy paper draft - needs 1 more revision - send to Gandalf to look at? Zelda pick up eye tracker - have her sign for it update biosketch for Co-PI unexpected drop in from Coolio! #alumni - now a PM working on TravelAdvisor, thinking about applying to grad school 3:15pm join call with Umbrella Corp and industry partnership staff - they want to hire 20 data science + SWE interns (year 3), 4 alums there as SWE 3:45pm advising meet with Oprah - enjoyed CS 33 - interning at Facebook 4pm Rihanna talk (368 CIT) 5pm 1:1 with Beyonce #phdadvisee - stuck on random graph generating crash - monitor memory/swap/disk? - ask Mario to help? - got internship at MSR with Cher - start May 15 or 22 - will send me study design outline before next meeting - interviewing Spartacus as potential RA for next semester 6pm faculty interview dinner with Madonna (Gracie\u0026#39;s) - ask about connection with computer vision - cool visual+audio unsupervised comparison, thoughtful about missing data, would work with ugrads (?), likes biking, teach compvis + graphics - vote #HIRE #note maybe visit Monsters University next spring, Bono does related work 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 2017-11-31 11:00 和首席助教开会 - 邀请投资联合审阅者的情况怎么样了？ A: 回复进度 7/29。 - 需要 3 名助教 - 星期一完成作业讲义的重写，星期四发送 11:30 和学生 Enya 开会（对研究感兴趣） - Enya 缺乏研究经验，建议她明年再申请。 检查并发布 A/B 测试作业评分 12:00 HCI 组会 - 根据网页版 zoom 的 自动化功能生成（会议）的概要图 - #idea 潜意识里的音频会让你梦到某些网站 - Eminem 将在 11 月 24 日出席 -为实验室零食投票 A：非官方通告：爆米花和海苔赢得了 ARO YIP 基金奖 #annual #cv CHI 外部评审发送提醒 阅读初稿 - 需要再修改几次 - 给 Gandal 看看？ Zelda 选择了眼动仪 -她已经签字了 CO-PI 更新生物素描 与 Coolio 不期而遇 #alumni - Coolio 现在是 TravelAdvisor 的产品经理，正在考虑申请研究生院。 15:15 和行业合作伙伴参加 Umbrella 公司的电话会议 - 他们想招聘 20 位 SWE 数据科学实习生（3 年级以上），有 4 个校友在 SWE 15:45 和 Oprah 的咨询会议 - CS 33 这个系列课程是一种享受 - Oprah 在 Facebook 实习 16:00 和 Rihanna 谈话（368 CIT） 17:00 和 Beyonce 1 对 1 谈话 #phd 咨询 - 在生成随机图上遇到了困难 - 检查 内存/交换机/磁盘了吗？ - 请 Mario 帮忙了吗？ - 和 Cher 一起去 MSR 实习 - 5 月 15 或者 22 日开始 - 在下次组会前给我研究设计大纲 - 下学期以 RA 备选的方式参加斯巴达克斯的面试 18:00 和 Madonna（Gracie\u0026#39;s) 的教师访谈晚餐 - 询问了与计算视觉的关系 - 很酷的视觉与无监督音频比较，对数据丢失很有想法，会和研究生一起工作（？）， 比如说骑自行车，教计算视觉和图形学 - 投票 #HIRE #note 或许明年春天会去参观 Monsters 大学，Bono 在那做相关的工作 快捷键和特点：我使用一致的写作风格，所以使用一些短语就可以很容易进行搜索。当我搜索「meet with」时，它显示我已经安排了 3,000 多次会议。我有一些标签，比如 #annual 用于在我的下一份年度报告中添加内容， #nextui 用于在我下次运行我的下一个 UI 课程时添加的内容。\n文本文件非常灵活，在任何时候，我都可以快速浏览一下那天我做了什么，还剩下什么。当一项任务完成时，这是最常见的情况，我就把这个文件抛之脑后。我可以使用搜索框计算聚合统计信息，或者列出包含标签的所有行，以及使用我的文本编辑器进行其他操作。我使用 Ultraedit 是因为我熟悉它，但我想任何文本编辑器都会有类似的功能。\n电子邮件 ：电子邮件显然是我工作流程的一部分。怎么处理电子邮件，但我发现一个简单的标记系统就足够了——如果我需要处理它，标记红色，如果我最终需要处理它但需要一些思考或其他人来标记橙色处理它，并为我发送的等待回复的电子邮件标记黄色，以便我知道稍后跟进。只要方便，我会在电子邮件进来时对其进行标记。\n在一天结束时，我会快速回顾一下橙色和黄色邮件，看看是否需要跟进或应该变成红色。有些人的工作流程围绕着痴迷地清理他们的收件箱。我真的不在乎让我的收件箱保持空白，因为每当收到电子邮件时，我都会觉得我有新的工作要做。\n所以我的日常生活就是：\n看看我昨晚写的待办清单，看看我今天要做什么 按清单上计划好的日程工作 当我有空闲（计划外）时间时，执行我列表中的浮动任务并在一天结束时处理红色标记的电子邮件 快速查看橙色/黄色电子邮件，看看它们是否需要任何处理 将第二天的日历项复制到文本文件的底部 这个过程有一些很好的特性 ：\n我醒来后可以很容易地知道要做什么 我不需要在脑海中记住以后要做的事情（跟进电子邮件，未来的任务） 很容易回忆过去发生的事情，看看我一天能完成多少 没有进行中的「待办事项」列表，这样项目不会日复一日地推迟 我使用远程桌面，因此可以从每台设备访问所有内容 前一天晚上我的日常工作量完全在我的控制之下；每当我对自己的长期承诺感到不知所措时，我就会通过积极地取消标记电子邮件、从日历中删除我不再感到兴奋的事情以及减少我未来分配给自己的工作量来减弱这种感觉。\n这样做确实意味着有时我会错过一些问题或不深入研究有趣的问题，但可以帮助我保持可管理的工作量。\n如果您愿意尝试我的系统，或者对此有一些想法，我很乐意收到您的来信！\n","date":"2024-07-13","permalink":"/posts/todolist-by-txt/","title":"Todolist by Txt"},{"categories":["Linux"],"content":"#linux #command #sed\nsed 打印匹配内容 代码 1 2 3 4 5 6 # sed -n -e \u0026#39;/build/p\u0026#39; filename [cli_rebuild] p -打印行 n - e --e\u0026lt;script\u0026gt;或--expression=\u0026lt;script\u0026gt;：以选项中的指定的script来处理输入的文本文件； sed 打印匹配内容行号 代码 1 sed -n -e \u0026#39;/build/=\u0026#39; filename sed 打印匹配内容和行号 代码 1 2 3 # sed -n -e \u0026#39;/build/p\u0026#39; -e \u0026#39;/build/=\u0026#39; filename [cli_rebuild] 28 打印文件中间几行，显示 5-10 行中间的内容 Shell 1 2 sed -n \u0026#34;3p\u0026#34; filename sed -n \u0026#39;5,10p\u0026#39; filename 打印文件某一行到最后一行 Shell 1 sed -n \u0026#39;5,$p\u0026#39; filename 某个时间段日志，2020-07-17T18:15:52 日志中的时间格式，并且存在该时间 Shell 1 sed -n \u0026#34;/2020-07-17T18:15:52/,/2020-07-17T22:52:45/p\u0026#34; filename ","date":"2024-07-13","permalink":"/posts/sed-command/","title":"SED 查询文本与行号"},{"categories":["ARTS"],"content":"ARTS-2024-week27 0701-0707\nAlgorithm 算法 题目\nReview 复审(英语) Specialization is a good thing 专业是一件好事\nI\u0026rsquo;m all for specialization! The society has come a long way from subsistence agriculture and that\u0026rsquo;s almost objectively a good thing. The economy organizes people into different trades and different professions and lets people benefit from their comparative advantage.\n我完全支持专业化！从农耕时代进化到现代社会花了好久，客观来讲，这是一件好事。经济将人们分工到不同行业和职业，让人们在比较优势只获益\nThere were a few replies (and subtweets) misunderstanding my tweet as an argument against specialization. Which is a bit frustrating, because I think it\u0026rsquo;s pretty useless to argue against for or against specialization. Clearly, neither extreme is good? Like, subsistance agriculture isn\u0026rsquo;t great, but you could imagine going in the other extreme and organizing the society around people doing exactly one thing well and nothing else.\n有一些回复（跟帖）误会我的推文是反对社会化(专业化)。这让我有点沮丧，因为我认为争论支持或反对专业化都毫无意义。很明显，极端好么？自给自足的小农方式不好，但你能想象另一个极端，组织社会上的所有人自己手里的事，不做别的事。\nA restaurant could have one chef who only chops onions, another one who only souffles things, another one who only makes apple tarts. If this sounds dumb, then it\u0026rsquo;s because it\u0026rsquo;s intentionally hyperbolic! My point is, neither extreme is good, so the question is: what\u0026rsquo;s the right level of specialization?\n一个饭厅可以只有一个厨师只会切洋葱，另一个只会蛋奶酥，另一位只做苹果馅饼。如果这听起来很蠢，因为这事故意歪曲事实的。我的观点是，不要走极端。所以，问题来了：什么样社会化（专业化）比较合理？\nSpecialization is a good thing\n专业化是一件好事\nI\u0026rsquo;m all for specialization! The society has come a long way from subsistence agriculture and that\u0026rsquo;s almost objectively a good thing. The economy organizes people into different trades and different professions and lets people benefit from their comparative advantage.\n我完全支持专业化！社会已经从自给自足的农业走了很长一段路，这几乎客观上是一件好事。经济将人们组织到不同的行业和不同的职业中，让人们从他们的比较优势中受益。\nThere were a few replies (and subtweets) misunderstanding my tweet as an argument against specialization. Which is a bit frustrating, because I think it\u0026rsquo;s pretty useless to argue against for or against specialization. Clearly, neither extreme is good? Like, subsistance agriculture isn\u0026rsquo;t great, but you could imagine going in the other extreme and organizing the society around people doing exactly one thing well and nothing else.\n有一些回复（和子推文）误解了我的推文，认为这是反对专业化的论据。这有点令人沮丧，因为我认为反对或反对专业化是没有用的。显然，这两个极端都不好？比如，自给自足的农业不是很好，但你可以想象走到另一个极端，把社会组织起来，让人们只做一件事，不做其他事情。\nA restaurant could have one chef who only chops onions, another one who only souffles things, another one who only makes apple tarts. If this sounds dumb, then it\u0026rsquo;s because it\u0026rsquo;s intentionally hyperbolic! My point is, neither extreme is good, so the question is: what\u0026rsquo;s the right level of specialization?\n一家餐馆可能有一个只切洋葱的厨师，另一个只做蛋奶酥的厨师，另一个只做苹果馅饼的厨师。如果这听起来很愚蠢，那是因为它是故意夸张的！我的观点是，两个极端都不是好的，所以问题是：什么是正确的专业化水平？ subsistence agriculture pottery vs pretty economy objectively trades professions benefit comparative advantage frustrating extreme chops intentionally hyperbolic\nTip 小技巧 ish 用的 ash shell ，可以在home目录下，配置 .profile 文件，内容如下\nShell 1 2 3 if [ -f ~/.ashrc ]; then source ~/.ashrc fi 再创建 .ashrc 文件，这样就可以在启动虚拟机(ish)的时候，做一些事情(开机启动)。 目前做的需求就是，启动的时候，自动拉取git仓库。\n也做了一个别名，这样能方便操作。别名配置在 /etc/profile.d/git_pull.sh\nShell 1 alias gl=\u0026#39;cd ~/path \u0026amp;\u0026amp; git pull\u0026#39; 这个其实也可以放在 ~/.ashrc 里，只是开始不知道这个文件什么时候才能生效。\nShare 技术分享 阮一峰对运维的看法\n七、运维工程师的出路 随着传统的运维角色的消失，现有的运维工程师必然面临着转型，不外乎有三种出路可以选择。\n（1）如果喜欢开发业务软件，可以选择成为 DevOps 工程师。 （2）如果喜欢开发平台软件，可以选择做平台工程，专注于基础设施的整合。 （3）如果更喜欢硬件和底层，可以选择加入\u0026quot;基础设施即服务\u0026quot;（IaaS）的云公司，深入研究基础设施。 碎碎念 todo list 一直在用，但在做一些大任务的时候，总是会卡壳。拖延症非常严重，结局几乎都是拖到最后放弃。 这两天发现可能根本原因还是专注力，因为当有多个大任务的时候，大脑总是希望能快速完成，然后来休息。 但事实却是每一件事情都要花费大概几个小时，几天，甚至更久来完成。 而且不止一个任务，比如阅读django源码，学习k8s。任务很大，没有拆解成一个具体的task，无法估算时间，于是开始无限拖延。 这两天想到的对策就是拆成一个小的task，且只完成一个task，不去多想。就像马拉松一样，只要关心跑到下一个马路牙子，剩下的就是坚持。 只去做一个简单的任务，规划好，不去想太多。\n放空自己，不需要给太大压力，做一件事就好。\n","date":"2024-07-07","permalink":"/posts/arts-2024-07-07/","title":"ARTS-WEEK-27"},{"categories":["tools"],"content":"#vim/vi #linux vim operation\n简明vim攻略 vim光标移动 vim笔记 Vim 学习笔记 2: 组合命令 打造vim ide\nvim 搜索忽略大小写\n代码 1 2 3 4 /foo\\c # 选区，在 Visual 模式下选择区域后输入 :，Vim 即可自动补全为 :\u0026#39;\u0026lt;,\u0026#39;\u0026gt;。 :\u0026#39;\u0026lt;,\u0026#39;\u0026gt;s/foo/bar/g 光标移动 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 --NROMAL-- w 跳到一个单词开头 b 跳到本单词或上一个单词开头 e 跳到本单词或下一个单词结尾 ge 跳到上一个单词结尾 0 跳到行首 ^ 跳到从行首开始第一个非空字符 $ 跳到行尾 g_ 到本行最后一个不是blank字符的位置 gg 跳到第一行 G 跳到最后一行 /pattern 搜索 pattern 的字符串(如果搜索出多个匹配,可按n键到下一个) u undo 撤销 Ctrl + r redo f{char} 光标跳到下个{char}所在位置 F{char} 反向移动到上一个{char}所在位置 t{char} 光标跳到下个{char}的前一个字符的位置 T{char} 光标反向移动到上个{char}的后一个字符的位置 这里的f/t查询均局限于当前行操作 ; 重复上次的字符查找操作 , 反向查找上次查找的命令 操作符 operator 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 --NROMAL-- d(delete) 删除 dd 删除当前行 p 粘贴剪切板 c(change) 修改(删除并进入插入模式) C 删除当前光标至结尾，并进入insert入模式 cc 删除当前行所有内容，并进入insert模式 y(yank) 复制 v(visual) 选中并进入 VISUAL 模式 x 删除光标后一个字符 X 删除光标前一个字符 Ctrl + v 进入 VISUAL BLOCK 模式, (列模式) Ctrl + v + I + # 批量使用 # 在行首注释 动作 motion 代码 1 2 3 4 5 6 i inner a around ciw 删除当前单词并进入编辑模式 cw 从当前位置删除到单词结尾并进入编辑模式 ca\u0026lt; 删除一对\u0026lt;\u0026gt; 匹配的内容和\u0026lt;\u0026gt;符号,并进入编辑模式 ci{ 删除一对{} 内匹配的内容,不包括符号,并进入编辑模式 切换大小写 代码 1 2 3 4 5 6 7 ~ 将光标下的字母改变大小写 3~ 将光标位置开始的3个字母改变其大小写 g~~ 改变当前行的字母的大小写 gUU 将当前行的字母改成大写 guu 将当前行的字母改成小写 gUaw(gUiw) 将光标下的单词改成大写 guaw(guiw) 将光标下的单词改成小写 滚屏操作 前缀(N)为按下的数字 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 --NROMAL-- (N)Ctrl-E 窗口向下滚动N行, 不加(N)默认向下滚动一行(光标没有移动) (N)Ctrl-Y 窗口向上滚动N行, 不加(N)默认向上滚动一行(光标没有移动) (N)Ctrl-D 窗口向下滚动N行, 不加(N)默认滚动窗口行数的一半(光标没有移动) (N)Ctrl-U 窗口向上滚动N行, 不加(N)默认滚动窗口行数的一半(光标没有移动) (N)Ctrl-F 窗口向下滚动N页, 不加(N)默认向下滚动一屏(光标被迫移动) (N)Ctrl-B 窗口向上滚动N页, 不加(N)默认向下滚动一屏(光标被迫移动) zt(小写) 重新绘制窗口,光标所在行移动到屏幕的顶端(相当于 z\u0026lt;enter\u0026gt;) zz(小写) 重新绘制窗口,光标所在行移动到屏幕的中间(相当于 z.) zb(小写) 重新绘制窗口,光标所在行移动到屏幕的底端(相当于 zb) H L 打开/保存/退出/改变文件(Buffer) 代码 1 2 3 4 5 6 7 --NROMAL-- :e \u0026lt;path/to/file\u0026gt; 打开一个文件 :w 存盘 :saveas \u0026lt;path/to/file\u0026gt; 另存为 \u0026lt;path/to/file\u0026gt; :x,ZZ 或 :wq 保存并退出 (:x 表示仅在需要时保存, ZZ不需要输入冒号并回车) :q! 退出不保存 :qa! 强行退出所有的正在编辑的文件,就算别的文件有更改 :bn 和 :bp 你可以同时打开很多文件，使用这两个命令来切换下一个或上一个文件(陈皓注:我喜欢使用:n到下一个文件) 文本编辑器的换行操作 Vim 通常会对长行自动回绕(换行),以便你可以看见所有的文字。但有时想让文字在一行中显示完。那么就要关闭自动回绕功能，你需要左右移动才能看到一整行。\n代码 1 2 3 --NROMAL-- set wrap set nowrap 当nowrap生效时\n代码 1 2 3 4 5 --NROMAL-- (N)zh 屏幕向右滚动N个字符 (N)zl 屏幕向左滚动N个字符 (N)zH 屏幕向右滚动半个屏幕宽度的字符 (N)zL 屏幕向左滚动半个屏幕宽度的字符 vim vscode plug\nhttps://coolshell.cn/articles/1651.html\n在VIM中输入:h!试试看会发现什么。\n再输入:h 42呢？又会有什么发现？\n查看历史命令\n代码 1 2 3 4 5 用 q: 可以查看最近的历史命令的命令行窗口。用 up and down 选定，使用 Enter 就可以执行这个命令。 :\u0026lt;up\u0026gt; and \u0026lt;down\u0026gt;, 也可查看并用 Enter 使用这个命令。 :　CTRL-P　and CTRL-N , 查看历史命令. :history :history / vim 边界符号(英文字符下)，可以使用b, \u0026lsquo;B\u0026rsquo;来获取边界 vim 下的 % 使用也是很神奇，会有偶尔用错的情况。 还有就是要看下vscode 下，vim 对应的 keymap 刚刚在练习使用 f{char} 速行定位时，用到 . 好像会删除行，这个不知道是不是跟我刚刚的操作有关，. 应该是重复上次的操作，刚好我上次操作是 \u0026lsquo;删除\u0026rsquo; vim 使用途中发现 u U 会出现很多问题，这个确实有点头疼。\nvscode vim插件使用问题汇总\nvim 批量操作行位处理，批量到行尾增加字符串(每行长度不一致，vscode可以直接command 方向键右，好像vim下也可以这么操作) ctrl + v 列块选择模式，切换到行尾 $，然后使用 A 在行位插入需要写入的字符串，然后两次 ESC，这样确实方便，以后在 vim 模式下也能操作，这个也是我比较喜欢的功能。 行位批量操作3中方式 批量正则替换，好像cammand + f也可以唤出替换，这样应该也是能满足我的需求 批量选择某个单词，command + d, 这个是 vscode 特有的, 需要写个插件配置 ide的相关快捷操作\n去定义的位置 gd go to define 快速格式化 vscode vim 无法正常翻页，只能向右移动一个字符 按住 j ，不会一直向下移动一行，只能按一次向下移动一行，这是因为 输入法控制，避免连续按键问题。 经常在vim 突然发现都变成一行了。自己也莫名其妙 J → 把所有的行连接起来（变成一行）\nvim 下全选复制粘贴的快捷键。 vim 快速增加引号 ysw\u0026quot;，原生 vim 是不支持该操作，需要安装 插件 常规操作下列几种，在 vscode vim 下这个是默认支持的。 https://blog.csdn.net/Xurui_Luo/article/details/106984427\nhttps://github.com/ahrencode/Miscellaneous/blob/master/vim-cheatsheet.pdf\nvim 光标乱跳的问题，不确定当前状态是啥，是不是已经触发按键 v/i/a 之类的 -\u0026gt; 这个需要看 vim 操作大全\n找了一篇关于 vscode vim 的使用文章 我如何用 VSCodeVim 提升開發效率\nvim 下的 d/y 命令会把当前剪切板内容覆盖，这个比较头疼。 可能需要直接v- 模式，然后粘贴。\nvim mark标记位置, 快速复制一段数据, 粘贴到当前光标下一行 vim 寄存器 剪切板 http://yyq123.blogspot.com/2011/06/vim-mark.html\nvim 的复制粘贴p, 只能粘贴一次, 再次使用数据粘贴板变了; vim 寄存器问题, 关键字 vim registers\nhttps://blog.csdn.net/weixin_43274002/article/details/120165079\nvim u 命令, 回退会到哪一步, 操作错一步, 回退步步错\nvim 大小写问题\nshift + h 会跳到上一个空格的位置\n大写的 hjkl 是什么意思，会突然变的换行\nhttps://hackmd.io/@lunzaizai/BJX4hlPKY\nhttps://www.cnblogs.com/yinheyi/p/6944144.html\nHL 命令解释\nvim yank 后 想粘贴到下一行\n","date":"2024-07-06","permalink":"/posts/vim-operation/","title":"Vim 与 VSCodeVim 操作笔记"},{"categories":["Linux"],"content":"#nvm #install\nnvm-github\n安装或更新 安装或更新脚本 安装 nvm或者更新nvm可以直接执行下列命令\nBash 1 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash Bash 1 wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash Running either of the above commands downloads a script and runs it. The script clones the nvm repository to ~/.nvm, and attempts to add the source lines from the snippet below to the correct profile file (~/.bash_profile, ~/.zshrc, ~/.profile, or ~/.bashrc). 脚本逻辑：将 nvm 存储库克隆到“~/.nvm”，并尝试将下面代码片段中的源代码行添加到正确的配置文件（“~/.bash_profile”、“~/.zshrc”、“~/.profile”） ，或~/.bashrc`）。\nBash 1 2 export NVM_DIR=\u0026#34;$([ -z \u0026#34;${XDG_CONFIG_HOME-}\u0026#34; ] \u0026amp;\u0026amp; printf %s \u0026#34;${HOME}/.nvm\u0026#34; || printf %s \u0026#34;${XDG_CONFIG_HOME}/nvm\u0026#34;)\u0026#34; [ -s \u0026#34;$NVM_DIR/nvm.sh\u0026#34; ] \u0026amp;\u0026amp; \\. \u0026#34;$NVM_DIR/nvm.sh\u0026#34; # This loads nvm nvm 常用命令 nvm install stable 安装最新稳定版node nvm install \u0026lt;version\u0026gt; 安装指定版本，如：安装v14.4.0，nvm install v14.4.0 nvm uninstall \u0026lt;version\u0026gt; 删除已安装的指定版本，语法与install类似 nvm use \u0026lt;version\u0026gt; 切换使用指定的版本node nvm ls 列出所有安装的版本 nvm alias default \u0026lt;version\u0026gt; 如: nvm alias default v11.1.0 ","date":"2024-07-06","permalink":"/posts/nvm-install/","title":"NVM 安装与常用命令"},{"categories":["Linux"],"content":"1.根据访问IP统计UV\nBash 1 awk \u0026#39;{print $1}\u0026#39; access.log | sort | uniq -c | wc -l 2.统计访问URL统计PV\nBash 1 awk \u0026#39;{print $7}\u0026#39; access.log | wc -l 3.查询访问最频繁的URL\nBash 1 awk \u0026#39;{print $7}\u0026#39; access.log | sort | uniq -c | sort -n -k 1 -r | more 4.查询访问最频繁的IP\nBash 1 awk \u0026#39;{print $1}\u0026#39; access.log | sort | uniq -c | sort -n -k 1 -r | more 5.根据时间段统计查看日志\nBash 1 cat access.log | sed -n \u0026#39;/14\\/Mar\\/2015:21/,/14\\/Mar\\/2015:22/p\u0026#39; | more 6.通过日志查看含有send的url,统计ip地址的总连接数\nBash 1 cat access.log | grep \u0026#34;send\u0026#34; | awk \u0026#39;{print $1}\u0026#39; | sort | uniq -c | sort -nr 7.通过日志查看当天访问次数最多的时间段\nBash 1 awk \u0026#39;{print $4}\u0026#39; access.log | grep \u0026#34;24/Mar/2011\u0026#34; |cut -c 14-18 | sort | uniq -c | sort -nr | head 8.通过日志查看当天指定ip访问次数过的url和访问次数\nBash 1 cat access.log | grep \u0026#34;192.0.2.10\u0026#34; | awk \u0026#39;{print $7}\u0026#39; | sort | uniq -c | sort -nr ","date":"2024-07-06","permalink":"/posts/access-log-statistics/","title":"Access Log 常用统计命令"},{"categories":["Linux"],"content":"screen 生成新的窗口 screen -ls 查看当前的screen窗口 exit 或者 ctrl d 退出 screen 将当前窗口放后台\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # 使用yum安装screen yum install screen # 创建一个名为test的会话窗口 screen -S test # 暂离窗口 Ctrl+a d(即按住Ctrl，依次再按a,d) # 查看存在的会话窗口 screen -ls # 进入窗口 screen -r test screen -r 进程ID # 关闭窗口 exit # 窗口切换 Ctrl+a c ：在当前screen会话中创建窗口 Ctrl+a w ：窗口列表 Ctrl+a n ：下一个窗口 Ctrl+a p ：上一个窗口 Ctrl+a 0-9 ：在第0个窗口和第9个窗口之间切换 https://www.cnblogs.com/Steven0805/p/7521719.html\nhttps://handerfly.github.io/linux/2019/03/31/Screan%E5%91%BD%E4%BB%A4%E7%9A%84%E4%BD%BF%E7%94%A8/\nhttps://blog.csdn.net/weixin_43332715/article/details/122022030\nhttps://cloud.tencent.com/developer/article/1722221 ","date":"2024-06-06","permalink":"/posts/screen-command/","title":"Screen 常用命令"},{"categories":["python"],"content":"python包都安装到哪里去了？ 原文链接: 你的 Python 包都装到哪了？\n终结一切找不到包、可执行文件的问题\n背景 经常在python社区看到的高频问题：\n我安装了pip为什么找不到可执行文件 import module 为什么报 ModuleNotFound? 为什么我用 Pycharm 能运行在cmd里运行不了？ Python是如何寻找包的 现在大家的电脑上很可能不只有一个Python，还有更多的虚拟环境，导致安装包的时候，一不小心你就忘记注意安装包的路径了。 首先我们来解决找包的问题，这个问题回答起来很简单，但很多人不知道这个原理。 假如你的Python解释器的路径是$path_prefix/bin/python，那么你启动Python交互环境或者用这个解释器运行脚本时，会默认寻找以下位置：\n$path_prefix/lib(标准库路径) $path_prefix/lib/pythonX.Y/site-packages(三方库路径， X.Y是对应Python的主次版本号，如3.8,2.7) 当前工作目录(pwd命令返回的结果) 这里如果你用的是Linux上默认的Python,$path_prefix就是/usr，如果你是自己使用默认选项编译的，$path_prefix就是/usr/local。从上面第二条可以看到不同的Python版本的第三方库路径不同，如果你把Python从3.6升级到3.8那么之前装的三方库都没法用了。当然你可以整个文件夹都拷贝过去，大部分情况不会出问题。\n几个有用的函数 sys.executable: 当前使用的Python解释器路径 sys.path: 当前包的搜索路径列表 sys.prefix: 当前使用的 $path_prefix 除此之外，还可以在命令行中运行python -m site，会打印出当前的 Python 的一些信息，包括搜索路径列表。 例：\nShell 1 2 3 4 5 6 7 8 9 10 Python 3.8.15 (default, Mar 27 2024, 17:03:04) [Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin Type \u0026#34;help\u0026#34;, \u0026#34;copyright\u0026#34;, \u0026#34;credits\u0026#34; or \u0026#34;license\u0026#34; for more information. \u0026gt;\u0026gt;\u0026gt; import sys \u0026gt;\u0026gt;\u0026gt; sys.executable \u0026#39;/Users/admin/PycharmProjects/opstools/.venv/bin/python\u0026#39; \u0026gt;\u0026gt;\u0026gt; sys.path [\u0026#39;\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python38.zip\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/lib-dynload\u0026#39;, \u0026#39;/Users/admin/PycharmProjects/opstools/.venv/lib/python3.8/site-packages\u0026#39;] \u0026gt;\u0026gt;\u0026gt; sys.prefix \u0026#39;/Users/admin/PycharmProjects/opstools/.venv\u0026#39; 使用环境变量添加搜索路径 如果你的包的路径不存在上面列出的搜索路径列表里，可以把路径加到PYTHONPATH环境变量里，多个路径用:隔开(Windows用;)。\n但需注意，避免把不同Python版本包的路径加到PYTHONPATH里，比如PYTHONPATH=/home/admin/.local/lib/python2.7/site-packages，因为PYTHONPATH中的路径是优先于默认搜索路径，如果用Python3的话会有兼容性问题。事实上PYTHONPATH里最好不要出现任何带site-packages的路径。\n简单附一个PYTHONPATH实际案例，django项目经常有人自定义一个apps目录用来放所有项目，一般都是将路径放到sys.path里。这里可以设置环境变量PYTHONPATH来解决这个问题，代码如下\nPython 1 2 3 4 BASE_DIR = Path(__file__).resolve().parent.parent.parent os.environ[\u0026#39;PYTHONPATH\u0026#39;] = os.path.join(BASE_DIR, \u0026#39;apps\u0026#39;) # sys.path.insert(0, os.path.join(BASE_DIR, \u0026#39;apps\u0026#39;)) ABS_BASE_DIR = os.path.abspath(BASE_DIR) 顺便说下PATH是用来找可执行程序的搜索路径，假如你在终端运行命令my_cmd，系统会依次扫描PATH中的路径，看my_cmd是否在于该路径下，所以如果是提示找不到程序或命令无法识别，那你就要看路径是否加到PATH里了。\nPython 是如何安装包的 现在用安装Python包基本是用的pip，就算你是用pipenv，poetry，底层依然是pip，一律适用。如果你没有安装pip，请参考pip 安装，如果安装了还无法使用pip命令请看上一节。\n运行pip有两种方式：\npip ... python -m pip ... 第一种方式和第二种方式大同小异，区别是第一种方式使用的 Python 解释器是写在 pip 文件的 shebang 里的，一般情况下，如果你的 pip 路径是$path_prefix/bin/pip，那么 Python 路径对应的就是 $path_prefix/bin/python。如果你用的是 Unix 系统则 cat $(which pip) 第一行就包含了 Python 解释器的路径。第二种方式则显式地指定了 Python 的位置。这条规则，对于所有 Python 的可执行程序都是适用的。流程如下图所示。\n那么，不加任何自定义配置时，使用 pip 安装包就会自动安装到 $path_prefix/lib/pythonX.Y/site-packages 下（$path_prefix 是从上一段里得到的），可执行程序安装到 $path_prefix/bin 下，如果需要在命令行直接使用 my_cmd 运行，记得加到 PATH。\npip 中更改安装位置的选项 --prefix PATH，替换path_pretix为给定的值 --root ROOT_PATH，在$path_prefix，前面加上ROOT_PATH，比如--root /home/admin，$path_prefix就会从/usr变成/home/admin/usr --target TARGET，直接指定安装到TARGET 虚拟环境 虚拟环境就是为了隔离不同项目的依赖包，使他们安装到不同的路径下，以防止依赖冲突的问题。理解了 Python 是如何安装包的机制之后就不难理解虚拟环境(virturalenv, venv模块)的原理。其实，运行 virtualenv myenv 会复制一个新的Python解释器到myenv/bin，并创建好myenv_lib，myenv/lib/pythonX.Y/site-packages等目录（venv模块不是用的复制，但结果基本一样）。执行source myenv/bin/activate以后会把myenv/bin塞到PATH前面，让这个复制出来的 Python 解释器最优先被搜索到。这样，后续安装包时，$path_prefix就会是myenv了，从而实现了安装路径的隔离。\n脚本运行方式对搜索路径的影响 从上面的介绍大家可以知道，Python 找不找得到一个包，最直接的原因是sys.path，而更进一步的原因是sys.executable的路径。程序写完了，我们总得需要运行它，不同的运行方法却有可能影响到sys.path而造成不同的行为，下面我们就来讨论这个问题。\n情况一 假设你的包结构如下\n代码 1 2 3 4 .py_test ├── main.py └── my_package └── b.py main.py的内容：\nPython 1 import my_package.b b.py 的文件内容很简单：\nPython 1 2 3 4 import sys print(\u0026#34;I\u0026#39;m b\u0026#34;) print(sys.path) 现在在 main.py 同级的目录下执行，可以看到正常输出path，且path路径第一个就是我们项目的当前目录py_test，也是我们的执行目录\nShell 1 2 3 $ py_test python main.py I\u0026#39;m b [\u0026#39;/Users/admin/Files/Projects/py_test\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python38.zip\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/lib-dynload\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/site-packages\u0026#39;] python xxx.py 的运行方式叫做直接运行，这时该文件中的 __name__ 值会指定为 __main__，IDE 中的「 Run File」「 运行脚本」用的就是这种方式。可以看到这时 sys.path的第一个值是该脚本文件所在的目录，随脚本路径而变化，记住我们执行测试始终是在 /home/admin/test_path 这个目录下。\n情况二 还是那个结构，我们将项目复制一份，在my_package目录里增加一个a.py文件，仅做一个简单的打印`print(\u0026ldquo;I\u0026rsquo;m a\u0026rdquo;)\n项目结构如下\n代码 1 2 3 4 5 .py_test1 ├── main.py └── my_package ├── a.py └── b.py a.py 的文件内容：\nPython 1 print(\u0026#34;I\u0026#39;m a\u0026#34;) 好，那么如果我们需要在 b.py 中导入 a.py，那么 b.py 脚本中应该怎么写呢？\nb.py 的文件内容，这里的import并没有写在最上面是为了能看到sys.path的输出：\nPython 1 2 3 4 5 6 import sys print(\u0026#34;I\u0026#39;m b\u0026#34;) print(sys.path) import a 此时，再执行一遍上面的测试 Shell 1 2 3 4 5 6 7 8 9 $ py_test1 python main.py I\u0026#39;m b [\u0026#39;/Users/admin/Files/Projects/py_test1\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python38.zip\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/lib-dynload\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/site-packages\u0026#39;] Traceback (most recent call last): File \u0026#34;main.py\u0026#34;, line 1, in \u0026lt;module\u0026gt; import my_package.b File \u0026#34;/Users/admin/Files/Projects/py_test1/my_package/b.py\u0026#34;, line 6, in \u0026lt;module\u0026gt; import a ModuleNotFoundError: No module named \u0026#39;a\u0026#39; 执行报错了，根据前面讲的内容，这个报错就是意料之中了——a.py所在的目录 /Users/admin/Files/Projects/py_test1/my_package 压根没有包含 sys.path中 ，自然就找不到 a 文件了。\n那换个思路，直接在当前main目录执行b.py是否会报错？\nShell 1 2 3 4 $ py_test1 python my_package/b.py I\u0026#39;m b [\u0026#39;/Users/admin/Files/Projects/py_test1/my_package\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python38.zip\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/lib-dynload\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/site-packages\u0026#39;] I\u0026#39;m a 结果没有报错，相应的目录位置也变了，不是main.py所在的/Users/admin/Files/Projects/py_test1目录，而是my_package目录\n情况三 还是刚刚的代码，只是把b.py的内容更新了下，将 import a 改成 from my_package import a\nPython 1 2 3 4 5 6 import sys print(\u0026#34;I\u0026#39;m b\u0026#34;) print(sys.path) from my_package import a 同样的先执行python main.py测试（我重新拷贝了项目命名为py_test2）\nShell 1 2 3 4 $ py_test2 python3 main.py I\u0026#39;m b [\u0026#39;/Users/admin/Files/Projects/py_test2\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python38.zip\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/lib-dynload\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/site-packages\u0026#39;] I\u0026#39;m a 改成 from my_package import a，执行不报错了，注意由于 b 是在 my_package 包中的，这时可以使用相对导入，写 from . import a 和 from my_package import a 的效果是一样的。\n因为基于同样的分析，我们可以推断直接执行 python my_package/b.py会报错提示找不到 my_package，运行结果如下\nShell 1 2 3 4 5 6 7 py_test2 python my_package/b.py I\u0026#39;m b [\u0026#39;/Users/admin/Files/Projects/py_test2/my_package\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python38.zip\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/lib-dynload\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/site-packages\u0026#39;] Traceback (most recent call last): File \u0026#34;my_package/b.py\u0026#34;, line 7, in \u0026lt;module\u0026gt; from my_package import a ModuleNotFoundError: No module named \u0026#39;my_package\u0026#39; 果然跟我们推理的一样，就是路径不对报错。\n情况四 那么我有没有让这两次运行都不报错的方法呢？有。我们要知道，一个项目中的入口是有限的，实际上不会出现可以执行的代码既在顶层有，又在子目录里也有。我们应该把主要的运行逻辑，都放在 main.py 中（不一定是这个名字，比如 Django 项目是 manage.py），如果这时确实需要运行一个子目录中某脚本的代码，应该用 python -m \u0026lt;module_name\u0026gt;，而 b.py 中导入 a 的语句应为 from my_package import a，我们来看一下运行效果：\nShell 1 2 3 4 5 6 7 8 $ py_test2 python main.py I\u0026#39;m b [\u0026#39;/Users/admin/Files/Projects/py_test2\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python38.zip\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/lib-dynload\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/site-packages\u0026#39;] I\u0026#39;m a $ py_test2 python -m my_package.b I\u0026#39;m b [\u0026#39;/Users/admin/Files/Projects/py_test2\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python38.zip\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/lib-dynload\u0026#39;, \u0026#39;/Users/admin/.pyenv/versions/3.8.15/lib/python3.8/site-packages\u0026#39;] I\u0026#39;m a 可以看到这两次运行的 sys.path 内容一致了，它的第一个值是当前运行所在的目录。这种运行方式叫做以模块方式运行，python -m 后面的参数是（以 . 分隔的）模块名，而不是路径名。由于这种统一性，你在项目中的所有导入都可以用相同的定义方式，而不用管是在哪个脚本中。这也是为什么 Django 官方文档中推荐导入名称全部用 myapp.models.users 这种形式。\n除此之外，以模块方式运行的时候，传入模块名的每一级父模块（或包）都会以模块形式运行，这意味着你可以在模块中使用相对导入（以直接运行方式运行不可以），并且传入的模块中__name__ 的值会置为 __main__，你依然可以应用 if __name__ == \u0026quot;__main__\u0026quot;: 的判断。如果 python -m \u0026lt;module_name\u0026gt; 中传入的模块是个包，那么会执行包目录中的 __main__.py 脚本（如果存在），此时该脚本的 __name__ 值为 __main__。\n总结 看到这里大家可以发现，关于包路径搜索最重要的就是这个$path_prefix路径前缀，而这个值又是从使用的 Python 解释器路径推导出来的。所以要找到包的路径，只需要知道解释器的路径就可以了，如果遇到改变包的路径，只需要通过正确的PATH设置，指定你想要的 Python 解释器即可。\n现在回到开头的三个问题，已经解决了。\n","date":"2024-05-22","permalink":"/posts/where-are-your-python-packages-installed/","title":"python包都安装到哪里去了"},{"categories":["tools"],"content":"git 仓库有 .DS_Store 文件排除 Bash 1 2 3 4 5 6 # remove any existing files from the repo, skipping over ones not in repo find . -name .DS_Store -print0 | xargs -0 git rm --ignore-unmatch # specify a global exclusion list git config --global core.excludesfile ~/.gitignore # adding .DS_Store to that list echo .DS_Store \u0026gt;\u0026gt; ~/.gitignore ","date":"2024-05-21","permalink":"/posts/git-ignore-ds-store/","title":"Git 全局忽略 .DS_Store"},{"categories":["mysql"],"content":"sql sql分类\nSQSQL（Structured Query Language）是用于管理和操作关系型数据库的语言。根据功能和用途，SQL语法可以分为以下几个主要分类：\n数据定义语言（DDL）：DDL用于定义和管理数据库对象，例如表、视图、索引等。常见的DDL语句包括CREATE（创建数据库对象）、ALTER（修改数据库对象）和DROP（删除数据库对象）等。 数据操作语言（DML）：DML用于对数据库中的数据进行操作，例如插入、更新和删除数据。常见的DML语句包括SELECT（查询数据）、INSERT（插入数据）、UPDATE（更新数据）和DELETE（删除数据）等。 数据查询语言（DQL）：DQL用于从数据库中查询数据。最常见的DQL语句是SELECT，它允许你指定要检索的数据、条件和排序等。 数据控制语言（DCL）：DCL用于控制数据库的访问权限和事务处理。常见的DCL语句包括GRANT（授权访问权限）、REVOKE（撤销访问权限）和COMMIT（提交事务）等。 事务控制语言（TCL）：TCL用于管理数据库中的事务。常见的TCL语句包括BEGIN（开始事务）、COMMIT（提交事务）和ROLLBACK（回滚事务）等。 这些分类涵盖了SQL语言的主要方面，每个分类都有特定的语法和用途。根据具体的需求，你可以选择适当的SQL语句来执行相应的操作。\nSQL四部分 (1)数据定义。(SQL DDL)用于定义SQL模式、基本表、视图和索引的创建和撤消操作。 (2)数据操纵。(SQL DML)数据操纵分成数据查询和数据更新两类。数据更新又分成插入、删除、和修改三种操作。 (3)数据控制。包括对基本表和视图的授权，完整性规则的描述，事务控制等内容。 (4)嵌入式SQL的使用规定。涉及到SQL语句嵌入在宿主语言程序中使用的规则。\n1.DDL(Data Definition Language)数据库定义语言statements are used to define the database structure or schema.\nDDL是SQL语言的四大功能之一。 用于定义数据库的三级结构，包括外模式、概念模式、内模式及其相互之间的映像，定义数据的完整性、安全控制等约束。 DDL不需要commit.\nCREATE ALTER DROP TRUNCATE COMMENT RENAME 2.DML(Data Manipulation Language)数据操纵语言statements are used for managing data within schema objects.\n由DBMS提供，用于让用户或程序员使用，实现对数据库中数据的操作。 DML分成交互型DML和嵌入型DML两类。 依据语言的级别，DML又可分成过程性DML和非过程性DML两种。 需要commit.\nSELECT INSERT UPDATE DELETE MERGE CALL EXPLAIN PLAN LOCK TABLE 3.DCL(Data Control Language)数据库控制语言 授权，角色控制等\nGRANT 授权 REVOKE 取消授权 4.TCL(Transaction Control Language)事务控制语言\nSAVEPOINT 设置保存点 ROLLBACK 回滚 SET TRANSACTION ","date":"2024-04-24","permalink":"/posts/sql-statement-categories/","title":"SQL 语句分类学习笔记"},{"categories":["django"],"content":"django restful 异常处理 相关链接\n标准restful异常处理 REST framework提供了异常处理，我们可以自定义异常处理函数。 Python 1 2 3 4 5 6 7 8 9 10 11 from rest_framework.views import exception_handler def custom_exception_handler(exc, context): # 先调用REST framework默认的异常处理方法获得标准错误响应对象 response = exception_handler(exc, context) # 在此处补充自定义的异常处理 if response is not None: response.data[\u0026#39;status_code\u0026#39;] = response.status_code return response 在配置文件中声明自定义的异常处理 Python 1 2 3 REST_FRAMEWORK = { \u0026#39;EXCEPTION_HANDLER\u0026#39;: \u0026#39;my_project.my_app.utils.custom_exception_handler\u0026#39; } 如果未声明，会采用默认的方式，如下 Python 1 2 3 REST_FRAMEWORK = { \u0026#39;EXCEPTION_HANDLER\u0026#39;: \u0026#39;rest_framework.views.exception_handler\u0026#39; } 数据库异常处理案例 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from rest_framework.views import exception_handler as drf_exception_handler from rest_framework import status from django.db import DatabaseError def exception_handler(exc, context): response = drf_exception_handler(exc, context) if response is None: view = context[\u0026#39;view\u0026#39;] if isinstance(exc, DatabaseError): print(\u0026#39;[%s]: %s\u0026#39; % (view, exc)) response = Response({\u0026#39;detail\u0026#39;: \u0026#39;服务器内部错误\u0026#39;}, status=status.HTTP_507_INSUFFICIENT_STORAGE) return response REST framework定义的异常 APIException 所有异常的父类 ParseError 解析错误 AuthenticationFailed 认证失败 NotAuthenticated 尚未认证 PermissionDenied 权限决绝 NotFound 未找到 MethodNotAllowed 请求方式不支持 NotAcceptable 要获取的数据格式不支持 Throttled 超过限流次数 ValidationError 校验失败\n如果是默认的Exception，就不会被restful的Exception捕获\n例如下面的\n代码 1 AttributeError: \u0026#39;AnonymousUser\u0026#39; object has no attribute \u0026#39;nickname\u0026#39; 这种属实python默认的异常，不会被Django框架捕获，这种异常需要自己处理返回内容。\n如果继承了restful的api，函数处理异常\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from rest_framework.views import APIView class UserInfoView(APIView): \u0026#34;\u0026#34;\u0026#34; 获取当前用户信息 \u0026#34;\u0026#34;\u0026#34; def get(self, request): data = { \u0026#39;id\u0026#39;: request.user.id, \u0026#39;username\u0026#39;: request.user.username, \u0026#39;nickname\u0026#39;: request.user.nickname, \u0026#39;email\u0026#39;: request.user.email, \u0026#39;is_active\u0026#39;: request.user.is_active, \u0026#39;roles\u0026#39;: [role[\u0026#34;name\u0026#34;] for role in request.user.roles.values()] } logger.info(f\u0026#39;获取用户信息成功, {request.user.username}\u0026#39;) return Response(data=data) 遇到异常就会走restful的框架的异常处理，返回json数据\n代码 1 2 3 { \u0026#34;msg\u0026#34;: \u0026#34;服务器错误:\u0026#39;AnonymousUser\u0026#39; object has no attribute \u0026#39;nickname\u0026#39;\u0026#34; } restful 的异常处理不是内部的exception，而是已知的异常优化展示。\nNote that the exception handler will only be called for responses generated by raised exceptions. It will not be used for any responses returned directly by the view, such as the HTTP_400_BAD_REQUEST responses that are returned by the generic views when serializer validation fails.\n请注意，仅对引发的异常生成的响应调用异常处理程序。它不会用于视图直接返回的任何响应，例如，当序列化程序验证失败时，泛型视图返回的 HTTP_400_BAD_REQUEST 响应。\n第三方的模块处理异常 如果第三方模块也做了异常处理展示，会被 exception_handler 包裹，只会在通用异常里\nInvalidToken({\u0026lsquo;detail\u0026rsquo;: \u0026lsquo;Given token not valid for any token type\u0026rsquo;, \u0026lsquo;messages\u0026rsquo;: [{\u0026rsquo;token_class\u0026rsquo;: \u0026lsquo;AccessToken\u0026rsquo;, \u0026rsquo;token_type\u0026rsquo;: \u0026lsquo;access\u0026rsquo;, \u0026lsquo;message\u0026rsquo;: \u0026lsquo;Token is invalid or expired\u0026rsquo;}]})\n使用 exception_handler 处理后的展示\n代码 1 2 3 4 { \u0026#34;code\u0026#34;: 400001, \u0026#34;msg\u0026#34;: \u0026#34;服务器错误:{\u0026#39;detail\u0026#39;: ErrorDetail(string=\u0026#39;Given token not valid for any token type\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;code\u0026#39;: ErrorDetail(string=\u0026#39;token_not_valid\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;messages\u0026#39;: [{\u0026#39;token_class\u0026#39;: ErrorDetail(string=\u0026#39;AccessToken\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;token_type\u0026#39;: ErrorDetail(string=\u0026#39;access\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;message\u0026#39;: ErrorDetail(string=\u0026#39;Token is invalid or expired\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;)}]}\u0026#34; } 走过 CustomRenderer\n经过 EXCEPTION_HANDLER 后还会经过 DEFAULT_RENDERER_CLASSES\n这个是有点意外的，那就在 DEFAULT_RENDERER_CLASSES 通配处理渲染\nPython 1 2 3 def render(self, data, accepted_media_type=None, renderer_context=None): print(\u0026#39;renderer_context =\u0026gt; \u0026#39;, renderer_context) # response = { 哪里调用了这个render\n这里的data\nPython 1 {\u0026#39;msg\u0026#39;: \u0026#34;服务器错误:{\u0026#39;detail\u0026#39;: ErrorDetail(string=\u0026#39;Given token not valid for any token type\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;code\u0026#39;: ErrorDetail(string=\u0026#39;token_not_valid\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;messages\u0026#39;: [{\u0026#39;token_class\u0026#39;: ErrorDetail(string=\u0026#39;AccessToken\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;token_type\u0026#39;: ErrorDetail(string=\u0026#39;access\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;message\u0026#39;: ErrorDetail(string=\u0026#39;Token is invalid or expired\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;)}]}\u0026#34;, \u0026#39;data\u0026#39;: {\u0026#39;detail\u0026#39;: ErrorDetail(string=\u0026#39;Given token not valid for any token type\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;code\u0026#39;: ErrorDetail(string=\u0026#39;token_not_valid\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;messages\u0026#39;: [{\u0026#39;token_class\u0026#39;: ErrorDetail(string=\u0026#39;AccessToken\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;token_type\u0026#39;: ErrorDetail(string=\u0026#39;access\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;), \u0026#39;message\u0026#39;: ErrorDetail(string=\u0026#39;Token is invalid or expired\u0026#39;, code=\u0026#39;token_not_valid\u0026#39;)}]}} 从里面没有太哦\n","date":"2024-04-19","permalink":"/posts/drf-exception-handler-notes/","title":"DRF 异常处理与自定义渲染笔记"},{"categories":["Linux"],"content":"vmware 给已有机器扩容 1 vmware 虚机增加磁盘空间 修改完成后，会提示硬盘已准备。需要重启虚拟机。\n在 VMware 的虚拟机设置中扩大已有虚拟磁盘容量，本例将磁盘从 60 GiB 扩到 80 GiB。\n1.1 查看重启后机器的磁盘是否有变化 下面的图是磁盘更新前跟磁盘更新后的 lsblk 命令的结果\nBash 1 lsblk 本例扩容前 /dev/sda 为 60G，扩容后为 80G；已有 /dev/sda3 分区仍为 44G，需要继续更新分区。\n2 更新磁盘分区 2.1 查看磁盘分区 Bash 1 fdisk -l /dev/sda 记录需要扩容的 /dev/sda3 分区起始扇区，重新创建时必须保留这个起点。\n2.2 修改磁盘分区 本例需要扩容的是 LVM 物理卷所在的 /dev/sda3 分区，简化为以下步骤：\n删除根分区，但是不要写入磁盘（即输入 w） 创建新的分区，新的分区的起点必须是删除的根分区的起点（默认就会是一样的，但是要非常注意） 执行 fdisk /dev/sda，用 p 查看分区表、d 删除目标分区、n 重新建立同编号分区；保持原起始扇区，把结束扇区扩至磁盘末尾。确认起点和分区类型后再写入。 写入磁盘（即输入 w） 本例重新创建后，/dev/sda3 从 44 GiB 扩到 64 GiB。其他分区保持原位置和大小。\n2.3 刷新一下分区信息 分区操作完成，刷新一下分区信息并再次查看分区信息\nBash 1 partprobe /dev/sda Bash 1 2 lsblk pvdisplay 刷新后分区显示为 64G，但 LVM 物理卷大小可能仍是 44G。\n3 调整LVM中物理卷 可以看到 pv的 /dev/sda3还是 44G 没有更新完成 需要调整LVM中物理卷的容量大小，执行命令\nBash 1 pvresize /dev/sda3 Bash 1 pvdisplay 确认 /dev/sda3 对应的物理卷大小已经更新为 64 GiB。\n可以看到 /dev/sda3 的pv 大小已经更新为 64G\n4 扩展逻辑卷 查看逻辑卷大小\nBash 1 lvdisplay 执行命令扩展逻辑卷，并再次查看\nBash 1 lvextend -l +100%FREE /dev/centos/root Bash 1 lvdisplay 逻辑卷 /dev/centos/root 扩容后，再确认其容量已经增长。\n5 文件系统扩容 df查看磁盘大小，还是没有更新，需要对文件系统扩容，默认文件系统为xfs，所以使用 xfs_growfs 命令\nBash 1 xfs_growfs /dev/centos/root Bash 1 df -Th / 最后查看磁盘大小已经更新为80G\n","date":"2024-04-15","permalink":"/posts/vmware-lvm-disk-extension/","title":"VMware 虚拟机扩容与 LVM 文件系统调整"},{"categories":["tools"],"content":"如何信任自签名证书 打开不受信任的页面\n点击地址栏左边的不安全按钮，在弹出的窗口中，查看详细信息\n在新的页面中，点击证书小图标\n在新出现的对话框中，进入详细信息页签，选择导出，保存到本地文件\n打开新下载的证书文件，macOS会要求输入电脑密码，然后将证书加入到【钥匙串访问】中\n在【钥匙串访问】应用中，在【系统钥匙串】搜索刚加入的证书名称。\n双击证书，然后展开信任，选择始终信任\n关闭证书的对话框，这时候会要求输入密码\n关闭浏览器（chrome必须关，safari可不关），然后重新打开页面\n","date":"2024-04-15","permalink":"/posts/macos-trust-self-signed-certificate/","title":"macOS 信任自签名证书的操作记录"},{"categories":["Linux"],"content":"函数作为命令行命令 配置函数脚本 Bash 1 2 3 4 5 6 7 8 9 10 11 12 13 # $ cat ~/.bashrc # .bashrc # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi # Uncomment the following line if you don\u0026#39;t like systemctl\u0026#39;s auto-paging feature: # export SYSTEMD_PAGER= # User specific aliases and functions . ~/.utils/toolsfunc.sh 然后把脚本放到对应目录就可以执行了\n","date":"2024-04-03","permalink":"/posts/bash-source-function-command/","title":"将 Bash 函数作为命令使用"},{"categories":["Linux"],"content":" 老生常谈的if判断字符串加x的问题\n代码 1 2 3 4 5 6 7 8 9 10 if [ \u0026#34;$3\u0026#34;x = \u0026#34;r\u0026#34;x ] string=\u0026#39;My string\u0026#39;; if [ $string = \u0026#34;My\u0026#34; ]; then echo \u0026#34;It\u0026#39;s there!\u0026#34;; else echo \u0026#34;xxxx\u0026#34;; fi 主要是双括号与单括号的情况区分\n双括号的情况下是不需要用x来替代的\n单括号的情况下，不加x，变量为空时，会报错\n执行报错结果 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 [root@iZf8z68utftnb7w6jdl1f0Z ~]# cat test.sh string=\u0026#39;\u0026#39;; if [ $string = \u0026#34;My\u0026#34; ]; then echo \u0026#34;It\u0026#39;s there!\u0026#34;; else echo \u0026#34;xxxx\u0026#34;; fi [root@iZf8z68utftnb7w6jdl1f0Z ~]# bash test.sh test.sh: line 4: [: =: unary operator expected xxxx [root@iZf8z6 ","date":"2024-04-02","permalink":"/posts/bash-if-empty-string-notes/","title":"Bash if 判断空字符串笔记"},{"categories":["Kubenertes"],"content":"pod内多个容器启动问题 问题描述 YAML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 apiVersion: v1 kind: Pod metadata: name: mc3 labels: app: mc3 spec: containers: - name: webapp image: training/webapp - name: nginx image: nginx:alpine ports: - containerPort: 80 volumeMounts: - name: nginx-proxy-config mountPath: /etc/nginx/nginx.conf subPath: nginx.conf volumes: - name: nginx-proxy-config configMap: name: mc3-nginx-conf 如上的pod容器启动，在一个pod内有多个容器的启动顺序问题，这种算不算 sidecar 模式\n找到一个解释\nIn what order containers are being started in a Pod?\nCurrently, all containers in a Pod are being started in parallel and there is no way to define that one container must be started after other container (however, there are Kubernetes Init Containers). Therefore, in your IPC example there is a chance that the second container starts before the first one. In this case, the second container will fail, because in the consumer mode it expects that the message queue exists. To fix this issue we, for example, can change the application to wait for the message queue to be created.\n翻译(微软翻译)：目前，Pod 中的所有容器都是并行启动的，并且无法定义一个容器必须在另一个容器之后启动（但是，有 Kubernetes 初始化容器）。因此，在 IPC 示例中，第二个容器有可能在第一个容器之前启动。在这种情况下，第二个容器将失败，因为在 consumer 该模式下，它期望消息队列存在。例如，为了解决此问题，我们可以更改应用程序以等待创建消息队列。\n原文链接\n解决方案1 具体的版本不清楚了，但这种方式用的人不多。或者用的是initContainers\nYAML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app.kubernetes.io/name: MyApp spec: containers: - name: myapp-container image: busybox:1.28 command: [\u0026#39;sh\u0026#39;, \u0026#39;-c\u0026#39;, \u0026#39;echo The app is running! \u0026amp;\u0026amp; sleep 3600\u0026#39;] initContainers: - name: init-myservice image: busybox:1.28 command: [\u0026#39;sh\u0026#39;, \u0026#39;-c\u0026#39;, \u0026#34;until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done\u0026#34;] - name: init-mydb image: busybox:1.28 command: [\u0026#39;sh\u0026#39;, \u0026#39;-c\u0026#39;, \u0026#34;until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done\u0026#34;] 案例来自官网\n","date":"2024-03-13","permalink":"/posts/pod-container-startup-order-notes/","title":"Pod 多容器启动顺序与 Init Container 笔记"},{"categories":["tools"],"content":"配置用户名，颜色 代码 1 2 3 git config --global user.name \u0026#34;you_name\u0026#34; # 设置全局用户名 git config --global user.email \u0026#34;user@example.com\u0026#34; # 设置全局邮箱 git config --global color.ui true # 设置全局颜色显示 git 相关配置 https://zhuanlan.zhihu.com/p/133706032\n配置全局 Bash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # git config --global alias.confg \u0026#39;config --global\u0026#39; # 目前看似乎没有生效 git config --global alias.st status git config --global alias.co checkout git config --global alias.ci commit git config --global alias.br branch git config --global alias.unstage \u0026#39;reset HEAD\u0026#39; git config --global alias.last \u0026#39;log -1\u0026#39; git config --global alias.lg \u0026#34;log --color --graph --pretty=format:\u0026#39;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)\u0026lt;%an\u0026gt;%Creset\u0026#39; --abbrev-commit\u0026#34; git config --global core.quotepath false # vscode git status/commit 乱码 # 全局忽略 .DS_Store 文件 # specify a global exclusion list git config --global core.excludesfile ~/.gitignore # adding .DS_Store to that list echo .DS_Store \u0026gt;\u0026gt; ~/.gitignore 配置针对当前仓库 Bash 1 2 git config alias.st status git config alias.co checkout 生成ssh-key generate Bash 1 ssh-keygen -t rsa -C \u0026#39;company name\u0026#39; 本地配置 代码 1 2 git config --local user.name \u0026#34;cxx\u0026#34; git config --local user.email \u0026#34;sxx\u0026#34; ","date":"2024-02-21","permalink":"/posts/git-dotfiles-setup/","title":"Git 本地与全局配置笔记"},{"categories":["think"],"content":"原文链接:https://36kr.com/p/1440244158946564\n学校的学习，使很多人留下了一个后遗症——误把「理解」当成了「掌握」。那会，我们掌握某个新知识点的标志是“能看懂”，也就是理解了。\n确实有点意外，我好像确实有这种想法，认为看懂一个内容就是会了，只有多次练习后才真正的掌握。\n我学习 django 框架的时候，练习了几十遍，其实最后还是跟着项目组一起写了自动化平台项目，才勉强算掌握了这个技能，现在才能自己独立搭建平台。 双拼音也是，开始特别难。我刻意花了两周的时间背诵声母韵母表，去年 8 月到现在 2 月，大概 5 个月的时间，勉强打字连贯，但速度（准确度）还是不太满意。 很多概念，python 的 super，class 的 type object 等等，其实这个概念我理解了太多次，但并没有真正的掌握。当初记住的概念，都只是那一瞬的。 当我放下这个理解，在过一段时间捡起来的时候并没有比之前理解更快。对相关的技术套路是有\u0026quot;感觉\u0026quot;了，更多的知识(套路)其实完全忘记了。回忆这类知识的时候，仿佛第一次接触这个知识点。\n相信大多人用微信读书的人都看过那本\u0026lt;认知觉醒\u0026gt;，书里讲了一些人脑的进化，似乎大家读完了解后就能知道自己大脑在干嘛，以后就能控制大脑。 然后升职加薪，担任总经理，出任CEO，迎娶白富美，走向人生巅峰。事情不是这样的，99%的人看完书后，可能还是立马去打了一把游戏。 该加班加班，该玩玩。并没有说学会了这个知识，思维，就能立马改变。\n上学的时候我们就知道，老师讲的知识需要去做课后练习，多次重复后，你可能对这个知识点比较通透，或者你能看到类似的题知道公式套路，知道该怎么做。所以，成年后我们就会了一种超能力，看完书就能融会贯通，改造自己。不可能的，甚至我们现在的毅力不如当初在学校里那个年轻的自己。\n该多读几遍就不要偷懒，该练习的时候就行动起来。\n好记性不如烂笔头 还有随着年龄的增长，记忆力会渐渐变差，更要好记性不如烂笔头。csdn，博客园，掘金等等平台的潮起潮落，还是要搞一个自己的发言地，看看自己啰嗦的内容。\n原文: 李少加 2021-10-14 12:51\n大脑厌恶改变——以保护你的名义\n生活中，我们常常发现，戒掉某个坏毛病，或做了新的改进，或学习了新知识，一段时间后，又统统归零——如果你对此愧疚，大可不必，家贼难防，是大脑“背叛”了你。\n无论是从认知心理学，还是从系统科学（把大脑神经网络视作系统）来看，我们通常说的“进步”（有效学习），意味着大脑系统的“改变”——而对大脑这一复杂精密的系统而言，改变，意味着风险，甚至是危险。\n尤其是成年人，大脑神经网络的“主干道”已成型，更不愿意改变，更固执。\n「与时俱进」、「终身学习」是人类杜撰的定义，但“脑细胞们”可不来这一套。就好比我们不能“命令”心脏每分钟跳多少下，我们也无法要求大脑跟着主观愿望瞎放电。\n事实上，跟大脑的主人——也就是“你”的欲望不同——大脑更倾向于“维稳”，这更符合大脑利益。\n大脑服务于“存活”，而非你的主观愿望——你认为有益的事，大脑未必认，反之亦然。\n我们憎恨自己循规蹈矩，缺乏创见，但在人类历史长河中做个统计，循规蹈矩在绝大多数时候，存活率远高于想法独到的人——大脑不蠢，它比你更懂“历史”，更懂“概率”（以基因淘汰的残忍方式）。\n简而言之，成年人的大脑会竭尽所能抑制改变——以保护你的名义。\n这就是为什么，市面上很多“成功学大师”教人各种学习“秘术”，用力过猛，只会激发大脑产生强烈的应急反应——拼命抵制。\n是不是很熟悉？跟我们身体的免疫反应对抗急性病一样。\n然而，「慢性病」却能完美避开抵制，慢慢渗透、累积、进而彻底改变机体——是不是有点感觉了？\n没错——正反派对调，把「稳定的大脑」视作需要被改变的目标，把「慢性病」改造成一种学习策略，那么它必定擅长「改变目标」。\n换句话说，把「慢性病逻辑」迁移武装到学习方法上——会是一把利刃。\n咱重温下“慢性病逻辑”的三个关键特征：\n后果累积不可逆 身心侵蚀神不知鬼不觉 多因多果不可控 把它“转化”为学习思想，对应的“翻译”是：\n学的慢，学的深刻 没意识到是在学习的学习 随机、跨界的学习 对照下主流学习观点，是不是发现，几乎是逆其道而行之？\n比方说，学习要慢。现在不是都讲究高效，什么都要快么？又比如，没意识到在学习。不是都提倡学习要针对、要刻意么？\n下面，我从认知神经科学、系统论控制论的视角展开来谈。\n「慢性病逻辑」衍生的第一个思想：学的慢，学的深刻 这里的「慢」，并不是说，明明可以一句话一句话的读，偏要改成一个字一个字的读，故意拖慢。「慢」指的是，不要为了求快，而跳过学习的「必要过程」。\n学校的学习，使很多人留下了一个后遗症——误把「理解」当成了「掌握」。那会，我们掌握某个新知识点的标志是“能看懂”，也就是理解了。\n但学校教材有个特点，知识之间前后贯通，后面的知识需要用到前面所学。\n除法运算需要懂乘法口诀，求体积需要懂面积——这无形中就产生了大量被动练习，更别提为了应付考试所做的主动练习，这些环节被我们遗忘、忽视了。\n于是，成年后的我们，通过阅读自学时，误以为「读懂」就是「会了」，然后，过，下一本。\n这就是为什么，读了很多书，隔一阵，好像还是什么也不会。\n真正意义的“学会“，也就是「内化」，至少经历了如下阶段：\n理解（即涉及的背景知识、推导都掌握） 练习或记忆（即能把理论与现实案例做关联） 情境迁移应用（能把知识抽象并跨域应用） “不用想”的思考（初步内化） 然而，成年人阅读时，多少人止步于「理解」？\n这种程度的学习当然无法契合“慢性病逻辑”的第一个特征：效果累积不可逆，学了没多久就遗忘，效果怎么累积？既无法积累，又如何动摇大脑强悍的“维稳”能力？\n所以，我强调「学的慢」，就是建议，每当遇到新知，别读懂了就过。\n应该在此基础上，多关联现实，或解释现象，或做出预测。联系你鲜活的生活、灵动富有情感的思绪，反思有哪些情境、阅历、活动是与该知识相关。\n记住，新知识一定要建立在牢靠的地基之上，否则就变成了无根之木，如何长久？\n还有一点很重要，千万别高估我们的记忆力。尤其是对于极具启发性的知识，务必隔个一年半载“温故而知新”。\n这是“慢性病逻辑”迁移到认知学习的第一个思想——学的慢，学的深刻。如此，才能实现“效果累积不可逆”。\n「慢性病逻辑」衍生的第二个思想——没意识到在学习的学习 前面说了，成年人的大脑天性倾向于保持稳定（而不是改变）。而学习的结果，却是改变大脑。\n成年人学习，功利导向，但你想学的，却未必是大脑当下愿意接纳的。\n从大脑立场看，你强迫它学习（改变），动机如此直白，就好像小偷朝人家喊“喂，我凌晨要光顾你家，记得留个门。”你说大脑会不会拼死反抗？\n这造成什么结果？\n就是很多人学习实用技能时，往往只能学到“拙劣模仿”的层次，一旦换个情境，就用不好，或不会用，甚至都没想到。\n这种“拙劣模仿”其实就是没有触动大脑的实质改变，是用你以前的旧技能生硬模拟新的技能。\n就好比很多人学游泳，只会“狗爬式”，这实际是婴儿时的爬行技能，而游泳的入门技（水中换气），却一窍不通。\n我们学新东西，无法为所欲为。\n许多知识，需要大量的前置条件。\n缺乏必要的前置知识，学习新事物时，很抱歉，你连“新”在哪里都看不到。\n我们常常以为，不同人看同样的东西，画面是一样的。科学早已实证否定了这一直觉。事实上，对于同一事物，每个人看到的略有不同。\n正如下经典测试图所示，你看到的是老妇人还是年轻女子？\n事实是，我们每个人看到的画面，不过是真实世界的某一小侧面。\n由于五官接收到的真实世界的信息量，远超大脑的处理能力，为了降低负荷，大脑采取了「简化」，即用“已懂的概念”去压缩外部信息。\n比如，我们懂得「鸟」的概念后，在公园里就不会细细「看」不同的鸟了。\n实际上，每个人都是以自身的专业、熟悉的概念去看待同样的事物。\n比如，同样一颗参天古树。\n物理学者看到的是力学结构（树干），数学家看到的是分形几何（叶子），搞历史的人会寻求朝代痕迹（细节），诗人看到的是沧海桑田（整体），而消费主义者们看到的是朋友圈素材。\n用控制论的话语来说，大脑的输入取决于“大脑已有的累积”。内行看门道，外行看热闹，说的就是这个理。\n在目标学习领域积累不够，那你学习时输入的多是无足轻重的噪音，就是通常说的“抓不住要点”，属无效输入，自然不会引起大脑内部系统的优化更新。\n我们越是感觉需要“刻意”去学的知识，往往是大脑积累不够的领域（不然就不会感觉到“刻意”）。\n那对于目标知识，如何甄别“大脑准备好了”？\n这正是「慢性病逻辑」的衍生思想——“没意识到在学习的学习”的高明之处，当大脑准备好时，它会激活你的欲望——你极欲获得某些问题的答案，因而主动、自发的闯入埋藏答案的新世界，探索其中。\n实际上我们年幼时都频繁体验过。只是成年后，过度的功利心态，应接不暇的物质欲望，浅薄娱乐……轮番撩拨你的欲望、削弱你的心智，并最终扼杀了那些“需要一点付出”才能获得的求知乐趣。\n简而言之，那些能够让你「内化」的有效学习，实际上非常主观，非常个人化，它只局限在有限的领域——当你接近这些领域时，会有种“答案近在眼前”，顿悟马上破蛹而出的兴奋感，类似解字谜、找规律，问题本身就是乐趣，诱惑着你，魂牵梦萦。\n你需要一点耐心、专注才能激发“独属于你”的深度好奇心。\n这种探索，看似不像学习，却是真正意义上的学习。它既能优化大脑神经网络优化，又不会引起抵触反应。\n随机、跨界的广义学习 第三个思想，即「慢性病逻辑」的多因多果不可控，对应的学习思想是：随机、跨界、广义的学习。\n大脑相当谨慎，它在骨子里就以「科学模型」认知新事物，即，不仅讲究实证，更讲究「多元实证」。如此，才会愿意接纳新知识、新观点。\n比如，相亲时，相亲对象的父母、三姑六婆、兄弟姐妹，都说这姑娘靠谱，可信度如何？不高。因为这些人都是姑娘家的人，“信源”雷同。\n但如果恰好你同事、你校友，或者小区门卫也认识这姑娘，评价都很好，那可信度就高了。\n这叫“多元实证”。当然，这还不算真正的“多元”，毕竟评价的都是“人类”。\n再以“大陆漂移说”为例。\n作为一个潜在推翻当时地理科学的理论，最初的提出者阿尔弗雷德·魏格纳（Alfred Lothar Wegener）从生物考古学、古气象学等维度提出证据，为当时的主流科学界所反对。\n直到30年后，陆续从地震地质学、古地磁研究、天体物理学等不同的学科获得依据，科学界才重新接纳了新大陆漂移说。\n这就是“多元化信源”的强大说服力，一个“信源”有可能会出错，但如果是多个不同维度的“信源”，同时出错的概率就微乎其微了。\n所以，你想要实质改变大脑（有效学习），那么，得用多元证据说服它。\n哪里来的多元？无他，学识别仅限于单一专业，视野要宽广，如此而已。\n同样，我这篇专题，如果仅仅是从“慢性病-瓦解身体”，就类比为“慢性病逻辑学习-改变大脑”，未免武断。\n但我撰写专题前，研究了认知心理学、控制论、系统论、以及大量日常观察（经验），均得出类似结论。由此，我才认为这个独创观点是为可靠。\n所以，如果你想要取得「实质进步」，切忌只盯着眼前那一亩三分的狭隘专业，而应该广泛涉猎不同领域的知识。\n你擅长数理逻辑语言，那么也该了解艺术人文语言，反之亦然。\n理由很简单——宽泛多元的“信源”，往往能起到“交叉验证”的效果，这能有力“说服固执的大脑”——让它乐于接受改变，积极拥抱正向的变化。\n结束专题前，重申一遍「慢性病学习思想」：\n慢才是快——「理解」不是结束，而是开始 没意识到在学习的学习——给思维多点留白，让求知欲引导你 跨界、随机、广义的学习——多元交叉验证，辅助大脑吸纳新知 有效学习的实质是「改变」。关于人类多么憎恨改变，哲学家托马斯·布朗（Thomas Browne）曾诗意的描述，“活着这一长期的习惯使我们不愿死亡”。\n每当你抱怨学习容易但进步很难时，千万别忘了，这是大脑对你的本能保护。\n设想下，如果改变很容易发生，那是多么可怕的一件事：你性情每天在变，价值观每天在变，记忆渐渐变得不牢靠，生命中任何亲密的关系将不复存在，直至分不清想象与真实……\n对「改变」保持敬畏，对「进步」保持平常心。\n一旦失去平常心，开始激进，幻想着超越生物规则时，也就无法体悟「慢性病思想」的益处了。\n","date":"2024-01-31","permalink":"/posts/how-to-learn-knowledge-without-forgetting-it/","title":"知识学习"},{"categories":["think"],"content":"原文：Why time seems to pass faster as we age 作者：Paras Chopra 翻译原文：https://baoyu.io/translations/life/why-time-seems-to-pass-faster-as-we-age#google_vignette\n1/ 这种感觉让我非常着迷。\n作为一个 36 岁的人，我感觉一年的时间比我还是个孩子或青少年时要短得多。\n这似乎是宇宙间的不公——我们的寿命更短了，而每一年过得也更快了。\n2/ 但为什么会这样呢？\n我初步认为，这是进化如何塑造我们的大脑成为一个高效存储装置的不幸副作用。\n3/ 我们的大脑本质上是一个预测机器。\n它的主要任务是构建一个世界模型，以此来获得生存和繁衍的优势。\n4/ 能预测现象意味着能控制它，从而拥有力量。因此，我们的大脑痴迷于预测事物的走向。\n它渴望能预见如何找到伴侣、赚钱的方式、什么能让人发笑等等。\n5/ 同时，它追求效率。\n如果某件事已经发生过，再次关注它并记住它有何意义呢？\n重复存储是低效的，因此大脑倾向于只关注并记忆那些新奇和令人惊讶的事物。\n6/ 对孩子来说，一切都是新奇和令人惊讶的。\n世界是一个充满学习机会的地方，因此大脑在记忆方面进行大量更新。\n你生日、假期、学校日子的完整记录，都被铭记。\n7/ 每天都有新奇的信息涌现，因此大脑极为关注，你就会感觉一天中有很多“时间片段”。\n这些丰富的信息被存储起来，使得即使是回顾，那些日子也似乎更加漫长。\n8/ 随着年龄的增长，新奇的事物仅仅成为旧记忆上的一小片“补丁”。\n为什么要存储你第 N 次假期的完整细节，而不是简单地记录下它与第一次的不同之处呢？\n9/ 换句话说，随着我们年龄的增长，我们的记忆和注意力成了它们过去的低保真版本。\n随着生活中的模式开始自我重复，我们注意到并记住的“时间片段”变得越来越少，越来越粗糙。\n10/ 因此，如果有人问你生命中的时间都去哪儿了，你回想起来，大部分记忆都与童年有关，近期的记忆却寥寥无几。\n这就是为什么时间感觉像是积累在了过去，而不是近现在。\n11/ 让你感觉时间加速的主要罪魁祸首是可预测性。\n你的日子越可预测，你就越会觉得它们短暂。\n12/ 来做个思想实验。\n如果你有一份稳定的工作，你可以在脑海中进行一整年的时间旅行，发现每一天都差不多。\n但如果我让你想象在一个外国大学攻读梵语博士学位，你将无法想象你的日子会是什么样子。\n13/ 因此，可预测性不仅影响我们对当前时间的感知，也影响对未来时间的预期。\n在童年时，假期因为充满了新奇信息，因此它让人觉得充实而漫长。\n现在，你第 N 次去某个地方旅行就感觉短多了，因为你知道将会发生什么。\n14/ 那么，我们该如何行动，以减缓时间的流逝呢？\n我能想到的唯一方法是打破日常的可预测性，主动寻找（大量的）惊喜。\n参与那些你完全不了解的项目。\n15/ 不幸的是，随着年龄的增长，进化让我们更倾向于避免探索和冒险。\n我们的大脑倾向于更多地利用我们已经较好理解的世界，而不是鼓励我们去探索更多。\n但这正是让你的岁月匆匆流逝的原因。\n16/ 你需要问自己：\n你想如何度过一生？\n是简单地活得久，还是让每一刻都活得充实和精彩？\n对自己来说，哪种生活更为重要。\n17/ 有趣的是，让时间放慢的解决方案不是无聊（正如我曾以为的）。\n无聊是一种消极状态。解决方法是勇敢地投身于未知的领域。\n也就是说，\n要么是身体上的旅行，体验不同的文化和环境，做一些从未尝试过的活动；\n要么是心灵上的旅行，阅读、学习新技能、挑战自我。\n18/ 注意，我们非常擅长掌握模式/建立预测模型。\n一旦我们弄清楚了游戏的获胜条件或故事情节，我们就会兴趣全无。\n19/ 因此，存在的危机实际上是对生活的一种预警。\n拥有所有预测模型的大脑会问：这就是生活的全部吗？\n但它错了——这只是它选择的生活的全部。\n20/ 它无法预测的（彻底）不同的生活会让大脑保持警觉。\n这里的关键词是“彻底”。\n变化越小，记忆留存的时间就越短。\n","date":"2024-01-31","permalink":"/posts/why-time-seems-to-pass-faster-as-we-age/","title":"为什么随着年龄增长，我们感觉时间似乎流逝得更快 [译][转]"},{"categories":["think"],"content":"2023年回顾\n你今年做了哪些之前从未做过的事？答: 去看了郭德纲相声 你有没有遵守年初时和自己许下的约定？答: 没有 你身边有人生孩子了吗？答: 有朋友生娃了 你身边有人去世了吗？答: 去年外婆走了。 你去了哪些城市/州/国家？答: 去了绍兴玩 明年你想要获得哪些你今年没有的东西？答: 不知道 今年的哪个或哪些日子会铭刻在你的记忆中，为什么？答: 老婆的生日吧，这次过的不一样 你今年最大的成就是什么？答: 你今年最大的失败是什么？答: 你今年还遇到过哪些困难？答: 工作不被认可 你今年是否生过病或受过伤？答: 嘴唇疱疹次数变多了 你今年买过的最好的东西是什么？答: 戴森，别的好像基本没买 谁的行为值得去表扬？答: 谁的行为令你感到震惊？答: 同事的发火 你大部分的钱都花到哪里去了？答: 吃，房贷 有什么事让你感到超级、超级、超级兴奋？答: 遇到志同道合，被认可 哪首歌会永远让你想起这一年？答: 17年是陈奕迅的约定 与去年的这个时候相比，你是：感到更快乐还是更悲伤了？变得更瘦还是更胖了？变得更富还是更穷了？答: 变更悲伤了 你希望自己能做得更多的是什么？答: 你希望自己能做得更少的是什么？答: 你是如何度过节假日的？答: 你今年坠入爱河了吗？答: 是的 你是否有讨厌某个你去年此时不觉得讨厌的人呢？答: 答: 你最喜欢的电视节目是什么？答: 你读过最好的一本书是什么？答: 陈皓的书，23年尾，24年初读完的 你今年发现的最好听的一首歌是什么？答: 你今年看过最喜欢的一部电影是什么？答: 你今年吃过最好吃的一顿饭是什么？答: 有什么是你想要且得到了的？答: 有什么是你想要却没有得到的？答: 你生日那天做了什么？答: 没印象了 有什么还未发生的事，如果发生了，会让你的这一年变得无比满足?答: 一份被认可的工作 你会如何描述你今年的个人时尚风格？答: 是什么让你保持理智？答: 你最欣赏哪个名人/公众人物？答: 哪个政治问题最令你有感而发？答: 你想念哪些人？答: 外婆 在你新认识的人之中，谁是最好的？答: 前同事 今年你学到了什么宝贵的人生经验？答: 好像有好多，但暂时说不上来 能够总结你这一年的一句话是什么？答: 没有认清自己 ","date":"2024-01-31","permalink":"/posts/40-%E9%97%AE/","title":"40 问"},{"categories":["tools"],"content":"https://docs.jumpserver.org/zh/v2/install/setup_by_fast/\nhttps://docs.jumpserver.org/zh/v2/dev/build/#_3\ncd /opt mkdir /opt/jumpserver-v2.28.8 wget -O /opt/jumpserver-v2.28.8.tar.gz https://github.com/jumpserver/jumpserver/archive/refs/tags/v2.28.8.tar.gz tar -xf jumpserver-v2.28.8.tar.gz -C /opt/jumpserver-v2.28.8 \u0026ndash;strip-components 1 cd jumpserver-v2.28.8 rm -f apps/common/utils/ip/geoip/GeoLite2-City.mmdb apps/common/utils/ip/ipip/ipipfree.ipdb wget https://download.jumpserver.org/files/ip/GeoLite2-City.mmdb -O apps/common/utils/ip/geoip/GeoLite2-City.mmdb wget https://download.jumpserver.org/files/ip/ipipfree.ipdb -O apps/common/utils/ip/ipip/ipipfree.ipdb\n","date":"2024-01-25","permalink":"/posts/jumpserver-manual-download-notes/","title":"JumpServer 手动下载与解压笔记"},{"categories":["reading"],"content":"关于作者陈浩 大概是我刚入行的时候，在通过各种百度搜索资料的时候，发现了他的网站-个人博客，当时还不知道这是怎样的一个人，后来在极客时间看到他出专栏了，才知道原来是大佬。\n再后来就是看到他很多关于技术思想的文章，开始\u0026quot;追星\u0026quot;。甚至在推特上也在关注他的发言，看看是怎样的一个人，想模仿他。但意外来临的太快，甚至我都没有参加过他的大会，他就离开了。\n当初学习极客时间学的太粗糙，很多都没理解，主要原因是我知识有限。终于他的书出来了，我知道有不少部分跟他专栏重合，但这样就等于我再复习一遍，有何不可。顺便记录点文字，待自己以后翻阅。\n收获 治愈焦虑 不在迷茫学什么，知道还有哪些运维的事情可以做 我的思考 \u0026hellip;\n","date":"2024-01-23","permalink":"/posts/zuoertingfeng-reading-thought/","title":"左耳听风读后感"},{"categories":["Linux"],"content":"更新策略\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 PUT _ilm/policy/hot-warm-cold-delete-30days { \u0026#34;policy\u0026#34;: { \u0026#34;phases\u0026#34;: { \u0026#34;hot\u0026#34;: { \u0026#34;min_age\u0026#34;: \u0026#34;0ms\u0026#34;, \u0026#34;actions\u0026#34;: { \u0026#34;rollover\u0026#34;: { \u0026#34;max_size\u0026#34;: \u0026#34;5gb\u0026#34;, \u0026#34;max_age\u0026#34;: \u0026#34;30d\u0026#34; }, \u0026#34;set_priority\u0026#34;: { \u0026#34;priority\u0026#34;: 50 } } }, \u0026#34;warm\u0026#34;: { \u0026#34;min_age\u0026#34;: \u0026#34;7d\u0026#34;, \u0026#34;actions\u0026#34;: { \u0026#34;shrink\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 1 }, \u0026#34;forcemerge\u0026#34;: { \u0026#34;max_num_segments\u0026#34;: 1 }, \u0026#34;set_priority\u0026#34;: { \u0026#34;priority\u0026#34;: 25 } } }, \u0026#34;cold\u0026#34;: { \u0026#34;min_age\u0026#34;: \u0026#34;14d\u0026#34;, \u0026#34;actions\u0026#34;: { \u0026#34;freeze\u0026#34;: {}, \u0026#34;set_priority\u0026#34;: { \u0026#34;priority\u0026#34;: 0 } } }, \u0026#34;delete\u0026#34;: { \u0026#34;min_age\u0026#34;: \u0026#34;30d\u0026#34;, \u0026#34;actions\u0026#34;: { \u0026#34;delete\u0026#34;: { \u0026#34;delete_searchable_snapshot\u0026#34;: true } } } } } } 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 PUT _ilm/policy/hot-warm-cold-delete-30days { \u0026#34;policy\u0026#34;: { \u0026#34;phases\u0026#34;: { \u0026#34;hot\u0026#34;: { \u0026#34;actions\u0026#34;: { \u0026#34;rollover\u0026#34;: { \u0026#34;max_size\u0026#34;:\u0026#34;5gb\u0026#34;, \u0026#34;max_age\u0026#34;:\u0026#34;30d\u0026#34; }, \u0026#34;set_priority\u0026#34;: { \u0026#34;priority\u0026#34;:50 } } }, \u0026#34;warm\u0026#34;: { \u0026#34;min_age\u0026#34;:\u0026#34;7d\u0026#34;, \u0026#34;actions\u0026#34;: { \u0026#34;forcemerge\u0026#34;: { \u0026#34;max_num_segments\u0026#34;:1 }, \u0026#34;shrink\u0026#34;: { \u0026#34;number_of_shards\u0026#34;:1 }, \u0026#34;allocate\u0026#34;: { \u0026#34;require\u0026#34;: { \u0026#34;data\u0026#34;: \u0026#34;warm\u0026#34; } }, \u0026#34;set_priority\u0026#34;: { \u0026#34;priority\u0026#34;:25 } } }, \u0026#34;cold\u0026#34;: { \u0026#34;min_age\u0026#34;:\u0026#34;14d\u0026#34;, \u0026#34;actions\u0026#34;: { \u0026#34;set_priority\u0026#34;: { \u0026#34;priority\u0026#34;:0 }, \u0026#34;freeze\u0026#34;: {}, \u0026#34;allocate\u0026#34;: { \u0026#34;require\u0026#34;: { \u0026#34;data\u0026#34;: \u0026#34;cold\u0026#34; } } } }, \u0026#34;delete\u0026#34;: { \u0026#34;min_age\u0026#34;:\u0026#34;30d\u0026#34;, \u0026#34;actions\u0026#34;: { \u0026#34;delete\u0026#34;: {} } } } } } PUT _template/hot-warm-cold-delete-30days-template { \u0026#34;order\u0026#34;:10, \u0026#34;index_patterns\u0026#34;: [\u0026#34;logstash-*\u0026#34;, \u0026#34;metricbeat-*\u0026#34;, \u0026#34;filebeat-*\u0026#34;], \u0026#34;settings\u0026#34;: { \u0026#34;index.routing.allocation.require.data\u0026#34;: \u0026#34;hot\u0026#34;, \u0026#34;index.lifecycle.name\u0026#34;: \u0026#34;hot-warm-cold-delete-30days\u0026#34; } } PUT _template/hot-warm-cold-delete-30days-template { \u0026#34;order\u0026#34;:10, \u0026#34;index_patterns\u0026#34;: [\u0026#34;logstash-sysjson-*\u0026#34;], \u0026#34;settings\u0026#34;: { \u0026#34;index.routing.allocation.require.data\u0026#34;: \u0026#34;hot\u0026#34;, \u0026#34;index.lifecycle.name\u0026#34;: \u0026#34;hot-warm-cold-delete-30days\u0026#34; } } { \u0026#34;index.blocks.read_only_allow_delete\u0026#34;: \u0026#34;false\u0026#34;, \u0026#34;index.priority\u0026#34;: \u0026#34;1\u0026#34;, \u0026#34;index.query.default_field\u0026#34;: [ \u0026#34;*\u0026#34; ], \u0026#34;index.write.wait_for_active_shards\u0026#34;: \u0026#34;1\u0026#34;, \u0026#34;index.routing.allocation.include._tier_preference\u0026#34;: \u0026#34;data_content\u0026#34;, \u0026#34;index.refresh_interval\u0026#34;: \u0026#34;5s\u0026#34;, \u0026#34;index.number_of_replicas\u0026#34;: \u0026#34;1\u0026#34; } { \u0026#34;index.blocks.read_only_allow_delete\u0026#34;: \u0026#34;false\u0026#34;, \u0026#34;index.query.default_field\u0026#34;: [ \u0026#34;*\u0026#34; ], \u0026#34;index.write.wait_for_active_shards\u0026#34;: \u0026#34;1\u0026#34;, \u0026#34;index.lifecycle.name\u0026#34;: \u0026#34;hot-warm-cold-delete-30days\u0026#34;, \u0026#34;index.routing.allocation.include._tier_preference\u0026#34;: \u0026#34;data_content\u0026#34;, \u0026#34;index.routing.allocation.require.data\u0026#34;: \u0026#34;hot\u0026#34;, \u0026#34;index.refresh_interval\u0026#34;: \u0026#34;5s\u0026#34;, \u0026#34;index.priority\u0026#34;: \u0026#34;50\u0026#34;, \u0026#34;index.number_of_replicas\u0026#34;: \u0026#34;1\u0026#34; } PUT _ilm/policy/hot-warm-cold-delete-7days { \u0026#34;policy\u0026#34;: { \u0026#34;phases\u0026#34;: { \u0026#34;delete\u0026#34;: { \u0026#34;min_age\u0026#34;:\u0026#34;7d\u0026#34;, \u0026#34;actions\u0026#34;: { \u0026#34;delete\u0026#34;: {} } } } } } ","date":"2024-01-19","permalink":"/posts/elasticsearch-ilm-hot-warm-cold-policy/","title":"Elasticsearch ILM 冷热分层与定时删除策略"},{"categories":["nginx"],"content":"nginx 4层代理(初级版)\nNginx从1.9.0版本开始，新增加了一个stream模块，用来实现四层协议的转发、代理或者负载均衡\n本笔记使用的 Nginx 1.16.1 软件包动态加载了 stream 模块。不同安装包的模块配置需要分别检查。\nhttp://nginx.org/en/docs/stream/ngx_stream_core_module.html\n暂不需要编译源码 http://nginx.org/en/download.html\n遇到问题：以下都是环境上缺少相关的依赖，安装即可\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ./configure: error: the HTTP XSLT module requires the libxml2/libxslt yum install libxslt-devel -y ./configure: error: the HTTP image filter module requires the GD library. yum install gd-devel -y ./configure: error: perl module ExtUtils::Embed is required yum -y install perl-devel perl-ExtUtils-Embed ./configure: error: the GeoIP module requires the GeoIP library. yum -y install GeoIP GeoIP-devel GeoIP-data ./configure: error: the Google perftools module requires the Google perftools yum install gperftools -y ./configure: error: SSL modules require the OpenSSL library. yum -y install openssl openssl-devel checking for --with-ld-opt=\u0026#34;-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E\u0026#34; ... not found ./configure: error: the invalid value in --with-ld-opt=\u0026#34;-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E\u0026#34; yum install redhat-rpm-config -y ./configure: error: perl module ExtUtils::Embed is required yum -y install perl-devel perl-ExtUtils-Embed 在执行make时候的报错： /root/nginx-auth-ldap/ngx_http_auth_ldap_module.c:33:18: fatal error: ldap.h: No such file or directory #include \u0026lt;ldap.h\u0026gt; yum install -y openldap-devel 配置文件样例 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 # For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } stream { upstream tcp_proxy { hash $remote_addr consistent; server 192.0.2.10:30400 max_fails=3 fail_timeout=30s; #server 192.0.2.11:30400 max_fails=3 fail_timeout=30s; } server { listen 443; proxy_connect_timeout 1s; proxy_timeout 3s; proxy_pass tcp_proxy; } } http { log_format main \u0026#39;$remote_addr - $remote_user [$time_local] \u0026#34;$request\u0026#34; \u0026#39; \u0026#39;$status $body_bytes_sent \u0026#34;$http_referer\u0026#34; \u0026#39; \u0026#39;\u0026#34;$http_user_agent\u0026#34; \u0026#34;$http_x_forwarded_for\u0026#34;\u0026#39;; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. include /etc/nginx/conf.d/*.conf; server { listen 80 default_server; server_name _; root /usr/share/nginx/html; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; location / { } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } } nginx yum安装1.16 配置参数 Bash 1 2 3 4 5 nginx version: nginx/1.16.1 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) built with OpenSSL 1.0.2k-fips 26 Jan 2017 TLS SNI support enabled configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-stream_ssl_preread_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-http_auth_request_module --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module --with-debug --with-cc-opt=\u0026#39;-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic\u0026#39; --with-ld-opt=\u0026#39;-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E\u0026#39; 有两个模块能达到效果:\nngx_stream_proxy_module ngx_stream_core_module ","date":"2024-01-14","permalink":"/posts/nginx-stream-proxy/","title":"Nginx Stream 四层代理配置笔记"},{"categories":["Kubenertes"],"content":"k8s kill restart exit status 137\n128+信号值\nexit 130 命令行输入 sleep 100000 然后执行 查看命令状态 echo $? 结果输出: 130(128+2)\nexit 137 终端1:命令行输入: sleep 100000 终端2:kill sleep 终端1:查看命令状态 echo $? 结果输出: 137(128+9)\n143 ps aux | grep sleep | grep -v grep | awk \u0026lsquo;{print $2}\u0026rsquo; kill -term kill -15 ","date":"2024-01-14","permalink":"/posts/process-exit-signal-notes/","title":"进程退出码 130、137、143 与信号"},{"categories":["tools"],"content":"zsh安装配置使用 ohmyz 官网 Zsh 是什么\nZsh 是一款强大的虚拟终端，既是一个系统的虚拟终端，也可以作为一个脚本语言的交互解析器。 打开终端，在终端上输入 zsh --version 这个命令来查看我们的电脑上是否安装了 Zsh 终端查询版本为： zsh 5.7.1 (x86_64-apple-darwin19.0) 查看系统当前 shell，命令行执行: echo $SHELL Oh My Zsh 是什么\nOh My Zsh 是一款社区驱动的命令行工具，正如它的主页上说的，Oh My Zsh 是一种生活方式。它基于 zsh 命令行，提供了主题配置，插件机制，已经内置的便捷操作。给我们一种全新的方式使用命令行。 Oh My Zsh 是基于 zsh 命令行的一个扩展工具集，提供了丰富的扩展功能。 安装 Oh My Zsh 前提条件：必须已安装 zsh 以下操作都是基于 Mac 系统。 安装oh-my-zsh verify zsh 查看系统是否有zsh shell\nBash 1 cat /etc/shells | grep zsh 如果当前环境shell已经有了zsh请自动忽略这一步\n代码 1 2 3 brew install zsh # mac os 的homebrew sudo apt install zsh # ubuntu sudo yum install zsh # centos oh-my-zsh github网址\n安装oh-my-zsh 这里提供三种方法安装oh-my-zsh\n1、使用wget安装： 代码 1 sh -c \u0026#34;$(wget -O- https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)\u0026#34; 2、使用curl安装： 代码 1 sh -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)\u0026#34; 3、手动安装： Bash 1 2 curl -Lo install.sh https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh sh install.sh 执行脚本会有命令行交互，询问是否要启用zsh shell，回车y。开始使用\noh-my-zsh插件安装 部分zsh插件地址\n安装命令行语法高亮插件 官方安装README\ncllone项目到$ZSH_CUSTOM/plugins文件夹下 (默认为 ~/.oh-my-zsh/custom/plugins) 代码 1 git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting 在 Oh My Zsh 的配置文件 (~/.zshrc)中设置: 代码 1 plugins=(其他插件 zsh-syntax-highlighting) 运行 source ~/.zshrc 使配置文件生效 安装命令行自动补全插件 官方安装README\nclone项目到$ZSH_CUSTOM/plugins文件夹下 (默认为 ~/.oh-my-zsh/custom/plugins) 代码 1 git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions 在 Oh My Zsh 的配置文件 (~/.zshrc)中设置: 代码 1 plugins=(其他插件 zsh-autosuggestions) 运行 source ~/.zshrc 使配置文件生效 oh-my-zsh主题替换 oh-my-zsh提供了很多主题，可以通过命令查看当前使用的主题：\nBash 1 ls ~/.oh-my-zsh/themes 可以通过编辑.zshrc文件来修改你的主题\nBash 1 vi ~/.zshrc 命令行终端显示乱码 基础操作，在~/.zshrc中添加如下配置\n代码 1 2 export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 但实际中，默认都会有以上配置\n终极问题：Mac terminal终端或iterm2出现无法显示字符集，显示为问号：\n问题根本原因是字体的缺失，或者解释为当前字体无法显示符号，随即解决方法就是安装字体：\nBash 1 2 3 4 5 # clone git clone https://github.com/powerline/fonts.git # install cd fonts ./install.sh 安装完成后再终端选择字体，字体名称：Meslo LG S DZ Regular for Powerline，或者相似的，包含Powerline即可。\n同理，在ide上也是，ide的命令行终端显示字体修改后即可同样正常显示\n","date":"2024-01-14","permalink":"/posts/zsh-oh-my-zsh-setup/","title":"Zsh 与 Oh My Zsh 安装配置笔记"},{"categories":["tools"],"content":"raycast 背景介绍 一直想要做个\u0026quot;极客\u0026quot;，用各种快捷解决方式，但之前的需求不大，感觉时间也没有那么紧张，事情也没有那么多，那么杂，感觉已经可以了。 最近一段时间，总是一个任务没有完成，突然中途给你来一个点点点点的任务，理智上这个点点点的任务很快就完成，尽管优先级不高(但催的也挺紧的) 大脑习惯性就会把这个事情做完，结果就是，需要切换当前手头任务，打开xx浏览器，然后点击标签，登录，打开。。。经常做一半的时候冒出一句，我刚刚要干嘛来着\nafraid 这个是老牌的软件了，我不喜欢他的配色，总感觉脱离一个时代。之前身边也没有更多的人讲怎么用(自己懒的搜)，看到用的场景就是打开app 后来苹果自带的Spotlight似乎也解决了这个问题，也就没有在意这个软件\nraycast 最初是在twitter上看到有人推荐这个软件，无一例外都在夸比afraid好用，好奇心下载了一个，但没有真正使用\n在某一天，突然受不了上面介绍的背景，想想这个能不能自动打开，然后就感受到了快捷。看同事给afraid增加快捷似乎很麻烦，但使用raycast有点像增加网页快捷的方式 今天配置了vscode打开项目目录，常用的项目，感受到了丝滑。\nRaycast Quicklink Raycast使用指南\n","date":"2024-01-14","permalink":"/posts/raycast-quicklink-notes/","title":"Raycast Quicklink 使用感受"},{"categories":["python"],"content":"web选型 web服务器首选nginx，但nginx不支持cgi，因为太古老了\n所以选用apache\n主要为理解cgi的模式流程 理解header的作用 apache的安装 原本想使用ubunt系统，但对配置文件路径不熟悉，作罢\nYum 安装\nyum install -y httpd\n配置\napache的配置文件为/etc/httpd/conf/httpd.conf\n文件需要修改根目录\n代码 1 2 3 4 5 6 7 8 9 10 11 12 将目录替换 \u0026lt;Directory /\u0026gt; AllowOverride none Require all denied \u0026lt;/Directory\u0026gt; 为： \u0026lt;Directory \u0026#34;/var/www/cgi-bin\u0026#34;\u0026gt; AllowOverride None Options +ExecCGI Order allow,deny Allow from all \u0026lt;/Directory\u0026gt; 修改 增加 AddHandler\n去掉注释\n代码 1 2 将注释去掉，并增加.py 后缀 AddHandler cgi-script .cgi .py 重启apache\nsystemctl start httpd.service #启动\nsystemctl stop httpd.service #停止\nsystemctl restart httpd.service #重启\n检查httpd状态 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@gitlab-test ~]# systemctl status httpd.service ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled) Active: active (running) since Wed 2020-02-12 12:07:30 CST; 12s ago Docs: man:httpd(8) man:apachectl(8) Main PID: 58363 (httpd) Status: \u0026#34;Total requests: 0; Current requests/sec: 0; Current traffic: 0 B/sec\u0026#34; Tasks: 7 Memory: 3.2M CGroup: /system.slice/httpd.service ├─58363 /usr/sbin/httpd -DFOREGROUND ├─58365 /usr/sbin/httpd -DFOREGROUND ├─58366 /usr/sbin/httpd -DFOREGROUND ├─58367 /usr/sbin/httpd -DFOREGROUND ├─58368 /usr/sbin/httpd -DFOREGROUND ├─58369 /usr/sbin/httpd -DFOREGROUND └─58370 /usr/sbin/httpd -DFOREGROUND Feb 12 12:07:30 gitlab-test systemd[1]: Starting The Apache HTTP Server... Feb 12 12:07:30 example-host httpd[58363]: AH00558: Set the \u0026#39;ServerName\u0026#39; directive to suppress this message Feb 12 12:07:30 gitlab-test systemd[1]: Started The Apache HTTP Server. Hint: Some lines were ellipsized, use -l to show in full. python代码 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/usr/bin/python #coding=utf-8 print \u0026#34;Content-type:text/html\u0026#34; print #空行，告诉服务器结束头部 print \u0026#39;\u0026lt;html\u0026gt;\u0026#39; print \u0026#39;\u0026lt;head\u0026gt;\u0026#39; print \u0026#39;\u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt;\u0026#39; print \u0026#39;\u0026lt;title\u0026gt;Hello Word - 我的第一个CGI程序！\u0026lt;/title\u0026gt;\u0026#39; print \u0026#39;\u0026lt;/head\u0026gt;\u0026#39; print \u0026#39;\u0026lt;body\u0026gt;\u0026#39; print \u0026#39;\u0026lt;h2\u0026gt;嘿！ this is cgi!~\u0026lt;/h2\u0026gt;\u0026#39; print \u0026#39;\u0026lt;/body\u0026gt;\u0026#39; print \u0026#39;\u0026lt;/html\u0026gt;\u0026#39; 增加权限\nBash 1 chmod 755 /var/www/cgi-bin/hello.py 测试结果 在当前服务器上使用命令\ncurl localhost/cgi-bin/hello.py\n结果成功\n","date":"2024-01-14","permalink":"/posts/python2-apache-cgi-notes/","title":"Python 2 与 Apache CGI 实验"},{"categories":["tools"],"content":"简介 rime是一款输入法，自定义配置的，为啥要搞这个，因为现在的信息泄露太严重了，实在忍不住了\n安装 官网下载地址\nmac 安装 Install via Homebrew: brew install --cask squirrel\n开源配置 配置管理工具 一个叫东风破的配置管理插件\nBash 1 curl -fsSL https://raw.githubusercontent.com/rime/plum/master/rime-install | bash 别人的配置 一个叫雾凇拼音的配置\n通过东风破plus安装\n代码 1 2 cd plum bash rime-install iDvel/rime-ice:others/recipes/full mac 增加 rime 输入法 系统配置 -\u0026gt; 键盘 -\u0026gt; 文字输入 -\u0026gt; 输入法 -\u0026gt; 编辑\n使用习惯 日常习惯的搜狗的shift中英文切换方式，但rime需要手动配置 配置链接\n点击鼠须管logo，点击用户设定，默认打开路径/Users/example/Library/Rime/目录，打开default.yaml文件\n参考下列配置\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # 中西文切换 # # 【good_old_caps_lock】 CapsLock 切换到大写或切换中英。 # （macOS 偏好设置的优先级更高，如果勾选【使用大写锁定键切换“ABC”输入法】则始终会切换输入法） # # 切换中英： # 不同的选项表示：打字打到一半时按下了 CapsLock、Shift、Control 后： # commit_code 上屏原始的编码，然后切换到英文 # commit_text 上屏拼出的词句，然后切换到英文 # clear 清除未上屏内容，然后切换到英文 # inline_ascii 无输入时，切换中英；有输入时，切换到临时英文模式，按回车上屏后回到中文状态 # noop 屏蔽快捷键，不切换中英，但不要屏蔽 CapsLock ascii_composer: good_old_caps_lock: true # true | false switch_key: Caps_Lock: clear # commit_code | commit_text | clear Shift_L: commit_code # commit_code | commit_text | inline_ascii | clear | noop Shift_R: commit_code # commit_code | commit_text | inline_ascii | clear | noop Control_L: noop # commit_code | commit_text | inline_ascii | clear | noop Control_R: noop # commit_code | commit_text | inline_ascii | clear | noop 将下面的shift_L/Shift_R 改为你想要的配置，我改成了commit_code\nmac自带的切换输入法是 Lock，将大写切换键改成 Shift-Lock\n这样就基本没有使用的不适\n","date":"2024-01-14","permalink":"/posts/rime-macos-install/","title":"macOS Rime 安装与中英文切换配置"},{"categories":["tools"],"content":"Gihub精准搜索开源项目 开源项目的组成部分 源项目组成部分：\nname: 项目名 description: 项目的简要描述 项目的源码 README.md: 项目的详细情况的介绍 除了这些要素之外，项目本身的star数和fork数，也是评判一个开源项目是否火热的标准，这同时也是一个很重要的搜索标准。另外我们也要注意观察这个项目的最近更新日期，因为项目越活跃，那么它的更新日期也更加频繁。\n以上要素就是我们在进行搜索的时候要注意的一些关键点。\n方法总结 增加筛选条件精准搜索。\nin:name xxx # 按照项目名搜索 in:readme xxx # 按照README搜索 in:description xxx # 按照description搜索 附加增加筛选条件\nstars:\u0026gt;xxx # stars数大于xxx forks:\u0026gt;3000 # forks数大于xxx language:xxx # 编程语言是xxx pushed:\u0026gt;YYYY-MM-DD # 最后更新时间大于YYYY-MM-DD 最后绝招: 官方提供精准搜索，官方搜索左下角: advanced\n","date":"2024-01-14","permalink":"/posts/github-project-search/","title":"GitHub 项目精准搜索笔记"},{"categories":["Kubenertes"],"content":"docker 迁移 Bash 1 2 3 4 5 6 7 8 9 10 systemctl enable docker systemctl start docker # 增加普通用户管理docker权限 usermod -aG docker your-user # 修改docker挂载目录 systemctl stop docker systemctl stop docker.socket mv /var/lib/docker /data/ ln -s /data/docker /var/lib/docker systemctl start docker docker log docker logs -f -t \u0026ndash;tail 100 CONTAINER_ID\n代码 1 2 3 4 5 6 7 8 $ docker logs [OPTIONS] CONTAINER Options: --details 显示更多的信息 -f, --follow 跟踪实时日志 --since string 显示自某个timestamp之后的日志，或相对时间，如42m（即42分钟） --tail string 从日志末尾显示多少行日志， 默认是all -t, --timestamps 显示时间戳 --until string 显示自某个timestamp之前的日志，或相对时间，如42m（即42分钟） 查看指定时间后的日志，只显示最后100行 代码 1 $ docker logs -f -t --since=\u0026#34;2018-02-08\u0026#34; --tail=100 CONTAINER_ID 查看最近30分钟的日志 代码 1 $ docker logs --since 30m CONTAINER_ID 查看某时间之后的日志 代码 1 $ docker logs -t --since=\u0026#34;2018-02-08T13:23:37\u0026#34; CONTAINER_ID 查看某时间段日志 代码 1 $ docker logs -t --since=\u0026#34;2018-02-08T13:23:37\u0026#34; --until \u0026#34;2018-02-09T12:23:37\u0026#34; CONTAINER_ID 允许Docker使用无权限验证的Docker Registry 配置insecure-registries，docker才能使用无权限验证的docker registry\n默认情况下，Docker不能使用没有配置权限验证的Docker Registry，会出现如下报错：\n代码 1 2 docker pull registry.example.com:5000/ubuntu Error response from daemon: server gave HTTP response to HTTPS client 这时需要修改 Docker 配置文件，将示例 Registry 地址 registry.example.com:5000 添加到 insecure-registries 中：\nBash 1 2 3 4 cat /etc/docker/daemon.json { \u0026#34;insecure-registries\u0026#34; : [\u0026#34;registry.example.com:5000\u0026#34;] } 然后重启docker\nBash 1 systemctl restart docker 容器内部操作 debain 系统换清华源，执行下列命令，换源然后安装应用\nBash 1 echo -e \u0026#34;# 默认注释了源码镜像以提高 apt update 速度，如有需要可自行取消注释\\ndeb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free\\n# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free\\ndeb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free\\n# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free\\ndeb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free\\n# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free\\ndeb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free\\n# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free\u0026#34; \u0026gt;\u0026gt; /etc/apt/sources.list 容器暴露端口 当容器内部没有启动一个端口，但外部暴露这个端口，现象: 宿主机会启动这个端口，端口被占用，但访问这个端口会失败，curl会提示\n代码 1 curl: (56) Recv failure: Connection reset by peer ","date":"2024-01-14","permalink":"/posts/docker-operations-notes/","title":"Docker 数据目录、日志与 Registry 配置笔记"},{"categories":["Linux"],"content":"SHELL 提示符 PS1/PS2/PS3/PS4 默认值定义 BASH含义： Bash 1 2 3 4 5 PS1 The value of this parameter is expanded (see PROMPTING below) and used as the primary prompt string. The default value is ``\\s-\\v\\$ \u0026#39;\u0026#39;. PS2 The value of this parameter is expanded as with PS1 and used as the secondary prompt string. The default is ``\u0026gt; \u0026#39;\u0026#39;. PS3 The value of this parameter is used as the prompt for the select command (see SHELL GRAMMAR above). PS4 The value of this parameter is expanded as with PS1 and the value is printed before each command bash displays during an execution trace. The first character of PS4 is repli‐ cated multiple times, as necessary, to indicate multiple levels of indirection. The default is ``+ \u0026#39;\u0026#39;. 默认系统变量 Bash 1 2 3 4 5 [root@local ~]# echo -e \u0026#34;\\$PS1默认值: $PS1\\n\\$PS2默认值: $PS2\\n\\$PS3默认值: $PS3\\n\\$PS4默认值: $PS4\u0026#34; $PS1默认值: [\\u@\\h \\W]\\$ $PS2默认值: \u0026gt; $PS3默认值: $PS4默认值: + 中文解释 PS1: 是在命令行提示的，例如[root@local ~]# PS2: 换行符提示符，默认 \\ PS3: 命令select的提示符 默认为空 PS4: 代码设置debug模式下的提示符 默认为 +\n使用场景 PS1 PS1是使用最多，修改最多的，一般是普通的修改和oh my zsh的安装\n字符 描述 \\a 铃声字符 \\d 格式为“日 月 年”的日期 \\e ASCII转义字符 \\h 本地主机名 \\H 完全合格的限定域主机名 \\j shell当前管理的作业数 \\1 shell终端设备名的基本名称 \\n ASCII换行字符 \\r ASCII回车 \\s shell的名称 \\t 格式为“小时:分钟:秒”的24小时制的当前时间 \\T 格式为“小时:分钟:秒”的12小时制的当前时间 @ 格式为am/pm的12小时制的当前时间 \\u 当前用户的用户名 \\v bash shell的版本 \\V bash shell的发布级别 \\w 当前工作目录 \\W 当前工作目录的基本名称 ! 该命令的bash shell历史数 # 该命令的命令数量 $ 如果是普通用户，则为美元符号$；如果超级用户（root 用户），则为井号#。 \\nnn 对应于八进制值 nnn 的字符 \\ 斜杠 [ 控制码序列的开头 ] 控制码序列的结尾 ","date":"2024-01-14","permalink":"/posts/bash-prompt-variables/","title":"Bash PS1、PS2、PS3、PS4 提示符笔记"},{"categories":["tools"],"content":"APM监控 一、概念 应用性能管理（Application Performance Management）是一个比较新的网络管理方向，主要指对企业的关键业务应用进行监测、优化，提高企业应用的可靠性和质量，保证用户得到良好的服务，降低IT总拥有成本(TCO)。使用全业务链的敏捷APM监控，可使一个企业的关键业务应用的性能更强大，可以提高竞争力，并取得商业成功，因此，加强应用性能管理（APM）可以产生巨大商业利益。\n二、开源APM比较 方案 cat zipkin pinpoint skywalking 依赖 Java 6 7 8、Maven 3+ MySQL 5.6 5.7、Linux 2.6+ hadoop可选 Java 6，7，8 Maven3.2+ rabbitMQ Java 6，7，8 maven3+ Hbase0.94+ Java 6，7，8 maven3.0+ nodejs zookeeper elasticsearch 实现方式 代码埋点（拦截器，注解，过滤器等） 拦截请求，发送（HTTP，mq）数据至zipkin服务 java探针，字节码增强 java探针，字节码增强 存储 mysql , hdfs in-memory ， mysql ， Cassandra ， Elasticsearch HBase elasticsearch , H2 jvm监控 不支持 不支持 支持 支持 trace查询 支持 支持 需要二次开发 支持 stars 12.8k 12.5k 10k 12.4k 侵入 高，需要埋点 高，需要开发 低 低 部署成本 中 中 较高 低 基于对应用尽可能的低侵入考虑，以上方案选型优先级pinpoint\u0026gt;skywalking\u0026gt;zipkin\u0026gt;cat。\n百度百科APM\nhttps://baike.baidu.com/item/%E5%BA%94%E7%94%A8%E6%80%A7%E8%83%BD%E7%AE%A1%E7%90%86/292984?fromtitle=APM\u0026amp;fromid=2132727\n维基百科APM(英文版)\nhttps://en.wikipedia.org/wiki/Application_performance_management\nskywalking官网文档\nhttps://skywalking.apache.org/zh/blog/2019-03-29-introduction-of-skywalking-and-simple-practice.html\n这些应用都是参考google的Dapper方式\nhttp://bigbully.github.io/Dapper-translation/\n网易云的APM思考\nhttps://sq.163yun.com/blog/article/168169689970475008\n网易云参考资料\nhttps://github.com/naver/pinpoint/blob/master/quickstart/ http://www.jianshu.com/p/37d9bb233936 http://bigbully.github.io/Dapper-translation https://skyao.gitbooks.io/learning-pinpoint/content/installation/quickstart.html Pinpoint v1.5.0 APM 视频介绍\n","date":"2024-01-14","permalink":"/posts/apm-comparison-notes/","title":"APM 监控方案比较笔记"},{"categories":["tools"],"content":"Git 修改已提交的 commit 信息，包括作者、邮箱 1. 背景 不同电脑配置了不同的用户名、邮箱，例如：不小心用公司电脑提交了 commit 到个人的github 仓库，想改掉已经提交的 commit 的信息。\n2. 修改用户名、邮箱 Bash 1 2 3 4 5 6 # 全局修改 git config --global user.name \u0026#34;clin\u0026#34; git config --global user.email \u0026#34;user@example.com\u0026#34; # 针对某个仓库修改 git config user.name \u0026#34;clin\u0026#34; git config user.email \u0026#34;user@example.com\u0026#34; 这里修改只对后续的提交有效\n3. 修改 commit 信息，包括作者、邮箱 3.1 修改最后一次 commit 的信息 直接使用 amend 进行修正\n3.1.1 修改 commit 注释信息 Bash 1 2 git commit --amend git commit --amend --message=\u0026#34;modify message\u0026#34; --author=\u0026#34;admin \u0026lt;user@example.com\u0026gt;\u0026#34; 出现修改注释信息的界面，默认是vim编辑模式，merge时会有#开头的注释，那部分可以不用修改，只修改对应无注释的内容。\n3.1.2 修改作者、邮箱 Bash 1 git commit --amend --author=\u0026#34;{username} \u0026lt;{email}\u0026gt;\u0026#34; 例如：git commit \u0026ndash;amend \u0026ndash;author=\u0026ldquo;clin user@example.com\u0026rdquo;\n3.2 修改某几次 commit 的信息 3.2.1 使用 log 查看提交记录 Bash 1 2 3 4 5 git log -2 # 查看最近两次的提交信息，注：-2 代表最后 2 条记录 git log --author=clin # 查看作者为 clin 的提交记录 git log --author=user@example.com # 查看作者(邮箱)为 user@example.com 的提交记录 git log --grep \u0026lt;pattern(commit-message)\u0026gt; # 查看提交记录包含 xx 的记录，支持正则匹配 git log --oneline -2 # 简短信息 （oneline：一行） 3.2.2 rebase 需要修改的 commit Bash 1 2 3 git rebase -i HEAD~2 # 或者 git rebase -i {commitID} # 例如 git rebase -i d95ddfb 此时会输出\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 pick abc1234 feat: update example widget pick def5678 feat: add example dashboard # Rebase dbca477..8bfe4f9 onto dbca477 (2 commands) # # Commands: # p, pick \u0026lt;commit\u0026gt; = use commit # r, reword \u0026lt;commit\u0026gt; = use commit, but edit the commit message # e, edit \u0026lt;commit\u0026gt; = use commit, but stop for amending # s, squash \u0026lt;commit\u0026gt; = use commit, but meld into previous commit # f, fixup [-C | -c] \u0026lt;commit\u0026gt; = like \u0026#34;squash\u0026#34; but keep only the previous # commit\u0026#39;s log message, unless -C is used, in which case # keep only this commit\u0026#39;s message; -c is same as -C but # opens the editor # x, exec \u0026lt;command\u0026gt; = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with \u0026#39;git rebase --continue\u0026#39;) # d, drop \u0026lt;commit\u0026gt; = remove commit # l, label \u0026lt;label\u0026gt; = label current HEAD with a name # t, reset \u0026lt;label\u0026gt; = reset HEAD to a label # m, merge [-C \u0026lt;commit\u0026gt; | -c \u0026lt;commit\u0026gt;] \u0026lt;label\u0026gt; [# \u0026lt;oneline\u0026gt;] # . create a merge commit using the original merge commit\u0026#39;s \u0026#34;~/File/Project/work/devops-front/.git/rebase-merge/git-rebase-todo\u0026#34; 30L, 1408B rebase 命令会进入编辑模式，这时可以修改commit信息，rebase 会合并一些分支，具体的暂时不讲述了。 最上面两行就是对应的 commit 记录，如果需要修改，需要把对应 commit 信息前的 pick 更改为 edit (vim编辑) 执行 rebase 命令后，会出现 reabse 的编辑窗口，窗口底下会有提示怎么操作。\n这里把需要修改的 commit 最前面的 pick 改为 edit，可以一条或者多条。\n3.2.3 修改 commit 信息 只修改注释信息\nBash 1 2 3 git commit --amend # 只修改注释信息，输入后，会跳转到编辑页面 git commit --amend --author=\u0026#34;clin \u0026lt;user@example.com\u0026gt;\u0026#34; --no-edit # 只修改作者、邮箱 git commit --amend --author=\u0026#34;{username} \u0026lt;{email}\u0026gt;\u0026#34; # 同时修改注释信息、作者、邮箱，输入后，会跳转到编辑页面 修改完成后，继续执行下面命令，表示确认更新\nBash 1 git rebase --continue 如果是修改多条的话，重复以上 3.3.2 操作即可。直到出现以下提示，说明全部修改已经完成。\nSuccessfully rebased and updated refs/heads/master.\n3.2.4 push 仓库更改到远程仓库 强制 push\nBash 1 git push --force origin master 注：当仓库是多人操作时，可能会覆盖别人push 的代码，请谨慎操作。\n3.3 使用脚本自动更改 这个操作原本是属于非常规操作，网上都是提供了一个脚本，确实能执行，但脚本的变量含义没有能解释的\n官方链接 https://git-scm.com/docs/git-filter-branch/zh_HANS-CN\n官方给出的脚本示例\nBash 1 2 3 4 5 6 7 8 9 10 git filter-branch --env-filter \u0026#39; if test \u0026#34;$GIT_AUTHOR_EMAIL\u0026#34; = \u0026#34;root@localhost\u0026#34; then GIT_AUTHOR_EMAIL=user@example.com fi if test \u0026#34;$GIT_COMMITTER_EMAIL\u0026#34; = \u0026#34;root@localhost\u0026#34; then GIT_COMMITTER_EMAIL=user@example.com fi \u0026#39; -- --all 网上流传的版本，我执行过了，暂时没遇到问题\n两者的区别是指定分支tag，和export命令，其余的都是一样的。\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 git filter-branch --env-filter \u0026#39; WRONG_EMAIL=\u0026#34;user@example.com\u0026#34; NEW_NAME=\u0026#34;New Name Value\u0026#34; NEW_EMAIL=\u0026#34;user@example.com\u0026#34; if [ \u0026#34;$GIT_COMMITTER_EMAIL\u0026#34; = \u0026#34;$WRONG_EMAIL\u0026#34; ] then export GIT_COMMITTER_NAME=\u0026#34;$NEW_NAME\u0026#34; export GIT_COMMITTER_EMAIL=\u0026#34;$NEW_EMAIL\u0026#34; fi if [ \u0026#34;$GIT_AUTHOR_EMAIL\u0026#34; = \u0026#34;$WRONG_EMAIL\u0026#34; ] then export GIT_AUTHOR_NAME=\u0026#34;$NEW_NAME\u0026#34; export GIT_AUTHOR_EMAIL=\u0026#34;$NEW_EMAIL\u0026#34; fi \u0026#39; --tag-name-filter cat -- --branches --tags git 工具重写 https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E7%BD%AE%E6%8F%AD%E5%AF%86\nBash 1 2 3 4 5 6 7 8 9 git filter-branch --commit-filter \u0026#39; if [ \u0026#34;$GIT_AUTHOR_EMAIL\u0026#34; = \u0026#34;schacon@localhost\u0026#34; ]; then GIT_AUTHOR_NAME=\u0026#34;Scott Chacon\u0026#34;; GIT_AUTHOR_EMAIL=\u0026#34;user@example.com\u0026#34;; git commit-tree \u0026#34;$@\u0026#34;; else git commit-tree \u0026#34;$@\u0026#34;; fi\u0026#39; HEAD ","date":"2024-01-14","permalink":"/posts/git-change-commit-author/","title":"Git 修改提交说明与作者信息"},{"categories":["tools"],"content":"网上一直看到有人把自己电脑备份，因为有很多自己储备了很久的配置，喜欢用的软件等等，之前看过知道不是个伪需求，但总感觉离自己很远。\n最近电脑重启的次数太多，已经影响我的日常了，电脑总是半夜无缘无故的关机，等我第二天想用的时候，需要重启。这个让我有点烦恼。\n记录下之前遇到的两个配置文件\nhttps://github.com/sxyazi/dotfiles\n软件：\n个人\nsecretCRT Logi Options oh my zsh VMware Fusion todesk The Unarchiver Tencent Lemon WPS 网易音乐 QQ音乐 IINA OTP 数据备份，非常重要 开发\nChrome Edge python pyenv node npm nvm Postman Wireshark Sourcetree Snipaste clash for windows pasteNow raycast orbstack SwitchHosts mat kitty DBeaver ide\npycharm idea goland vscode 聊天\n钉钉 QQ 微信 备份数据 mails\nfoxmail 163 网易 dotfiles\nclin:~/ $ brew list [16:55:54] ==\u0026gt; Formulae aircrack-ng git libmpc mercurial pkg-config python@3.12 telnet ansible gmp libomp mpdecimal pstree python@3.8 tldr autoconf go libpng mpfr pycparser python@3.9 tree automake grep libssh msgpack pyenv pyyaml tree-sitter bzip2 helm libtermkey mtr pygments readline unibilium ca-certificates htop libtiff mysql-client python-certifi redis webp cffi hugo libunistring mysql-client@5.7 python-cryptography screenresolution wget cocoapods hydra libuv neofetch python-lxml sdl xz crunch icu4c libyaml neovim python-markupsafe sdl12-compat zlib docutils iproute2mac libzip nginx python-packaging sdl_image zsh-syntax-highlighting freetype isl little-cms2 nmap python-pytz sdl_ttf gawk jansson lrzsz node python-setuptools shellcheck gcc jpeg luajit openssl@1.1 python-tabulate six gdbm kubectx luv openssl@3 python@2 sphinx-doc gettext kubernetes-cli m4 pcre python@3.10 sqlite giflib libidn2 maven pcre2 python@3.11 tcl-tk\n","date":"2024-01-13","permalink":"/posts/macos-migration-checklist/","title":"macOS 电脑迁移与开发环境备份清单"},{"categories":["Linux"],"content":"查看 Linux 发行版名称和版本号有很多种方法 参考原文\n偶尔需要查看系统的版本，安装对应的包，整理的一些方法，依次列举\n1. /proc/version 文件 这个是系统生成的虚拟文件，直接可以查看内核版本，也能列举系统名称，但不能快速区分centos与redhat区别\nBash 1 2 [root@example-host ~]# cat /proc/version Linux version 3.10.0-957.27.2.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) ) #1 SMP Mon Jul 29 17:46:05 UTC 2019 2. uname 命令 这个命令能查到信息，但似乎只有内核信息，没有系统层面的\nBash 1 2 [root@example-host ~]# uname -a Linux example-host 3.10.0-957.27.2.el7.x86_64 #1 SMP Mon Jul 29 17:46:05 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux 3./etc/*-release 文件 Bash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@example-host ~]# ll /etc/*-release -rw-r--r--. 1 root root 38 Nov 23 2018 /etc/centos-release -rw-r--r--. 1 root root 393 Nov 23 2018 /etc/os-release lrwxrwxrwx. 1 root root 14 Aug 8 2019 /etc/redhat-release -\u0026gt; centos-release lrwxrwxrwx. 1 root root 14 Aug 8 2019 /etc/system-release -\u0026gt; centos-release [root@example-host ~]# cat /etc/centos-release CentOS Linux release 7.6.1810 (Core) [root@example-host ~]# cat /etc/os-release NAME=\u0026#34;CentOS Linux\u0026#34; VERSION=\u0026#34;7 (Core)\u0026#34; ID=\u0026#34;centos\u0026#34; ID_LIKE=\u0026#34;rhel fedora\u0026#34; VERSION_ID=\u0026#34;7\u0026#34; PRETTY_NAME=\u0026#34;CentOS Linux 7 (Core)\u0026#34; ANSI_COLOR=\u0026#34;0;31\u0026#34; CPE_NAME=\u0026#34;cpe:/o:centos:centos:7\u0026#34; HOME_URL=\u0026#34;https://www.centos.org/\u0026#34; BUG_REPORT_URL=\u0026#34;https://bugs.centos.org/\u0026#34; CENTOS_MANTISBT_PROJECT=\u0026#34;CentOS-7\u0026#34; CENTOS_MANTISBT_PROJECT_VERSION=\u0026#34;7\u0026#34; REDHAT_SUPPORT_PRODUCT=\u0026#34;centos\u0026#34; REDHAT_SUPPORT_PRODUCT_VERSION=\u0026#34;7\u0026#34; [root@example-host ~]# 4. dmesg 命令 某次在 busybox 环境执行时没有查到结果，原因尚未确认。\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 [root@example-host ~]# dmesg | grep \u0026#34;Linux\u0026#34; [ 0.000000] Linux version 3.10.0-957.27.2.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) ) #1 SMP Mon Jul 29 17:46:05 UTC 2019 [ 0.254399] SELinux: Initializing. [ 0.255084] SELinux: Starting in permissive mode [ 0.487992] ACPI: Added _OSI(Linux-Dell-Video) [ 1.050709] SELinux: Registering netfilter hooks [ 1.094702] Linux agpgart interface v0.103 [ 1.131062] usb usb1: Manufacturer: Linux 3.10.0-957.27.2.el7.x86_64 uhci_hcd [ 1.143529] Loaded X.509 cert \u0026#39;CentOS Linux kpatch signing key: ea0413152cde1d98ebdca3fe6f0230904c9ef717\u0026#39; [ 1.143544] Loaded X.509 cert \u0026#39;CentOS Linux Driver update signing key: 7f421ee0ab69461574bb358861dbe77762a4201b\u0026#39; [ 1.144013] Loaded X.509 cert \u0026#39;CentOS Linux kernel signing key: 520a4e2d9d553ef84201c188b87fe51b9de11a5e\u0026#39; [ 6.210714] SELinux: 2048 avtab hash slots, 112490 rules. [ 6.252057] SELinux: 2048 avtab hash slots, 112490 rules. [ 6.278128] SELinux: 8 users, 14 roles, 5036 types, 318 bools, 1 sens, 1024 cats [ 6.278131] SELinux: 129 classes, 112490 rules [ 6.281956] SELinux: Class bpf not defined in policy. [ 6.282833] SELinux: the above unknown classes and permissions will be allowed [ 6.284136] SELinux: Completing initialization. [ 6.284137] SELinux: Setting up existing superblocks. [ 6.298096] systemd[1]: Successfully loaded SELinux policy in 106.735ms. [2823938.364972] SELinux: 2048 avtab hash slots, 112490 rules. [2823938.406930] SELinux: 2048 avtab hash slots, 112490 rules. [2823938.434186] SELinux: 8 users, 14 roles, 5036 types, 318 bools, 1 sens, 1024 cats [2823938.434190] SELinux: 129 classes, 112490 rules [2823938.438594] SELinux: Class bpf not defined in policy. [2823938.439621] SELinux: the above unknown classes and permissions will be allowed [2823938.440956] SELinux: Converting 2330 SID table entries... [2824055.815791] SELinux: 2048 avtab hash slots, 112502 rules. [2824055.854177] SELinux: 2048 avtab hash slots, 112502 rules. [2824055.880975] SELinux: 8 users, 14 roles, 5036 types, 318 bools, 1 sens, 1024 cats [2824055.880978] SELinux: 129 classes, 112502 rules [2824055.885003] SELinux: Class bpf not defined in policy. [2824055.886025] SELinux: the above unknown classes and permissions will be allowed [2824055.887375] SELinux: Converting 2336 SID table entries... [root@example-host ~]# 5.lsb_release 命令 需要安装相关的包，centos7 如下，执行的命令其实是 lsb_release lsb_release -a 感觉这个方法是这几个里最麻烦的\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@example-host ~]# yum install redhat-lsb-core # 此处省略软件包下载和安装输出 [root@example-host ~]# lsb_release LSB Version: :core-4.1-amd64:core-4.1-noarch [root@example-host ~]# lsb_release -a LSB Version: :core-4.1-amd64:core-4.1-noarch Distributor ID: CentOS Description: CentOS Linux release 7.6.1810 (Core) Release: 7.6.1810 Codename: Core [root@example-host ~]# lsb_release -a LSB Version: :core-4.1-amd64:core-4.1-noarch Distributor ID: CentOS Description: CentOS Linux release 7.6.1810 (Core) Release: 7.6.1810 Codename: Core ","date":"2024-01-08","permalink":"/posts/linux-distribution-kernel-version/","title":"查看 Linux 发行版与内核版本"},{"categories":["python"],"content":"https://gist.github.com/laixintao/e9eae48a835e741969ae06af3ad45f71\nPython 1 2 3 4 5 6 7 8 9 10 11 from requests.adapters import HTTPAdapter, Retry from requests import Session retries = Retry( total=5, backoff_factor=1, status_forcelist=[502, 503, 504] ) session = Session() # reuse tcp connection session.mount(\u0026#34;http://\u0026#34;, HTTPAdapter(max_retries=retries)) session.mount(\u0026#34;https://\u0026#34;, HTTPAdapter(max_retries=retries)) session.get(\u0026#34;https://example.com\u0026#34;, timeout=5) # seconds ","date":"2024-01-08","permalink":"/posts/requests-session-retry/","title":"Requests Session 重试配置笔记"},{"categories":["tools"],"content":"oh-my-zsh扫描git仓库卡慢的解决方法 oh-my-zsh在git目录下执行命令会卡顿明显，简单的cd和ls都会，原因是插件会读取git的配置信息，如果项目目录下有太多的文件，卡顿会非常明显。\n可以使用以下命令禁止zsh自动获取git信息，解决卡顿问题：\n1、设置 oh-my-zsh 不读取文件变化信息\nBash 1 git config --add oh-my-zsh.hide-dirty 1 2、设置 oh-my-zsh 不读取任何 git 信息\nBash 1 git config --add oh-my-zsh.hide-status 1 3、全局设置 oh-my-zsh 不读取文件变化信息\nBash 1 git config --global oh-my-zsh.hide-dirty 1 4、全局设置 oh-my-zsh 不读取任何 git 信息\nBash 1 git config --global oh-my-zsh.hide-status 1 如果想恢复，设置成0就好\n","date":"2024-01-08","permalink":"/posts/oh-my-zsh-git-slowness/","title":"oh-my-zsh 在 Git 仓库中卡顿的处理"},{"categories":[],"content":"一、概念: 什么是ARTS ARST 是陈浩发起的一个活动, 原文链接 https://www.zhihu.com/question/301150832/answer/529809529\nARST 分别代表了四个单词，四个动作\nAlgorithm 每周至少做一个 Leetcode 的算法题。主要是为了编程训练和学习。如果不训练你看再多的算法书，你依然不会做算法题。关于做 Leetcode 的优势，你可以看一下我在 coolshell 上的文章。\nReview 阅读并点评至少一篇英文技术文章。主要是为了学习英文，如果你的英文不行，你基本上无缘技术高手；我个人最喜欢去的地方是 Medium（需要梯子）以及各个公司的技术 blog，如 Netflix 的。\nTip 学习至少一个技术技巧。主要是为了总结和归纳你在日常工作中所遇到的知识点。\nShare 分享一篇有观点和思考的技术文章。主要是为了建立你的影响力，能够输出价值观。\n二、发起ARTS的原因(为什么要做这个事情) 我们整个世界进入了前所未有的信息爆炸时代，人们担忧的不再是无知识可学，而是有学不完的知识。而且时代的节奏变得越来越快，你可能再也不像 20 年前，可以沉着优雅平和地泡上一杯茶，坐在一个远离喧嚣的环境下，认认真真地看本书。这个时代，你再也不会有大块大块的时间，你的时间都被打成碎片了，不知不觉你也成为了快餐文化的拥趸……\n在这样一个时代下，快速、简单、轻松的方式给人带来的快感更强烈，而高层次的思考、思辨和逻辑则被这些频度高的快餐信息感所弱化。商家们看到在这样的时代里怎么治愈这些人在学习上的焦虑的商机，他们在想方设法地用一些手段推出各种代读、领读和听读类产品，让人们可以在短时间内体会到轻松获取知识的快感，并产生勤奋好学和成长的幻觉（老实说，像我这种付费专栏或是得到等知识付费产品基本上就是类似的产物）。\n这些所谓的“快餐文化”可以让你有短暂的满足感，但是无法让你有更深层次的思考和把知识转换成自己的技能的有效路径，因为那些都是需要大量时间和精力的付出，不符合现代人的生活节奏。人们开始在朋友圈、公众号、得到等这样的地方进行学习，导致他们越学越焦虑，越学越浮燥，越学越不会思考。于是，他们成了“什么都懂，但依然过不好这一生”的状态。\n你有没有发现，在知识的领域也有阶层之分，那些长期在底层知识阶层的人，需要等着高层的人来喂养，他们长期陷于各种谣言和不准确的信息环境中，于是就导致错误或幼稚的认知，并习惯于那些不费劲儿的轻度学习方式，从而一点点地丧失了深度学习的独立思考能力，从而再也没有能力打破知识阶层的限制，被困在认知底层翻不了身。\n三、高效学习 学习金字塔图\n我们可以看到，你听别人讲，或是自己看书，或是让别人演示给你，这些都不能让你真正获得学习能力，因为你是在被别人灌输，在听别人说。\n只有你开始自己思考，开始自己总结和归纳，开始找人交流讨论，开始践行，并开始对外输出，你才会掌握到真正的学习能力。\n所以，学习不是努力读更多的书，盲目追求阅读的速度和数量，这会让人产生低层次的勤奋和成长的感觉，这只是在使蛮力。要思辨，要践行，要总结和归纳，否则，你只是在机械地重复某件事（记忆知识），而不会有质的成长的。\n四、ARTS的初衷 ARTS的初衷Algorithm。主要是为了编程训练和学习。每周至少做一个 leetcode 的算法题（先从Easy开始，然后再Medium，最后才Hard）。进行编程训练，如果不训练你看再多的算法书，你依然不会做算法题，看完书后，你需要训练。关于做Leetcode的的优势，你可以看一下我在coolshell上的文章 Leetcode 编程训练 - 酷 壳 - CoolShell。Review：主要是为了学习英文，如果你的英文不行，你基本上无缘技术高手。所以，需要你阅读并点评至少一篇英文技术文章，我个人最喜欢去的地方是http://Medium.com（需要梯子）以及各个公司的技术blog，如Netflix的。Tip：主要是为了总结和归纳你在是常工作中所遇到的知识点。学习至少一个技术技巧。你在工作中遇到的问题，踩过的坑，学习的点滴知识。Share：主要是为了建立你的影响力，能够输出价值观。分享一篇有观点和思考的技术文章。这就是ARTS的全部动机。\n五、为什么要写下来 因为写作是一种非常重要的技能，写作不仅仅可以帮你组织你的思路和语言，帮你梳理你的知识，更重要的是写作其实是一种自己对自己的思考，同时还可以很容易地扩散并引入其它人的讨论、指正、批评，这对个人来说是非常重要的。\n快速创建命令 hugo 如何指定模板\n","date":"2024-01-07","permalink":"/posts/what-is-arts/","title":"What is ARTS"},{"categories":[],"content":"Algorithm Review Tip Share ","date":"2024-01-07","permalink":"/posts/arts-20240107/","title":"ARTS-20240107"},{"categories":["year","2023"],"content":"2023年做了那些事:\n坚持写周报，虽然很水，质量有待提高 java 学习计划报废 学习k8s，勉强及格 django 学习计划报废 搞定博客，但还想继续优化主题 双拼音输入法，也算是一个坑 搞了一个 plantegg 的星球，学习了 tcp 相关的知识，但还是没用起来。因该是方向错了，不应该上来就搞 tcp，而是在应用层看看 看完 wireshark 这本书 看完《蛤蟆先生去看心里医生》 看完《认知觉醒》 学习英语，多邻国坚持了 57 天，确实有些是通过零点的bug 混过去的，2024 年希望能坚持 ARST 打卡 一、遗憾\n翻了翻周报，今年一如既往的整了一些小玩意儿，应该算是证明我还没有失去热情，还能拯救下。 年初的时候，外婆离开了，猜测大概率是新冠离世，外公离开4年了，他俩下去一起陪伴了。也好，能互相又个照应，每次路过外婆家都回去看一眼，现在已经不能这么称呼了。\n今年也听说了很多厉害的人离开，但最意外的是陈浩，我也很好奇为啥我会对他的离开有深深的遗憾，在我心里他也许是一导师，在我做技术撑不下去的时候，抬头看看发现前面有个影子，我可以看看他走过的痕迹跟着走。 明明有很大的差距，但还是会继续跟着前行。可能是我在身边总是难找到志同道合的人，很多人在一定程度后就不在相信技术可以改变命运。我也没有天真认为技术真可以改变一切，但我就是想坚持走技术这条路，但身边这样的人太少了。 也许有跟我差不多的想法的人，在默默独行，这条路太难走了，我想在路上找个能说话的就行。\n二、学习\n看书几乎没啥动静，之前买pad想用来看书，还是B站香些。《认知觉醒》《wireshark》《蛤蟆先生去看心里医生》，还有很多只看一半的书，就不提了，24年想啃一本厚点技术书，不知道能不能完成。 跟别的书友不能比，但比我之前是有进步了，还是那句，希望能坚持下去。\n看完了《永生之酒》《进击的巨人》两部动漫，看动漫的一个原因，自己给自己的压力有点大，想用这种发式来解压的。从结果看这个方式效果一般，但我还是挺喜欢动漫的，喜欢大家一起讨论剧情的氛围。\n今年为数不多能夸夸自己的就是在坚持写周报，虽然还是有很多都是敷衍凑数，能坚持也是一种胜利。也勉强把自己的博客搭建起来，也算有个门面了，不在全是搭建xx这种水技术文。 也尝试了不少小技能，正在使用的双拼音输入法，remi输入法，vim高级操作，tcp抓包，wireshark使用，java的简单启动使用等等，需要继续坚持，其次就是，不在满足于玩小工具，而是能形成体系。 23年初就说了要学习java，想了n个计划，但仅仅是做了些简单的尝试，需要做更深层次的学习，说的简单点就是专注力，(查理·芒格：我的成功靠的不是智商而是专注力。) 还有学习网络知识计划，包括各种网络相关的书籍，点子实体都买了，在看书这块确实遇到不小的\u0026quot;困难\u0026quot; 在代码层面，就是想把源码好好学习，也是早就埋在心里的计划，到现在都没有执行完成。\n三、生活\n今年尝试了很多不一样的体验，更多的陪老婆，22年，我们在小区里，走遍小区的每个角落。今年把周围和小红书上的打卡点也几乎走遍。 稍微远点的就是去了绍兴，夜游拙政园，元旦又去了一趟迪士尼，准备了一些小惊喜，陪老婆过一个难忘的生日。\n今年回老家的次数比之前少了，更多的花在陪自己身上。五一也没有回家，老丈人颈椎问题，来上海做手术，做了一回孝顺的子女。 朋友和表弟也结婚了，身边单身的人又少了两个。大家都在走向\u0026quot;正轨\u0026quot;。\n唯一遗憾的点可能是陪朋友少了，陪爸妈的时间少了，跟爸妈沟通的时间少了。 因为一些原因跟一个朋友疏远了。长大的路上是不是朋友会越来越少\n四、未来\n能做好arts，坚持下去。 读完tcpip权威指南 学习java 廖雪峰，评判标准有点头疼 多陪陪家人 ","date":"2024-01-01","permalink":"/posts/2023-year-end-summary/","title":"2023年终总结"},{"categories":["Kubenertes"],"content":"kubernetes deploy ingress-controller 会启动job，是一次性任务。主要是创建证书使用\ndocker 镜像使用 orbstack 当成 docker 容器会开启 docker 最新实验特性，根据芯片下载对应的版本，这也算一个坑\nyaml 文件提供的镜像地址都是国外的，需要转换为国内的， 刚好也自建 nexus docker 仓库，需要配置服务器配置 docker 信任源\n代码 1 2 3 4 { \u0026#34;storage-driver\u0026#34;: \u0026#34;overlay2\u0026#34;, \u0026#34;insecure-registries\u0026#34; : [\u0026#34;registry.example.com:5000\u0026#34;] } 当前对应的k8s版本是 1.23，(官网提示)能安装的 ingress 版本 v1.3.0、v1.3.1、v1.4.0、v1.5.1、v1.6.4\n之前找到一个 ingress 安装入门文档，结果文档太老，使用的是 v0.15.0镜像，对应的部署控制器还是 rc ，这个控制器在 0.47这个版本里已经被删除了。 相关文档链接 ，吐槽一句，国内文档还是有点跨，不够新。\nhttps://blog.csdn.net/zhou920786312/article/details/126246557 比较老\nhttps://ytool.cloud/k8s/net/ingress/install.html ，这个文档比较新，但我没有采用，因为不是很想用helm 无脑安装，不清楚做了啥\nhttps://www.cnblogs.com/wtzbk/p/15427237.html 这个版本比较新，但我没用 https://cloud.tencent.com/developer/article/1579931 这个讲的也行，只有回过头才知道哪些是对的\n代码 1 registry.k8s.io/ingress-nginx/controller:v1.9.4@sha256:5b161f051d017e55d358435f295f5e9a297e66158f136321d9b04520ec6c48a3 后面的@sha 表示文件的校验码，类似 md5，我直接下载这个链接的时候，发现下载的镜像没有对应的版本，后来还是指定版本了\n安装 ingress 的 yaml 文件里有job，这个是一次性的任务，如果执行报错了，再次执行\n代码 1 kubectl apply -f ingress-nginx.yaml 会提示报错，需要执行删除 job 命令\nBash 1 kubectl delete job --all -n ingress-nginx --context=huawei 查看容器是否启动，直接看应用是否 ready\nBash 1 kubectl get po -n ingress-nginx --context=huawei -w -w 表示\u0026ndash;watch，就是监视，在 watch 的时候出现一次弹出多条重复的信息，表示有多个变动，只是展示信息不够详细 只能看到多条重复信息，不是实现故障，信息重复\n容器未启动需要查看事件 events\nBash 1 kubectl describe po xxx 查看事件只提示拉镜像成功，没有更多的有用信息，就需要在看看容器日志\nBash 1 kubectl logs xxx ingress-nginx.yaml 文件解释\n创建两个账户 ServiceAccount\ningress-nginx ingress-nginx-admission 区别在于 ingress-nginx 设置了 automountServiceAccountToken: true 属于 namespace 下自动调用的 token，不需要在调用时指定 ServiceAccountName 代码 1 2 使用默认的 Service Account 访问 API server 当您创建 pod 的时候，如果您没有指定一个 service account，系统会自动得在与该pod 相同的 namespace 下为其指派一个default service account。如果您获取刚创建的 pod 的原始 json 或 yaml 信息（例如使用kubectl get pods/podename -o yaml命令），您将看到spec.serviceAccountName字段已经被设置为 default。 然后就是对应的 Role ClusterRole RoleBinding ClusterRoleBinding\nServiceAccount(ingress-nginx) \u0026ndash;RoleBinding\u0026ndash;\u0026gt; Role(ingress-nginx) ServiceAccount(ingress-nginx) \u0026ndash;ClusterRoleBinding\u0026ndash;\u0026gt; ClusterRole(ingress-nginx)\nServiceAccount(ingress-nginx-admission) \u0026ndash;RoleBinding\u0026ndash;\u0026gt; Role(ingress-nginx-admission) ServiceAccount(ingress-nginx-admission) \u0026ndash;ClusterRoleBinding\u0026ndash;\u0026gt; ClusterRole(ingress-nginx-admission)\n标准的 rbac 那一套，指定当前账户有哪个角色，角色对应的有哪些资源(pod/configmap/service\u0026hellip;)的哪些权限(get/list/update/watch\u0026hellip;)\n在 deployment 中用到了 serviceAccountName: ingress-nginx 在 2 个 job 中用到了 serviceAccountName: ingress-nginx-admission\n下面是一个摘要\nYAML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 apiVersion: apps/v1 kind: Deployment metadata: labels:... name: ingress-nginx-controller namespace: ingress-nginx spec: minReadySeconds: 0 revisionHistoryLimit: 10 selector: matchLabels: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx strategy:... template: metadata: labels:... spec: containers: - args:... dnsPolicy: ClusterFirst nodeSelector:... serviceAccountName: ingress-nginx terminationGracePeriodSeconds: 300 volumes:... YAML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 apiVersion: batch/v1 kind: Job metadata: labels: app.kubernetes.io/component: admission-webhook app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/version: 1.5.1 name: ingress-nginx-admission-create namespace: ingress-nginx spec: template: metadata: labels: app.kubernetes.io/component: admission-webhook app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/version: 1.5.1 name: ingress-nginx-admission-create spec: containers: - args: - create - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc - --namespace=$(POD_NAMESPACE) - --secret-name=ingress-nginx-admission env: - name: POD_NAMESPACE valueFrom:... image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20220916-gd32f8c343@sha256:39c5b2e3310dc4264d638ad28d9d1d96c4cbb2b2dcfb52368fe4e3c63f61e10f imagePullPolicy: IfNotPresent name: create securityContext:... nodeSelector:... restartPolicy: OnFailure securityContext:... serviceAccountName: ingress-nginx-admission ValidatingWebhookConfiguration 描述准入Webhook 的配置，该Webhook 可在不更改对象的情况下接受或拒绝对象请求。\n准入 Webhook 是一种用于接收准入请求并对其进行处理的 HTTP 回调机制。 可以定义两种类型的准入 Webhook， 即验证性质的准入 Webhook 和变更性质的准入 Webhook。 变更性质的准入 Webhook 会先被调用。它们可以修改发送到 API 服务器的对象以执行自定义的设置默认值操作。\n","date":"2023-11-17","permalink":"/posts/ingress-nginx-deployment-notes/","title":"Ingress NGINX 部署与 RBAC 学习笔记"},{"categories":["tools"],"content":"python requirements 忽略报错 在安装 python requirements 时，总是会遇到依赖版本不匹配，忽略后又找不到开关。在此把相关截图备注下。\npycharm 提示requirements 文件里有相关依赖包没安装或者版本不匹配。如果不小心点了忽略，后续就找不到在哪里打开。\n目录在 pycharm 里的 editor -\u0026gt; inspections -\u0026gt; package requeirements\n如果将整个菜单都不够选，就会忽略整个 requeirements.txt 文件 但只想忽略某几个包，可以在右下侧把对应的报名加进去。如下图 ","date":"2023-11-12","permalink":"/posts/pycharm-package-requirements-inspection/","title":"PyCharm requirements 检查设置"},{"categories":["python"],"content":"oss 文件上传 oss 本地文件上传方案 背景 自己写了一个服务需要文件上传功能，但会遇到文件很大的情况，也经常遇到文件上传到一半突然断掉，这让我很烦恼，服务器很难排查问题，甚至远程用户的电脑完全复现却还是不能定位问题，于是想到 oss 本地文件直接上传到阿里云的 oss 服务器，能节保证文件上传的稳定性。\n下面是阿里云提供的方案调用图\n这个方案的实施背景依赖阿里云的API，阿里云提供了一个sts token方案。可以做临时授权，这样才能做到前端上传，且不用把账号密码写到前端代码里\n使用STS临时访问凭证访问OSS官方文档链接\n流程分析\n步骤一：创建RAM用户 步骤二：为RAM用户授予请求AssumeRole的权限 步骤三：创建用于获取临时访问凭证的角色 步骤四：为角色授予上传文件的权限 步骤五：获取临时访问凭证 步骤六：使用临时访问凭证上传文件至OSS sts生成本地临时token 通过阿里云sts sdk获取临时token，附加时效、文件名限制\n获取扮演角色的临时身份凭证 通过调用AssumeRole接口，获取一个扮演RAM角色的临时身份凭证（STS Token）\n下面是我自己封装的代码\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import logging from alibabacloud_sts20150401.client import Client as Sts20150401Client from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_sts20150401 import models as sts_20150401_models from alibabacloud_tea_util import models as util_models logger = logging.getLogger(__name__) class StsTokenGenerate: \u0026#34;\u0026#34;\u0026#34; aliyun sts 接口，通用封装 endpoint 默认为北京 duration_seconds 最低值为15min/1hr policy=\u0026#39;{\u0026#34;Statement\u0026#34;: [{\u0026#34;Action\u0026#34;: [\u0026#34;*\u0026#34;],\u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;,\u0026#34;Resource\u0026#34;: [\u0026#34;*\u0026#34;]}],\u0026#34;Version\u0026#34;:\u0026#34;1\u0026#34;}\u0026#39; \u0026#34;\u0026#34;\u0026#34; @staticmethod def get_sts_token(access_key_id, access_key_secret, role_arn, session_name, sts_policy=None, duration_seconds=900, endpoint=\u0026#39;sts.cn-beijing.aliyuncs.com\u0026#39;): config = open_api_models.Config( access_key_id=access_key_id, access_key_secret=access_key_secret ) config.endpoint = endpoint client = Sts20150401Client(config) request_params = { \u0026#39;role_arn\u0026#39;: role_arn, \u0026#39;role_session_name\u0026#39;: session_name, \u0026#39;duration_seconds\u0026#39;: duration_seconds } if sts_policy: request_params[\u0026#39;policy\u0026#39;] = sts_policy assume_role_request = sts_20150401_models.AssumeRoleRequest(**request_params) runtime = util_models.RuntimeOptions() return client.assume_role_with_options(assume_role_request, runtime).to_map().get(\u0026#39;body\u0026#39;) RAM角色和STS Token常见问题\n前端使用oss sdk上传 通过阿里云sts sdk获取临时token，附加时效、文件名限制\nNode.js oss 文件 分片上传\n下面是使用 vue 封装代码，包括自动生成 MD5 码，异步逻辑处理，回调等问题\nvue 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 importFile(file, fileList) { // 由于文件过大时会导致页面崩溃，放弃改方案 // https://github.com/forsigner/browser-md5-file // 浏览器获取文件 md5 值 // https://juejin.cn/post/7023763648833126408 // MD5的实现方式 var objFile = file.raw const bmf = new BMF() // const el = document.getElementById(\u0026#39;upload\u0026#39;); // el.addEventListener(\u0026#39;change\u0026#39;, handle, false);//VUE使用这个会报错 bmf.md5( objFile, (err, md5) =\u0026gt; { if (err) { console.log(\u0026#39;err:\u0026#39;, err) } this.uploadForm.file_md5 = md5 }, progress =\u0026gt; { if (progress === 1) { console.log(\u0026#39;md5 生成完成\u0026#39;) } } ) }, async uploadFile(file) { if (!(this.uploadForm.remote_ip \u0026amp;\u0026amp; this.uploadForm.file_desc)) { this.$message({ message: \u0026#39;请选择服务器ip或备注信息\u0026#39;, type: \u0026#39;warning\u0026#39; }) return } const now = new Date() const currentTime = now.getFullYear().toString() + (now.getMonth() + 1) + now.getDate() + now.getMinutes() + now.getHours() + now.getSeconds() this.uploadForm.file_path = \u0026#39;file/\u0026#39; + currentTime + \u0026#39;_\u0026#39; + file.file.name // 获取oss文件上传token let res try { res = await getUploadStsToken({ \u0026#39;file_path\u0026#39;: this.uploadForm.file_path }) this.loading = true this.Loading = true } catch (error) { console.log(\u0026#39;upload\u0026#39;, error) this.$message({ message: \u0026#39;文件大小超出限制,请重新选择\u0026#39;, type: \u0026#39;warning\u0026#39; }) return } const credentials = res.data // 如需保存应用元数据，可在上传成功后单独处理。 const OSS = require(\u0026#39;ali-oss\u0026#39;) const client = new OSS({ region: \u0026#39;oss-\u0026#39; + credentials.region, accessKeyId: credentials.AccessKeyId, accessKeySecret: credentials.AccessKeySecret, stsToken: credentials.SecurityToken, bucket: credentials.bucket }) const progress = async(p, _checkpoint) =\u0026gt; { this.percentage = Math.floor(p * 100) console.log(this.percentage) if (this.percentage === 100) { this.fileList = [] this.loading = false this.Loading = false } } try { this.uploadForm.file_name = file.file.name this.uploadForm.file_size = file.file.size client.multipartUpload(this.uploadForm.file_path, file.file, { progress }).catch(e =\u0026gt; { this.loading = false this.Loading = false console.log(\u0026#39;文件上传oss异常 multipartUpload\u0026#39; + e) this.$notify.error({ title: \u0026#39;错误\u0026#39;, message: \u0026#39;文件上传oss异常\u0026#39; }) }) } catch (e) { console.log(\u0026#39;文件上传oss异常\u0026#39; + e) this.loading = false this.Loading = false this.$notify.error({ title: \u0026#39;错误\u0026#39;, message: \u0026#39;文件上传oss异常\u0026#39; }) } }, ","date":"2023-11-12","permalink":"/posts/oss-sts-direct-upload/","title":"使用 STS 临时授权直接上传 OSS"},{"categories":["python"],"content":"问题django启动异常 提示跟包 cffi 有关，甚至还和 arm 芯片有关\n代码 1 2 3 4 5 6 7 8 这里是相关的关键字 mach-o file, but is an incompatible architecture (have \u0026#39;x86_64\u0026#39;, need \u0026#39;arm64\u0026#39;) from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions pyo3_runtime.PanicException: Python API call failed pyo3-0.18.3/src/err/mod.rs:790:5 No module named _cffi_backend 后续安装\n代码 1 2 pip uninstall cffi pip install cffi==1.5.2 ","date":"2023-11-09","permalink":"/posts/cffi-apple-silicon-error-notes/","title":"Apple Silicon cffi 架构错误排查记录"},{"categories":["django"],"content":"drf渲染模块源码分析 原文链接\n搜了好多源码文章，都只是简单介绍 drf 的 rest_framework.response.Response 方法，这块的源码虽然没有完全读懂，但是也看了大概，感觉没有讲到点子上 都没有跟 DEFAULT_RENDERER_CLASSES 这个关联起来。\n前言 渲染模块的原理和解析模块是一样，drf默认的渲染有2种方式，一种是json格式，另一种是模板方式。\n渲染模块源码入口 入口：APIView类中dispatch方法中的：self.response = self.finalize_response(request, response, *args, **kwargs)\n渲染模块源码分析 我们首先点击finalize_response进入查看源码\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def finalize_response(self, request, response, *args, **kwargs): \u0026#34;\u0026#34;\u0026#34; Returns the final response object. \u0026#34;\u0026#34;\u0026#34; # 断言是否是HttpResponseBase对象 # Make the error obvious if a proper response is not returned assert isinstance(response, HttpResponseBase), ( \u0026#39;Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` \u0026#39; \u0026#39;to be returned from the view, but received a `%s`\u0026#39; % type(response) ) # 判断是否是Response对象 if isinstance(response, Response): if not getattr(request, \u0026#39;accepted_renderer\u0026#39;, None): neg = self.perform_content_negotiation(request, force=True) request.accepted_renderer, request.accepted_media_type = neg response.accepted_renderer = request.accepted_renderer response.accepted_media_type = request.accepted_media_type response.renderer_context = self.get_renderer_context() # Add new vary headers to the response instead of overwriting. vary_headers = self.headers.pop(\u0026#39;Vary\u0026#39;, None) if vary_headers is not None: patch_vary_headers(response, cc_delim_re.split(vary_headers)) for key, value in self.headers.items(): response[key] = value return response 上述代码是响应模块的源码，该源码中包含了渲染模块的源码，就是这句neg = self.perform_content_negotiation(request, force=True)，我们可以点击查看\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def perform_content_negotiation(self, request, force=False): \u0026#34;\u0026#34;\u0026#34; Determine which renderer and media type to use render the response. \u0026#34;\u0026#34;\u0026#34; # 确定使用哪种渲染器和媒体类型来渲染响应。 # 渲染器列表 renderers = self.get_renderers() conneg = self.get_content_negotiator() try: return conneg.select_renderer(request, renderers, self.format_kwarg) except Exception: if force: return (renderers[0], renderers[0].media_type) raise 代码中有个get_renderers就是渲染器列表，点击查看\nPython 1 2 3 4 5 def get_renderers(self): \u0026#34;\u0026#34;\u0026#34; Instantiates and returns the list of renderers that this view can use. \u0026#34;\u0026#34;\u0026#34; return [renderer() for renderer in self.renderer_classes] 这就是渲染器列表的源码，跟解析器列表的源码大同小异，接着再点击renderer_classer查看\nPython 1 renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES 到这里我们就知道了，drf默认的渲染器在settings下的DEFAUIT_RENDERER_CLASSES中，配置如下\nPython 1 2 3 4 5 6 7 8 9 10 11 12 DEFAULTS = { # Base API policies \u0026#39;DEFAULT_RENDERER_CLASSES\u0026#39;: [ \u0026#39;rest_framework.renderers.JSONRenderer\u0026#39;, \u0026#39;rest_framework.renderers.BrowsableAPIRenderer\u0026#39;, ], \u0026#39;DEFAULT_PARSER_CLASSES\u0026#39;: [ \u0026#39;rest_framework.parsers.JSONParser\u0026#39;, \u0026#39;rest_framework.parsers.FormParser\u0026#39;, \u0026#39;rest_framework.parsers.MultiPartParser\u0026#39; ], } 如果我们想局部配置渲染器，只需在自己定义的视图类中添加render_classes 即可，一般也不用做修改，以上分析只是让大家知道drf是怎么配置渲染器的，这样以后我们自定义渲染器也就十分简单了\n参考 https://cloud.tencent.com/developer/article/2114529\n","date":"2023-10-25","permalink":"/posts/drf-response-rendering/","title":"DRF finalize_response 与渲染器配置"},{"categories":["django"],"content":"allowed_methods Python 1 2 3 4 5 6 7 8 class APIView(View): ... @property def allowed_methods(self): \u0026#34;\u0026#34;\u0026#34; Wrap Django\u0026#39;s private `_allowed_methods` interface in a public property. \u0026#34;\u0026#34;\u0026#34; return self._allowed_methods() Python 1 2 def _allowed_methods(self): return [m.upper() for m in self.http_method_names if hasattr(self, m)] Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class View: \u0026#34;\u0026#34;\u0026#34; Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking. \u0026#34;\u0026#34;\u0026#34; http_method_names = [ \u0026#34;get\u0026#34;, \u0026#34;post\u0026#34;, \u0026#34;put\u0026#34;, \u0026#34;patch\u0026#34;, \u0026#34;delete\u0026#34;, \u0026#34;head\u0026#34;, \u0026#34;options\u0026#34;, \u0026#34;trace\u0026#34;, ] ","date":"2023-10-25","permalink":"/posts/drf-allowed-methods-source-notes/","title":"Django 与 DRF allowed_methods 源码笔记"},{"categories":["python"],"content":"pyenv install\n官方文档 pyenv office doc\nhttps://github.com/pyenv/pyenv\n默认简单方式 brew install pyenv\n代码 1 2 3 4 5 6 # Github # curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash # recommand curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash # simple curl https://pyenv.run | bash 查看当前版本 代码 1 pyenv versions 查看当前可安装版本 代码 1 pyenv install -l 安装对应版本 Intel 芯片安装\n代码 1 pyenv install 3.10.6 Arm 芯片安装\n代码 1 2 export ARCHFLAGS=\u0026#34;-arch arm64\u0026#34;; pyenv install 3.10.6 export ARCHFLAGS=\u0026#34;-arch arm64\u0026#34;;arch --arm64 pyenv install 3.7.15 一个神奇的点，使用kitty终端能安装成功，但用secretCRT却不行\n查看对应的版本是否存在，可以去指定目录下看是否有对应版本号\n代码 1 2 echo $(pyenv root) /usr/local/var/pyenv/versions/ 设置当前使用的python版本 代码 1 2 3 4 5 6 7 8 9 10 11 #配置当前shell的python版本，退出shell则失效 pyenv shell 3.7.17 #配置所在项目（目录）的python版本 pyenv local 3.7.17 #配置当前用户的系统使用的python版本 pyenv global 3.7.17 #查看当前版本 python -V install python develop\nBash 1 2 3 4 5 6 7 8 9 10 export ARCHFLAGS=\u0026#34;-arch arm64\u0026#34;; pyenv install 3.8-dev python-build: use openssl@1.1 from homebrew python-build: use readline from homebrew Cloning https://github.com/python/cpython... Installing Python-3.8-dev... python-build: use readline from homebrew python-build: use zlib from xcode sdk Installed Python-3.8-dev to /usr/local/var/pyenv/versions/3.8-dev cd /usr/local/var/pyenv/versions/3.8-dev 查看安装报错详情 代码 1 pyenv install 3.9.15 --verbose 安装失败 报错提示缺少lib，搜索关键字ModuleNotFoundError: No module named '_lzma' WARNING: The Python lzma extension was not compiled. Missing the lzma lib\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 admin@admins-MacBook-Pro ~ % export ARCHFLAGS=\u0026#34;-arch arm64\u0026#34;;arch --arm64 pyenv install 3.7.15 pyenv: /Users/example/.pyenv/versions/3.7.15 already exists continue with installation? (y/N) y python-build: use openssl from homebrew python-build: use readline from homebrew Downloading Python-3.7.15.tar.xz... -\u0026gt; https://www.python.org/ftp/python/3.7.15/Python-3.7.15.tar.xz Installing Python-3.7.15... patching file \u0026#39;Doc/library/ctypes.rst\u0026#39; patching file \u0026#39;Lib/test/test_unicode.py\u0026#39; patching file \u0026#39;Modules/_ctypes/_ctypes.c\u0026#39; patching file \u0026#39;Modules/_ctypes/callproc.c\u0026#39; patching file \u0026#39;Modules/_ctypes/ctypes.h\u0026#39; patching file setup.py patching file \u0026#39;Misc/NEWS.d/next/Core and Builtins/2020-06-30-04-44-29.bpo-41100.PJwA6F.rst\u0026#39; patching file \u0026#39;Modules/_decimal/libmpdec/mpdecimal.h\u0026#39; patching file setup.py python-build: use readline from homebrew python-build: use zlib from xcode sdk Traceback (most recent call last): File \u0026#34;\u0026lt;string\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt; File \u0026#34;/Users/example/.pyenv/versions/3.7.15/lib/python3.7/lzma.py\u0026#34;, line 27, in \u0026lt;module\u0026gt; from _lzma import * ModuleNotFoundError: No module named \u0026#39;_lzma\u0026#39; WARNING: The Python lzma extension was not compiled. Missing the lzma lib? Installed Python-3.7.15 to /Users/example/.pyenv/versions/3.7.15 解决方案，安装对应的包\n代码 1 brew install readline xz arm 芯片 3.9 版本安装问题 安装python3.9.15时，提示readline-8.1.tar.gz包下载失败，报错如下。\n手动从网上找到一个地址下载了这个包。想能不能自己搭建个服务器提供下载。但因为是https，无法进行host写死转发下载。于是被迫看执行脚本，看看能不能搜索到执行脚本，然后更改下载源。\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 clin:Project/ $ pyenv install 3.9.15 --verbose python-build: use openssl@1.1 from homebrew /var/folders/y7/s2k99dl93dz6gwl73yxjtn2c0000gn/T/python-build.20240124165710.23936 ~/File/Project Downloading readline-8.1.tar.gz... HTTP/1.1 200 Connection established HTTP/2 404 server: GitHub.com content-type: text/html; charset=utf-8 permissions-policy: interest-cohort=() access-control-allow-origin: * etag: \u0026#34;64d39a40-24a3\u0026#34; content-security-policy: default-src \u0026#39;none\u0026#39;; style-src \u0026#39;unsafe-inline\u0026#39;; img-src data:; connect-src \u0026#39;self\u0026#39; x-proxy-cache: MISS x-github-request-id: 6004:22C29E:99AB1:AFE1F:65B0CC14 accept-ranges: bytes date: Wed, 24 Jan 2024 08:57:11 GMT via: 1.1 varnish age: 1235 x-served-by: cache-qpg1234-QPG x-cache: HIT x-cache-hits: 1 x-timer: S1706086632.903138,VS0,VE2 vary: Accept-Encoding x-fastly-request-id: aac04c88e0b4189654405bc0e51408bad9c54033 content-length: 9379 -\u0026gt; https://ftpmirror.gnu.org/readline/readline-8.1.tar.gz curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to mirror.ossplanet.net:443 error: failed to download readline-8.1.tar.gz BUILD FAILED (OS X 14.2.1 using python-build 20180424) Results logged to /var/folders/y7/s2k99dl93dz6gwl73yxjtn2c0000gn/T/python-build.20240124165710.23936.log Last 10 log lines: age: 1235 x-served-by: cache-qpg1234-QPG x-cache: HIT x-cache-hits: 1 x-timer: S1706086632.903138,VS0,VE2 vary: Accept-Encoding x-fastly-request-id: aac04c88e0b4189654405bc0e51408bad9c54033 content-length: 9379 curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to mirror.ossplanet.net:443 通过多次关键字搜索，在git上发现脚本，https://github.com/pyenv/pyenv/blob/2374260efa9af987f2c046475dcd22804a81fb6d/plugins/python-build/share/python-build/3.9.17#L7\nBash 1 2 3 4 5 6 7 8 9 10 11 12 prefer_openssl11 export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1 # Avoid a compilation error when linking against OpenSSL built with SSLv3 support (fixed in 3.10.0) (#2181) export PYTHON_CFLAGS=\u0026#34;-DOPENSSL_NO_SSL3${PYTHON_CFLAGS:+ $PYTHON_CFLAGS}\u0026#34; install_package \u0026#34;openssl-1.1.1u\u0026#34; \u0026#34;https://www.openssl.org/source/openssl-1.1.1u.tar.gz#e2f8d84b523eecd06c7be7626830370300fbcc15386bf5142d72758f6963ebc6\u0026#34; mac_openssl --if has_broken_mac_openssl install_package \u0026#34;readline-8.1\u0026#34; \u0026#34;https://ftpmirror.gnu.org/readline/readline-8.1.tar.gz#f8ceb4ee131e3232226a17f51b164afc46cd0b9e6cef344be87c65962cb82b02\u0026#34; mac_readline --if has_broken_mac_readline if has_tar_xz_support; then install_package \u0026#34;Python-3.9.17\u0026#34; \u0026#34;https://www.python.org/ftp/python/3.9.17/Python-3.9.17.tar.xz#30ce057c44f283f8ed93606ccbdb8d51dd526bdc4c62cce5e0dc217bfa3e8cee\u0026#34; standard verify_py39 copy_python_gdb ensurepip else install_package \u0026#34;Python-3.9.17\u0026#34; \u0026#34;https://www.python.org/ftp/python/3.9.17/Python-3.9.17.tgz#8ead58f669f7e19d777c3556b62fae29a81d7f06a7122ff9bc57f7dd82d7e014\u0026#34; standard verify_py39 copy_python_gdb ensurepip fi 于是搜了下本地，发现了类似的脚本。 本地执行的时候发现执行的命令其实是python-build，于是开始排查这是不是执行脚本，里面有没有readline相关的包或关键字\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 clin:Project/ $ which python-build /opt/homebrew/bin/python-build clin:Project/ $ ll /opt/homebrew/bin/python-build lrwxr-xr-x@ 1 clin admin 38B Aug 21 10:37 /opt/homebrew/bin/python-build -\u0026gt; ../Cellar/pyenv/2.3.6/bin/python-build clin:Project/ $ cd /opt/homebrew/bin/ clin:bin/ (stable) $ cd ../Cellar/pyenv/ clin:pyenv/ (stable) $ ll total 0 drwxr-xr-x 22 clin admin 704B Nov 14 2022 2.3.6 clin:pyenv/ (stable) $ cd 2.3.6 clin:2.3.6/ (stable) $ ll total 440 -rw-r--r-- 1 clin admin 41K Nov 3 2022 CHANGELOG.md -rw-r--r-- 1 clin admin 11K Nov 3 2022 COMMANDS.md -rw-r--r-- 1 clin admin 3.3K Nov 3 2022 CONDUCT.md -rw-r--r-- 1 clin admin 6.7K Nov 3 2022 CONTRIBUTING.md -rw-r--r-- 1 clin admin 680B Nov 3 2022 Dockerfile -rw-r--r-- 1 clin admin 1.6K Nov 14 2022 INSTALL_RECEIPT.json -rw-r--r-- 1 clin admin 1.1K Nov 3 2022 LICENSE -rw-r--r-- 1 clin admin 1.0K Nov 3 2022 MAINTENANCE.md -rw-r--r-- 1 clin admin 928B Nov 3 2022 Makefile -rw-r--r-- 1 clin admin 26K Nov 3 2022 README.md drwxr-xr-x 6 clin admin 192B Nov 3 2022 bin drwxr-xr-x 5 clin admin 160B Nov 3 2022 completions drwxr-xr-x 29 clin admin 928B Nov 14 2022 libexec drwxr-xr-x 4 clin admin 128B Nov 3 2022 plugins drwxr-xr-x 5 clin admin 160B Nov 3 2022 pyenv.d drwxr-xr-x 3 clin admin 96B Nov 3 2022 share drwxr-xr-x 9 clin admin 288B Nov 3 2022 src -rw-r--r-- 1 clin admin 102K Nov 3 2022 terminal_output.png drwxr-xr-x 30 clin admin 960B Nov 3 2022 test 通过上面的发现执行脚本就是pyenv每次更新，然后去拉取对应版本的执行脚本\n代码 1 2 clin:python-build/ (stable) $ pwd /opt/homebrew/Cellar/pyenv/2.3.6/plugins/python-build/share/python-build 在这个目录下有对应版本的执行脚本，里面就有readline包下载地址，更新后就可以正常跑了。之前不清楚这些脚本都是怎么运行的，以为都是各种特殊的逻辑进行安装。现在发现都是类似打表，有人维护好各种兼容脚本供大家使用。加上互联网的支持，及时更新。\n","date":"2023-10-23","permalink":"/posts/pyenv-install-troubleshooting/","title":"pyenv 安装与 Apple Silicon 故障排查"},{"categories":["Linux"],"content":"[user@local ~]$ cat trap.sh #!/bin/bash trap \u0026ldquo;echo xxxxx; exit\u0026rdquo; SIGINT mkdir -p /home/username/traptest/docs while : do echo $(date) Writing fortune to /home/username/traptest/docs/index.html echo \u0026ldquo;do something $(date)\u0026rdquo; \u0026gt; /home/username/traptest/docs/index.html sleep 10 done\n","date":"2023-10-06","permalink":"/posts/bash-trap-sigint/","title":"Bash trap 捕获 SIGINT 的示例"},{"categories":["django"],"content":"django restful 可以用 create retrive 等方法，好奇源码里怎么做的\n入口方法，ViewSet 代码 1 2 3 4 5 6 7 8 9 10 11 class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet): \u0026#34;\u0026#34;\u0026#34; A viewset that provides default `create()`, `retrieve()`, `update()`, `partial_update()`, `destroy()` and `list()` actions. \u0026#34;\u0026#34;\u0026#34; pass 继承两种类，对应方法的视图类\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class CreateModelMixin: \u0026#34;\u0026#34;\u0026#34; Create a model instance. \u0026#34;\u0026#34;\u0026#34; def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer): serializer.save() def get_success_headers(self, data): try: return {\u0026#39;Location\u0026#39;: str(data[api_settings.URL_FIELD_NAME])} except (TypeError, KeyError): return {} class ListModelMixin: \u0026#34;\u0026#34;\u0026#34; List a queryset. \u0026#34;\u0026#34;\u0026#34; def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) 通用视图类\nmixin 包含了 create list update class GenericViewSet(ViewSetMixin, generics.GenericAPIView): \u0026quot;\u0026quot;\u0026quot; The GenericViewSet class does not provide any actions by default, but does include the base set of generic view behavior, such as the get_object and get_queryset methods. \u0026quot;\u0026quot;\u0026quot; pass\nViewSetMixin 这个算是个魔术方法，将action与method对应起来，在view里没有看到相关的代码\n原来在 register 里也有设置，在路由上配置了 action 与 method 的 map，所以源码里一直看不到。\nhttps://juejin.cn/post/7200200835232481341\n","date":"2023-09-25","permalink":"/posts/drf-viewset-action-routing/","title":"DRF ViewSet action 与 HTTP method 映射"},{"categories":["tools"],"content":"vue-element-admin集成Keycloak实现统一身份验证、权限控制 vue-element-admin集成Keycloak实现统一身份验证、权限控制\nvue-element-admin vue-element-admin github link\n我的前端使用 vue-element-admin，接入 Keycloak 后希望共享登录会话，减少重复登录。\n既然是要将vue-element-admin与Keycloak进行集成，那么有必要先来对vue-element-admin本身的登录、身份验证、权限控制相关的功能是如何实现的进行一个了解。核心代码主要位于如下几个文件中：\nsrc/permission.js：针对vue-router进行的全局导航守卫配置，身份验证、权限控制最为核心的逻辑都在这个文件中 src/router/index.js：vue-router相关的路由配置，其中asyncRoutes是与roles相关的动态路由配置 src/modules/permission.js：根据roles进行权限控制、生成动态路由的vuex相关操作 src/modules/user.js：登录登出、用户信息、Token等的vuex相关操作 src/api/user.js：登录登出、用户信息的API操作 src/views/login/index.vue：登录页面 src/layout/components/Navbar.vue：登出入口所在组件 接入思路 前提概要，系统原本有一套权限系统，暂时不想完全使用 keycloak 的角色，仅使用登录方案，免去重复登录。\n去公共 keycloak 地址访问 session，如果没有则跳转到登录页，获取 token 后发送给后端，后端拿到 token 后去校验， 然后返回系统原来的 token 方案，保存，后续请求使用。\n前端接入配置 使用 keycloak 组件对接，配置好访问地址;\n这里有个 URL 要跟当前域名使用的协议一致，如果一个是 https 另一个是 http，这会出现跨域问题。\n在 src/modules/permission.js 拦截器里执行登录，因为请求参数变化，后端也需要更新。\n需要将登录、用户认证信息获取、登出部分进行改动，交给Keycloak处理。我们将对如下文件进行修改：\nsrc/store/modules/user.js：将vuex中登录、获取用户信息、登出相关的action改为通过Keycloak进行管理 src/main.js：添加Keycloak初始化集成，将身份验证及登录部分交给Keycloak接管 src/permission.js：获取roles部分改为从Keycloak获取 src/layout/components/Navbar.vue：登出逻辑改为调用Keycloak的登出 文件更新 安装 keycloak-js vuejs-logger 代码 1 2 \u0026#34;keycloak-js\u0026#34;: \u0026#34;^22.0.1\u0026#34;, \u0026#34;vuejs-logger\u0026#34;: \u0026#34;^1.5.5\u0026#34;, env 配置 代码 1 2 3 4 VUE_APP_KEYCLOAK_OPTIONS_URL = \u0026#39;http://keycloak.domain.net/auth\u0026#39; VUE_APP_KEYCLOAK_OPTIONS_REALM = \u0026#39;realm\u0026#39; VUE_APP_KEYCLOAK_OPTIONS_CLIENTID = \u0026#39;client-demo\u0026#39; VUE_APP_KEYCLOAK_OPTIONS_ONLOAD = \u0026#39;login-required\u0026#39; src/layout/components/Navbar.vue 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 \u0026lt;script\u0026gt; -import { mapGetters } from \u0026#39;vuex\u0026#39; import Breadcrumb from \u0026#39;@/components/Breadcrumb\u0026#39; import Hamburger from \u0026#39;@/components/Hamburger\u0026#39; +import { mapGetters } from \u0026#39;vuex\u0026#39; // import ErrorLog from \u0026#39;@/components/ErrorLog\u0026#39; // import Screenfull from \u0026#39;@/components/Screenfull\u0026#39; // import SizeSelect from \u0026#39;@/components/SizeSelect\u0026#39; ... }, async logout() { await this.$store.dispatch(\u0026#39;user/logout\u0026#39;) - this.$router.push(`/login?redirect=${this.$route.fullPath}`) + // this.$router.push(`/login?redirect=${this.$route.fullPath}`) + this.$router.push(`/?redirect=${this.$route.fullPath}`) } } } src/permission.js 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 -import { getToken } from \u0026#39;@/utils/auth\u0026#39; // get token from cookie +// get token from cookie +import { getToken } from \u0026#39;@/utils/auth\u0026#39; import getPageTitle from \u0026#39;@/utils/get-page-title\u0026#39; import { Message } from \u0026#39;element-ui\u0026#39; -import NProgress from \u0026#39;nprogress\u0026#39; // progress bar -import \u0026#39;nprogress/nprogress.css\u0026#39; // progress bar style +import Keycloak from \u0026#39;keycloak-js\u0026#39; +// progress bar +import NProgress from \u0026#39;nprogress\u0026#39; +// progress bar style +import \u0026#39;nprogress/nprogress.css\u0026#39; import router from \u0026#39;./router\u0026#39; import store from \u0026#39;./store\u0026#39; ... const whiteList = [\u0026#39;/login\u0026#39;, \u0026#39;/auth-redirect\u0026#39;] // no redirect whitelist +const initOptions = { + url: process.env.VUE_APP_KEYCLOAK_OPTIONS_URL, + realm: process.env.VUE_APP_KEYCLOAK_OPTIONS_REALM, + clientId: process.env.VUE_APP_KEYCLOAK_OPTIONS_CLIENTID, + onLoad: process.env.VUE_APP_KEYCLOAK_OPTIONS_ONLOAD +} + +const keycloak = new Keycloak(initOptions) + router.beforeEach(async(to, from, next) =\u0026gt; { // start progress bar NProgress.start() // remove token and go to login page to re-login await store.dispatch(\u0026#39;user/resetToken\u0026#39;) Message.error(error || \u0026#39;Has Error\u0026#39;) - next(`/login?redirect=${to.path}`) + // next(`/login?redirect=${to.path}`) + next(`/?redirect=${to.path}`) NProgress.done() } } next() } else { // other pages that do not have permission to access are redirected to the login page. - next(`/login?redirect=${to.path}`) - NProgress.done() + keycloak.init({ onLoad: initOptions.onLoad, checkLoginIframe: false }).then(async authenticated =\u0026gt; { + if (!authenticated) { + console.log(\u0026#39;not auth\u0026#39;) + window.location.reload() + return + } else { + // await store.dispatch(\u0026#39;user/keycloakLogin\u0026#39;, keycloak.idToken) + await store.dispatch(\u0026#39;user/keycloakLogin\u0026#39;, keycloak) + next(`/?redirect=${to.path}`) + NProgress.done() + } + }).catch(error =\u0026gt; { + console.log(\u0026#39;Authenticated Failed\u0026#39;, error) + }) + // next(`/login?redirect=${to.path}`) + // NProgress.done() } } }) src/store/getters.js 代码 1 2 3 4 5 6 7 8 department: state =\u0026gt; state.user.department, roles: state =\u0026gt; state.user.roles, permission_routes: state =\u0026gt; state.permission.routes, - errorLogs: state =\u0026gt; state.errorLog.logs + errorLogs: state =\u0026gt; state.errorLog.logs, + keycloak: state =\u0026gt; state.user.keycloak } export default getters src/store/modules/user.js 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 nickname: \u0026#39;\u0026#39;, introduction: \u0026#39;\u0026#39;, department: \u0026#39;\u0026#39;, - roles: [] + roles: [], + keycloak: null } const mutations = { }, SET_ROLES: (state, roles) =\u0026gt; { state.roles = roles + }, + SET_KEYCLOAK: (state, keycloak) =\u0026gt; { + state.keycloak = keycloak } } ... }) }) }, + // keycloak login + keycloakLogin({ commit }, keycloak) { + return new Promise((resolve, reject) =\u0026gt; { + login({ token: keycloak.idToken.trim() }).then(response =\u0026gt; { + const data = response.data + const { token } = data + // const userinfo = jwtDecode(token) + // const { username, roles } = userinfo + commit(\u0026#39;SET_TOKEN\u0026#39;, token) + setToken(token) + commit(\u0026#39;SET_KEYCLOAK\u0026#39;, keycloak) + resolve() + }).catch(error =\u0026gt; { + reject(error) + }) + }) + }, // get user info getInfo({ commit, state }) { commit(\u0026#39;SET_ROLES\u0026#39;, []) removeToken() resetRouter() + state.keycloak.logout() // reset visited views and cached views // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485 最后还留了一个问题，就是登录不能跳转到默认页面，需要解析下 ","date":"2023-08-31","permalink":"/posts/vue-element-admin-keycloak-notes/","title":"vue-element-admin 接入 Keycloak 的学习记录"},{"categories":["Linux"],"content":"Linux下磁盘写满问题 一、问题描述 总是会出现磁盘不小心打满，然后执行 rm 命令发现 rm: cannot remove 'test': No space left on device，这种情况不知道如何处理。\n二、解决方案 1. 扩磁盘 现在都是云，可以尝试挂多块盘，具体操作看厂商的操作文档吧，我也不熟悉。\n2. 保留磁盘空间 linux的硬盘分区程序会自动为root或指定的用户保留一定的磁盘空间默认是5％，可以通过这个方式解决。这其实也是这篇文章的主要目的之一。\n本次实验使用 ext 文件系统。ext2/ext3/ext4 可以通过保留块机制为超级用户留出空间；本文的 tune2fs 操作针对这类文件系统。\nBash 1 2 3 4 5 6 7 [root@centos7.6 ~]# man mkfs.ext4 ... OPTIONS ... -m reserved-blocks-percentage Specify the percentage of the filesystem blocks reserved for the super-user. This avoids fragmentation, and allows root-owned daemons, such as syslogd(8), to continue to function correctly after non-privileged processes are prevented from writing to the filesystem. The default percentage is 5%. 只摘要重点，谷歌机器翻译下\n代码 1 指定为超级用户保留的文件系统块的百分比。这可以避免碎片，并允许 root 拥有的守护进程（例如 syslogd(8)）在阻止非特权进程写入文件系统后继续正常运行。默认百分比为 5%。 简单说就是系统怕磁盘打满，给管理员留了 5% 的磁盘空间，给特殊处理情况。\n一个猜想 一次排障时，虚拟机上直接运行的 Docker 应用持续报错并输出到控制台，容器日志文件增长到 93G。 想起来释放磁盘这个方法，就决定试一试。\n当时释放保留空间没有解决问题，猜测可能是日志写入过快，释放的空间很快再次耗尽。这个猜测没有直接验证。\n相关命令 相关的执行命令\nBash 1 2 3 4 5 tune2fs -l /dev/sda1 # 查看所有磁盘信息 tune2fs -l /dev/sda1 | grep \u0026#34;Reserved block count\u0026#34; # 查看保留区间 tune2fs -r 25600 /dev/sda1 # 手动指定磁盘 Reserved block 大小 tune2fs -m 0 /dev/sda1 # 指定 Reserved block 百分比，0 表示 0% tune2fs -m 5 /dev/sda1 # 指定 Reserved block 百分比为 5% 系统执行命令\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [root@centos7.6 ~]# df Filesystem 1K-blocks Used Available Use% Mounted on devtmpfs 7878504 0 7878504 0% /dev tmpfs 7888884 0 7888884 0% /dev/shm tmpfs 7888884 731848 7157036 10% /run tmpfs 7888884 0 7888884 0% /sys/fs/cgroup /dev/sda1 103079844 19579640 78240996 21% / overlay 103079844 19579640 78240996 21% /var/lib/docker/overlay2/EXAMPLE_LAYER/merged tmpfs 1577780 0 1577780 0% /run/user/0 [root@centos7.6 ~]# tune2fs -m 0 /dev/sda1 tune2fs 1.42.9 (28-Dec-2013) Setting reserved blocks percentage to 0% (0 blocks) [root@centos7.6 ~]# df Filesystem 1K-blocks Used Available Use% Mounted on devtmpfs 7878504 0 7878504 0% /dev tmpfs 7888884 0 7888884 0% /dev/shm tmpfs 7888884 731848 7157036 10% /run tmpfs 7888884 0 7888884 0% /sys/fs/cgroup /dev/sda1 103079844 19579644 83483816 19% / overlay 103079844 19579644 83483816 19% /var/lib/docker/overlay2/EXAMPLE_LAYER/merged tmpfs 1577780 0 1577780 0% /run/user/0 Bash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 [root@centos7.6 ~]# df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 7.6G 0 7.6G 0% /dev tmpfs 7.6G 0 7.6G 0% /dev/shm tmpfs 7.6G 715M 6.9G 10% /run tmpfs 7.6G 0 7.6G 0% /sys/fs/cgroup /dev/sda1 99G 19G 75G 21% / overlay 99G 19G 75G 21% /var/lib/docker/overlay2/EXAMPLE_LAYER/merged tmpfs 1.6G 0 1.6G 0% /run/user/0 [root@centos7.6 ~]# tune2fs -l /dev/sda1 tune2fs 1.42.9 (28-Dec-2013) Filesystem volume name: \u0026lt;none\u0026gt; Last mounted on: / Filesystem UUID: 1cf0b662-ebd1-44a2-bbd2-0a6e58aec5fa Filesystem magic number: 0xEF53 Filesystem revision #: 1 (dynamic) Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize Filesystem flags: signed_directory_hash Default mount options: user_xattr acl Filesystem state: clean Errors behavior: Continue Filesystem OS type: Linux Inode count: 6553600 Block count: 26214139 Reserved block count: 1310706 Free blocks: 24938639 Free inodes: 6477545 First block: 0 Block size: 4096 Fragment size: 4096 Group descriptor size: 64 Reserved GDT blocks: 1016 Blocks per group: 32768 Fragments per group: 32768 Inodes per group: 8192 Inode blocks per group: 512 Flex block group size: 16 Filesystem created: Fri Feb 26 16:09:26 2021 Last mount time: Tue Aug 1 10:17:16 2023 Last write time: Sat Aug 26 19:24:38 2023 Mount count: 9 Maximum mount count: -1 Last checked: Fri Feb 26 16:09:26 2021 Check interval: 0 (\u0026lt;none\u0026gt;) Lifetime writes: 6584 MB Reserved blocks uid: 0 (user root) Reserved blocks gid: 0 (group root) First inode: 11 Inode size: 256 Required extra isize: 28 Desired extra isize: 28 Journal inode: 8 Default directory hash: half_md4 Directory Hash Seed: eb7540bb-6293-40e6-b29c-da8dc74aea46 Journal backup: inode blocks [root@centos7.6 ~]# tune2fs -m 0 /dev/sda1 tune2fs 1.42.9 (28-Dec-2013) Setting reserved blocks percentage to 0% (1310706 blocks) [root@centos7.6 ~]# df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 7.6G 0 7.6G 0% /dev tmpfs 7.6G 0 7.6G 0% /dev/shm tmpfs 7.6G 715M 6.9G 10% /run tmpfs 7.6G 0 7.6G 0% /sys/fs/cgroup /dev/sda1 99G 19G 80G 19% / overlay 99G 19G 80G 19% /var/lib/docker/overlay2/EXAMPLE_LAYER/merged tmpfs 1.6G 0 1.6G 0% /run/user/0 [root@centos7.6 ~]# tune2fs -l /dev/sda1 tune2fs 1.42.9 (28-Dec-2013) Filesystem volume name: \u0026lt;none\u0026gt; Last mounted on: / Filesystem UUID: 1cf0b662-ebd1-44a2-bbd2-0a6e58aec5fa Filesystem magic number: 0xEF53 Filesystem revision #: 1 (dynamic) Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize Filesystem flags: signed_directory_hash Default mount options: user_xattr acl Filesystem state: clean Errors behavior: Continue Filesystem OS type: Linux Inode count: 6553600 Block count: 26214139 Reserved block count: 0 Free blocks: 24938639 Free inodes: 6477545 First block: 0 Block size: 4096 Fragment size: 4096 Group descriptor size: 64 Reserved GDT blocks: 1016 Blocks per group: 32768 Fragments per group: 32768 Inodes per group: 8192 Inode blocks per group: 512 Flex block group size: 16 Filesystem created: Fri Feb 26 16:09:26 2021 Last mount time: Tue Aug 1 10:17:16 2023 Last write time: Sat Aug 26 18:32:52 2023 Mount count: 9 Maximum mount count: -1 Last checked: Fri Feb 26 16:09:26 2021 Check interval: 0 (\u0026lt;none\u0026gt;) Lifetime writes: 6584 MB Reserved blocks uid: 0 (user root) Reserved blocks gid: 0 (group root) First inode: 11 Inode size: 256 Required extra isize: 28 Desired extra isize: 28 Journal inode: 8 Default directory hash: half_md4 Directory Hash Seed: eb7540bb-6293-40e6-b29c-da8dc74aea46 Journal backup: inode blocks [root@centos7.6 ~]# tune2fs -m 5 /dev/sda1 tune2fs 1.42.9 (28-Dec-2013) Setting reserved blocks percentage to 5% (1310706 blocks) 实际解决方案 /dev/null 说到上面的问题，磁盘已经打满，当时因为只有一块盘，rm 也失效。在我手足无措的时候，同事来帮忙解决了。\nBash 1 2 container_id=$(docker inspect --format \u0026#39;{{.Id}}\u0026#39; example-container) cat /dev/null \u0026gt; \u0026#34;/var/lib/docker/containers/${container_id}/${container_id}-json.log\u0026#34; 通过将 /dev/null 重定向到日志文件，直接把现有日志截断为零字节。当时删除文件失败，而截断文件成功释放了空间；这不意味着 rm 必须申请与文件大小相同的空间。\n当然似乎是运气不错，理论上磁盘打满夸张点会直接导致 ssh 都无法登录。\n","date":"2023-08-26","permalink":"/posts/disk-reserved-blocks-troubleshooting/","title":"Linux 磁盘写满与保留空间排查"},{"categories":["tools"],"content":"java 自问自答 java 函数语法吐槽 java 的 \u0026ldquo;方法\u0026rdquo; 真的好想吐槽。与 python go c 等不一样，它没有类似 func def 等关键字来区别。 \\\njava 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class ApiController { @Autowired private ApiServices apiServices; @RequestMapping(value = \u0026#34;/api01\u0026#34;, method = RequestMethod.GET) public String api01() { return apiServices.api01(); } @RequestMapping(value = \u0026#34;/api02\u0026#34;, method = RequestMethod.GET) public String api02() { return apiServices.api01(); } @RequestMapping(value = \u0026#34;/apiparam\u0026#34;, method = RequestMethod.GET) public String api() { return apiServices.api_param(\u0026#34;api param\u0026#34;); } } 只能通过 括号、花括号 识别，虽然在 python 里也是这个逻辑，但少了关键字多少有点怪怪的\njava 的引入 spring 怎么知道 control 里的类，是有引用还是会自动搜索\n","date":"2023-08-17","permalink":"/posts/java-method-syntax-notes/","title":"从 Python 看 Java 方法语法"},{"categories":["tools"],"content":"网上找了一个教程 https://www.jianshu.com/p/56e600280ad1 快速入门 spring, spring 从入门到安装各个组件\n一直听说 java 的思想很牛, 跟 python 形成鲜明的对比.\n想快速学习, 但感觉落下很多基础知识, 亡羊补牢却不知道从何学起. 只能强迫自己静下心来慢慢看(心里还是有点做不到)\n缘起 无奈之下想起之前看的廖雪峰, 文字版可以快速跳过一些不重要的知识(还是浮躁) 廖雪峰的教程会感觉有点详细, 更适合做一个词典用来查阅怎么用, 目前的方法论是快速学习, 也算是培养成就感.\njava 基础知识 hello world! 入门教学里的知识点\njava 每个文件只能有一个 pulic class 类, 对应的类名要与文件名一致 java 有入口函数 main java 必须都被类包着 java 的方法函数, 这个廖雪峰几乎没讲, 看到 w3schools 讲了一点点\njava 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Main { static int plusMethodInt(int x, int y) { return x + y; } static double plusMethodDouble(double x, double y) { return x + y; } public static void main(String[] args) { int myNum1 = plusMethodInt(8, 5); double myNum2 = plusMethodDouble(4.3, 6.26); System.out.println(\u0026#34;int: \u0026#34; + myNum1); System.out.println(\u0026#34;double: \u0026#34; + myNum2); } } 学习疑惑: 所有的 java 必须有个 main 函数，只能写这么写 public static void main 不能写成 public static main，然后 return 一个函数么？ 网上搜到很多文章, 摘要两篇感觉通俗易懂的:\nJava之主函数——main函数 探秘Java：从main函数启动开始 答疑 main函数特殊之处：\n格式是固定的。 被jvm所识别和调用。 main函数关键字分析 public static void main(String[] args)\npublic: 是权限修饰词，表明任何类或对象都可以访问这个方法 static: 表明main() 是一个静态方法，即方法中的代码是存储在静态存储区的，只要类被加载就可以使用该方法而不需要通过实例化对象来访问，可以直接通过 类名.main() 来自直接访问 void: 主函数没有具体的返回值。 main: 函数名，不是关键字，只是一个jvm识别的固定的名字。 String[] arg: 这是主函数的参数列表，是一个数组类型的参数，而且元素都是字符串类型的。 在web 应用中，一个程序只能有一个入口(main 函数)，要是写多个 main 入口也可以，但启动只能指定一个。\\\njava -classpath /home/ds/import/dc x-1.5.2.jar\nJAVA虚拟机不管有几个main方法， 一个JVM只可以运行一个main方法。它只管运行的哪一个main方法，如果想同时运行多个main方法就得用多个JVM\nspring 启动 https://start.spring.io/\n配置项\nproject: maven language: java spring-boot version: 2.7.14 package: java java version: 8 dependencies: spring web dependencies: lombok ","date":"2023-08-10","permalink":"/posts/java-spring-first-notes/","title":"Java 与 Spring Boot 入门学习记录"},{"categories":["week"],"content":"2023年第33周，0807-0813 胡算乱想 心态平衡后, 对学习知识的效率要求大大降低, 反而让自己更耐心. 对于知识星球的网络只是学习, 放松了很多. 理解一年做一个实验的道理, 反复做, 每次都有不同的收获. 尽量在实际场景中理解知识点, 也就理解那张图, 零散的知识串成体系, 不再是孤岛. 看到有人提到 李笑来 《财富自由之路》和《把时间当作朋友》这两本书。第二本我也看过，确实不错。网友说的很精准，不买币，只看书。 对 时间的朋友这书的内容不太记得，想到复利，也是最近比较感慨的，做事总是没有沉淀，每次都像是第一次做，不是好事。要多做笔记，搞博客。前期不要太在意内容质量不高，尽量去写高质量的，后期慢慢迭代。知识需要升级的。 《财富自由之路》 这本书微信读书已经下架, 考虑要不要买本实体书 听了 tinyfool 关于学英语的一期视频, 最让我意外的是, 他建议学英语不要看字幕, 会让大脑偷懒, 只去理解字幕, 不在乎听力. 如果经常听到一个单词, 根据语境能猜出大概得意思, 这种方式学习会更牢靠, 好像有点道理. 这个现在感觉不是完全可行, 一个单纯的聊天, 并不会把一些单词关联到场景里. 今天在电梯里听到老外聊天说到送花啥的, 我只能猜到大概, 但并不清楚具体的一些单词词义, 这种场景也不会给我反复练习的机会. 还是得不停的重复看一些剧, 看到最后能直接不看字幕, 或者只看英文字幕就算成功. 阅读 TTY 到底是什么？ 这周再把文章看一遍, 然后写下笔记跟读后感吧, 记录下自己不熟悉的知识, 这周看下就先放下, 这个知识点暂时不知道怎么用, 实际使用场景也很少. 过于在意大而全并不是件好事, 放下也是不错的选择\n架构设计 the Easy Way\n文章还没有来的及仔细研读，但总体内容比较喜欢，讲了一些\u0026quot;大道理\u0026quot;，也举例具体案例，都是比较贴近我的日常实践。\n决定后续还是慢慢维护自己的博客，博客还是比较重要得，既能形成自己的知识体系，也能向别人展示自己的技能。\n关于半队列, 全队列的知识点, 之前不是很理解. 最近复习了一些网络基础知识再看就清晰很多了. 从一次线上问题说起，详解 TCP 半连接队列、全连接队列 TCP 半连接队列和全连接队列 再回过头看 小林coding 讲的网络基础知识确实不错, 而且会有一些\u0026quot;课后作业\u0026quot;, 似乎更懂我哪里还不熟\n本周的 wireshark 并没有看太多, 但更多的心思在了解一些概念上, 直接通过 wireshark 实验了解. 这点感觉做的还行. 但整体进度确实慢.\n视频 永生之酒, 剧场版已经看完, 还有三个 ov 版没看. 开始看的云里雾里, 后面能渐渐理清故事线, 最后爱上整个设定架构. 在我看来永生之酒有点像蝙蝠侠小丑那集, 小丑给两艘游轮安装了炸药, 把遥控器分别给了对方. 堵得是人性. 永生之酒也一样, 给他们一个似乎只有你死我活的背景. 但如果人人都忘记这个规则, 那就不能构成威胁, 甚至更有意思.\n博客/笔记 wireshark 一些理解 java 笔记 django 序列化 context 本周行动 上周说记录学习笔记, 8-10 写了一篇入门笔记: java_spring 笔记\n四象限法 【重要不紧急1(有计划的执行)】 【重要紧急2(立即处理)】 【不重要紧急3(合理授权)】 【不重要不紧急4(控制比例)】 vim学习(每周更新) 本周 vim 没有进展, 几乎停止, 打算找本书详细看看.\n虚无主义想法 搞清楚request请求的链路，比如view层的get请求 ==\u0026gt; 可以让代码写的更快更好，然后呢？对，就是为了熟练的curd，然后把这个思想用来看java django restful serializer 与 get_queryset 的代码执行顺序, 怎么看源码 解决当前自己写的一些bug，不能解决的问题，感觉需要优化的点。自定义的response里有两层data，不知道怎么解决 mysql的orm调用返回datetime是时区类型，怎么能自动改成普通类型 django debug tool https://github.com/jazzband/django-debug-toolbar django prometheus 暴露 https://pypi.org/project/django-sql-explorer/ (这个已经不重要了，有sql抓包) xops的好多功能都没有玩过，要加油了，要自己写个服务，开源玩起来。 多逛逛大佬的博客，看看他们的知识体系构建，平时都记录了哪些 这周找到两个关于网络底层的文章，真的很想自己也能有这样的能力，争取能写成文章 https://plantegg.github.io/2019/06/21/%E5%B0%B1%E6%98%AF%E8%A6%81%E4%BD%A0%E6%87%82%E6%8A%93%E5%8C%85--WireShark%E4%B9%8B%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%89%88tshark/ 整理笔记，对个人成长很重要的，而且不是几个小时就能学会的内容，需要几周甚至几个月才能学完的知识，对以后工作，职业都有很大的帮助，这个已经在做了。 websocket: 找到 k8s websocket 代码，有空抄一抄。 失败，客户端调用失败 hugo 主题美化, 改成自己喜欢的样子. 再次学习陈浩的专栏，应该会有收获 学习k8s -\u0026gt; kubernetes网络权威指南，这个优先级降低，遇到问题解决问题，因为系统过于庞大，系统性的学习成本很大，优势不清楚。 看老友记，学习英语 / 影子英语，学习方法。还保存了英语学习的方法 追动漫 算法学习 devops项目todo, 同步资源，application最好要提上日程，因为定时任务时间改成半小时执行一次。 devops项目todo, k8s命令行交互 django源码学习，是否需要每周产出笔记？但不需要为了产出而产出 jvm优化(没有方向，目前实在点的就是学会看gc日志，与中信沟通交流) -\u0026gt; 遇到问题查问题，不主动丰富知识体系，因为缺口太大，短时间不会见效。知识利用率低 李三红的极客时间视频学习。这个应该能坚持学完吧，貌似对调优有提高。 java入门(廖雪峰java学习指南) -\u0026gt; 学习使用spring框架，写一个小demo网上收集了一个文章讲破解java的json序列化的，用来学习 基础入门Fastjson系列漏洞 wireshark这么薄的一本书，都没有看完。看到一半，想记笔记，搭建博客，博客丑，想搞主题，太分心了。需要多做思想的减法，专注。 django的中间件记录请求日志，中间件的使用，高级 django channels功能学习 / channel官网教学深刻理解，然后修改成自己的技能，要学会修改 Task(1-2小时) 网上收集了一个文章讲破解java的json序列化的，用来学习，基础入门Fastjson系列漏洞 django的单元测试学习 TODO: 处理文件上传卡住的问题，学会在实践中运用知识，不再对tcpdump害怕，不知所措。确认好问题，给大佬一个答复 TODO: 实验问题，原计划是上周五在公司完成的，但还是偷懒了。想法是今天大致写下实验步骤，下周记录实验数据。这个已经搁置很久了，先下调一个级别，是mysql实验，还是服务器实验，需要看看最初的目的，了解知识点 学习 java, 写了 java 博客. 后续把同事写的 java 项目自己启动起来. 然后加个接口. 想做的事情, 用 python/go 写 socket 服务, 了解下 socket 相关实验, 理解 tcp 的相关处理.\n总结 还是老方法比价舒服，把需要做的事情写下来，完成一件就用笔划去，这样真的能减轻焦虑。 做好劳逸结合, 上班时间也可以学习, 不是单纯的下楼摸鱼. 不要为了摸鱼而摸鱼. 我好像特别喜欢记录, 把自己的生活记录下, 会感觉非常有意思, 可以回顾自己, 而不是一遍又一遍的犯同样的错 ","date":"2023-08-09","permalink":"/posts/week33/","title":"week33"},{"categories":["travel"],"content":"说走就走的旅行 周四晚上临时决定去绍兴玩, 但却没有合适的回城票, 周五发现居然有票了. 早上 7.30 出发去虹桥火车站, 特意选了小红书介绍的(虹桥2号航站楼)捷径, 第一次上候车厅 2 楼, 从二楼看候车厅, 感受到密密麻麻的人群. 之前都是从高楼上俯瞰, 感受个人的渺小, 但第一次从高空俯瞰这么密集的人群, 心里还是很震撼的.\n绍兴东湖 这次去绍兴计划先去东湖, 直接打车去, 攻略上说这里的风景不错, 因为与鲁迅故居那一片景点离的远, 所以先来东湖看风景, 然后再去鲁迅故里. 亲近大自然能让人有种原理工作的感受, 毕竟在这样的环境里不会给你各种需要工作的暗示, 远离城市, 忘记工作, 这感觉挺好. 东湖的景色最不能错过的就是在船上看山, 有些一线天的角度, 让人感叹大自然的鬼斧神工. 后来听边上的人讲, 这是人工景色, 赶紧百科一波.\n东湖所在地，原为一座青石山，秦始皇东巡时曾在此驻驾饮马，故被称为箬篑山。汉代以后，箬篑山成了绍兴的一处石料场，经过千百年的凿穿斧削，又是采用特殊的取石方法，搬走了半座青山，并形成了高达50多米的悬崖峭壁。劳动者取石还普遍深入到地下20多米，有的甚至四五十米处，日子一久，形成了长过200米，宽约80米的清水塘。清末，绍兴著名乡贤陶浚宣眼光独到，利用采石场筑起围墙，对水面稍加拓宽，遂成山水相映的东湖。东湖经过百年的人工装扮，成为一处巧夺天工的山水大盆景。 \u0026ndash;百度百科\n鲁迅故里 东湖游玩的时间多了, 回宾馆休息了一会儿去鲁迅故居, 鲁迅故居的那一片称之为鲁迅故里, 包含鲁迅祖屋, 鲁迅纪念馆, 三味书屋. 鲁迅的并不是一个平民子弟, 祖父做官, 青少年逢家道中落, 走向不一样的道路. 鲁迅的两个弟弟, 一个周作人(评价是汉奸文人), 周建人(浙江省长) 相关的内容是参观后才查资料了解的, 不过看到这些后, 再结合历史, 人在历史的车轮面前太无力了, 或者也可以说, 是历史创造了他们, 他们成就了历史. 绍兴的名人很多, 王羲之(书圣故里)/蔡元培/鲁迅三兄弟, 算是一种良性循环, 我猜也许跟周建人当官有关, 扶持当地教育事业有关.\n美食 绍兴的特产其实就是黄酒, 应该是与时俱进, 产生了一些新奇的玩意儿. 黄酒奶茶, 黄酒冰棍; 这两个至少原来是没有的, 应该是后来某个商人开发的.\n晚上在肯德基吹着空调思考去哪里吃, 原计划要找一家饭店好好吃一顿, 最后想直接去仓桥直街(商业街)逛逛, 看看有没有好吃的. 最后被一家心心念的醉蟹的小店吸引了, 点了几个特色菜, 醉蟹/醉虾/绍三鲜, 终于感觉这次旅游圆满了, 当时只顾吃忘记拍照了(饿死鬼附身).\n思考 这段是刚刚想到加上的, 跟媳妇儿聊到旅游的时候发现鲁迅的祖宅没有周作人的相关简介信息. 在听到鲁迅是三兄弟, 但在祖宅里只介绍了周树人/周建人, 没有周作人的信息. 当时参观的时候认为周作人可能比较平平无奇, 只是一个普通人罢了, 后来查百科才知道并不是.\n俗话总说: 历史总是赢家写的. 这个只能概括战争史, 我想说: 历史书上只给你想让你看见的.\n了解周作人的相关生平: 他的文学造诣甚至比鲁迅更高, 但他似乎不在乎站队, 对这点不知道怎么评价, 有得有失吧.\n百度百科相关介绍 全国性抗战时期曾任伪华北政务委员会教育总署督办。抗战结束后被捕入狱，1949年释放，晚年主要从事翻译工作。著有《自己的园地》《雨天的书》《谈龙集》《鲁迅的故家》 《鲁迅小说中的人物》 《知堂文集》《知堂回想录》等，译有《日本狂言选》《伊索寓言》《路吉阿诺斯对话集》等\n听说他是个\u0026quot;反派\u0026quot;的时候, 有种想抓住他一切把柄, 认为他就是亲日, 甚至取日本人做老婆. 但后来发现, 周建人也是取的日本人, 周作人/周建人 他两的老婆还是姐妹, 只能说我太天真, 有些东西真的没法评价.\n","date":"2023-08-06","permalink":"/posts/travel-shaoxing/","title":"shaoxing"},{"categories":["Linux"],"content":"User already exists error when user doesn\u0026rsquo;t exist on the system » Easy as Linux\n问题描述 创建本地 admin 用户时系统提示该用户已存在，但本地用户文件中找不到该用户，需要排查 NSS 目录服务是否返回了同名账户。\n目标 查出原因，并解决问题\n设计 无\n核心技术 google，经验\n猜测1: 用户创建时关联的group存在，不能创建。\n代码 1 2 3 4 [user@example-host ~]$ id admin uid=10000(admin) gid=10000(example-group) groups=10000(example-group) [user@example-host ~]$ grep -w admin /etc/group # 本地 group 文件没有对应的 admin 用户组 发现并不存在对应用户组\n猜测2: 谷歌搜到方法: easyaslinux easyaslinux\n使用 getent 命令 搜索用户信息 getent is a Linux command that helps the user to get the entries in a number of important text files called databases.\n代码 1 2 3 4 [user@example-host ~]$ getent passwd admin admin:*:10000:10000:Directory User:/home/admin:/bin/bash [user@example-host ~]$ getent passwd example-user example-user:x:1001:1001::/home/example-user:/bin/bash 发现admin用户输出的信息跟普通用户有区别 密码栏(冒号为分隔符第二个)，显示的结果是* ，一般是x，表示密码存在 /etc/shadow 文件里 Lets dig into Name Service Switch library config file. 查查nss的库文件 备注: The Name Service Switch (NSS) framework was designed to let administrators specify which files or directory services to query to obtain information. \\\n代码 1 2 [user@example-host ~]$ grep \u0026#39;^passwd:\u0026#39; /etc/nsswitch.conf passwd: files sss 问题找到了，nss在ldap中搜到当前用户，(sss被当成一个ldap客户端配置相关信息)\nBash 1 systemctl status sssd SSSD 正在运行，NSS 会通过目录服务查询用户。\n机器使用目录服务进行统一身份管理，SSSD 作为客户端也返回了同名 admin 用户，导致本地创建用户发生冲突。\n动手实践 卸载后这台机器信息就能新增用户了，此时发现用户密码配置变了\n代码 1 2 [user@example-host ~]$ getent passwd admin admin:x:1002:1002:Local User:/home/admin:/bin/bash 此时表示用户不在是一个ldap用户了，他的密码配置文件又变成了 /etc/shadow\n权限相关的问题也解决了\n优化 找到问题的根源，现在有三个方式解决问题\nRemove the user from ldap server.\nRemove the ldap reference from the /etc/nsswitch.conf file so that NSS library dont look for the user in ldap server.\nKeep the user in the ldap as it is, but create the same user in the system! Let me explain how we do it further.\n删除ldap中的用户\n从 /etc/nsswitch.conf 文件中删除 ldap 引用，这样 NSS 库就不会在 ldap 服务器中查找用户。\n保持用户在 ldap 中的原样，但在系统中创建相同的用户！让我解释一下我们如何进一步做到这一点。\n很明显，最好的解决方法是第三种: 使用luseradd命令\n代码 1 2 yum install libuser luseradd admin 不需要停止ipa服务，不影响权限，完美解决问题\n","date":"2023-07-29","permalink":"/posts/linux-local-user-ldap-conflict/","title":"Linux 本地用户与 LDAP 同名冲突排查"},{"categories":["think"],"content":"博客折腾 这两天折腾博客，总是遇到困难就想放弃。想法无非就是博客的主题哪哪不好看，不满意。人生呐，很难满足(就是想安慰自己不努力)。 不过早就明白了，博客的本质还是要内容好，而不是花里胡哨的样子。但都想有点虚荣心。\n这两天还是看见了一个基本满意的主题，但作者是外国人，估计对中文支持有点问题。其次是主题本地测试跑不起来，感觉是自愿获取被墙拦截了。 好像代码也有bug，本着偷懒原则，暂停折腾。这两天折腾的结果只有心累，还是跟原来一样，想速成，一点耐心都没有，硬生生找了两个主题跟着敲了一遍代码。 有点样子了，但又嫌弃太慢，不能立马写出我想要的主题，又开始改动原来的主题。于是想了想，开个主题，给自己挖个坑，慢慢填吧。\n折腾记录 跟着简书上的文章写 最初的想法是，类似于学习demo一样，找个中文版本，跟着手敲一遍，想想大概就能搞定了。抱着这个想法，我打开了第一个收藏链接\n如何创建自己的hugo主题，天真的我，看内容写的非常详细，还贴了很多代码。 我想，只要我跟着后面做，铁定就会了。事实是，跟着后面做，根本跑不起来，只记录了大概得过程，最后的截图更像是hugo的 bootstrip 主题替换的结果。 不知道是我跟着写的步骤不对，还是被欺骗了。代码也没有贴上，大概理清楚了 hugo 的架构\n疯狂搜索，一目十行 疯狂谷歌 \u0026ldquo;hugo 主题开发\u0026rdquo; , 找到两个比较靠谱的方案，都是有经验的，而且把博客主题代码放在 github 上。\nhttps://io-oi.me/tech/developing-a-hugo-theme/ https://blog.gimo.me/posts/creating-a-hugo-theme/ 我想，这总能行了吧，大家对 hugo 主题开发的基本功要求不一样，或者我的基础太差。实在是跟不上，半天想搞起来确实费劲。但经验确实值得参考。\n后面也看了 转向 Hugo：主题制作指北 确实很详细，但不是我想要的一步一步带着我写好，其实他已经提醒了很多，给了官网的链接，已经想跟着官网写，最后还是放弃了。官网写的感觉并不友好。 最后发现问题是我对 golang 不熟悉，其实这 hugo 可以当成一个 python 的 jinja2 模板来理解。可能本质就是一样。\n找一个已经写出来的博客跟着做 跟着一个靠谱的写一个主题，最后的效果还是有点问题，熟悉了架构 主要是自己不想从零开始写主题，更多的是想修改一个现有主题\nhttps://retrolog.io/blog/creating-a-hugo-theme-from-scratch/\n这个博客的代码其实也有一点点的问题，比如链接失效，最新版本不一致，但幸好都找到替代方案。最后代码成功的运行起来了。 结果这才发现是我想要的功能，大概怎么实现，距离实现的目标还差多少。\n给自己挖了个坑，虽然跟着这个做出了一个完全不能用的主题，但还是勇敢的把这行代码加在现在的主题上。我一直想做成上下布局的结构。 想修改当前主题，一直不知道怎么下手，慌忙之间把一些基础知识都搞忘记了，最后发现居然就是加了一行 html ,本质上还是有很多坑。\n总结 回归最初的目的\n我在友链的文章里写了好几个主题是我喜欢的风格，但作者都没有开源，想到我最初的目标就是把博客搭建起来，然后修改成自己喜欢的样子。 增加一些想要的功能，仅此而已，对现有主题的不满没有那么夸张，只是在看到别人折腾主题的过程，发现更多的细节，自己也先要，或者也要踩坑。 由此慢慢偏离最初的目的，现在功能基本都满足自己，只剩一些样式的问题，干脆开个坑，慢慢积累，反正我也不是那种一鼓作气搞定一切的人。 或许，慢慢来，比较快。\n","date":"2023-07-19","permalink":"/posts/blog-theme/","title":"blog-theme"},{"categories":["think"],"content":"方法论：通过拆解马拉松，深刻领会阶梯原理 原文链接\n原文摘要 你一定知道那个日本的马拉松冠军选手——山田本一。山田本一获得多个国际马拉松赛事冠军后，退役，并揭秘了自己的夺冠技巧。\n那就是，在每次马拉松比赛前，他都要驱车把比赛路程提前走一遍。然后，他把整个比赛路段，分成若干段，分别以各种标志建筑物、地貌特征为标记。\n正式比赛时，山田本一就会只考虑当前这一段路怎么跑，然后全力以赴完成这一段赛程。当一段赛程完成后，他就又会全力以赴地完成下一个赛段。\n看到这里，我以为，跑马拉松，只要把整个赛段分解成多个赛段，问题就解决了。就好像做学习或工作计划，只要把学习或工作任务，按照时间，分配好进度就能做好。\n可事实却打了我的脸。\n我的学习计划，工作计划，都不能按期完成，看来拆解目标，可不只是分段、分时、分任务，应该设立进阶条件。\n使用阶梯原理的关键，就是如何合理地拆解目标，山田本一的拆解，只是调整了我们实现目标时的心态，那就是心无旁骛，全力以赴做好当前的任务。 而真正合理有效的目标拆解，一定要多设立几个中间标准，并且，你需要先完成前一个，再去进阶下一个目标。\n我的思考 学习是一件需要终生持续的事情，因此我总是认为只要我学习了就没有\u0026quot;浪费生命\u0026quot;，为此我陷入每天都要学习的\u0026quot;自我感动\u0026quot;中，不去考虑学习的效率。 毕竟再笨也会偶尔全身心投入到学习中，并且能从中感受到快乐，也许那就是\u0026quot;内啡肽\u0026quot;。\n我对学习效率要求很低，或者很少考虑这项指标，更多的是凭感觉做事。 毕竟这个指标的考量难度和维度都比较复杂，不像从学习时间这个维度参考。甚至学习时间还有水分，愣神，玩手机偶尔也会被归算到学习时间内。 对自己学习的计算没有办法能真正精确，还有就是人性，总是美化自己，眼睛会自动给自己的外貌进行美化，也会美化自己的学习能力，记忆力等等。\n参考山田本一的马拉松阶梯原理: 小步子原理，也叫阶梯原理，心理学上也叫里程碑效应。\n他的诀窍: 那就是，在每次马拉松比赛前，他都要驱车把比赛路程提前走一遍。然后，他把整个比赛路段，分成若干段，分别以各种标志建筑物、地貌特征为标记。\n一、熟悉整个流程 跟了解马拉松比赛路程一样，学习一个知识需要知道学会哪些东西才能叫入门，知道整个比赛路程，就会对学习的知识任务量有概念。\n我总是一股脑找一堆学习方法论，学习资料，文章、书籍、视频。然后想当然的对自己说，坚持看完就会了。\n比如学习java，想当然的认为跟着廖雪峰的java资料后面敲一遍代码就会了。事实是，看了两篇文章后就一直停止在那一章节。 我对学会java没有一个概念，不知道怎样才叫入门，把别人的目标直接拿过来，没仔细想过这个是不是自己的目标，当前的教程内容合不合适我， 是偷懒认为跟着别人的教程就会了。\n这种情况生活中太多了，买了书(资料)认为就是学会了，收藏锻炼视频就是锻炼了。\n二、设立目标 都知道需要设立目标，我平时也没少给自己设目标，甚至会对身边的人说出来，让身边的来一起督促自己，最后结果是周围的人也知道我喜欢乱立Flag了。\n上面说的是一个典型的问题，设目标，但是目标其实不清晰，跟第一点有关联，但不全一样。\n偶尔会出现我确实立了一个明确的目标，例如看java学习视频，一共大概128个视频，计划一个月学完，然后一个除法，128/30=4.2。拍脑袋决定一天看5个视频。前期看的时候发现还行，每个视频的时间在13分钟左右，每天看完大概要一个小时。但后来就坚持不下去了。\n并不能保证每天能抽空出1个多小时，碰到加班或者家庭活动，几乎完全抽不出时间来学习。 前期的介绍类的视频，一个视频只有10分钟，但后期详情视频一个大约30分钟，要按计划的话，2.5小时。远超计划。 因为对学习内容的不熟悉，看视频会停下来思考，或者跟着敲代码，即使是1个小时的视频，加上理解可能会时间翻倍。 要做出合理的计划，总之不能拍脑袋定了目标后，完成不了就放弃，需要动态更新计划，而不是拖延跟放弃。\n目标内容合理，慢就慢点，每天一共2个小时的学习时间应该算是一种极限。 目标清晰，看视频或文章，如果没有自己的思考等于白看，甚至记录笔记但转头就忘也是需要改进的。 比如想自己写个博客主题，这个目标就不清晰，怎么做，做成什么样子都是空的。 做这个主题要多久，没有一个考量，一个任务(task)的时间大概控制在1小时左右 认识到自己的精力是有限的，不能总是在理想情况下做计划，满负荷学习(工作)，总会遇到问题，需要劳逸结合 三、冷启动，坚持 学习一个新的知识一般都会很容易产生新奇感，但要是遇到难点，总是解决不了，大概率就是放弃。\n因为这个问题经常困扰我，可能我以前解决过这个情况，但没有形成一套方法论，这里先推出一个想法: 继续学习，挑有成就感的知识点学习。 总之需要先继续学习，遇到难题记录下来，然后继续学习。本身学习也是需要通过笔记来复习巩固，记忆力再好还是不如笔记来的实在。 而且能通过复习笔记发现之前的问题。\n笔记有一个问题，不像早期知识内容比较贫乏的时候，现在网上能搜到一堆写的比你好的笔记，但这个时候用这个笔记当成自己的笔记等于没有思考，变相等于没有做笔记。这也是一个很要注意的点。\n坚持学下去，但不要每次都是从0开始，就想高中被英语单词，最熟悉的第一页，项目是要有推进的。\n四、设立奖励 与上面讲的劳逸结合一个意思，人不是机器，不可能总是高强度满负荷学习，需要给自己主动休息的时间。\n主动休息也是一种给自己一个信号，我休息了，而不是自己陷入不想开始学习的状态，不停的刷小视频，玩2-3小时，最后人更累。 主动玩游戏，看看动漫视频，做点与学习无关自己喜欢的事情，让大脑接受到\u0026quot;我正在休息\u0026quot;的信号，也是给自己奖励，建立一个正向反馈机制。\n五、优先级 四象限法，一个老生常谈的方式，但实际情况是我们不能分清哪些事情优先级高，需要多思考。\n当然前面也提到，设立了阶段目标后，只需要心无旁骛，全力以赴\n","date":"2023-07-16","permalink":"/posts/study_think/","title":"Study_think"},{"categories":["tools"],"content":"Gitlab commit hook 1 hook概念 git 提供了一些hook，可以在提交代码时进行相关的检测，这些hook可以在客户端，也可以在服务端。目前主流都是在服务端进行检测。\n1.1 客户端钩子 客户端钩子分为很多种。 下面把它们分为：提交工作流钩子、电子邮件工作流钩子和其它钩子。\n1.2 服务器端钩子 除了客户端钩子，作为系统管理员，你还可以使用若干服务器端的钩子对项目强制执行各种类型的策略。 这些钩子脚本在推送到服务器之前和之后运行。 推送到服务器前运行的钩子可以在任何时候以非零值退出，拒绝推送并给客户端返回错误消息，还可以依你所想设置足够复杂的推送策略。\npre-receive 处理来自客户端的推送操作时，最先被调用的脚本是 pre-receive。 它从标准输入获取一系列被推送的引用。如果它以非零值退出，所有的推送内容都不会被接受。 你可以用这个钩子阻止对引用进行非快进（non-fast-forward）的更新，或者对该推送所修改的所有引用和文件进行访问控制。\nupdate update 脚本和 pre-receive 脚本十分类似，不同之处在于它会为每一个准备更新的分支各运行一次。 假如推送者同时向多个分支推送内容，pre-receive 只运行一次，相比之下 update 则会为每一个被推送的分支各运行一次。 它不会从标准输入读取内容，而是接受三个参数：引用的名字（分支），推送前的引用指向的内容的 SHA-1 值，以及用户准备推送的内容的 SHA-1 值。 如果 update 脚本以非零值退出，只有相应的那一个引用会被拒绝；其余的依然会被更新。\npost-receive post-receive 挂钩在整个过程完结以后运行，可以用来更新其他系统服务或者通知用户。 它接受与 pre-receive 相同的标准输入数据。 它的用途包括给某个邮件列表发信，通知持续集成（continous integration）的服务器， 或者更新问题追踪系统（ticket-tracking system） —— 甚至可以通过分析提交信息来决定某个问题（ticket）是否应该被开启，修改或者关闭。 该脚本无法终止推送进程，不过客户端在它结束运行之前将保持连接状态， 所以如果你想做其他操作需谨慎使用它，因为它将耗费你很长的一段时间。\n原文链接 https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-Git-%E9%92%A9%E5%AD%90\n2 配置hook 2.1 查看仓库id gitlab 使用hashid来作为目录名字，在服务器上找目录不是那么容易，甚至不知道怎么找。有两种方式，一个最简单，管理员可以直接看到对应仓库的hashid，这种暂不过多描述。\n另外一种是查看gitlab服务端的仓库的 Settings -\u0026gt; General 页面有个 Project ID，拿到这个值，执行shell命令\nBash 1 2 # 以 Project ID 123 为示例计算仓库目录的哈希 echo -n 123 | sha256sum 对应仓库位于 /var/opt/gitlab/git-data/repositories/@hashed/ 下，通常按哈希的前四位分为两级目录，再进入完整哈希命名的仓库目录。\n2.2 添加hook配置 创建hook目录，custom_hooks\nBash 1 2 3 # 将 \u0026lt;project-repository\u0026gt; 替换为对应仓库目录 cd /var/opt/gitlab/git-data/repositories/@hashed/\u0026lt;project-repository\u0026gt;.git mkdir -p custom_hooks/pre-receive.d 2.3 添加hook文件，文件名为pre-receive gitlab-hook 可执行文件 pre-receive\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #!/bin/bash # @author example@example.com # @date 2023-03-15 # @description # 在代码push时预检查备注信息是否符合规范，如不符合规范会被退回 # 该脚本安装在gitlab服务器的/opt/bcds-gitlab-hooks/hooks/install/custom_hooks/pre-receive.d/目录 # 安装时，脚本需要重命名为pre-receive，并通过命令 chmod +x pre-receive 设置权限 # 错误信息模版 ERROR_FORAMT=\u0026#34;| %-40s| %-26s| %-15s| %-30s| %s\\n\u0026#34; # 错误信息头标题 ERROR_HRADER=$(printf \u0026#34;${ERROR_FORAMT}\u0026#34; \u0026#34;COMMIT ID\u0026#34; \u0026#34;ERROR\u0026#34; \u0026#34;AUTHOR\u0026#34; \u0026#34;DATE\u0026#34; \u0026#34;MESSAGE\u0026#34;) # 错误信息数组 declare -a errorArray=() # 零COMMIT ID ZERO_COMMIT_ID=\u0026#39;0000000000000000000000000000000000000000\u0026#39; # Merge操作的正则表达式 MERGE_REGEX=\u0026#39;^Merge (remote-tracking )?branch(es)? (.+) into (.+)$\u0026#39; # 提交规范的正则表达式 REGEX=\u0026#39;^(feat|fix|docs|style|build|refactor|revert|test|perf|ci|chore|hotfix)(\\(([A-Za-z0-9]+-[0-9]+)?\\))?!?: (.+)$\u0026#39; echo \u0026#34;【pre-receive】开始检查提交信息...\u0026#34; while read oldrev newrev refname; do echo \u0026#34;【pre-receive】您提交的 branch : ${refname}\u0026#34; echo \u0026#34;【pre-receive】您提交的 oldrev : ${oldrev}\u0026#34; echo \u0026#34;【pre-receive】您提交的 newrev : ${newrev}\u0026#34; # Branch or tag got deleted, ignore the push [[ \u0026#34;${newrev}\u0026#34; == \u0026#34;${ZERO_COMMIT_ID}\u0026#34; ]] \u0026amp;\u0026amp; continue # Calculate range for new branch/updated branch [[ \u0026#34;${oldrev}\u0026#34; == \u0026#34;${ZERO_COMMIT_ID}\u0026#34; ]] \u0026amp;\u0026amp; range=\u0026#34;${newrev}\u0026#34; || range=\u0026#34;${oldrev}..${newrev}\u0026#34; # 获取代码提交的信息 for commitId in $(git rev-list \u0026#34;${range}\u0026#34; --not --all); do # 获取提交者 user=$(git log --pretty=format:\u0026#34;%an\u0026#34; \u0026#34;${commitId}\u0026#34; -1) # 获取提交日期 commitDate=$(git log --pretty=format:\u0026#34;%cd\u0026#34; \u0026#34;${commitId}\u0026#34; -1) # 获取提交日志 msg=$(git log --pretty=format:\u0026#34;%s\u0026#34; \u0026#34;${commitId}\u0026#34; -1) # 忽略Merge操作 [[ ${msg} =~ ${MERGE_REGEX} ]] \u0026amp;\u0026amp; continue # 找到匹配说明是符合规范的 if [[ ${msg} =~ ${REGEX} ]]; then # 代码提交的类型 type=${BASH_REMATCH[1]} # 关联的STORY或REQ编号 code=${BASH_REMATCH[3]} # 只有hotfix类型可以不关联STORY或REQ if [[ -z ${code} \u0026amp;\u0026amp; ${type} != \u0026#34;hotfix\u0026#34; ]]; then item=$(printf \u0026#34;${ERROR_FORAMT}\u0026#34; \u0026#34;${commitId}\u0026#34; \u0026#34;No STORY or REQ associated\u0026#34; \u0026#34;${user}\u0026#34; \u0026#34;${commitDate}\u0026#34; \u0026#34;${msg}\u0026#34;) errorArray+=(\u0026#34;${item}\u0026#34;) fi else item=$(printf \u0026#34;${ERROR_FORAMT}\u0026#34; \u0026#34;${commitId}\u0026#34; \u0026#34;Invalid format\u0026#34; \u0026#34;${user}\u0026#34; \u0026#34;${commitDate}\u0026#34; \u0026#34;${msg}\u0026#34;) errorArray+=(\u0026#34;${item}\u0026#34;) fi done done # 判断是否有异常信息 if [[ ${#errorArray[@]} \u0026gt; 0 ]]; then echo -e \u0026#34;【pre-receive】[error]代码提交信息不符合规范，模板：\u0026lt;type\u0026gt;(\u0026lt;STORY/REQ\u0026gt;): \u0026lt;description\u0026gt;，详情请看: http://wiki.company.org/pages/viewpage.action?pageId=12345678\u0026#34; echo -e \u0026#34;【pre-receive】[error]代码提交信息详情请看下表：\u0026#34; echo \u0026#34;${ERROR_HRADER}\u0026#34; for item in \u0026#34;${errorArray[@]}\u0026#34;; do echo \u0026#34;${item}\u0026#34; done exit 1 else echo \u0026#34;【pre-receive】代码提交信息校验通过\u0026#34; exit 0 fi ","date":"2023-07-05","permalink":"/posts/gitlab-pre-receive-hook/","title":"GitLab pre-receive 提交检查笔记"},{"categories":["tcp"],"content":"文件上传卡住时的 TCP 与 Nginx 排查 一、背景 我写了一个python服务，里面有个文件上传的功能，直接把本地文件上传到服务器 我的服务：入口是nginx，用gunicorn 启动 django 框架 本地测试\u0026quot;文件上传接口\u0026quot;，170M的文件传输没有问题，但部署在服务器上，传输会卡在47%的进度条这里。 分析抓包，发现上传卡死的时候，会出现 tcp zero window，window窗口直接变0，然后一直不会恢复\n二、实验环境 入口为 Nginx，后端使用 Gunicorn 启动 Django，监听 127.0.0.1:8000。当时服务器使用 Linux 3.10 内核。此前做过 tc 实验，排查时先确认原有延迟和丢包规则已经清理。\n三、最初的排查思路 最初怀疑是否需要通过内核参数强制 reset 连接，后来发现，应先检查接收方应用是否及时读取数据，以及反向代理的缓冲行为。\n四、解决方案 确认 TCP 出现 Zero Window 后，最初认为可以通过调整某个内核参数直接解决。后续排查发现，窗口变为零只描述接收方暂时无法继续接收的状态，仍需要找到具体原因。\nTCP 数据先进入接收方的套接字接收缓冲区，再由应用读取。当接收方通告的可用接收窗口变为零时，发送方需要等待窗口恢复；排查时需要结合缓冲区状态和应用读取数据的行为。\n后续想到一个方法就是进行抓包，诡异的是，最外层nginx暴露的是443端口，抓包有数据，但python服务8000端口抓包却没有数据，最后发现是抓包条件有问题，在服务器上应该\n抓取nginx数据包\n代码 1 sudo tcpdump -i eth0 \u0026#34;tcp port 443\u0026#34; 因为python服务是绑定是本地网卡，\n代码 1 2 3 4 sudo netstat -lntp # 本例只保留与上传链路相关的两条监听信息： # Nginx: 0.0.0.0:443 # Gunicorn: 127.0.0.1:8000 根据网卡的 inet 来判断当前使用的网卡为lo，所以用如下命令进行python服务抓包\n代码 1 tcpdump -i lo \u0026#34;tcp port 8000\u0026#34; 通过抓包发现，当传输一个大文件时，在nginx端能抓包，但在python端却没有数据，最开始是tcpdump直接写入文件，发现python抓包数据包为0，后面就改为直接标准输出\n通过输出发现当进行文件传输时，nginx有数据，但python没有数据，这时开始怀疑是不是nginx的问题，多次搜索后发现python的Gunicorn服务建议nginx配置参数\n代码 1 proxy_buffering off; 这个参数在前端传大json文件给后端时也有人遇到过，似乎看到一丝希望，兴致冲冲的配置玩参数后测试，还是nginx端接收完数据最后一次性传给后端，外加几个buffer_size的结果还是一样， 这就让我郁闷，我开始怀疑nginx版本问题，查看怎么确认参数是否正确，是否生效，但并没有找到一个标准结果，无奈只能先放弃\n后来在搜索 proxy_buffering 时发现了 proxy_request_buffering。两者方向不同：前者控制从上游收到的响应缓冲，后者控制发往上游的请求体缓冲。\n关闭请求体缓冲后重新测试，大文件上传期间，Nginx 与 Python 后端都能同时抓到数据，说明请求体开始持续传给上游。\n保留一个只展示关键请求缓冲参数的配置示例：\nnginx 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 upstream app_backend { server 127.0.0.1:8000; } server { listen 8080; server_name localhost; client_max_body_size 200m; location /api/ { proxy_pass http://app_backend; proxy_http_version 1.1; proxy_request_buffering off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } 至此，这个问题暂时先告一段落，不过这次也确实会发现不像以前特别害怕排查这类问题，完全没有思路，也不知道怎么面对wireshark\n参考文件 Gunicorn Doc MySQL JDBC StreamResult通信原理浅析 wireshark tcp segment of a reassembled pdu TCP报文（ tcp dup ack 、TCP Retransmission） 两张动图-彻底明白TCP的三次握手与四次挥手 Nginx的proxy buffer参数总结 Module ngx_http_proxy_module nginx-proxy-request-buffering-is-not-working-as-expected\n","date":"2023-06-28","permalink":"/posts/nginx-upload-zero-window-troubleshooting/","title":"文件上传卡住时排查 TCP Zero Window 与 Nginx 缓冲"},{"categories":["tools"],"content":"Filco圣手二代双模蓝牙机械键盘连接方法 原文链接 https://www.cnblogs.com/dreamer-fish/p/11842667.html\n官方连接操作教程视频\n背景 买了两个键盘，偶尔忘记重连，总是记不得操作，忘记一些之前的操作。 PS：「Alt」键是空格键左边的，因为我换键了\n斐尔可小键盘的蓝牙名字短些:\n87键盘名 =\u0026gt; Convertible 2 TKL(目前连接MacBookPro, 是第3号键) 104键盘名 =\u0026gt; Majestouch Convertible 2 常规方法 确认键盘的电源接通。 同时按下「Ctrl」+「Alt」+「Fn」执行装置切换模式。配对LED灯（蓝）和低电量显示LED灯（红）约同时亮10秒左右。 想移除已登录的装置时，请从「蓝牙装置登录／切换键」①～④按下任一键 新添加的电脑 如果是新添加的电脑，会在首次配置的时候输入验证码，这时候，需要快速在键盘上按下验证码，输入后就可正常连接了 注意，如果失败的次数过多，那么就会提示输入PIN码 输入PIN码的方式 当你要连接蓝牙键盘的时电脑端出现输入PIN 此时先在本机键盘上输入任意六位PIN 接下来电脑开始验证时，迅速在你的蓝牙设备上输入刚才的六位PIN，然后回车，成功！记得使用回车，不然就会等于没哟操作 想要清楚以前绑定的设备 如果被绑定的①～④有某个想重新绑定新的电脑，可以按如下步骤：\n同时按下crtl+alt+Fn，红蓝灯闪烁4秒。 键盘背面的清除键，拿笔点住2秒。 3。 再按数字键1-4的任意一个想清除配置的数字。 然后从想连接的设备蓝牙列表中选择键盘，点击连接。 该设备屏幕出现4或6位的配对码。在键盘上依次按下配对码，回车。OK，该设备与filco蓝牙键盘连接成功。 ","date":"2023-06-18","permalink":"/posts/filco-bluetooth-pairing/","title":"Filco 蓝牙键盘连接与重新配对"},{"categories":["read"],"content":" 平时喜欢用chrome书签收集博客文章，收藏再看，最后大都没有下文了。想自己也好好整理博客，干脆就重新整理到这里，也算是单项友链\n感觉文章很有深度，记录下 博客渊源介绍: 这个好像是一个学生，但好厉害发现这个博客主要是因为对象存储的文章吸引，虽然没看，但感觉能写出这么详细的文章的人真的很厉害。\n跟着大佬后面自己每周记录周报，给自己复习回顾使用 博客渊源介绍: 最早是在推上无意间发现的，感觉写的东西很厉害，最近发现大佬也会有烦恼，润或者英语，说他是大佬的一个原因是因为他有创业的想法，我感觉很厉害。至少目前我完全没有，能在这个行业混下去就已经很不错了。他的一个周报讲到创业\nlaixintao，经常打开他的博客当wiki用 博客渊源介绍: 好像也是推特上发现的，他自己发的。他也是一个播客: 捕蛇者说 的主持之一，lanxintao。\n他的很多文章我都有看，感觉他的博客技术比我高一层，(至少我目前看是这样，有种似乎可以摸到的感觉)。但大佬的技术还是很厉害。 特别喜欢他的几篇博客\n用 Wireshark 分析 TCP 吞吐瓶颈 珍藏资料 编译LFS的一些经验 最初喜欢博客的样子 博客渊源介绍: 大概也是在推上看到的，主要是看他的布局样式很好看，就很像要拥有一个 https://blog.skk.moe/post/use-nextjs-and-hexo-to-rebuild-my-blog/\nlaike9m，捕蛇者说播客作者之一 博客渊源介绍: 捕蛇者说的另一个作者，在陈皓过世后的发了一篇博客 里面提到了一个新奇的观点，这个目前我是比较接受的 https://laike9m.com/blog/people-die-but-long-live-github,122/\n陈皓，一个永远不会更新的博客 博客渊源介绍: 很早发现的一个博客，不止是影响我一个人，无限唏嘘 他其实也是我半个导师吧，现在我还有很多想法回去看看，不过一个月过去了，没有开始看的那么多了 https://coolshell.cn/articles/21672.html\n一个热爱奥特曼的公益少年 博客渊源介绍: Manjusaka，之前看过他的相关博客，但印象最深的是我转发给媳妇儿的一篇关于信仰 她也很喜欢奥特曼 https://www.manjusaka.blog/posts/2023/06/19/why-do-I-love-ultraman/index.html\n感觉记录了很多内容，留着慢慢啃 博客渊源介绍: 无意间搜索发现的博客，意外的发现很简约，没有花里胡哨的主题，只有内容 收藏他的博客是 https://einverne.github.io/post/2023/05/the-great-depression.html 美国大萧条 读书笔记 https://einverne.github.io/post/2023/05/nonviolent-communication.html 非暴力沟通 读书笔记 是他博客的样式让我又想自己尽快学习搞个主题\n一个感觉k8s挺厉害的老外博客 博客渊源介绍: 搜主题时看到的，又喜欢上了，果然博爱。但这个主题跟上面第八个主题很相近，应该是我喜欢的类型了。 简约而不简单，这个主题样式相对好看些，间距也相对舒服些，要是自己在这个上面优化的话，大概率是加个评论跟文章目录。\n意外发下博主的文章都是k8s相关的，想想留着，总归要用的。\nzhaohuabing-CNCF Ambassador 博客渊源介绍: 搜索博客主题时，发现一篇入门博客主题就是博主发的，点进看demo，居然又是个k8s大佬，不管三七二十一，先粉了再说。\n","date":"2023-06-12","permalink":"/posts/technical-blog-reading-notes/","title":"我常看的技术博客与阅读记录"},{"categories":["tools"],"content":"博客搭建 前言 最早博客搭建是用的wordpress，挑了好久(就是拖延)，最后选了hugo，说是快。因为看到很多人从hexo转hugo，想一步到位。其实可能并不能写太多文章发布 自己也测试了hexo，体验了一把，确实感觉速度慢，不能接受(虽然时间没那么宝贵，但有点耗神)\n记录下操作记录吧，方便后期回顾 最开始也想用自己的域名，但在申请域名的时候难倒了，纠结于名字，最后决定，快速上线，先用 github.io 这个\n最后就是hugo的使用，跟主题修改了，现在还没做好，慢慢改\n是否需要英文版面 决定有哪几个大分类 评论系统 最后就是样式优化，参考: https://blog.skk.moe/ 需求\n有顶层一级导航朗 主页、归档、tags 分两列，文章和侧边栏，不用三列布局 差不多就是这些，特比担心三分钟热的，甚至不足三分钟。先记录下来\nHugo 与 Hexo 的异同 https://io-oi.me/tech/hugo-vs-hexo/\n自己写了一个主题 meme https://io-oi.me/tech/documentation-of-hugo-theme-meme/\nSukka 他的博客风格样式很喜欢，但不会开发，一直想拿来做参考 我的博客有多快\nHugo 博客搭建教程 Github Pages + Hugo 搭建个人博客 2022 年了，聊聊我为什么还在写博客 deploy_your_blog_using_hugo_and_github_action Hugo博客自定义shortcodes Hugo Stack主题更新小记 博客切换到STACK 主题 个人网站的建立过程（三）：Hugo主题stack的使用与优化 Hugo主题开发日记 Hugo 详细笔记（建站教程、工作原理） Hugo 从入门到会用 hugo搭建个人博客2-文章写作 手把手使用Hugo搭建网站-中级篇 gohugo官方文档 quickstart gohugo官方文档 organization gohugo官方文档 hosting-on-github github官方文档 actions-hugo cloudflare Deploy a Hugo site 博客安装 代码 1 2 3 4 5 6 7 arch -arm64 brew install hugo brew install hugo git clone https://github.com/sona201/blogs.git git submodule git submodule init git submodule update git子模块功能安装使用 Git submodule 子模块的管理和使用\n代码 1 2 3 4 5 6 7 git submodule add https://github.com/sona201/hugo-theme-stack themes/hugo-theme-stack # 删除 submodule 方式，自定义主题 rm -rf .gitmodules # 删除项目目录下.gitmodules文件中子模块相关条目 vi .git/config # 删除配置项中子模块相关条目 rm .git/module/* 删除模块下的子模块目录，每个子模块对应一个目录，注意只删除对应的子模块目录即可 git rm --cached themes/hugo-theme-stack -rf Hugo 配置讲解 Hugo 分类讲解 Hugo 博客主题开发1 Hugo 博客主题开发2 Hugo 自定义Hugo主题样式 Hugo 自定义hugo主题\u0026ndash;从内容页开始 Hugo 框架指引 hugo主题开发流程 动手写一个 Hugo 博客主题 hugo主题开发 可参考官方文档的 Introduction to Hugo Templating 可参考 Régis Philibert 写的相关教程。 推荐安装相应的语法高亮插件，以更高效开发，博主后来开发时使用的是 VS Code 的 Hugo Language and Syntax Support。\nhugo 配置文件官方说明\n喜欢的主题参考 hugo-notepadium hugo-theme-meme 个人博客链接\n优化 参考链接区分优先级别，哪些价值不大，哪些还需要继续参考研究\nhttps://zz2summer.github.io/github-pages-hugo-%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/#%E5%9B%9B%E9%80%89%E6%8B%A9-hugo-%E4%B8%BB%E9%A2%98\nhttps://www.sulvblog.cn/posts/blog/shortcodes/\nhttps://chupai.github.io/posts/200316_hugo/\nhttps://www.gohugo.org/doc/overview/quickstart/\nhttps://www.gohugo.org/doc/content/organization/\nhttps://shuzang.github.io/2019/hugo-blog-article-write/\nhugo 的配置使用 https://www.yunduan.io/posts/hugo-medium-level-lesson/\nhttps://gohugo.io/hosting-and-deployment/hosting-on-github/\n好的链接 https://strrl.dev/links/\n路径配置最关键\nhttps://qwqaq.com/2022/04/migrate-to-hugo/#%E5%88%B6%E4%BD%9C%E4%B8%BB%E9%A2%98\n这个链接讲内容很详细，但没有讲怎么搞配置文件。所以要结合上面一篇文章\n关于博客搭建(建站之初link)\nhttps://stack-theme-mod.vercel.app/2022/stack-theme-mod/ https://viflythink.com/Migrate_from_Material_to_Stack/ https://viflythink.com/links/ ","date":"2023-05-26","permalink":"/posts/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/","title":"博客搭建"},{"categories":["tcp"],"content":"相关文章 wireshark 常用功能笔记 用 Wireshark 分析 TCP 吞吐瓶颈\nwireshark你一定会喜欢的技巧摘要\n修改seq的为绝对值 relative 配置rt 分析 分析图\nwireshark https 抓包 tls contains \u0026ldquo;baidu.com\u0026rdquo;\nwireshark 没有权限\n文本 1 2 3 4 5 6 You do not have permission to capture on device \u0026#34;utun7\u0026#34;. ((cannot open BPF device) /dev/bpf0: Permission denied) Please check to make sure you have sufficient permissions. If you installed Wireshark using the package from wireshark.org, close this dialog and click on the \u0026#34;installing ChmodBPF\u0026#34; link in \u0026#34;You can fix this by installing ChmodBPF.\u0026#34; on the main screen, and then complete the installation procedure. 或者下图\n命令行赋权\n直接简单粗暴的777 代码 1 sudo chmod 777 /dev/bpf* 查看当前用户名，给当前用户赋权 代码 1 sudo chown clin:wheel /dev/bpf* 本来这个命令都不打算记录，但最近在自己电脑上重新安装就出现一次，还是老老实实的记录下\n一、抓包 拿到一个网络包时，我们总是希望它尽可能小。因为操作一个大包相当费时，有时甚至会死机。如果让初学者分析1GB以上的包，估计会被打击得信心全无。所以抓包时应该尽量只抓必要的部分。有很多方法可以实现这一点。\n1．只抓包头。一般能抓到的每个包（称为“帧”更准确，但是出于表达习惯，本书可能会经常用“包”代替“帧”和“分段”）的最大长度为1514字节，启用了Jumbo Frame（巨型帧）之后可达9000字节以上，而大多数时候我们只需要IP头或者TCP头就足够分析了。在Wireshark上可以这样抓到包头：单击菜单栏上的Capture\u0026ndash;\u0026gt;Options，然后在弹出的窗口上定义“Limit each packet to”的值。我一般设个偏大的数字：80字节，也就是说每个包只抓前80字节。这样TCP层、网络层和数据链路层的信息都可以包括在内（见图1）。\n3.0之前版本wireshark更改方式\n图一\n3.0之后的版本\n如果问题涉及应用层，就应该再加上应用层协议头的长度。如果你像我一样经常忘记不同协议头的长度，可以输入一个大点的值。即便设成200字节，也比1514字节小多了。\n以上是使用Wireshark抓包时的建议。用tcpdump命令抓包时可以用“-s”参数达到相同效果。比如以下命令只抓eth0上每个包的前80字节，并把结果存到/tmp/tcpdump.cap文件中。\n使用tcpdump指定抓包字节命令\n代码 1 [root@server_1 /]# tcpdump -i eth0 -s 80 -w /tmp/tcpdump.cap 2．只抓必要的包。服务器上的网络连接可能非常多，而我们只需要其中的一小部分。Wireshark的Capture Filter可以在抓包时过滤掉不需要的包。比如在成百上千的网络连接中，我们只对IP为10.32.200.131的包感兴趣，那就可以在Wireshark上这样设置：单击菜单栏上的Capture\u0026ndash;\u0026gt;Options，然后在Capture Filter中输入“host 10.32.200.131”（见图2）。\n老版本\n图二\nVersion 4.0.5版本\nwireshark官方的过滤文档 用tcpdump命令抓包时，也可以用“host”参数达到相同效果。比如以下命令只抓与10.32.200.131通信的包，并把结果存到/tmp/tcpdump.cap文件中。\n代码 1 [root@server_1 /]# tcpdump -i eth0 host 10.32.200.131 -w /tmp/tcpdump.cap 注意：设置Capture Filter之前务必三思，以免把有用的包也过滤掉，尤其是容易被忽略的广播包。当然有时候再怎么考虑也会失算，比如我有一次把对方的IP地址设为filter，结果一个包都没抓到。最后只能去掉filter再抓，才发现是NAT（网络地址转换）设备把对方的IP地址改掉了。 抓的包除了要小，最好还能为每步操作打上标记。这样的包一目了然，赏心悦目。比如要在Windows上抓一个包含三步操作的问题，我会这样抓。\n想指定ping，发送三次包，每次只发送一个ping包(count)，不通次数发送的ping的size大小不一样，\nwindows机器指定ping方式\n代码 1 2 3 4 5 6 （1）ping \u0026lt;IP\u0026gt; -n 1 -l 1 （2）操作步骤1 （3）ping \u0026lt;IP\u0026gt; -n 1 -l 2 （4）操作步骤2 （5）ping \u0026lt;IP\u0026gt; -n 1 -l 3 （6）操作步骤3 windows ping命令解释\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 用法: ping [-t] [-a] [-n count] [-l size] [-f] [-i TTL] [-v TOS] [-r count] [-s count] [[-j host-list] | [-k host-list]] [-w timeout] [-R] [-S srcaddr] [-c compartment] [-p] [-4] [-6] target_name 选项: -t Ping 指定的主机，直到停止。 若要查看统计信息并继续操作，请键入 Ctrl+Break； 若要停止，请键入 Ctrl+C。 -a 将地址解析为主机名。 -n count 要发送的回显请求数。 -l size 发送缓冲区大小。 -f 在数据包中设置“不分段”标记(仅适用于 IPv4)。 -i TTL 生存时间。 -v TOS 服务类型(仅适用于 IPv4。该设置已被弃用， 对 IP 标头中的服务类型字段没有任何 影响)。 -r count 记录计数跃点的路由(仅适用于 IPv4)。 -s count 计数跃点的时间戳(仅适用于 IPv4)。 -j host-list 与主机列表一起使用的松散源路由(仅适用于 IPv4)。 -k host-list 与主机列表一起使用的严格源路由(仅适用于 IPv4)。 -w timeout 等待每次回复的超时时间(毫秒)。 -R 同样使用路由标头测试反向路由(仅适用于 IPv6)。 根据 RFC 5095，已弃用此路由标头。 如果使用此标头，某些系统可能丢弃 回显请求。 -S srcaddr 要使用的源地址。 -c compartment 路由隔离舱标识符。 -p Ping Hyper-V 网络虚拟化提供程序地址。 -4 强制使用 IPv4。 -6 强制使用 IPv6。 mac/linux ping命令执行\n代码 1 2 3 4 5 6 ping \u0026lt;IP\u0026gt; -c 1 -s 1 操作步骤1 ping \u0026lt;IP\u0026gt; -c 1 -s 2 操作步骤2 ping \u0026lt;IP\u0026gt; -c 1 -s 3 操作步骤3 这个抓包在mac上命令方式不一样，导致我一直没有执行成功\n如图3所示，如果我需要分析步骤1，则只要看146～183之间的包即可。注意到146号包最底下的“Data（1 byte）”了吗？byte的数目表示是第几步，这样就算在步骤很多的情况下也不会混乱。 抓包的技巧还有很多，比如可以写一个脚本来循环抓包，等侦察到某事件时自动停止。一位工程师即便不懂网络分析，但如果能抓得一手好包，也是一项很了不起的技能了。\n二、个性化设置 Wireshark的默认设置堪称友好，但不同用户的从事领域和使用习惯各有不同，所以有时需要根据自己的情况对配置略作修改。\n1．我经常需要参照服务器上的日志时间，找到发生问题时的网络包。所以就把Wireshark的时间调成跟服务器一样的格式。单击Wireshark的View\u0026ndash;\u0026gt;Time Display Format\u0026ndash;\u0026gt;Date and Time of Day，就可以实现此设置（见图4）。\n图四\nVersion 4.0.5版本设置\n2．不同类型的网络包可以自定义颜色，比如网络管理员可能会把OSPF等协议或者与Spanning Tree Protocol（生成树协议）相关的网络包设成最显眼的颜色。而文件服务器的管理员则更关心FTP、SMB和NFS协议的颜色。我们可以通过View \u0026ndash;\u0026gt;Coloring Rules来设置颜色。如果同事已经有一套非常适合你工作内容的配色方案，可以请他从Coloring Rules窗口导出，然后导入到你的Wireshark里（见图5）。记得下次和他吃饭时主动买单，要知道配一套养眼的颜色可要花不少时间。\n图五\nVersion 4.0.5版本的样式\n3．更多的设置可以在Edit\u0026ndash;\u0026gt;Preferences窗口中完成。这个窗口的设置精度可以达到一些协议的细节。比如在此窗口单击Protocols\u0026ndash;\u0026gt;TCP就可以看到多个TCP相关选项，将鼠标停在每一项上都会有详细介绍。假如经常要对Sequence Number做加减运算，不妨选中Relative sequence numbers（见图6），这样会使Sequence number看上去比实际小很多。\nVersion 4.0.5版本的样式\n4．如果你在其他时区的服务器上抓包，然后下载到自己的电脑上分析，最好把自己电脑的时区设成跟抓包的服务器一样。这样，Wireshark显示的时间才能匹配服务器上日志的时间。比如说，服务器的日志显示2/13/2014 13:01:32有一个错误信息。那我们要在自己电脑上调整时区之后，才能到Wireshark上检查2/13/2014 13:01:32左右的包，否则就得先换算时间。\n三、过滤 很多时候，解决问题的过程就是层层过滤，直至找到关键包。前面已经介绍过抓包时的Capture Filter功能了。其实在包抓下来之后，还可以进一步过滤，而且这一层的过滤功能更加强大。下图表示一个\u0026quot;IP为10.32.106.50，且TCP端口为445\u0026quot;的过滤表达式。\nVersion 4.0.5版本的样式\n要说过滤的作用与技巧，就算专门写一本小册子都不为过。篇幅所限，本文只能\u0026quot;过滤\u0026quot;出最适合初学者的部分。\n1．如果已知某个协议发生问题，可以用协议名称过滤一下。以Windows Domain的身份验证问题为例，如果已知该域的验证协议是Kerberos，那么就在Filter框输入Kerberos作为关键字过滤。除了纯粹的Kerberos包，你还将得到Session Setup之类包含Kerberos的包。\n用协议过滤时务必考虑到协议间的依赖性。比如NFS共享挂载失败，问题可能发生在挂载时所用的mount协议，也可能发生在mount之前的portmap协议。这种情况下就需要用\u0026quot;portmap || mount\u0026quot;来过滤了。如果不懂协议间的依赖关系怎么办？我也没有好办法，只能暂时放弃这个技巧，等熟悉了该协议后再用。\n2．IP地址加port号是最常用的过滤方式。除了手工输入ip.addreq \u0026amp;\u0026amp;tcp.porteq\u0026lt;端口号\u0026gt;之类的过滤表达式，Wireshark还提供了更快捷的方式：右键单击感兴趣的包，选择Follow TCP/UDP Stream（选择TCP还是UDP要视传输层协议而定）就可以自动过滤。而且该Stream的对话内容会在新弹出的窗口中显示出来。\nVersion 4.0.5版本的样式\n经常有人在论坛上问，Wireshark是按照什么过滤出一个TCP/UDP Stream的？答案就是：两端的IP加port。单击Wireshark的Statistics\u0026ndash;\u0026gt;Conversations，再单击TCP或者UDP标签就可以看到所有的Stream。\nVersion 4.0.5版本的样式\n3．用鼠标帮助过滤。我们有时因为Wireshark而苦恼，并不是因为它功能不够，而是强大到难以驾驭。比如在过滤时，有成千上万的条件可供选择，但怎么写才是合乎语法的？虽然 http://www.wireshark.org/docs/dfref/ 提供了参考，但经常查找毕竟太费时费力了。Wireshark考虑到了这个需求，右键单击Wireshark上感兴趣的内容，然后选择Prepare a Filter\u0026ndash;\u0026gt;Selected，就会在Filter框中自动生成过滤表达式。在有复杂需求的时候，还可以选择And、Or等选项来生成一个组合的过滤表达式。\n假如右键单击之后选择的不是Prepare a Filter，而是Apply as Filter\u0026ndash;\u0026gt;Selected，则该过滤表达式生成之后还会自动执行。图12显示了在一个SMB包的SMB Command: Read AndX上右键单击，并选择Selected之后，所有的Read包都会被过滤出来。\nVersion 4.0.5版本的样式\n4．我们可以把过滤后得到的网络包存在一个新的文件里，因为小文件更方便操作。单击Wireshark的File\u0026ndash;\u0026gt;Save As，选中Displayed单选按钮再保存，得到的新文件就是过滤后的部分。\n有时候你会发现，保存后的文件再打开时会显示很多错误。这是因为过滤后得到的不再是一个完整的TCP Stream，就像抓包时漏抓了很多一样。所以选择Displayed选项时要慎重考虑。\n注意：有些Wireshark版本把这个功能移到了菜单File\u0026ndash;\u0026gt;Export Specified Packets…选项中，如下图所示。\nVersion 4.0.5版本的样式\n总体来说，过滤是Wireshark中最有趣，最难，也是最有价值之处，值得我们用心学习。\n四、让Wireshark自动分析 有些类型的问题，我们根本不需要研究包里的细节，直接交给Wireshark分析就行了。\n1．单击Wireshark的Analyze\u0026ndash;\u0026gt;Expert Info Composite，就可以在不同标签下看到不同级别的提示信息。比如重传的统计、连接的建立和重置统计，等等。在分析网络性能和连接问题时，我们经常需要借助这个功能。下图是TCP包的重传统计。\nVersion 4.0.5版本的样式\n2．单击Statistics\u0026ndash;\u0026gt;Service Response Time，再选定协议名称，可以得到响应时间的统计表。我们在衡量服务器性能时经常需要此统计结果。图16展示的是SMB2读写操作的响应时间。\n4.0.5版本的协议很少，甚至没有看到tcp协议，把所有协议看了，只有NCP、SMB两种协议有内容，内容不像分析数据。\n3．单击Statistics\u0026ndash;\u0026gt;TCP Stream Graph，可以生成几类统计图。比如我曾经用Time-Sequence Graph (Stevens)生成了下图。\nVersion 4.0.5版本的样式\n从老图中可以看出25～40秒，以及65～75秒之间没有传输数据。进一步研究，发现发送方内存不足，所以偶尔出现暂停现象，添加内存后问题就解决了。\n为什么Wireshark要把这个图称为“Stevens”呢？我猜是为了向《TCP/IP Illustrated》的作者Richard Stevens致敬。这也是我非常喜欢的一套书，在此推荐给所有读者。\n4．单击Statistics\u0026ndash;\u0026gt;Summary，可以看到一些统计信息，比如平均流量等，这有助于我们推测负载状况。比如下图中的网络包才1.594Mbit/s，说明流量低得很。\nVersion 4.0.5版本没有找到 Statistics\u0026ndash;\u0026gt;Summary 选项，但找到 Statistics\u0026ndash;\u0026gt;Conversations(对话)，但看起来结果差不多\nVersion 4.0.5版本没有找到 Statistics\u0026ndash;\u0026gt;Summary 选项，但找到 Statistics\u0026ndash;\u0026gt;Capture File Properties，菜单改了\n参考最新菜单解释https://www.wireshark.org/docs/wsug_html_chunked/ChUseStatisticsMenuSection.html\n五、最容易上手的搜索功能 与很多软件一样，Wireshark也可以通过“Ctrl+F”搜索关键字。假如我们怀疑包里含有“error”一词，就可以按下“Ctrl+F”之后选中“String”单选按钮，然后在Filter中输入“error”进行搜索。很多应用层的错误都可以靠这个方法锁定问题包。\n一篇文章不可能涵盖所有技巧，本文就到此为止。最后要分享的，是我认为最“笨”但也是最重要的一个技巧——勤加练习。只要练到这些技巧都变成习惯，就可以算登堂入室了。\n","date":"2023-05-19","permalink":"/posts/wireshark%E6%8A%80%E5%B7%A7/","title":"Wireshark技巧"},{"categories":["tcp"],"content":"相关文章\n方案: 手动启动浏览器，让浏览器把证书放到指定目录，wireshark去读取，然后解析，就可以得到解密后的包了，等同于http请求抓包\n一、启动浏览器 在终端执行命令，打开新的 Chrome 浏览器\n代码 1 /Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --user-data-dir=/tmp/chrome --ssl-key-log-file=/tmp/.ssl-key.log 二、配置wireshark 代码 1 打开 Wireshark ，Wireshark - Perferences - Protocols - TLS ，在 (Pre)-Master-Secret log filename 输入 /tmp/.ssl-key.log 三、抓包 打开浏览器，发出请求，开始抓包 四、异常情况 异常情况暂未遇到过，这个先记录一下\n出现 Opening in existing browser session. 解决方式：关闭掉用命令启动的 Chrome，然后重新运行 Chrome 启动命令\n代码 1 2 $ ps -ef | grep /tmp/.ssl | awk \u0026#39;NR==1{print $2}\u0026#39; | xargs kill -9 $ /Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --user-data-dir=/tmp/chrome --ssl-key-log-file=/tmp/.ssl-key.log 参考文章 使用wireshark分析https\n","date":"2023-05-19","permalink":"/posts/wireshark%E6%9C%AC%E5%9C%B0%E5%8A%AB%E6%8C%81https/","title":"Wireshark本地劫持https"},{"categories":["tcp"],"content":"排查python server当时的系统信息 发现并没有异常，仅记录device eth0 entered promiscuous mode表示当时网卡 eth0 进入混杂模式,应该与当时启用tcpdump有关。\n一、将python server改为nginx server方式，测试下载有没有异常 代码 1 2 3 4 5 6 7 8 [root@172.19.9.147 ~]# curl -O http://172.19.5.43/404.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 3650 100 3650 0 0 2313k 0 --:--:-- --:--:-- --:--:-- 3564k [root@172.19.9.147 ~]# curl -O http://172.19.5.43/zookeeper.out % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1880M 100 1880M 0 0 396M 0 0:00:04 0:00:04 --:--:-- 396M 一切正常\n二、服务端参数修改异常参数测试 使用nginx server，设置成net.ipv4.tcp_wmem=\u0026quot;4096 4096 4096\u0026quot;\n顺便记录下nginx配置\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 server { listen 80; listen [::]:80; server_name _; root /usr/share/nginx/html; # Load configuration files for the default server block. # include /etc/nginx/default.d/*.conf; error_page 404 /404.html; location = /404.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } 2.1 测试改为异常数据时，能否同时下载别的文件，排除线程卡死原因 命令行窗口1 代码 1 2 3 4 [root@172.19.9.147 ~]# curl -O http://172.19.5.43/zookeeper.out % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- 0:00:13 --:--:-- 0 命令行窗口2 代码 1 2 3 4 5 6 7 8 9 10 11 12 [root@172.19.9.147 ~]# curl -O http://172.19.5.43/404.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 3650 100 3650 0 0 2676k 0 --:--:-- --:--:-- --:--:-- 3564k [root@172.19.9.147 ~]# curl -O http://172.19.5.43/zookeeper.out % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- 0:01:00 --:--:-- 0^C [root@172.19.9.147 ~]# curl -O http://172.19.5.43/404.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 3650 100 3650 0 0 2664k 0 --:--:-- --:--:-- --:--:-- 3564k 2.2 测试改为异常数据的临界值，使用二分法测试 代码 1 2 3 4 5 6 7 8 9 10 11 [root@172.19.5.43 html]# sysctl -w net.ipv4.tcp_wmem=\u0026#34;4096 4096 9000\u0026#34; net.ipv4.tcp_wmem = 4096 4096 9000 # 这个可以正常下载 [root@172.19.5.43 html]# sysctl -w net.ipv4.tcp_wmem=\u0026#34;4096 4096 8259\u0026#34; net.ipv4.tcp_wmem = 4096 4096 8259 # 这个可以正常下载 [root@172.19.5.43 html]# sysctl -w net.ipv4.tcp_wmem=\u0026#34;4096 4096 8257\u0026#34; net.ipv4.tcp_wmem = 4096 4096 8257 [root@172.19.5.43 html]# sysctl -w net.ipv4.tcp_wmem=\u0026#34;4096 4096 8258\u0026#34; net.ipv4.tcp_wmem = 4096 4096 8258 # 这个不能下载 结论：参数配置再 net.ipv4.tcp_wmem=\u0026quot;4096 4096 8258\u0026quot; 及以下不能正常请求\n三、排除文件原因，使用dd生成2G文件 设置为sysctl -w net.ipv4.tcp_wmem=\u0026quot;4096 4096 4096\u0026quot;,大文件确实会被卡住，小文件不受影响。\n用dd if=/dev/zero of=large_file bs=1G count=2生成的文件2G大小文件。\n命令行窗口1 代码 1 2 3 4 [root@172.19.9.147 ~]# curl -O http://172.19.5.43/large_file % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- 0:00:04 --:--:-- 0 命令行窗口2 代码 1 2 3 4 [root@172.19.9.147 ~]# curl -O http://172.19.5.43/404.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 3650 100 3650 0 0 2905k 0 --:--:-- --:--:-- --:--:-- 3564k 同样，换成 large_file 文件一样不能下载。得出结果与之前文件一致：\n临界值就是在net.ipv4.tcp_wmem = 4096 4096 8259 下载大文件卡住时，同时下载小文件不受影响。 四、确认内核版本 当前是用的linux版本内核为\n代码 1 2 [root@172.19.5.43 html]# uname -a Linux 172.19.5.43 3.10.0-957.27.2.el7.x86_64 #1 SMP Mon Jul 29 17:46:05 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux ","date":"2023-05-19","permalink":"/posts/tc_%E5%AE%9E%E9%AA%8C/","title":"Tc_实验"},{"categories":["think"],"content":"我是2014年毕业，去过工地，干过销售。16年，我妈查出胃癌早期，一年后，她身体差不多康复，于是我决定出去闯闯。\n其实也算不上闯，只是听起来好听而已。在大学的时候，我学的是电气工程及其自动化，一个跟计算机几乎没有关系的专业。对口工作就是水电工，或者去供电局。但我其实小时候就对计算机感兴趣，高考填志愿的时候，我想选计算机，结果家里没同意，怕找不到工作。我也没有跟家里犟，毕竟我也不知道未来啥样。大学也会看看相关的消息，拿的出手的事就是用自己电脑安装了双系统，win7/ubuntu。2017年，我拿出4000块报名了一个运维线上培训班，其实15年就想报名了，但没有下定决心，担心自己做不好等等。16年照顾我妈身体的时候，买了本《鸟哥的私房菜》。然后就慢慢开始了我的IT路。\n17好不容易找了一家公司要我，毕竟培训班出来的，水平菜的不像话。那段时间疯狂学习，接触到了coolshell.cn，发现这个人好厉害，怎么会这么多东西，关键是这些东西都学的很深。夸张点讲，每个名词都我要去百度查询下。在推上看到好多人都说被陈皓帮助过，感叹我怎么还没能成长起来。年龄也不小了。\n当时特别沉迷他的博客，虽然没有看完所有，但真的好多对我帮助很大。找到当时一篇文章陈皓的经历，下午与同事聊天说到他这段故事，还是感叹他厉害。不过这会儿又翻了好多他的博客，心里有点欣慰，还是有很多宝藏在的，只是我之前不珍惜。总感觉没有人给我指路。也是他的数字遗产，珍惜。\n想想他 5/1 发的推文，将他爸爸每天发送平安。\n第一次意识到死亡\n小时候，大概是10岁左右，上上上辈儿的老人相继离世，那个时候还不太明白生命的意义，直到某次参加葬礼，在火葬场差点丢了，没找到回去灵车，感觉是不是那天没人找我，是不是要死在那里了，没人认识我。后来我经常熬夜，上床后听到自己心跳声很大，担心突然猝死。小时候也梦到过自己死亡。\n同龄人离世\n上高一时，一个同年级的孩子突然睡梦中离世，听说学校的处理方式就是把几个同宿舍的孩子换个寝室。那阵子，税前也总是担心看不到明天的太阳。直到现在，我也是习惯把自己搞的很累，几乎倒床就睡的那种，不然就会疯狂玩手机。睡前胡思乱想已经是常态。\n同龄人离死亡最近的时候。\n朋友2019年去医院看病，当时是肚子不舒服，在上海瑞金医院看病，大概是做了常规检查:心电图，然后就被医生叫住，直接送进重症病房，甚至他手里还拎着肚子疼开的药。电话联系家属，告知以后不能喝酒等，注意饮食。不能过量运动。后来他出院，我打电话听到消息，那个周末去找他，跟我说着说着就哭了，担心白发人送黑发人。那段时间，我真的有在想自己的健康好不好。\n陈皓的突然离世\n这个是真的意外，总感觉这是命运的玩笑，看到消息第一反应是不是谣言，希望辟谣，但看了推文，甚至还@haoel，太唏嘘了。很多推友在考虑养生，但这个大概率又是3分钟热度吧。最近也是焦虑成性，经常熬夜，到家总是看短视频。陈皓说，短视频最浪费时间，最没营养。今天把微信的短视频入口关了。\n陈皓留的数字文化遗产还挺多的，够学好久了。\nR.I.P(Requiescat in pace)\n关于数字遗产这件事，今天看到laike9m的文章people-die-but-long-live-github，感叹人的第二次消失有时候也挺容易的。\n也同时感慨，人要做什么才会有意义。这个问题需要好好问问自己。想到和菜头的一篇文章，讲一个美国的地勤偷飞机，然后完成了自己的梦想，最后与飞机一起坠毁文章链接，故事在结尾，有时候对别的朋友圈生活嗤之以鼻，但也会羡慕。媳妇儿却很简单，想去尝试下那种生活。今年的计划，去趟香港，开开眼。\n文章链接 infoQ-纪念陈皓（左耳朵耗子） 和菜头-仅有他汀是不够的 laike9m-R.I.P. 陈皓 laixintao-缅怀陈皓\n","date":"2023-05-15","permalink":"/posts/haoel/","title":"Haoel"},{"categories":["tcp"],"content":"tcp实验 1 实验准备 1.1 虚拟机 使用 VMware 创建两台本地隔离虚拟机，系统为 Ubuntu 20.04 LTS。以下私有地址只作为本地实验示例，与图中的地址一致：\ntcp-server: 192.168.77.131 tcp-client: 192.168.77.132 1.1.1 虚拟机相关信息 系统相关信息\n内核版本 tc qdesc信息 系统内核相关参数 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 root@tcp-server:~# uname -a Linux tcp-server 5.4.0-149-generic #166-Ubuntu SMP Tue Apr 18 16:51:45 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux root@tcp-server:~# tc qdisc show dev ens33 qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn root@tcp-server:~# sudo sysctl -a | egrep \u0026#34;rmem|wmem|tcp_mem|adv_win|moderate\u0026#34; net.core.rmem_default = 212992 net.core.rmem_max = 212992 net.core.wmem_default = 212992 net.core.wmem_max = 212992 net.ipv4.tcp_adv_win_scale = 1 net.ipv4.tcp_mem = 44544 59392 89088 net.ipv4.tcp_moderate_rcvbuf = 1 net.ipv4.tcp_rmem = 4096 131072 6291456 net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.udp_rmem_min = 4096 net.ipv4.udp_wmem_min = 4096 vm.lowmem_reserve_ratio = 256 256 32 0 0 Tips: 可以给虚拟机挂载本地文件目录，可以免去拷贝tcpdump file到本地，直接使用\n1.2 大文件 1.2.1 dd生成大文件 代码 1 dd if=/dev/zero of=large_file bs=1G count=2 1.3 服务器 1.3.1 ubuntu安装nginx 最早使用 Python HTTP 服务器，多次实验结果不够稳定。为减少应用服务器差异，本组实验改用 Nginx。\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 root@tcp-server:~# apt install -y nginx Reading package lists... Done Building dependency tree Reading state information... Done nginx is already the newest version (1.18.0-0ubuntu1.4). 0 upgraded, 0 newly installed, 0 to remove and 19 not upgraded. root@tcp-server:~# systemctl start nginx root@tcp-server:~# systemctl status nginx ● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2023-05-29 15:09:35 UTC; 23h ago Docs: man:nginx(8) Main PID: 2154 (nginx) Tasks: 3 (limit: 4573) Memory: 515.4M CGroup: /system.slice/nginx.service ├─2154 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; ├─2155 nginx: worker process └─2156 nginx: worker process May 29 15:09:35 tcp-server systemd[1]: Starting A high performance web server and a reverse proxy server... May 29 15:09:35 tcp-server systemd[1]: Started A high performance web server and a reverse proxy server. 1.3.2 配置nginx 修改nginx配置文件，能通过http://ip/path访问到文件。具体操作如下。\nnginx默认配置文件启动了80端口，配置文件为/etc/nginx/sites-enabled/default，查看配置文件发现默认nginx的html路径为/var/www/html。 将刚刚生成的大文件large_file移动到/var/www/html。\n1.3.3 测试nginx服务器配置 在tcp-client机器上访问文件\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 root@tcp-client:~# curl -O http://192.168.77.131/large_file % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2048M 100 2048M 0 0 32.4M 0 0:01:03 0:01:03 --:--:-- 30.9M root@tcp-client:~# ping 192.168.77.131 PING 192.168.77.131 (192.168.77.131) 56(84) bytes of data. 64 bytes from 192.168.77.131: icmp_seq=1 ttl=64 time=0.480 ms 64 bytes from 192.168.77.131: icmp_seq=2 ttl=64 time=0.716 ms 64 bytes from 192.168.77.131: icmp_seq=3 ttl=64 time=0.671 ms 64 bytes from 192.168.77.131: icmp_seq=4 ttl=64 time=0.602 ms ^C --- 192.168.77.131 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3064ms rtt min/avg/max/mdev = 0.480/0.617/0.716/0.089 ms 不调整网络测试下载速度/时间，看看抓包分析rtt，tcptrace图\n客户端机器上抓包 代码 1 2 3 4 5 root@tcp-client:~# tcpdump -i ens33 \u0026#34;host 192.168.77.131\u0026#34; -w tcpdump-clinet-none.cap tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes ^C155254 packets captured 155254 packets received by filter 0 packets dropped by kernel 服务端上抓包 代码 1 2 3 4 5 root@tcp-server:~# tcpdump -i ens33 -s 120 \u0026#34;host 192.168.77.132\u0026#34; -w tcpdump-server-none.cap tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 120 bytes ^C133195 packets captured 133195 packets received by filter 0 packets dropped by kernel 抓包命令解释:\n可以不指定 -s，抓全量，这样可以看出更多信息，但也会导致文件下载很大。 分析tcp图表，rtt(Round Trip Time)一个tcp包往返的时间，一般使用ping命令也能发现，但不能完全代替，因为会有内核数据处理损耗等导致误差。 分析sequence(tcptrace)图标，详细到50ms内，看网络延迟，ack响应时间。 服务端抓包rtt图，rtt变化比较大\n客户端端抓包rtt图，rtt时间大概在10以内\n2 实验1:增加rt，验证 tc 延迟 clinet使用tc模拟延迟\nroot netem 作用于所配置网卡的出站流量。本实验在 client 端配置，因此其发往 server 的请求和 ACK 会受到延迟影响；若在 server 端配置，则延迟作用于 server 发出的数据。\n代码 1 2 3 4 5 6 7 root@tcp-client:~# tc qdisc add dev ens33 root netem delay 100ms root@tcp-client:~# tc qdisc show dev ens33 qdisc netem 8002: root refcnt 2 limit 1000 delay 100.0ms root@tcp-client:~# curl -O http://192.168.77.131/large_file % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2048M 100 2048M 0 0 13.9M 0 0:02:27 0:02:27 --:--:-- 23.8M 服务端抓包，有几段很明显的ack响应延迟，平均下载速度也小了，13.9M\n客户端抓包，rtt图延迟直接在100以上，与预期一致；对比没有延迟的rtt图，几乎是所有数据点上浮100ms\n3 实验2:写死 server wmem 删除之前增加的延迟配置\n代码 1 2 3 4 root@tcp-client:~# tc qdisc del dev ens33 root root@tcp-client:~# tc qdisc show dev ens33 qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn root@tcp-client:~# 调整内核参数\n内核参数说明\nBash 1 2 3 4 sudo sysctl -a | egrep \u0026#34;rmem|wmem|tcp_mem|adv_win|moderate\u0026#34; net.ipv4.tcp_rmem = 4096 131072 6291456 // 最小值、默认值、最大值 net.ipv4.tcp_wmem = 4096 16384 4194304 // 最小值、默认值、最大值 sysctl：可以查看和修改内核参数 rmem：套接字接收缓冲区大小 wmem：套接字发送缓冲区大小\n下面的实现会调整 rmem 和 wmem 的大小做相关的实验。\n修改前参数\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 root@tcp-client:~# sysctl -a | egrep \u0026#34;rmem|wmem|tcp_mem|adv_win|moderate\u0026#34; net.core.rmem_default = 212992 net.core.rmem_max = 212992 net.core.wmem_default = 212992 net.core.wmem_max = 212992 net.ipv4.tcp_adv_win_scale = 1 net.ipv4.tcp_mem = 44544 59392 89088 net.ipv4.tcp_moderate_rcvbuf = 1 net.ipv4.tcp_rmem = 4096 131072 6291456 net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.udp_rmem_min = 4096 net.ipv4.udp_wmem_min = 4096 vm.lowmem_reserve_ratio = 256 256 32 0 0 3.1 不调整 RT，仅调整server的wmem参数 修改，限死 wmem\n代码 1 2 root@tcp-server:~# sysctl -w net.ipv4.tcp_wmem=\u0026#34;4096 4096 4096\u0026#34; net.ipv4.tcp_wmem = 4096 4096 4096 查看curl命令使用时间结果，下载速度很快，平均速度甚至大约不限制的情况\n代码 1 2 3 4 root@tcp-client:~# curl -O http://192.168.77.131/large_file % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2048M 100 2048M 0 0 48.4M 0 0:00:42 0:00:42 --:--:-- 43.5M 平均下载速度Average Dload: 48.4M\nserver抓包\n代码 1 2 3 4 5 root@tcp-server:~# tcpdump -i ens33 -s 120 \u0026#34;host 192.168.77.132\u0026#34; -w tcpdump-server-wmem4096.cap tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 120 bytes ^C55406 packets captured 55406 packets received by filter 0 packets dropped by kernel client抓包\n代码 1 2 3 4 5 root@tcp-client:~# tcpdump -i ens33 \u0026#34;host 192.168.77.131\u0026#34; -w tcpdump-clinet-wmem4096.cap tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes ^C72485 packets captured 72485 packets received by filter 0 packets dropped by kernel 看stevens概览图，很平滑 放大看stevens图，也没有很明显的延迟，两个包之间的ack时间也很短 看看server的window窗口图，暂时也没看出啥特别的 3.2 接下来对 client 端调整 rt 为 100ms，看下整个下载情况。 代码 1 2 3 4 5 6 root@tcp-client:~# tc qdisc add dev ens33 root netem delay 100ms root@tcp-client:~# root@tcp-client:~# curl -O http://192.168.77.131/large_file % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2048M 100 2048M 0 0 2497k 0 0:13:59 0:13:59 --:--:-- 11.4M 平均下载速度Average Dload: 2497k\n很明显速度下降很多，最后的下载速度只有11.4M\n从stevens图里看结果，斜度比较平，中途还有很大的曲折\n另一个角度看ack响应时间 窗口明显上不去 rtt时间也是很明显100以上 3.3 调整 client 端调整 rt 为 10ms，看下整个下载情况。 代码 1 2 3 4 5 6 root@tcp-client:~# tc qdisc del dev ens33 root root@tcp-client:~# tc qdisc add dev ens33 root netem delay 10ms root@tcp-client:~# curl -O http://192.168.77.131/large_file % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2048M 100 2048M 0 0 9.9M 0 0:03:25 0:03:25 --:--:-- 8479k 平均下载速度Average Dload: 9.9M\n窗口明显上不去 rtt时间也是很明显10以上 与上述100ms对比，发现同样的wmem 4096参数下, rt越大，速度越慢\n3.4 调整 client 端调整 rt 为 200ms，看下整个下载情况。 代码 1 2 3 4 5 6 7 8 root@tcp-client:~# tc qdisc del dev ens33 root root@tcp-client:~# tc qdisc add dev ens33 root netem delay 200ms root@tcp-client:~# tc qdisc show dev ens33 qdisc netem 8005: root refcnt 2 limit 1000 delay 200.0ms root@tcp-client:~# curl -O http://192.168.77.131/large_file % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2048M 100 2048M 0 0 2069k 0 0:16:53 0:16:53 --:--:-- 6733k rt延迟时间200ms，平均下载速度Average Dload: 2069k\n","date":"2023-05-15","permalink":"/posts/tcp-netem-buffer-experiments/","title":"用 Netem 和 TCP 缓冲区观察下载速度"},{"categories":["django"],"content":"django 使用的最小web服务模块 使用的大致逻辑处理\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 # from wsgiref.simple_server import make_server from wsgiref.simple_server import WSGIServer, WSGIRequestHandler def routers(): # URLConf 配置 urlpatterns = ( (\u0026#39;/book\u0026#39;, foo), (\u0026#39;/web\u0026#39;, bar), ) return urlpatterns def foo(x): return [b\u0026#39;\u0026lt;h1\u0026gt;Hello, book\u0026lt;/h1\u0026gt;\u0026#39;] def bar(x): return [b\u0026#39;\u0026lt;h1\u0026gt;Hello, web\u0026lt;/h1\u0026gt;\u0026#39;] def application(environ, start_response): start_response(\u0026#39;200 OK\u0026#39;, [(\u0026#39;Content-Type\u0026#39;, \u0026#39;text/html\u0026#39;)]) urlpatterns = routers() path = environ[\u0026#39;PATH_INFO\u0026#39;] # http://127.0.0.1:8000/book func = None for item in urlpatterns: if item[0] == path: func = item[1] break if func: return func(environ) else: return [\u0026#39;\u0026lt;h1\u0026gt;404\u0026lt;/h1\u0026gt;\u0026#39;.encode(\u0026#39;utf-8\u0026#39;)] # httpd = make_server(\u0026#39;127.0.0.1\u0026#39;, 8000, application) # def make_server( # host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler # ) httpd = WSGIServer((\u0026#39;127.0.0.1\u0026#39;, 8000), WSGIRequestHandler) httpd.set_app(application) print(\u0026#39;Serving HTTP on port 8000...\u0026#39;) httpd.serve_forever() application 其实是 mvc架构的 control 层，负责路由的处理跟转发 然后对应的函数 foo/bar 进行处理\nhttpd.server_forever() 可以理解为while循环，等待请求\napplication 负责处理，将请求(参数、环境变量)转给对应的 func处理\n这里的env\ndjango封装 django 中的run 方法其实就是wsgiref的make_server的升级版\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): server_address = (addr, port) if threading: httpd_cls = type(\u0026#34;WSGIServer\u0026#34;, (socketserver.ThreadingMixIn, server_cls), {}) else: httpd_cls = server_cls httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) if threading: # ThreadingMixIn.daemon_threads indicates how threads will behave on an # abrupt shutdown; like quitting the server by the user or restarting # by the auto-reloader. True means the server will not wait for thread # termination before it quits. This will make auto-reloader faster # and will prevent the need to kill the server manually if a thread # isn\u0026#39;t terminating correctly. httpd.daemon_threads = True httpd.set_app(wsgi_handler) httpd.serve_forever() 抽象下\nPython 1 2 3 4 5 6 # def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): # server_address = (addr, port) httpd = WSGIServer((addr, port), WSGIRequestHandler, ipv6=ipv6) # django的 WSGIServer 不是wsgiref的 WSGIServer, 但基本一样，加了ipv6的配置，加了错误处理逻辑 httpd.set_app(wsgi_handler) httpd.serve_forever() django封装的WSGIServer方法\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class WSGIServer(simple_server.WSGIServer): \u0026#34;\u0026#34;\u0026#34;BaseHTTPServer that implements the Python WSGI protocol\u0026#34;\u0026#34;\u0026#34; request_queue_size = 10 def __init__(self, *args, ipv6=False, allow_reuse_address=True, **kwargs): if ipv6: self.address_family = socket.AF_INET6 self.allow_reuse_address = allow_reuse_address super().__init__(*args, **kwargs) def handle_error(self, request, client_address): if is_broken_pipe_error(): logger.info(\u0026#34;- Broken pipe from %s\\n\u0026#34;, client_address) else: super().handle_error(request, client_address) django 中 wsgi_handler 等同于 wsgiref 中 application 区别在于 wsgi_handler 是一个对象，而 application 是个函数，可以直接调用\nwsgi_handler的延伸 wsgi_handler跟django项目的配置settings一起, 有一个wsgi.py文件，内容如下\nPython 1 2 3 4 5 6 7 8 9 def get_wsgi_application(): \u0026#34;\u0026#34;\u0026#34; The public interface to Django\u0026#39;s WSGI support. Return a WSGI callable. Avoids making django.core.handlers.WSGIHandler a public API, in case the internal WSGI implementation changes or moves in the future. \u0026#34;\u0026#34;\u0026#34; django.setup(set_prefix=False) return WSGIHandler() 这里的 WSGIHandler() 就是一个核心类，用来处理\u0026quot;每个进来的请求\u0026quot;\ndjango请求\nhttps://0x90e.github.io/django-launch-and-request-flow/ https://www.brennantymrak.com/articles/comprehending-class-based-views-view-base-class https://stackoverflow.com/questions/28770701/django-class-based-views-understanding-the-arguments-in-the-view-class\n","date":"2023-05-05","permalink":"/posts/wsgiref/","title":"Wsgiref"},{"categories":["django"],"content":"django 封装模型采用 wsgiref 模块\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from wsgiref.simple_server import make_server def routers(): # URLConf 配置 urlpatterns = ( (\u0026#39;/book\u0026#39;, foo), (\u0026#39;/web\u0026#39;, bar), ) return urlpatterns def foo(x): return [b\u0026#39;\u0026lt;h1\u0026gt;Hello, book\u0026lt;/h1\u0026gt;\u0026#39;] def bar(x): return [b\u0026#39;\u0026lt;h1\u0026gt;Hello, web\u0026lt;/h1\u0026gt;\u0026#39;] def application(environ, start_response): start_response(\u0026#39;200 OK\u0026#39;, [(\u0026#39;Content-Type\u0026#39;, \u0026#39;text/html\u0026#39;)]) urlpatterns = routers() path = environ[\u0026#39;PATH_INFO\u0026#39;] # http://127.0.0.1:8000/book func = None for item in urlpatterns: if item[0] == path: func = item[1] break if func: return func(environ) else: return [\u0026#39;\u0026lt;h1\u0026gt;404\u0026lt;/h1\u0026gt;\u0026#39;.encode(\u0026#39;utf-8\u0026#39;)] httpd = make_server(\u0026#39;127.0.0.1\u0026#39;, 8000, application) print(\u0026#39;Serving HTTP on port 8000...\u0026#39;) httpd.serve_forever() 这里启动了一个server, 做了一个路由分发, 函数处理\n","date":"2023-05-05","permalink":"/posts/django-wsgiref/","title":"django wsgiref"},{"categories":["django"],"content":"WSGIHandler Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) # WSGIRequest对象 -\u0026gt; HttpRequest类 # def echo(request, *args, **kwargs): # data = request.GET # ret_value = data.get(\u0026#39;name\u0026#39;, \u0026#39;world\u0026#39;) # return HttpResponse(ret_value, content_type=\u0026#39;text/plain\u0026#39;) # 在 app/view/echo 中, 传入的 request 即为当前封装的request_class对象 request = self.request_class(environ) # HttpResponse 类 response = self.get_response(request) response._handler_class = self.__class__ status = \u0026#34;%d %s\u0026#34; % (response.status_code, response.reason_phrase) response_headers = [ *response.items(), *((\u0026#34;Set-Cookie\u0026#34;, c.output(header=\u0026#34;\u0026#34;)) for c in response.cookies.values()), ] start_response(status, response_headers) if getattr(response, \u0026#34;file_to_stream\u0026#34;, None) is not None and environ.get( \u0026#34;wsgi.file_wrapper\u0026#34; ): # If `wsgi.file_wrapper` is used the WSGI server does not call # .close on the response, but on the file wrapper. Patch it to use # response.close instead which takes care of closing all files. response.file_to_stream.close = response.close response = environ[\u0026#34;wsgi.file_wrapper\u0026#34;]( response.file_to_stream, response.block_size ) return response self.get_response(request) 是继承的父类 BaseHandler 的 get_response 方法 site-packages/django/core/handlers/base.py\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def get_response(self, request): \u0026#34;\u0026#34;\u0026#34;Return an HttpResponse object for the given HttpRequest.\u0026#34;\u0026#34;\u0026#34; # Setup default url resolver for this thread set_urlconf(settings.ROOT_URLCONF) # self._middleware_chain() 是处理的主要逻辑, 包含中间件和自己的视图层处理逻辑 response = self._middleware_chain(request) response._resource_closers.append(request.close) if response.status_code \u0026gt;= 400: log_response( \u0026#34;%s: %s\u0026#34;, response.reason_phrase, request.path, response=response, request=request, ) return response 处理 self._middleware_chain() 逻辑 在 BaseHandler 类的 load_middleware 方法里\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 class BaseHandler: _view_middleware = None _template_response_middleware = None _exception_middleware = None _middleware_chain = None def load_middleware(self, is_async=False): \u0026#34;\u0026#34;\u0026#34; Populate middleware lists from settings.MIDDLEWARE. Must be called after the environment is fixed (see __call__ in subclasses). \u0026#34;\u0026#34;\u0026#34; self._view_middleware = [] self._template_response_middleware = [] self._exception_middleware = [] # 关键代码 get_response = self._get_response_async if is_async else self._get_response handler = convert_exception_to_response(get_response) handler_is_async = is_async for middleware_path in reversed(settings.MIDDLEWARE): middleware = import_string(middleware_path) middleware_can_sync = getattr(middleware, \u0026#34;sync_capable\u0026#34;, True) middleware_can_async = getattr(middleware, \u0026#34;async_capable\u0026#34;, False) if not middleware_can_sync and not middleware_can_async: raise RuntimeError( \u0026#34;Middleware %s must have at least one of \u0026#34; \u0026#34;sync_capable/async_capable set to True.\u0026#34; % middleware_path ) elif not handler_is_async and middleware_can_sync: middleware_is_async = False else: middleware_is_async = middleware_can_async try: # Adapt handler, if needed. adapted_handler = self.adapt_method_mode( middleware_is_async, handler, handler_is_async, debug=settings.DEBUG, name=\u0026#34;middleware %s\u0026#34; % middleware_path, ) mw_instance = middleware(adapted_handler) except MiddlewareNotUsed as exc: if settings.DEBUG: if str(exc): logger.debug(\u0026#34;MiddlewareNotUsed(%r): %s\u0026#34;, middleware_path, exc) else: logger.debug(\u0026#34;MiddlewareNotUsed: %r\u0026#34;, middleware_path) continue else: handler = adapted_handler if mw_instance is None: raise ImproperlyConfigured( \u0026#34;Middleware factory %s returned None.\u0026#34; % middleware_path ) if hasattr(mw_instance, \u0026#34;process_view\u0026#34;): self._view_middleware.insert( 0, self.adapt_method_mode(is_async, mw_instance.process_view), ) if hasattr(mw_instance, \u0026#34;process_template_response\u0026#34;): self._template_response_middleware.append( self.adapt_method_mode( is_async, mw_instance.process_template_response ), ) if hasattr(mw_instance, \u0026#34;process_exception\u0026#34;): # The exception-handling stack is still always synchronous for # now, so adapt that way. self._exception_middleware.append( self.adapt_method_mode(False, mw_instance.process_exception), ) handler = convert_exception_to_response(mw_instance) handler_is_async = middleware_is_async # Adapt the top of the stack, if needed. handler = self.adapt_method_mode(is_async, handler, handler_is_async) # We only assign to this when initialization is complete as it is used # as a flag for initialization being complete. self._middleware_chain = handler 在 WSGIHandler 初始化 init 的时候, 就执行了 self.load_middleware() 方法\nPython 1 2 3 4 5 6 class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() django 服务器起来后, 请求进来 实例化 WSGIHandler() ,执行 load_middleware, 得到 HttpResponse 对象\n","date":"2023-05-05","permalink":"/posts/django-wsgihandler/","title":"django WSGIHandler"},{"categories":["django"],"content":"启动命令\n代码 1 python3 manage.py runserver 0.0.0.0:8000 平时用的命令就是这个, 入口是 manage.py 文件\nmanage.py 管理入口文件 Python 1 2 3 4 5 6 7 8 9 10 11 12 def main(): \u0026#34;\u0026#34;\u0026#34;Run administrative tasks.\u0026#34;\u0026#34;\u0026#34; os.environ.setdefault(\u0026#39;DJANGO_SETTINGS_MODULE\u0026#39;, \u0026#39;firstDjango.settings\u0026#39;) try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( \u0026#34;Couldn\u0026#39;t import Django. Are you sure it\u0026#39;s installed and \u0026#34; \u0026#34;available on your PYTHONPATH environment variable? Did you \u0026#34; \u0026#34;forget to activate a virtual environment?\u0026#34; ) from exc execute_from_command_line(sys.argv) manage.py 先配置环境变量, 执行命令后会把参数 runserver 0.0.0.0:8000 作为系统参数传给函数 execute_from_command_line\nsite-packages/django/core/management/init.py Python 1 2 3 4 def execute_from_command_line(argv=None): \u0026#34;\u0026#34;\u0026#34;Run a ManagementUtility.\u0026#34;\u0026#34;\u0026#34; utility = ManagementUtility(argv) utility.execute() 该函数仅仅是处理命令的方法, 实际做的就是实例化 ManagementUtility() , 调用实例的 execute() 方法 ManagementUtility 和 execute_from_command_line 都在 management 的 init 下\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 def execute(self): \u0026#34;\u0026#34;\u0026#34; Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. \u0026#34;\u0026#34;\u0026#34; try: subcommand = self.argv[1] except IndexError: subcommand = \u0026#34;help\u0026#34; # Display help if no arguments were given. # Preprocess options to extract --settings and --pythonpath. # These options could affect the commands that are available, so they # must be processed early. parser = CommandParser( prog=self.prog_name, usage=\u0026#34;%(prog)s subcommand [options] [args]\u0026#34;, add_help=False, allow_abbrev=False, ) parser.add_argument(\u0026#34;--settings\u0026#34;) parser.add_argument(\u0026#34;--pythonpath\u0026#34;) parser.add_argument(\u0026#34;args\u0026#34;, nargs=\u0026#34;*\u0026#34;) # catch-all try: options, args = parser.parse_known_args(self.argv[2:]) handle_default_options(options) except CommandError: pass # Ignore any option errors at this point. try: settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc except ImportError as exc: self.settings_exception = exc if settings.configured: # Start the auto-reloading dev server even if the code is broken. # The hardcoded condition is a code smell but we can\u0026#39;t rely on a # flag on the command class because we haven\u0026#39;t located it yet. if subcommand == \u0026#34;runserver\u0026#34; and \u0026#34;--noreload\u0026#34; not in self.argv: try: autoreload.check_errors(django.setup)() except Exception: # The exception will be raised later in the child process # started by the autoreloader. Pretend it didn\u0026#39;t happen by # loading an empty list of applications. apps.all_models = defaultdict(dict) apps.app_configs = {} apps.apps_ready = apps.models_ready = apps.ready = True # Remove options not compatible with the built-in runserver # (e.g. options for the contrib.staticfiles\u0026#39; runserver). # Changes here require manually testing as described in # #27522. _parser = self.fetch_command(\u0026#34;runserver\u0026#34;).create_parser( \u0026#34;django\u0026#34;, \u0026#34;runserver\u0026#34; ) _options, _args = _parser.parse_known_args(self.argv[2:]) for _arg in _args: self.argv.remove(_arg) # In all other cases, django.setup() is required to succeed. else: django.setup() self.autocomplete() if subcommand == \u0026#34;help\u0026#34;: if \u0026#34;--commands\u0026#34; in args: sys.stdout.write(self.main_help_text(commands_only=True) + \u0026#34;\\n\u0026#34;) elif not options.args: sys.stdout.write(self.main_help_text() + \u0026#34;\\n\u0026#34;) else: self.fetch_command(options.args[0]).print_help( self.prog_name, options.args[0] ) # Special-cases: We want \u0026#39;django-admin --version\u0026#39; and # \u0026#39;django-admin --help\u0026#39; to work, for backwards compatibility. elif subcommand == \u0026#34;version\u0026#34; or self.argv[1:] == [\u0026#34;--version\u0026#34;]: sys.stdout.write(django.get_version() + \u0026#34;\\n\u0026#34;) elif self.argv[1:] in ([\u0026#34;--help\u0026#34;], [\u0026#34;-h\u0026#34;]): sys.stdout.write(self.main_help_text() + \u0026#34;\\n\u0026#34;) else: self.fetch_command(subcommand).run_from_argv(self.argv) 截取了 ManagementUtility 的 execute 方法代码 这个就是 CommandParser 对命令的解析, 优先加载django的配置 最重要的是最后一句 self.fetch_command(subcommand).Command(self.argv) 执行 fetch_command(subcommand) 得到了 \u0026lsquo;django.contrib.staticfiles.management.commands.runserver\u0026rsquo; 结果 对应的文件路径: site-packages/django/contrib/staticfiles/management/commands/runserver.py\nsite-packages/django/core/management/commands/runserver.py 执行函数导入\nPython 1 2 3 4 5 6 7 8 def load_command_class(app_name, name): \u0026#34;\u0026#34;\u0026#34; Given a command name and an application name, return the Command class instance. Allow all errors raised by the import process (ImportError, AttributeError) to propagate. \u0026#34;\u0026#34;\u0026#34; module = import_module(\u0026#34;%s.management.commands.%s\u0026#34; % (app_name, name)) return module.Command() 即将 command 模块导入 然后实例化 django.contrib.staticfiles.management.commands.runserver.Command() 所有的命令方法都是有 command 类\n这个函数就是获取对应文件名, 然后导入Command类, 执行 run_from_argv 方法\ndjango.contrib.staticfiles.management.commands.runserver 中没有 run_from_argv 方法 django.core.management.commands.runserver 中也没有 run_from_argv 方法 django.core.management.base.BaseCommand 中有 run_from_argv 方法 主要是执行 self.execute 方法\ndjango.core.management.commands.runserver 的execute方法\nPython 1 2 3 4 5 6 7 def execute(self, *args, **options): if options[\u0026#34;no_color\u0026#34;]: # We rely on the environment because it\u0026#39;s currently the only # way to reach WSGIRequestHandler. This seems an acceptable # compromise considering `runserver` runs indefinitely. os.environ[\u0026#34;DJANGO_COLORS\u0026#34;] = \u0026#34;nocolor\u0026#34; super().execute(*args, **options) django.core.management.base.BaseCommand 的execute 方法\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 def execute(self, *args, **options): \u0026#34;\u0026#34;\u0026#34; Try to execute this command, performing system checks if needed (as controlled by the ``requires_system_checks`` attribute, except if force-skipped). \u0026#34;\u0026#34;\u0026#34; if options[\u0026#34;force_color\u0026#34;] and options[\u0026#34;no_color\u0026#34;]: raise CommandError( \u0026#34;The --no-color and --force-color options can\u0026#39;t be used together.\u0026#34; ) if options[\u0026#34;force_color\u0026#34;]: self.style = color_style(force_color=True) elif options[\u0026#34;no_color\u0026#34;]: self.style = no_style() self.stderr.style_func = None if options.get(\u0026#34;stdout\u0026#34;): self.stdout = OutputWrapper(options[\u0026#34;stdout\u0026#34;]) if options.get(\u0026#34;stderr\u0026#34;): self.stderr = OutputWrapper(options[\u0026#34;stderr\u0026#34;]) if self.requires_system_checks and not options[\u0026#34;skip_checks\u0026#34;]: if self.requires_system_checks == ALL_CHECKS: self.check() else: self.check(tags=self.requires_system_checks) if self.requires_migrations_checks: self.check_migrations() output = self.handle(*args, **options) if output: if self.output_transaction: connection = connections[options.get(\u0026#34;database\u0026#34;, DEFAULT_DB_ALIAS)] output = \u0026#34;%s\\n%s\\n%s\u0026#34; % ( self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()), output, self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()), ) self.stdout.write(output) return output 执行 self.handle 方法 django.core.management.commands.runserver 的 handle 方法\ndjango.core.management.commands.runserver 的 run 方法 django.core.management.commands.runserver 的 inner_run 方法\nPython 1 2 3 4 5 6 7 8 9 handler = self.get_handler(*args, **options) run( self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls, ) runserver 最终执行逻辑 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): # django WSGIServer server_address = (addr, port) # httpd_cls = WSGIServer if threading: httpd_cls = type(\u0026#34;WSGIServer\u0026#34;, (socketserver.ThreadingMixIn, server_cls), {}) else: httpd_cls = server_cls # httpd = WSGIServer((\u0026#39;0.0.0.0\u0026#39;, 8000), WSGIRequestHandler, ipv6=False) httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) if threading: # ThreadingMixIn.daemon_threads indicates how threads will behave on an # abrupt shutdown; like quitting the server by the user or restarting # by the auto-reloader. True means the server will not wait for thread # termination before it quits. This will make auto-reloader faster # and will prevent the need to kill the server manually if a thread # isn\u0026#39;t terminating correctly. httpd.daemon_threads = True # wsgi_handler 是一个 WSGIHandler 对象, wsgi_handler() httpd.set_app(wsgi_handler) # wsgi_handler 就是请求处理器, 即 func 函数 # /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/socketserver.py # serve_forever 方法是继承 socketserver.BaseServer.serve_forever() httpd.serve_forever() run 方法 与 wsgiref封装的模型中的 make_server 很相似 表面上都是实例化一个叫 WSGIServer 的类, 然后执行实例的 serve_forever 方法(这个方法是通过多层继承 BaseServer 类的方法, 乃 python 原生方法) 但django run 中 WSGIServer 与 make_server 中 WSGIServer 不是同一个类\nPython 1 2 3 4 5 6 7 def make_server( host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler ): \u0026#34;\u0026#34;\u0026#34;Create a new WSGI server listening on `host` and `port` for `app`\u0026#34;\u0026#34;\u0026#34; server = server_class((host, port), handler_class) server.set_app(app) return server 在 wsgiref封装的模型中, WSGIServer 是使用 simple_server.WSGIServer 这个类\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class WSGIServer(HTTPServer): \u0026#34;\u0026#34;\u0026#34;BaseHTTPServer that implements the Python WSGI protocol\u0026#34;\u0026#34;\u0026#34; application = None def server_bind(self): \u0026#34;\u0026#34;\u0026#34;Override server_bind to store the server name.\u0026#34;\u0026#34;\u0026#34; HTTPServer.server_bind(self) self.setup_environ() def setup_environ(self): # Set up base environment env = self.base_environ = {} env[\u0026#39;SERVER_NAME\u0026#39;] = self.server_name env[\u0026#39;GATEWAY_INTERFACE\u0026#39;] = \u0026#39;CGI/1.1\u0026#39; env[\u0026#39;SERVER_PORT\u0026#39;] = str(self.server_port) env[\u0026#39;REMOTE_HOST\u0026#39;]=\u0026#39;\u0026#39; env[\u0026#39;CONTENT_LENGTH\u0026#39;]=\u0026#39;\u0026#39; env[\u0026#39;SCRIPT_NAME\u0026#39;] = \u0026#39;\u0026#39; def get_app(self): return self.application def set_app(self,application): self.application = application Django 中 run 方法中的 WSGIServer 继承了 simple_server.WSGIServer 增加了一些错误处理, 本质上其实一样\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class WSGIServer(simple_server.WSGIServer): \u0026#34;\u0026#34;\u0026#34;BaseHTTPServer that implements the Python WSGI protocol\u0026#34;\u0026#34;\u0026#34; request_queue_size = 10 def __init__(self, *args, ipv6=False, allow_reuse_address=True, **kwargs): if ipv6: self.address_family = socket.AF_INET6 self.allow_reuse_address = allow_reuse_address super().__init__(*args, **kwargs) def handle_error(self, request, client_address): if is_broken_pipe_error(): logger.info(\u0026#34;- Broken pipe from %s\\n\u0026#34;, client_address) else: super().handle_error(request, client_address) runserver 最终执行逻辑 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): # django WSGIServer server_address = (addr, port) # httpd_cls = WSGIServer if threading: httpd_cls = type(\u0026#34;WSGIServer\u0026#34;, (socketserver.ThreadingMixIn, server_cls), {}) else: httpd_cls = server_cls # httpd = WSGIServer((\u0026#39;0.0.0.0\u0026#39;, 8000), WSGIRequestHandler, ipv6=False) httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) if threading: httpd.daemon_threads = True # wsgi_handler 是一个 WSGIHandler 对象, wsgi_handler() httpd.set_app(wsgi_handler) # wsgi_handler 就是请求处理器, 返回对应的 func 函数 httpd.serve_forever() handler = self.get_handler(*args, **options) -\u0026gt; get_internal_wsgi_application() -\u0026gt; get_wsgi_application() -\u0026gt; WSGIHandler() handler = WSGIHandler() -\u0026gt; handler(environ, start_response) -\u0026gt; WSGIHandler.call(environ, start_response)\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) request = self.request_class(environ) response = self.get_response(request) response._handler_class = self.__class__ status = \u0026#34;%d %s\u0026#34; % (response.status_code, response.reason_phrase) response_headers = [ *response.items(), *((\u0026#34;Set-Cookie\u0026#34;, c.output(header=\u0026#34;\u0026#34;)) for c in response.cookies.values()), ] start_response(status, response_headers) if getattr(response, \u0026#34;file_to_stream\u0026#34;, None) is not None and environ.get( \u0026#34;wsgi.file_wrapper\u0026#34; ): # If `wsgi.file_wrapper` is used the WSGI server does not call # .close on the response, but on the file wrapper. Patch it to use # response.close instead which takes care of closing all files. response.file_to_stream.close = response.close response = environ[\u0026#34;wsgi.file_wrapper\u0026#34;]( response.file_to_stream, response.block_size ) return response ","date":"2023-05-05","permalink":"/posts/django-runserver/","title":"django runserver"},{"categories":["django"],"content":"MIDDLEWARE/get_response 5.2 django处理http请求源码\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) # WSGIRequest对象 -\u0026gt; HttpRequest类 request = self.request_class(environ) # response是什么 response = self.get_response(request) def get_response(self, request): \u0026#34;\u0026#34;\u0026#34;Return an HttpResponse object for the given HttpRequest.\u0026#34;\u0026#34;\u0026#34; # Setup default url resolver for this thread set_urlconf(settings.ROOT_URLCONF) response = self._middleware_chain(request) response._resource_closers.append(request.close) if response.status_code \u0026gt;= 400: log_response( \u0026#34;%s: %s\u0026#34;, response.reason_phrase, request.path, response=response, request=request, ) return response 研究 django.http.request.py 研究 django.http.response.py self._middleware_chain(request) 他的上一步是 self.load_middleware() 在实例化 WSGIHandler 的时候就有 self.load_middleware()\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) # WSGIRequest对象 -\u0026gt; HttpRequest类 request = self.request_class(environ) # response是什么 response = self.get_response(request) 研究url 分发\nPython 1 set_urlconf(settings.ROOT_URLCONF) 大的议题\nrequest response callback, callback_args, callback_kwargs = self.resolve_request(request) 视图层的问题, as_view Python 1 2 3 4 5 6 7 8 9 MIDDLEWARE = [ \u0026#39;django.middleware.security.SecurityMiddleware\u0026#39;, \u0026#39;django.contrib.sessions.middleware.SessionMiddleware\u0026#39;, \u0026#39;django.middleware.common.CommonMiddleware\u0026#39;, \u0026#39;django.middleware.csrf.CsrfViewMiddleware\u0026#39;, \u0026#39;django.contrib.auth.middleware.AuthenticationMiddleware\u0026#39;, \u0026#39;django.contrib.messages.middleware.MessageMiddleware\u0026#39;, \u0026#39;django.middleware.clickjacking.XFrameOptionsMiddleware\u0026#39;, ] Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 初始化中间件进行层层包裹 self.load_middleware() # 将 settings.MIDDLEWARE # reversed # import_string # handler \u0026lt;-\u0026gt; self._get_response 方法 # handler = warp(XFrameOptionsMiddleware(self._get_response)) # handler = warp(MessageMiddleware(XFrameOptionsMiddleware(self._get_response))) # handler() -\u0026gt; 1. MessageMiddleware.process_request() # 2. XFrameOptionsMiddleware.process_request() # 3. self._get_response # 4. XFrameOptionsMiddleware.process_response() # 5. MessageMiddleware.process_response() # # client -\u0026gt; 1 -\u0026gt; 2 -\u0026gt; 3 -\u0026gt; request # view -\u0026gt; self._get_response # client \u0026lt;- 1 \u0026lt;- 2 \u0026lt;- 3 \u0026lt;- response 代码 1 2 3 4 5 6 7 8 SecurityMiddleware(SessionMiddleware(CommonMiddleware(CsrfViewMiddleware(AuthenticationMiddleware(MessageMiddleware(XFrameOptionsMiddleware(self._get_response))))))) SessionMiddleware(CommonMiddleware(CsrfViewMiddleware(AuthenticationMiddleware(MessageMiddleware(XFrameOptionsMiddleware(self._get_response)))))) CommonMiddleware(CsrfViewMiddleware(AuthenticationMiddleware(MessageMiddleware(XFrameOptionsMiddleware(self._get_response))))) CsrfViewMiddleware(AuthenticationMiddleware(MessageMiddleware(XFrameOptionsMiddleware(self._get_response)))) AuthenticationMiddleware(MessageMiddleware(XFrameOptionsMiddleware(self._get_response))) MessageMiddleware(XFrameOptionsMiddleware(self._get_response)) XFrameOptionsMiddleware(self._get_response) self._get_response 变成一个链式调用, 请求进来时, 最外层先执行\n请求进来WSGIHandler() 实例化后, 调用 call\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) # WSGIRequest对象 -\u0026gt; HttpRequest类 request = self.request_class(environ) # response是什么 response = self.get_response(request) 然后调用\nPython 1 response = self._middleware_chain(request) 会执行调用链 SecurityMiddleware(SessionMiddleware(CommonMiddleware(CsrfViewMiddleware(AuthenticationMiddleware(MessageMiddleware(XFrameOptionsMiddleware(self._get_response)))))))\n因为继承了 MiddlewareMixin, 调用触发 call\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 class MiddlewareMixin: def __call__(self, request): # Exit out to async mode, if needed if asyncio.iscoroutinefunction(self.get_response): return self.__acall__(request) response = None if hasattr(self, \u0026#34;process_request\u0026#34;): response = self.process_request(request) # 当response 为空时,会执行self.get_response(request), 这个又会调用下一个函数的self.get_response(request),是一个递归 response = response or self.get_response(request) if hasattr(self, \u0026#34;process_response\u0026#34;): response = self.process_response(request, response) return response 会递归执行\nPython 1 response = response or self.get_response(request) 然后才执行\nPython 1 self.process_response(request, response) Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class BaseHandler: _view_middleware = None _template_response_middleware = None _exception_middleware = None _middleware_chain = None def _get_response(self, request): \u0026#34;\u0026#34;\u0026#34; Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happens inside the request/response middleware. \u0026#34;\u0026#34;\u0026#34; response = None # 核心内容 callback, callback_args, callback_kwargs = self.resolve_request(request) # Apply view middleware for middleware_method in self._view_middleware: response = middleware_method( request, callback, callback_args, callback_kwargs ) if response: break if response is None: wrapped_callback = self.make_view_atomic(callback) # If it is an asynchronous view, run it in a subthread. if asyncio.iscoroutinefunction(wrapped_callback): wrapped_callback = async_to_sync(wrapped_callback) try: # 自定义的视图函数处理 WSGIRequest对象 response = wrapped_callback(request, *callback_args, **callback_kwargs) except Exception as e: response = self.process_exception_by_middleware(e, request) if response is None: raise ","date":"2023-05-05","permalink":"/posts/django-middleware/","title":"django middleware"},{"categories":["tcp"],"content":"抓包: 两台服务器，一台启动python http服务，上面有一个2G的文件。另一台下载。 怎么抓包，在那台机器上抓包，是不是指定来源ip就可以了？ 是指定抓n个包，还是说指定ip，从开始下载到结束？ 抓完包后怎么分析，需要分析哪些内容？arp协议？地址询问？http三次握手？三次挥手？ 怎么调整内核，又要重新抓包？内核调整时，服务器有什么变化，用top命令能看出来么？ rt，丢包率，curl速度怎么查看？使用\u0026ndash;help，需要谷歌协助么？ 执行curl就出问题了\n服务端\n代码 1 2 3 4 5 server:172.19.5.43 python -m SimpleHTTPServer 8089 ll -h -rw-r--r--. 1 root root 1.9G Mar 25 11:37 zookeeper.out 客户端\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 client:172.19.9.147 tcpdump -i eth0 tcp port 8089 and host 172.19.5.43 -w tcpdump-$(date +%Y-%m-%d-%H-%M-%S).cap [root@172.19.9.147 ~]# wget http://172.19.5.43:8089/zookeeper.out --2023-03-26 00:49:47-- http://172.19.5.43:8089/zookeeper.out Connecting to 172.19.5.43:8089... connected. HTTP request sent, awaiting response... 200 OK Length: 1972256747 (1.8G) [application/octet-stream] Saving to: ‘zookeeper.out’ 100%[==========================================================================================================================================\u0026gt;] 1,972,256,747 53.2MB/s in 25s 2023-03-26 00:50:11 (76.3 MB/s) - ‘zookeeper.out’ saved [1972256747/1972256747] [root@172.19.9.147 ~]# curl http://172.19.5.43:8089/zookeeper.out --output zookeeper.out.file % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1880M 100 1880M 0 0 77.0M 0 0:00:24 0:00:24 --:--:-- 54.3M curl 控制速度\n代码 1 2 3 4 [root@172.19.9.147 ~]# curl http://172.19.5.43:8089/zookeeper.out --output zookeeper.out.file --limit-rate 1M % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 33 1880M 33 625M 0 0 1023k 0 0:31:21 0:10:25 0:20:56 996k tc命令介绍\nBDP和buffer、RT的关系 搜这个问题发现别人早就看过任总的文章了，还写了笔记。https://blog.csdn.net/fdsafwagdagadg6576/article/details/121011575\n相关概念\nBDP: (Bandwidth-Delay Product) 带宽时延积 Buffer: 指的是sysctl中的 rmem或者wmem rt: tcp的timestamps-\u0026gt;time since previous frame in this tcp stream(我猜就是响应时间的一种) 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@172.19.5.43 ~]# sysctl -a | grep \u0026#39;net.ipv4.tcp_wmem\u0026#39; sysctl: reading key \u0026#34;net.ipv6.conf.all.stable_secret\u0026#34; net.ipv4.tcp_wmem = 4096 16384 4194304 sysctl: reading key \u0026#34;net.ipv6.conf.default.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.docker0.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.eth0.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.lo.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.veth59705c1.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.veth6cbfe80.stable_secret\u0026#34; [root@172.19.5.43 ~]# sysctl -a | grep \u0026#39;net.ipv4.tcp_wmem\u0026#39; sysctl: reading key \u0026#34;net.ipv6.conf.all.stable_secret\u0026#34; net.ipv4.tcp_wmem = 4096 4096 4096 sysctl: reading key \u0026#34;net.ipv6.conf.default.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.docker0.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.eth0.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.lo.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.veth59705c1.stable_secret\u0026#34; sysctl: reading key \u0026#34;net.ipv6.conf.veth6cbfe80.stable_secret\u0026#34; 参数相关\n更新了配置后，直接服务也启动失败，wget也异常了。\n","date":"2023-05-05","permalink":"/posts/tcp%E5%AE%9E%E9%AA%8C/","title":"tcp实验"},{"categories":["tools"],"content":" 可以手动创建目录\n使用 ansible-galaxy 方式创建目录\n代码 1 ansible-galaxy init nginx https://galaxy.ansible.com/nginxinc/nginx https://github.com/nginxinc/ansible-role-nginx\nhttps://blog.csdn.net/m0_52544877/article/details/119461763\nhttps://github.com/ansible/ansible-examples/blob/master/lamp_haproxy/site.yml\n","date":"2023-04-07","permalink":"/posts/ansible-role-scaffolding/","title":"使用 ansible-galaxy 创建 Role"},{"categories":["django"],"content":"根据视频学习django 记录学习历程\n一直学习都是 狗熊掰玉米,掰一个仍一个。\n想系统的学习, 没有头绪, 找了n 个xmind ,手抄一遍, 都是不顶用, 只停留在方法论上, 从没有真正执行过\n学了django-restful, 以为自己入门了, 但看文档还是挺吃力, 还有就是知其然不知其所以然. 啰啰嗦嗦的说了一堆, 总之就是一直以来都感觉自己在门外徘徊, 极力想找突破口, 总是坚持不了几天. 亦或坚持看完视屏, 但没有在脑子里留下什么\n这个是为了记录下学习django 源码的笔记, 也算自己思考过, 反复钻研视频留下的笔记\n视频地址: https://www.bilibili.com/video/BV173411s79r/?spm_id_from=pageDriver\n感谢作者沈奇才无私奉献, 已买实体书《深入理解Django》. 再次感谢作者\n最早看入门的视频, 太累, 跟不上也不是我特别关心的点, 然后我就大概看了前面的5个视频, 然后开始看我最需要的请求那一章 作者的视频是用vscode搭建, 因为不熟悉, 也一直没有搭建好. 后面就不再纠结这个, 使用pycharm, 配置3.10的环境, 使用的是4.0.5版本\n很多时候我总是纠结是不是一样的环境, 版本, 配置. 入门时跟着教程的印象太深刻, 太担心有问题不能处理, 总是忘记我看视屏的初衷 看最初的几个视屏环境也没有配置好, 就无脑看视频, 但总是看完就忘, 最近把看完的视频内容自己在本地环境回忆, 温故而知新, 于是有了开始学习的动力, 写笔记的动力\n共勉\ncore/runserver\nview/\n逻辑思路 实战演练的内容需要加上 模拟实验\n","date":"2023-04-06","permalink":"/posts/django-study/","title":"django-study"},{"categories":["python"],"content":"问题 遇到python的try except功能，确实很好，能处理报错，但有时候懒，直接在function中被try包含，但这样就不能定位到底哪一行代码出问题\n错误处理\n解决方案 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import sys, traceback def lumberjack(): bright_side_of_death() def bright_side_of_death(): return tuple()[0] try: lumberjack() except IndexError: exc_type, exc_value, exc_traceback = sys.exc_info() print \u0026#34;*** print_tb:\u0026#34; traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) print \u0026#34;*** print_exception:\u0026#34; traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout) print \u0026#34;*** print_exc:\u0026#34; traceback.print_exc() print \u0026#34;*** format_exc, first and last line:\u0026#34; formatted_lines = traceback.format_exc().splitlines() print formatted_lines[0] print formatted_lines[-1] print \u0026#34;*** format_exception:\u0026#34; print repr(traceback.format_exception(exc_type, exc_value, exc_traceback)) print \u0026#34;*** extract_tb:\u0026#34; print repr(traceback.extract_tb(exc_traceback)) print \u0026#34;*** format_tb:\u0026#34; print repr(traceback.format_tb(exc_traceback)) print \u0026#34;*** tb_lineno:\u0026#34;, exc_traceback.tb_lineno 输出打印\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 *** print_tb: File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 10, in \u0026lt;module\u0026gt; lumberjack() *** print_exception: Traceback (most recent call last): File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 10, in \u0026lt;module\u0026gt; lumberjack() File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 4, in lumberjack bright_side_of_death() IndexError: tuple index out of range *** print_exc: Traceback (most recent call last): File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 10, in \u0026lt;module\u0026gt; lumberjack() File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 4, in lumberjack bright_side_of_death() IndexError: tuple index out of range *** format_exc, first and last line: Traceback (most recent call last): IndexError: tuple index out of range *** format_exception: [\u0026#39;Traceback (most recent call last):\\n\u0026#39;, \u0026#39; File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 10, in \u0026lt;module\u0026gt;\\n lumberjack()\\n\u0026#39;, \u0026#39; File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 4, in lumberjack\\n bright_side_of_death()\\n\u0026#39;, \u0026#39; File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 7, in bright_side_of_death\\n return tuple()[0]\\n\u0026#39;, \u0026#39;IndexError: tuple index out of range\\n\u0026#39;] *** extract_tb: [(\u0026#39;\u0026lt;doctest...\u0026gt;\u0026#39;, 10, \u0026#39;\u0026lt;module\u0026gt;\u0026#39;, \u0026#39;lumberjack()\u0026#39;), (\u0026#39;\u0026lt;doctest...\u0026gt;\u0026#39;, 4, \u0026#39;lumberjack\u0026#39;, \u0026#39;bright_side_of_death()\u0026#39;), (\u0026#39;\u0026lt;doctest...\u0026gt;\u0026#39;, 7, \u0026#39;bright_side_of_death\u0026#39;, \u0026#39;return tuple()[0]\u0026#39;)] *** format_tb: [\u0026#39; File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 10, in \u0026lt;module\u0026gt;\\n lumberjack()\\n\u0026#39;, \u0026#39; File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 4, in lumberjack\\n bright_side_of_death()\\n\u0026#39;, \u0026#39; File \u0026#34;\u0026lt;doctest...\u0026gt;\u0026#34;, line 7, in bright_side_of_death\\n return tuple()[0]\\n\u0026#39;] *** tb_lineno: 10 ","date":"2023-03-08","permalink":"/posts/python-traceback-notes/","title":"Python traceback 异常定位笔记"},{"categories":["tools"],"content":"source link\nHow to switch off Spotlight indexing\nOpen up a Terminal window and copy and paste this:\nBash 1 sudo mdutil -i off How to subseqently enable Spotlight indexing If you want to allow Spotlight to index your files again, copy and paste this:\nBash 1 sudo mdutil -i on It may take some time to index so best to do this when you don’t need to use your Mac for a while.\nWhy switch off Spotlight indexing? As mentioned at the start of this post, the CPU hogging was starting to get pretty annoying and I really only use Spotlight for launching apps. It’s great being able to Command+Space and start typing in the app name and hit return when it’s found what I want.\nmac 自带键盘长按时，显示特殊符号\n当您在 Mac 上长按键盘上的某些字母键（比如 A, E, O, U, N, S, C, Z 等）时，系统会弹出一个 重音符号/特殊字符选择菜单，让您可以方便地输入带有变音符号的字符，这在输入欧洲语言（如法语、德语、西班牙语）时非常有用。\n代码 1 defaults write -g ApplePressAndHoldEnabled -bool false ","date":"2023-02-27","permalink":"/posts/macos-spotlight-key-repeat/","title":"macOS Spotlight 索引与键盘长按设置"},{"categories":["Linux"],"content":"Chapter 3. Special Characters link\n搜到一个命令\nBash 1 cat yaml | kubectl replace -f - 这让我想到我平时用的命令\nBash 1 cat file | vim - 原本以为 - 是vim的一个特殊用法，让我意识到这应该是shell的用法，而不是vim的特定方法\n网上就搜到上面的文章，主要是讲特殊字符meta字符，大概意思就是\n- 会转发到 stdin/stdout\nredirection from/to stdin or stdout [dash].\nexample\n代码 1 2 3 4 5 6 7 bash$ cat - abc abc ... Ctl-D 实际运用 Backup of all files changed in last day\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #!/bin/bash # Backs up all files in current directory modified within last 24 hours #+ in a \u0026#34;tarball\u0026#34; (tarred and gzipped file). BACKUPFILE=backup-$(date +%m-%d-%Y) # Embeds date in backup filename. # Thanks, Joshua Tschida, for the idea. archive=${1:-$BACKUPFILE} # If no backup-archive filename specified on command-line, #+ it will default to \u0026#34;backup-MM-DD-YYYY.tar.gz.\u0026#34; tar cvf - `find . -mtime -1 -type f -print` \u0026gt; $archive.tar gzip $archive.tar echo \u0026#34;Directory $PWD backed up in archive file \\\u0026#34;$archive.tar.gz\\\u0026#34;.\u0026#34; # Stephane Chazelas points out that the above code will fail #+ if there are too many files found #+ or if any filenames contain blank characters. # He suggests the following alternatives: # ------------------------------------------------------------------- # find . -mtime -1 -type f -print0 | xargs -0 tar rvf \u0026#34;$archive.tar\u0026#34; # using the GNU version of \u0026#34;find\u0026#34;. # find . -mtime -1 -type f -exec tar rvf \u0026#34;$archive.tar\u0026#34; \u0026#39;{}\u0026#39; \\; # portable to other UNIX flavors, but much slower. # ------------------------------------------------------------------- exit 0 ","date":"2023-02-27","permalink":"/posts/shell-dash-standard-input/","title":"命令参数 - 与标准输入输出笔记"},{"categories":["django"],"content":"Django源码: 从入口到请求响应\nexecute_from_command_line(sys.argv)\ndjango python manage.py runserver 流程 python manage.py runserver 流程分析 版本 python27 django 1.0 搭建可运行的环境 创建python27 虚拟环境 github 下载 django-1.0.tar.gz（1.0 版本的django） 解压 可以看到，有个 demo 在 examples 目录 把 django 目录拷贝到 examples 下面，这样 example 可以正确导入 django1.0 启动项目\nBash 1 python manage.py runserver 项目启动成功，可以修改代码来跟踪执行流程\n流程 以下代码存在删减，主要展示代码流程\n从 manage.py 开始，执行了 execute_manager 方法，传入 settings 模块\nPython 1 2 3 4 5 6 7 8 9 10 11 execute_manager(settings) django.core.management.execute_manager 方法 def execute_manager(settings_mod, argv=None): # setup_environ 函数，只是设置了环境变量，执行配置模块 # os.environ[\u0026#39;DJANGO_SETTINGS_MODULE\u0026#39;] = examples.settting setup_environ(settings_mod) # admin manage 工具类 utility = ManagementUtility(argv) utility.execute() ManagementUtility 类\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class ManagementUtility(object): def __init__(self, argv=None): # 初始化，例如 self.argv = [\u0026#39;.../examples/manage.py\u0026#39;, \u0026#39;runserver\u0026#39;] self.prog_name = \u0026#39;manage.py\u0026#39; def execute(self): # 删除了部分代码，最终执行代码大致如下 # 这是一个命令行工具类，表名能接受什么样的参数，这里主要检查两个参数 # --settings 指定配置文件 # --pythonpath 执行 python 环境变量 parser = LaxOptionParser(usage=\u0026#34;%prog subcommand [options] [args]\u0026#34;, version=get_version(), option_list=BaseCommand.option_list) # 使用命令行工具类解析命令行参数，也就是获取 --settings 和 --pythonpath 的参数值 options, args = parser.parse_args(self.argv) # 如果 --settings 参数存在，会覆盖之前设置的 os.environ[\u0026#39;DJANGO_SETTINGS_MODULE\u0026#39;] # 如果 --pythonpath 参数存在，会把指定路径添加到 sys.path 的第一位，优先从此处加载模块 handle_default_options(options) # fetch_command # fetch_command 分析在下边 # fetch_command 返回 django.core.management.commands.runserver.Command # run_from_argv # run_from_argv 分析在下边 self.fetch_command(subcommand).run_from_argv(self.argv) def fetch_command(self, subcommand): # get_commands # get_commands 返回 django.core.management.commands 目录下的所有模块，每个模块处理对应的参数 # 每个模块的值都是 django.core，app_name = \u0026#39;django.core\u0026#39; app_name = get_commands()[subcommand] # load_command_class 方法 # 返回了 django.core.management.commands.runserver.Command klass = load_command_class(app_name, subcommand) return klass run_from_argv 方法\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # django.core.management.commands.runserver.Command # 继承 django.core.management.base import BaseCommand # run_from_argv 也是继承的 def run_from_argv(self, argv): # 调用 execute self.execute(*args, **options.__dict__) def execute(self, *args, **options): # 调用 handle # 注意 handle 被重写了 # 调用的是 django.core.management.commands.runserver.Command.handle output = self.handle(*args, **options) handle def handle(self, addrport=\u0026#39;\u0026#39;, *args, **options): def inner_run(): # WSGI 处理程序 # WSGIHandler 可调用，是 WSGI 处理程序 # AdminMediaHandler 是对 WSGIHandler 的封装 # AdminMediaHandler 特殊处理媒体文件请求 # AdminMediaHandler 非媒体文件的 HTTP 请求，直接返回 WSGIHandler handler = AdminMediaHandler(WSGIHandler(), path) # run(addr, int(port), handler) # run 在 django.core.servers.basehttp.run # run 定义如下 # run 启动了 HTTP 服务，这个服务器只能用于开发调试 def run(addr, port, wsgi_handler): # 绑定地址端口 server_address = (addr, port) # 服务实例 httpd = WSGIServer(server_address, WSGIRequestHandler) # 传入 WSGI 处理程序 httpd.set_app(wsgi_handler) # 监听请求 httpd.serve_forever() inner_run() ","date":"2023-02-27","permalink":"/posts/django-1-runserver-source-notes/","title":"Django 1.0 runserver 启动流程"},{"categories":["Linux"],"content":" Bash 1 2 3 4 5 6 7 8 9 10 11 get_pod=\u0026#34;NAME READY STATUS RESTARTS AGE pod-a 2/2 Running 0 6h pod-b 2/2 Running 0 5h pod-c 2/2 Running 0 4h pod-d 2/2 Running 0 3h\u0026#34; IFS=\u0026#34;\\n\u0026#34; while read line; do echo $line; done \u0026lt;\u0026lt;\u0026lt; $get_pod ","date":"2023-02-27","permalink":"/posts/bash-while-read-lines/","title":"Bash while read 逐行处理"},{"categories":["Linux"],"content":"shell readline for Bash 1 2 3 4 while read -r line; do echo \u0026#34;$line\u0026#34; ; done \u0026lt; input.file Bash 1 2 3 while read x; do echo $x; done \u0026lt;\u0026lt; EOF $(ls -l $1) EOF Bash 1 2 ls -l $1 | while read x; do echo $x; done ls | while read line;do echo \u0026#34;/data/path/start.sh\u0026#34;;done Bash 1 2 3 IFS=\u0026#39; \u0026#39; for x in `ls -l $1`; do echo $x; done 不改变当前shell的IFS;Put a sub-shell around it if you don\u0026rsquo;t want to set IFS permanently:\nBash 1 2 3 (IFS=\u0026#39; \u0026#39; for x in `ls -l $1`; do echo $x; done) 嵌套循环\nBash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/bin/bash #set -x cd /data/app while read -r line; do if [ ! -d $line ] then echo \u0026#34;$line not exist\u0026#34; fi ; done \u0026lt; /tmp/applist ","date":"2023-02-27","permalink":"/posts/bash-line-iteration-notes/","title":"Bash 逐行读取与嵌套循环笔记"},{"categories":["python"],"content":"原文链接\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Test: def __new__(cls): print(\u0026#39;__new__\u0026#39;) return object().__new__(cls) def __init__(self): print(\u0026#39;__init__\u0026#39;) test = Test() class SingletonObject: def __new__(cls, *args, **kwargs): if not hasattr(SingletonObject, \u0026#34;_instance\u0026#34;): SingletonObject._instance = object.__new__(cls) return SingletonObject._instance def __init__(self): pass singleton = SingletonObject() print(singleton.__dict__) class NonZero(int): def __new__(cls, value): return super().__new__(cls, value) if value != 0 else None res = NonZero(0) print(\u0026#39;res 0\u0026#39;, res) res1 = NonZero(1) print(\u0026#39;res 1\u0026#39;, res1) https://www.cnblogs.com/techflow/p/13156361.html\nhttps://changchen.me/blog/20201122/metaclass-with-django-orm/\n实战演示 https://segmentfault.com/a/1190000004426130\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 sensitive_words_list = [\u0026#39;asshole\u0026#39;, \u0026#39;fuck\u0026#39;, \u0026#39;shit\u0026#39;] def detect_sensitive_words(string): \u0026#34;\u0026#34;\u0026#34;检测敏感词汇\u0026#34;\u0026#34;\u0026#34; # words_detected = filter(lambda word: word in string.lower(), sensitive_words_list) words_detected = None for item in sensitive_words_list: if item in string.lower(): words_detected = True if words_detected: raise NameError(\u0026#39;Sensitive words {0} detected in the string \u0026#34;{1}\u0026#34;.\u0026#39;.format(words_detected, string)) class CleanerMeta(type): def __new__(mcs, class_name, bases, attrs): detect_sensitive_words(class_name) # 检查类名 # map(detect_sensitive_words, attrs.iterkeys()) # 检查属性名 print(\u0026#34;Well done! You are a polite coder!\u0026#34;) # 如无异常，输出祝贺消息 return super(CleanerMeta, mcs).__new__(mcs, class_name, bases, attrs) # 重要！这行一定不能漏！！这回调用内建的类构造器来构造类，否则定义好的类将会变成 None class APIBase(metaclass=CleanerMeta): pass class ImAGoodBoy(APIBase): a_polite_attribute = 1 class FuckMyBoss(APIBase): pass # detect_sensitive_words(\u0026#39;avc\u0026#39;) ","date":"2023-02-14","permalink":"/posts/python-new-construction-notes/","title":"python __new__"},{"categories":["python"],"content":"本文主要介绍Python的MetaClass的用法。\nMetaClass也是一个class，这个class继承自type，它用于生产新的class对象。定义class的时候，就会执行到MetaClass的__new__()代码。\n从这里出发，我们需要一个新的class的时候，就可以使用MetaClass定义它的行为。本文介绍MetaClass的三种用法：\n定义新的class的时候，执行一些检查操作 定义新的class的时候，将新class进行注册动作 定义新的class的时候，自动修改class的属性 MetaClass可以读取和修改class的属性。\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Meta(type): def __new__(meta, name, bases, class_dict): print(\u0026#39;meta =\u0026gt; \u0026#39;, meta) print(\u0026#39;name =\u0026gt; \u0026#39;, name) print(\u0026#39;bases =\u0026gt; \u0026#39;, bases) print(\u0026#39;class_dict =\u0026gt; \u0026#39;, class_dict) return type.__new__(meta, name, bases, class_dict) class MyClass(metaclass=Meta): stuff = 123 def __init__(self): self.foo() def foo(self): print(\u0026#39;MyClass =\u0026gt; foo\u0026#39;) pass obj = MyClass() # 这里就会执行 __new__ 方法 print(dir(obj)) # print(\u0026#39;__dict__\u0026#39;, help(obj)) # 看看python方法 \u0026#34;\u0026#34;\u0026#34; 在继承的时候就会触发metaclass的__new__方法调用，new meta =\u0026gt; \u0026lt;class \u0026#39;__main__.Meta\u0026#39;\u0026gt; name =\u0026gt; MyClass bases =\u0026gt; () class_dict =\u0026gt; {\u0026#39;__module__\u0026#39;: \u0026#39;__main__\u0026#39;, \u0026#39;__qualname__\u0026#39;: \u0026#39;MyClass\u0026#39;, \u0026#39;stuff\u0026#39;: 123, \u0026#39;__init__\u0026#39;: \u0026lt;function MyClass.__init__ at 0x105002cb0\u0026gt;, \u0026#39;foo\u0026#39;: \u0026lt;function MyClass.foo at 0x105002c20\u0026gt;} MyClass =\u0026gt; foo [\u0026#39;__class__\u0026#39;, \u0026#39;__delattr__\u0026#39;, \u0026#39;__dict__\u0026#39;, \u0026#39;__dir__\u0026#39;, \u0026#39;__doc__\u0026#39;, \u0026#39;__eq__\u0026#39;, \u0026#39;__format__\u0026#39;, \u0026#39;__ge__\u0026#39;, \u0026#39;__getattribute__\u0026#39;, \u0026#39;__gt__\u0026#39;, \u0026#39;__hash__\u0026#39;, \u0026#39;__init__\u0026#39;, \u0026#39;__init_subclass__\u0026#39;, \u0026#39;__le__\u0026#39;, \u0026#39;__lt__\u0026#39;, \u0026#39;__module__\u0026#39;, \u0026#39;__ne__\u0026#39;, \u0026#39;__new__\u0026#39;, \u0026#39;__reduce__\u0026#39;, \u0026#39;__reduce_ex__\u0026#39;, \u0026#39;__repr__\u0026#39;, \u0026#39;__setattr__\u0026#39;, \u0026#39;__sizeof__\u0026#39;, \u0026#39;__str__\u0026#39;, \u0026#39;__subclasshook__\u0026#39;, \u0026#39;__weakref__\u0026#39;, \u0026#39;foo\u0026#39;, \u0026#39;stuff\u0026#39;] \u0026#34;\u0026#34;\u0026#34; 关于__new__方法讲解文章： 一文详解Python中__new__方法的作用\n用MetaClass验证 定义一个新的class相当于调用type创建一个新的class对象，所以覆盖__new__()方法可以实现一些验证的动作。\n下面的代码定义了一个多边形的MetaClass，在定义新的class的时候，验证边数是否小于3，如果小于3，这不科学。\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class ValidatePolygon(type): def __new__(meta, name, bases, class_dict): # Don’t validate the abstract Polygon class # if bases != (object,): # if class_dict[\u0026#39;sides\u0026#39;]: if class_dict[\u0026#39;sides\u0026#39;] \u0026lt; 3: raise ValueError(\u0026#39;Polygons need 3+ sides\u0026#39;) return type.__new__(meta, name, bases, class_dict) class Polygon(metaclass=ValidatePolygon): # sides = None # Specified by subclasses @classmethod def interior_angles(cls): return (cls.sides - 2) * 180 class Triangle(Polygon): sides = 3 # obj = Triangle() \u0026#34;\u0026#34;\u0026#34; 这里执行会有问题，因为python会先创建类 Polygon ，在元类中 Polygon 的属性 sides 不满足条件，会导致报错 因为继承了元类 ValidatePolygon ，所以每次创建类的时候都会触发逻辑 \u0026#34;\u0026#34;\u0026#34; 这一段有问题，会触发sides=None的报错，应该是执行顺序问题\nrealpython metaclass\n修改class的属性 假设我们想要实现一个Python对象到数据库字段的对应（也就是ORM），我们可以使用前面博客提到的Descriptor，如下。\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Field(object): def __init__(self, name): self.name = name self.internal_name = ‘_’ + self.name def __get__(self, instance, instance_type): if instance is None: return self return getattr(instance, self.internal_name, ”) def __set__(self, instance, value): setattr(instance, self.internal_name, value) class Customer(object): # Class attributes first_name = Field(‘first_name’) last_name = Field(‘last_name’) prefix = Field(‘prefix’) suffix = Field(‘suffix’) 但是这里存在的问题是，first_name已经写了一次了，还要在创建Field的时候再写一次，不是有些多余吗？Field应该自动根据引用它的变量名自动命名。这可以通过MetaClass来做到。\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class Meta(type): def __new__(meta, name, bases, class_dict): for key, value in class_dict.items(): if isinstance(value, Field): value.name = key value.internal_name = \u0026#39;_\u0026#39; + key cls = type.__new__(meta, name, bases, class_dict) return cls # Field不需要在接受参数创建实例了 class Field(): def __init__(self): # These will be assigned by the metaclass. self.name = None self.internal_name = None # 这里由MetaClass代劳 class BetterCustomer(metaclass=Meta): first_name = Field() # 类中类，绑定类对象的地址 last_name = Field() prefix = Field() suffix = Field() print(help(BetterCustomer)) # print(help(BetterCustomer.first_name)) print(dir(BetterCustomer.first_name)) print(BetterCustomer.first_name.name, BetterCustomer.first_name.internal_name) print(BetterCustomer.last_name) print(BetterCustomer.prefix) print(BetterCustomer.suffix) \u0026#34;\u0026#34;\u0026#34; Help on class BetterCustomer in module __main__: class BetterCustomer(builtins.object) | # 这里由MetaClass代劳 | | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | first_name = \u0026lt;__main__.Field object\u0026gt; | | last_name = \u0026lt;__main__.Field object\u0026gt; | | prefix = \u0026lt;__main__.Field object\u0026gt; | | suffix = \u0026lt;__main__.Field object\u0026gt; None [\u0026#39;__class__\u0026#39;, \u0026#39;__delattr__\u0026#39;, \u0026#39;__dict__\u0026#39;, \u0026#39;__dir__\u0026#39;, \u0026#39;__doc__\u0026#39;, \u0026#39;__eq__\u0026#39;, \u0026#39;__format__\u0026#39;, \u0026#39;__ge__\u0026#39;, \u0026#39;__getattribute__\u0026#39;, \u0026#39;__gt__\u0026#39;, \u0026#39;__hash__\u0026#39;, \u0026#39;__init__\u0026#39;, \u0026#39;__init_subclass__\u0026#39;, \u0026#39;__le__\u0026#39;, \u0026#39;__lt__\u0026#39;, \u0026#39;__module__\u0026#39;, \u0026#39;__ne__\u0026#39;, \u0026#39;__new__\u0026#39;, \u0026#39;__reduce__\u0026#39;, \u0026#39;__reduce_ex__\u0026#39;, \u0026#39;__repr__\u0026#39;, \u0026#39;__setattr__\u0026#39;, \u0026#39;__sizeof__\u0026#39;, \u0026#39;__str__\u0026#39;, \u0026#39;__subclasshook__\u0026#39;, \u0026#39;__weakref__\u0026#39;, \u0026#39;internal_name\u0026#39;, \u0026#39;name\u0026#39;] first_name _first_name \u0026lt;__main__.Field object at 0x102bff0d0\u0026gt; \u0026lt;__main__.Field object at 0x102bfeb60\u0026gt; \u0026lt;__main__.Field object at 0x102bfdd20\u0026gt; \u0026#34;\u0026#34;\u0026#34; 综上，MetaClass是非常实用的，可以帮我们减少很多需要重复复制粘贴的代码。\npython learning website Python new Python type Class Python Metaclass Python Metaclass Example\n","date":"2023-02-13","permalink":"/posts/python-metaclass-notes/","title":"metaclass学习"},{"categories":["django"],"content":"Django4.0源码分析-1.9 关于MigrationRecorder类的解读 在了解 makemigrations migrate 这两个命令前需要看下db目录下的文件\n先了解MigrationRecorder这个类\n关键方法\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class MigrationRecorder: \u0026#34;\u0026#34;\u0026#34; Deal with storing migration records in the database. Because this table is actually itself used for dealing with model creation, it\u0026#39;s the one thing we can\u0026#39;t do normally via migrations. We manually handle table creation/schema updating (using schema backend) and then have a floating model to do queries with. If a migration is unapplied its row is removed from the table. Having a row in the table always means a migration is applied. \u0026#34;\u0026#34;\u0026#34; _migration_class = None @classproperty def Migration(cls): \u0026#34;\u0026#34;\u0026#34; Lazy load to avoid AppRegistryNotReady if installed apps import MigrationRecorder. \u0026#34;\u0026#34;\u0026#34; if cls._migration_class is None: class Migration(models.Model): # 定义迁移模型类 app = models.CharField(max_length=255) # 应用名称 name = models.CharField(max_length=255) # 迁移文件名 applied = models.DateTimeField(default=now) # 操作时间 class Meta: apps = Apps() # App对象 app_label = \u0026#34;migrations\u0026#34; # 应用标签 db_table = \u0026#34;django_migrations\u0026#34; # 表名 def __str__(self): return \u0026#34;Migration %s for %s\u0026#34; % (self.name, self.app) cls._migration_class = Migration return cls._migration_class # 返回迁移类 def __init__(self, connection): self.connection = connection 只有在调用的时候才会触发赋值，_migration_class，否则值为None\n跟着执行操作\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 (venv) clin:firstDjango/ $ python3 manage.py shell Python 3.10.5 (main, Jul 22 2022, 15:13:07) [Clang 12.0.5 (clang-1205.0.22.11)] on darwin Type \u0026#34;help\u0026#34;, \u0026#34;copyright\u0026#34;, \u0026#34;credits\u0026#34; or \u0026#34;license\u0026#34; for more information. (InteractiveConsole) \u0026gt;\u0026gt;\u0026gt; from django.db import connection \u0026gt;\u0026gt;\u0026gt; from django.db.migrations.recorder import MigrationRecorder \u0026gt;\u0026gt;\u0026gt; recorder = MigrationRecorder(connection) \u0026gt;\u0026gt;\u0026gt; recorder._migration_class \u0026gt;\u0026gt;\u0026gt; recorder.Migration() \u0026lt;Migration: Migration for \u0026gt; \u0026gt;\u0026gt;\u0026gt; recorder._migration_class \u0026lt;class \u0026#39;django.db.migrations.recorder.MigrationRecorder.Migration.\u0026lt;locals\u0026gt;.Migration\u0026#39;\u0026gt; \u0026gt;\u0026gt;\u0026gt; recorder.Migration \u0026lt;class \u0026#39;django.db.migrations.recorder.MigrationRecorder.Migration.\u0026lt;locals\u0026gt;.Migration\u0026#39;\u0026gt; \u0026gt;\u0026gt;\u0026gt; recorder._migration_class.objects.all() \u0026lt;QuerySet [\u0026lt;Migration: Migration 0001_initial for contenttypes\u0026gt;]\u0026gt; \u0026gt;\u0026gt;\u0026gt; recorder.migration_qs \u0026lt;QuerySet [\u0026lt;Migration: Migration 0001_initial for contenttypes\u0026gt;]\u0026gt; \u0026gt;\u0026gt;\u0026gt; recorder.has_table() True \u0026gt;\u0026gt;\u0026gt; recorder.applied_migrations() {(\u0026#39;contenttypes\u0026#39;, \u0026#39;0001_initial\u0026#39;): \u0026lt;Migration: Migration 0001_initial for contenttypes\u0026gt;} \u0026gt;\u0026gt;\u0026gt; recorder.record_applied(\u0026#39;xxx\u0026#39;, \u0026#39;0002.xxx\u0026#39;) \u0026gt;\u0026gt;\u0026gt; recorder.record_unapplied(\u0026#39;xxx\u0026#39;, \u0026#39;0002.xxx\u0026#39;) \u0026gt;\u0026gt;\u0026gt; recorder.flush() 相关的类方法解释\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def applied_migrations(self): \u0026#34;\u0026#34;\u0026#34; Return a dict mapping (app_name, migration_name) to Migration instances for all applied migrations. \u0026#34;\u0026#34;\u0026#34; if self.has_table(): return { # 用元组表示key，用迁移记录表示value (migration.app, migration.name): migration for migration in self.migration_qs } else: # If the django_migrations table doesn\u0026#39;t exist, then no migrations # are applied. return {} 相关的类方法解释\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 def record_applied(self, app, name): \u0026#34;\u0026#34;\u0026#34;Record that a migration was applied.\u0026#34;\u0026#34;\u0026#34; self.ensure_schema() self.migration_qs.create(app=app, name=name) # 数据库生成一条migrations记录 def record_unapplied(self, app, name): \u0026#34;\u0026#34;\u0026#34;Record that a migration was unapplied.\u0026#34;\u0026#34;\u0026#34; self.ensure_schema() self.migration_qs.filter(app=app, name=name).delete() # 数据库删除一条migrations记录 def flush(self): \u0026#34;\u0026#34;\u0026#34;Delete all migration records. Useful for testing migrations.\u0026#34;\u0026#34;\u0026#34; self.migration_qs.all().delete() # 数据库清空migrations记录 ","date":"2023-02-08","permalink":"/posts/django-migration-recorder-notes/","title":"Django MigrationRecorder 源码与交互实验"},{"categories":["django"],"content":"Django4.0源码分析-1.10 关于MigrationGraph类的解读与实战\n为了迁移文件之间的校验，然后有这个类。 也不清楚这个类是干嘛的，好像是画图的，但涉及算法深度优先，链表\n弹幕有说，这段代码是在makemigrations后，生成migrations文件，盲猜是分析那些文件关系用的。。。\n跟着操作下\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 (venv) clin:firstDjango/ $ python3 manage.py shell Python 3.10.5 (main, Jul 22 2022, 15:13:07) [Clang 12.0.5 (clang-1205.0.22.11)] on darwin Type \u0026#34;help\u0026#34;, \u0026#34;copyright\u0026#34;, \u0026#34;credits\u0026#34; or \u0026#34;license\u0026#34; for more information. (InteractiveConsole) \u0026gt;\u0026gt;\u0026gt; from django.db.migrations.graph import Node,DummyNode, MigrationGraph \u0026gt;\u0026gt;\u0026gt; graph = MigrationGraph() \u0026gt;\u0026gt;\u0026gt; graph.add_node(\u0026#39;k1\u0026#39;, \u0026#39;m1\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_node(\u0026#39;k2\u0026#39;, \u0026#39;m2\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_node(\u0026#39;k3\u0026#39;, \u0026#39;m3\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_node(\u0026#39;k4\u0026#39;, \u0026#39;m4\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_node(\u0026#39;k5\u0026#39;, \u0026#39;m5\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_node(\u0026#39;k6\u0026#39;, \u0026#39;m6\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_dependency(\u0026#39;m1\u0026#39;, \u0026#39;k1\u0026#39;, \u0026#39;k2\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_dependency(\u0026#39;m2\u0026#39;, \u0026#39;k2\u0026#39;, \u0026#39;k3\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_dependency(\u0026#39;m3\u0026#39;, \u0026#39;k3\u0026#39;, \u0026#39;k4\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.add_dependency(\u0026#39;m5\u0026#39;, \u0026#39;k5\u0026#39;, \u0026#39;k6\u0026#39;) \u0026gt;\u0026gt;\u0026gt; k1 -\u0026gt; k2 -\u0026gt; k3 -\u0026gt; k4 k5 -\u0026gt; k6 KeyboardInterrupt \u0026gt;\u0026gt;\u0026gt; graph.validate_consistency() \u0026gt;\u0026gt;\u0026gt; graph.forwards_plan(\u0026#39;k3\u0026#39;) [\u0026#39;k4\u0026#39;, \u0026#39;k3\u0026#39;] \u0026gt;\u0026gt;\u0026gt; graph.forwards_plan(\u0026#39;k1\u0026#39;) [\u0026#39;k4\u0026#39;, \u0026#39;k3\u0026#39;, \u0026#39;k2\u0026#39;, \u0026#39;k1\u0026#39;] \u0026gt;\u0026gt;\u0026gt; graph.forwards_plan(\u0026#39;k5\u0026#39;) [\u0026#39;k6\u0026#39;, \u0026#39;k5\u0026#39;] \u0026gt;\u0026gt;\u0026gt; graph.backwards_plan(\u0026#39;k5\u0026#39;) [\u0026#39;k5\u0026#39;] \u0026gt;\u0026gt;\u0026gt; graph.backwards_plan(\u0026#39;k6\u0026#39;) [\u0026#39;k5\u0026#39;, \u0026#39;k6\u0026#39;] \u0026gt;\u0026gt;\u0026gt; graph.backwards_plan(\u0026#39;k3\u0026#39;) [\u0026#39;k1\u0026#39;, \u0026#39;k2\u0026#39;, \u0026#39;k3\u0026#39;] \u0026gt;\u0026gt;\u0026gt; graph.add_dependency(\u0026#39;m3\u0026#39;, \u0026#39;k3\u0026#39;, k6\u0026#39;) File \u0026#34;\u0026lt;console\u0026gt;\u0026#34;, line 1 graph.add_dependency(\u0026#39;m3\u0026#39;, \u0026#39;k3\u0026#39;, k6\u0026#39;) ^ SyntaxError: unterminated string literal (detected at line 1) \u0026gt;\u0026gt;\u0026gt; graph.add_dependency(\u0026#39;m3\u0026#39;, \u0026#39;k3\u0026#39;, \u0026#39;k6\u0026#39;) \u0026gt;\u0026gt;\u0026gt; graph.backwards_plan(\u0026#39;k3\u0026#39;) [\u0026#39;k1\u0026#39;, \u0026#39;k2\u0026#39;, \u0026#39;k3\u0026#39;] \u0026gt;\u0026gt;\u0026gt; graph.forwards_plan(\u0026#39;k3\u0026#39;) [\u0026#39;k6\u0026#39;, \u0026#39;k4\u0026#39;, \u0026#39;k3\u0026#39;] \u0026gt;\u0026gt;\u0026gt; graph.node_map[\u0026#39;k6\u0026#39;].chilren Traceback (most recent call last): File \u0026#34;\u0026lt;console\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt; AttributeError: \u0026#39;Node\u0026#39; object has no attribute \u0026#39;chilren\u0026#39; \u0026gt;\u0026gt;\u0026gt; graph.node_map[\u0026#39;k6\u0026#39;].children {\u0026lt;Node: (\u0026#39;k\u0026#39;, \u0026#39;5\u0026#39;)\u0026gt;, \u0026lt;Node: (\u0026#39;k\u0026#39;, \u0026#39;3\u0026#39;)\u0026gt;} \u0026gt;\u0026gt;\u0026gt; graph.node_map[\u0026#39;k3\u0026#39;].parent Traceback (most recent call last): File \u0026#34;\u0026lt;console\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt; AttributeError: \u0026#39;Node\u0026#39; object has no attribute \u0026#39;parent\u0026#39; \u0026gt;\u0026gt;\u0026gt; graph.node_map[\u0026#39;k3\u0026#39;].parents {\u0026lt;Node: (\u0026#39;k\u0026#39;, \u0026#39;4\u0026#39;)\u0026gt;, \u0026lt;Node: (\u0026#39;k\u0026#39;, \u0026#39;6\u0026#39;)\u0026gt;} \u0026gt;\u0026gt;\u0026gt; graph.ensure_not_cyclic() \u0026gt;\u0026gt;\u0026gt; graph.remove_replaced_nodes(\u0026#39;k1\u0026#39;, [\u0026#39;k6\u0026#39;]) \u0026gt;\u0026gt;\u0026gt; graph.node_map[\u0026#39;k1\u0026#39;].parents {\u0026lt;Node: (\u0026#39;k\u0026#39;, \u0026#39;2\u0026#39;)\u0026gt;} \u0026gt;\u0026gt;\u0026gt; graph.node_map[\u0026#39;k1\u0026#39;].children {\u0026lt;Node: (\u0026#39;k\u0026#39;, \u0026#39;5\u0026#39;)\u0026gt;, \u0026lt;Node: (\u0026#39;k\u0026#39;, \u0026#39;3\u0026#39;)\u0026gt;} \u0026gt;\u0026gt;\u0026gt; graph.ensure_not_cyclic() Traceback (most recent call last): File \u0026#34;\u0026lt;console\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt; File \u0026#34;/Users/example/File/Project/firstDjango/venv/lib/python3.10/site-packages/django/db/migrations/graph.py\u0026#34;, line 285, in ensure_not_cyclic \u0026#34;, \u0026#34;.join(\u0026#34;%s.%s\u0026#34; % n for n in cycle) File \u0026#34;/Users/example/File/Project/firstDjango/venv/lib/python3.10/site-packages/django/db/migrations/graph.py\u0026#34;, line 285, in \u0026lt;genexpr\u0026gt; \u0026#34;, \u0026#34;.join(\u0026#34;%s.%s\u0026#34; % n for n in cycle) TypeError: not enough arguments for format string ","date":"2023-02-08","permalink":"/posts/django-migration-graph-notes/","title":"Django MigrationGraph 交互实验"},{"categories":["django"],"content":"Django4.0源码分析-1.8 关于shell命令的答疑 os.environ.setdefault(\u0026lsquo;DJANGO_SETTINGS_MODULE\u0026rsquo;, \u0026lsquo;firstDjango.settings\u0026rsquo;) os.environ.get(\u0026lsquo;DJANGO_SETTINGS_MODULE\u0026rsquo;) # 这个似乎不能修改系统的环境变量，只改变python环境的变量 from django.apps import apps from django.urls import set_script_prefix from django.utils.log import configure_logging\nconfigure_logging(settings.LOGGING_CONFIG, settings.LOGGING) if set_prefix: set_script_prefix( \u0026ldquo;/\u0026rdquo; if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME ) apps.populate(settings.INSTALLED_APPS)\nPython 1 2 3 4 5 os.environ.setdefault(\u0026#39;DJANGO_SETTINGS_MODULE\u0026#39;, \u0026#39;firstDjango.settings\u0026#39;) from django.apps import apps from django.conf import settings apps.populate(settings.INSTALLED_APPS) apps.apps_ready 这样也能执行数据库操作from web.models import Room\n大致讲了django的setup做了啥\n","date":"2023-02-07","permalink":"/posts/django-setup-shell-notes/","title":"Django setup 与 shell 初始化笔记"},{"categories":["django"],"content":"python manage.py help shell\nPython 1 2 3 4 from django.core.management import ManagementUtility ManagementUtility().main_help_text() \u0026#34;\\nType \u0026#39;pydevconsole.py help \u0026lt;subcommand\u0026gt;\u0026#39; for help on a specific subcommand.\\n\\nAvailable subcommands:\\n\\n[auth]\\n changepassword\\n createsuperuser\\n\\n[channels]\\n runworker\\n\\n[contenttypes]\\n remove_stale_contenttypes\\n\\n[django]\\n check\\n compilemessages\\n createcachetable\\n dbshell\\n diffsettings\\n dumpdata\\n flush\\n inspectdb\\n loaddata\\n makemessages\\n makemigrations\\n migrate\\n sendtestemail\\n shell\\n showmigrations\\n sqlflush\\n sqlmigrate\\n sqlsequencereset\\n squashmigrations\\n startapp\\n startproject\\n test\\n testserver\\n\\n[sessions]\\n clearsessions\\n\\n[staticfiles]\\n collectstatic\\n findstatic\\n runserver\u0026#34; print(ManagementUtility().main_help_text()) 判断继承类有没有该方法\n文本 1 2 3 4 5 6 7 8 def handle(self, *args, **options): \u0026#34;\u0026#34;\u0026#34; The actual logic of the command. Subclasses must implement this method. \u0026#34;\u0026#34;\u0026#34; raise NotImplementedError( \u0026#34;subclasses of BaseCommand must provide a handle() method\u0026#34; ) 执行find方法\n每个命令都是对应一个py文件，文件都有Command类，本质是执行excuse方法，最终调用handle方法\n1.6 Django中startproject命令追踪\nfor root, dirs, files in os.walk(template_dir) for dirname in dirs[:]: if \u0026ldquo;exclude\u0026rdquo; not in options: if dirname.startswith(\u0026quot;.\u0026quot;) or dirname == \u0026ldquo;pycache\u0026rdquo;: dirs.remove(dirname) elif dirname in excluded_directories: dirs.remove(dirname) def walkFile(file): for root, dirs, files in os.walk(file):\n# root 表示当前正在访问的文件夹路径 # dirs 表示该文件夹下的子目录名list # files 表示该文件夹下的文件list # 遍历文件 for f in files: print(os.path.join(root, f)) # 遍历所有的文件夹 for d in dirs: print(os.path.join(root, d)) for root, dirs, files in os.walk(\u0026quot;/Users/example/File/Project/firstDjango/web\u0026quot;, topdown=False): for name in files: print(os.path.join(root, name)) for name in dirs: print(os.path.join(root, name))\nshutil.copymode(old_path, new_path)\nif new_path.endswith(extensions) or filename in extra_files: with open(old_path, encoding=\u0026quot;utf-8\u0026quot;) as template_file: content = template_file.read() template = Engine().from_string(content) content = template.render(context) with open(new_path, \u0026quot;w\u0026quot;, encoding=\u0026quot;utf-8\u0026quot;) as new_file: new_file.write(content) else: shutil.copyfile(old_path, new_path) django 自定义模板引擎 Engine() Python 1 2 3 4 5 6 \u0026gt;\u0026gt;\u0026gt; content_tp = \u0026#34;hello, {{ name }}\u0026#34; \u0026gt;\u0026gt;\u0026gt; from django.template import Context, Engine \u0026gt;\u0026gt;\u0026gt; template = Engine().from_string(content_tp) \u0026gt;\u0026gt;\u0026gt; context = Context({\u0026#39;name\u0026#39;: \u0026#39;clin\u0026#39;}) \u0026gt;\u0026gt;\u0026gt; template.render(context) \u0026#39;hello, clin\u0026#39; 1.7\npython3 manage.py shell进入交互模式会做什么\n可以增加参数-c参数指定使用哪个交互python 可选项有三个，需要提前安装 shells = [\u0026lsquo;ipython\u0026rsquo;, \u0026lsquo;bpython\u0026rsquo;, \u0026lsquo;python\u0026rsquo;] 若没有指定，则默认按照顺序导入，报错则执行下一个 \\\nPython 1 2 3 4 5 6 for shell in available_shells: try: return getattr(self, shell)(options) except ImportError: pass raise CommandError(\u0026#34;Couldn\u0026#39;t import {} interface.\u0026#34;.format(shell)) getattr(self, shell)(options) 这也算是一个常用方法，getattr判断是否有该属性，然后加括号执行\npython进入交互模式\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import traceback import os import sys from django.utils.datastructures import OrderedSet def python(): import code # Set up a dictionary to serve as the environment for the shell. imported_objects = {} # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system # conventions and get $PYTHONSTARTUP first then .pythonrc.py. \u0026#34;\u0026#34;\u0026#34; 机器翻译: # 我们要尊重 $PYTHONSTARTUP 和 .pythonrc.py，所以遵循系统 # 约定并首先获取 $PYTHONSTARTUP，然后获取 .pythonrc.py。 对代码逻辑没有影响，可以去掉 \u0026#34;\u0026#34;\u0026#34; for pythonrc in OrderedSet( [os.environ.get(\u0026#34;PYTHONSTARTUP\u0026#34;), os.path.expanduser(\u0026#34;~/.pythonrc.py\u0026#34;)] ): if not pythonrc: continue if not os.path.isfile(pythonrc): continue with open(pythonrc) as handle: pythonrc_code = handle.read() # Match the behavior of the cpython shell where an error in # PYTHONSTARTUP prints an exception and continues. \u0026#34;\u0026#34;\u0026#34; 机器翻译: # 匹配出现错误的 cpython shell 的行为 # PYTHONSTARTUP 打印异常并继续。 对代码逻辑没有影响，可以去掉 \u0026#34;\u0026#34;\u0026#34; try: exec(compile(pythonrc_code, pythonrc, \u0026#34;exec\u0026#34;), imported_objects) except Exception: traceback.print_exc() # By default, this will set up readline to do tab completion and to read and # write history to the .python_history file, but this can be overridden by # $PYTHONSTARTUP or ~/.pythonrc.py. try: hook = sys.__interactivehook__ except AttributeError: # Match the behavior of the cpython shell where a missing # sys.__interactivehook__ is ignored. pass else: try: hook() except Exception: # Match the behavior of the cpython shell where an error in # sys.__interactivehook__ prints a warning and the exception # and continues. print(\u0026#34;Failed calling sys.__interactivehook__\u0026#34;) traceback.print_exc() # Set up tab completion for objects imported by $PYTHONSTARTUP or # ~/.pythonrc.py. try: import readline import rlcompleter readline.set_completer(rlcompleter.Completer(imported_objects).complete) except ImportError: pass # Start the interactive interpreter. code.interact(local=imported_objects) python() ","date":"2023-02-01","permalink":"/posts/django-management-shell-source-notes/","title":"Django 4.0 命令管理与 shell 启动流程"},{"categories":["python"],"content":"json dumps date类型数据 原文参考\njson 默认只支持6种数据格式转换，遇到特殊的类型，需要自己定制化, json提供了json.JSONEncoder 类来定制化解决这个问题\nPython json module provides a json.JSONEncoder Class. We can extend it If we want more customized output. i.e., To serialize the DateTime instance into JSON, we need to subclass JSONEncoder so we can implement our custom serialization.\n事情背景 网上找到一个抓取节假日的脚本，发现使用了dumps方法，这种方式没有用过，了解后发现是json模块dump 日期类型。\n脚本链接\n代码分析 1. dump原理 在json.dumps方法中，本质还是执行序列化类cls, 默认这个类为JSONEncoder，可以根据自己的情况定制。\npython的json模块里，源码如下\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw): \u0026#34;\u0026#34;\u0026#34;Serialize ``obj`` to a JSON formatted ``str``. If ``skipkeys`` is true then ``dict`` keys that are not basic types (``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped instead of raising a ``TypeError``. If ``ensure_ascii`` is false, then the return value can contain non-ASCII characters if they appear in strings contained in ``obj``. Otherwise, all such characters are escaped in JSON strings. If ``check_circular`` is false, then the circular reference check for container types will be skipped and a circular reference will result in an ``OverflowError`` (or worse). If ``allow_nan`` is false, then it will be a ``ValueError`` to serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). If ``indent`` is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. ``None`` is the most compact representation. If specified, ``separators`` should be an ``(item_separator, key_separator)`` tuple. The default is ``(\u0026#39;, \u0026#39;, \u0026#39;: \u0026#39;)`` if *indent* is ``None`` and ``(\u0026#39;,\u0026#39;, \u0026#39;: \u0026#39;)`` otherwise. To get the most compact JSON representation, you should specify ``(\u0026#39;,\u0026#39;, \u0026#39;:\u0026#39;)`` to eliminate whitespace. ``default(obj)`` is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. If *sort_keys* is true (default: ``False``), then the output of dictionaries will be sorted by key. To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the ``.default()`` method to serialize additional types), specify it with the ``cls`` kwarg; otherwise ``JSONEncoder`` is used. \u0026#34;\u0026#34;\u0026#34; # cached encoder if (not skipkeys and ensure_ascii and check_circular and allow_nan and cls is None and indent is None and separators is None and default is None and not sort_keys and not kw): return _default_encoder.encode(obj) if cls is None: cls = JSONEncoder return cls( skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, indent=indent, separators=separators, default=default, sort_keys=sort_keys, **kw).encode(obj) JSONEncoder的方法使用 在JSONEncoder模块中，关键点如下\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 class JSONEncoder(object): \u0026#34;\u0026#34;\u0026#34;Extensible JSON \u0026lt;http://json.org\u0026gt; encoder for Python data structures. Supports the following objects and types by default: +-------------------+---------------+ | Python | JSON | +===================+===============+ | dict | object | +-------------------+---------------+ | list, tuple | array | +-------------------+---------------+ | str | string | +-------------------+---------------+ | int, float | number | +-------------------+---------------+ | True | true | +-------------------+---------------+ | False | false | +-------------------+---------------+ | None | null | +-------------------+---------------+ To extend this to recognize other objects, subclass and implement a ``.default()`` method with another method that returns a serializable object for ``o`` if possible, otherwise it should call the superclass implementation (to raise ``TypeError``). \u0026#34;\u0026#34;\u0026#34; item_separator = \u0026#39;, \u0026#39; key_separator = \u0026#39;: \u0026#39; def __init__(self, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None): \u0026#34;\u0026#34;\u0026#34;Constructor for JSONEncoder, with sensible defaults. If skipkeys is false, then it is a TypeError to attempt encoding of keys that are not str, int, float or None. If skipkeys is True, such items are simply skipped. If ensure_ascii is true, the output is guaranteed to be str objects with all incoming non-ASCII characters escaped. If ensure_ascii is false, the output can contain non-ASCII characters. If check_circular is true, then lists, dicts, and custom encoded objects will be checked for circular references during encoding to prevent an infinite recursion (which would cause an OverflowError). Otherwise, no such check takes place. If allow_nan is true, then NaN, Infinity, and -Infinity will be encoded as such. This behavior is not JSON specification compliant, but is consistent with most JavaScript based encoders and decoders. Otherwise, it will be a ValueError to encode such floats. If sort_keys is true, then the output of dictionaries will be sorted by key; this is useful for regression tests to ensure that JSON serializations can be compared on a day-to-day basis. If indent is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. None is the most compact representation. If specified, separators should be an (item_separator, key_separator) tuple. The default is (\u0026#39;, \u0026#39;, \u0026#39;: \u0026#39;) if *indent* is ``None`` and (\u0026#39;,\u0026#39;, \u0026#39;: \u0026#39;) otherwise. To get the most compact JSON representation, you should specify (\u0026#39;,\u0026#39;, \u0026#39;:\u0026#39;) to eliminate whitespace. If specified, default is a function that gets called for objects that can\u0026#39;t otherwise be serialized. It should return a JSON encodable version of the object or raise a ``TypeError``. \u0026#34;\u0026#34;\u0026#34; self.skipkeys = skipkeys self.ensure_ascii = ensure_ascii self.check_circular = check_circular self.allow_nan = allow_nan self.sort_keys = sort_keys self.indent = indent if separators is not None: self.item_separator, self.key_separator = separators elif indent is not None: self.item_separator = \u0026#39;,\u0026#39; if default is not None: self.default = default def default(self, o): \u0026#34;\u0026#34;\u0026#34;Implement this method in a subclass such that it returns a serializable object for ``o``, or calls the base implementation (to raise a ``TypeError``). For example, to support arbitrary iterators, you could implement default like this:: def default(self, o): try: iterable = iter(o) except TypeError: pass else: return list(iterable) # Let the base class default method raise the TypeError return JSONEncoder.default(self, o) \u0026#34;\u0026#34;\u0026#34; raise TypeError(f\u0026#39;Object of type {o.__class__.__name__} \u0026#39; f\u0026#39;is not JSON serializable\u0026#39;) 使用案例 默认方式执行 直接使用json.dumps会报错没有对应的类型，JSONEncoder只支持6种基础类型转换\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 import json import datetime from json import JSONEncoder employee = { \u0026#34;id\u0026#34;: 456, \u0026#34;name\u0026#34;: \u0026#34;William Smith\u0026#34;, \u0026#34;salary\u0026#34;: 8000, \u0026#34;joindate\u0026#34;: datetime.datetime.now() } employeeJSONData = json.dumps(employee) print(employeeJSONData) output\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 employeeJSONData = json.dumps(employee) print(employeeJSONData) Encode DateTime Object into JSON using custom JSONEncoder Traceback (most recent call last): File \u0026#34;/usr/local/var/pyenv/versions/3.8.15/lib/python3.8/code.py\u0026#34;, line 90, in runcode exec(code, self.locals) File \u0026#34;\u0026lt;input\u0026gt;\u0026#34;, line 2, in \u0026lt;module\u0026gt; File \u0026#34;/usr/local/var/pyenv/versions/3.8.15/lib/python3.8/json/__init__.py\u0026#34;, line 231, in dumps return _default_encoder.encode(obj) File \u0026#34;/usr/local/var/pyenv/versions/3.8.15/lib/python3.8/json/encoder.py\u0026#34;, line 199, in encode chunks = self.iterencode(o, _one_shot=True) File \u0026#34;/usr/local/var/pyenv/versions/3.8.15/lib/python3.8/json/encoder.py\u0026#34;, line 257, in iterencode return _iterencode(o, 0) File \u0026#34;/usr/local/var/pyenv/versions/3.8.15/lib/python3.8/json/encoder.py\u0026#34;, line 179, in default raise TypeError(f\u0026#39;Object of type {o.__class__.__name__} \u0026#39; TypeError: Object of type datetime is not JSON serializable 使用自定义DateTimeEncoder序列化 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import json import datetime from json import JSONEncoder employee = { \u0026#34;id\u0026#34;: 456, \u0026#34;name\u0026#34;: \u0026#34;William Smith\u0026#34;, \u0026#34;salary\u0026#34;: 8000, \u0026#34;joindate\u0026#34;: datetime.datetime.now() } # subclass JSONEncoder class DateTimeEncoder(JSONEncoder): #Override the default method def default(self, obj): if isinstance(obj, (datetime.date, datetime.datetime)): return obj.isoformat() print(\u0026#34;Printing to check how it will look like\u0026#34;) print(DateTimeEncoder().encode(employee)) print(\u0026#34;Encode DateTime Object into JSON using custom JSONEncoder\u0026#34;) employeeJSONData = json.dumps(employee, indent=4, cls=DateTimeEncoder) # indent=4 缩进为4个空格 # DateTimeEncoder 自定义转换，可以做更多自定义格式 print(employeeJSONData) output\n代码 1 2 3 4 5 6 7 8 9 Printing to check how it will look like {\u0026#34;id\u0026#34;: 456, \u0026#34;name\u0026#34;: \u0026#34;William Smith\u0026#34;, \u0026#34;salary\u0026#34;: 8000, \u0026#34;joindate\u0026#34;: \u0026#34;2023-01-16T13:28:19.018879\u0026#34;} Encode DateTime Object into JSON using custom JSONEncoder { \u0026#34;id\u0026#34;: 456, \u0026#34;name\u0026#34;: \u0026#34;William Smith\u0026#34;, \u0026#34;salary\u0026#34;: 8000, \u0026#34;joindate\u0026#34;: \u0026#34;2023-01-16T13:28:19.018879\u0026#34; } 使用自定义DateTimeEncoder序列化，更改缩进，序列化值优化 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import json import datetime from json import JSONEncoder employee = { \u0026#34;id\u0026#34;: 456, \u0026#34;name\u0026#34;: \u0026#34;William Smith\u0026#34;, \u0026#34;salary\u0026#34;: 8000, \u0026#34;joindate\u0026#34;: datetime.datetime.now() } # subclass JSONEncoder class DateTimeEncoder(JSONEncoder): #Override the default method def default(self, obj): if isinstance(obj, (datetime.date, datetime.datetime)): res = obj.isoformat() return res[:10] print(\u0026#34;Printing to check how it will look like\u0026#34;) print(DateTimeEncoder().encode(employee)) print(\u0026#34;Encode DateTime Object into JSON using custom JSONEncoder\u0026#34;) employeeJSONData = json.dumps(employee, indent=0, cls=DateTimeEncoder) # indent=4 缩进为4个空格 # DateTimeEncoder 自定义转换，可以做更多自定义格式 print(employeeJSONData) output\n代码 1 2 3 4 5 6 7 8 9 10 Printing to check how it will look like {\u0026#34;id\u0026#34;: 456, \u0026#34;name\u0026#34;: \u0026#34;William Smith\u0026#34;, \u0026#34;salary\u0026#34;: 8000, \u0026#34;joindate\u0026#34;: \u0026#34;2023-01-16T13:28:19.018879\u0026#34;} Encode DateTime Object into JSON using custom JSONEncoder Encode DateTime Object into JSON using custom JSONEncoder { \u0026#34;id\u0026#34;: 456, \u0026#34;name\u0026#34;: \u0026#34;William Smith\u0026#34;, \u0026#34;salary\u0026#34;: 8000, \u0026#34;joindate\u0026#34;: \u0026#34;2023-01-16\u0026#34; } ","date":"2023-01-16","permalink":"/posts/jsonencoder-datetime-notes/","title":"JSONEncoder"},{"categories":["tools"],"content":"git rebase medium参考\n掘金参考\n简单理解，git rebase 会省去一些不必要的记录，让整个log更好看\n最实在就是，当你从master checkout 一个分支进行feature功能开发，别人开发的功能完成了。你需要合并别人的代码，然后继续开发\n这个时候使用git merge会记录别的数据，如果是用git rebase会更好些\n当前分支为clin, 远程分支别人更新过后 Bash 1 2 git rebase origin/release # 合并远程 origin/release 分支 git rebase master # 合并本地master分支 查看clin的git log发现 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 commit 512d838187acdcb33338fa6960a5cb1f8dc0644f (HEAD -\u0026gt; clin) Author: clin \u0026lt;user@example.com\u0026gt; Date: Sat Jan 14 16:35:03 2023 +0800 rm cdn picture icon commit 880816383efc00551ffbf4aa30d217ab1aba3e56 (origin/release) Author: nickname \u0026lt;user@example.com\u0026gt; Date: Sat Jan 14 08:36:15 2023 +0000 删除msg commit cd32eff64d2a60cfdda784de472d415d97c55191 (origin/master, release, master) Merge: 6e2e9cc c77e957 Author: nickname \u0026lt;user@example.com\u0026gt; Date: Fri Jan 13 02:06:55 2023 +0000 查看origin/release的git log 代码 1 2 3 4 5 6 7 8 9 10 commit 880816383efc00551ffbf4aa30d217ab1aba3e56 (HEAD -\u0026gt; release, origin/release) Author: nickname \u0026lt;user@example.com\u0026gt; Date: Sat Jan 14 08:36:15 2023 +0000 删除msg commit cd32eff64d2a60cfdda784de472d415d97c55191 (origin/master, master) Merge: 6e2e9cc c77e957 Author: nickname \u0026lt;user@example.com\u0026gt; Date: Fri Jan 13 02:06:55 2023 +0000 对比log日志，明显会让强迫症舒服\ngit pull自动rebase 冲突 有时候git pull 自动变成 git rebase git pull 是一个组合命令，等于 git fetch + git merge。 目前不是很清楚为什么会出现rebase，但猜测应该是pull自动执行的 git pull \u0026ndash;rebase origin master\n代码 1 2 3 4 5 6 7 8 Auto-merging src/App.vue CONFLICT (content): Merge conflict in src/App.vue error: could not apply 953b627... active hint: Resolve all conflicts manually, mark them as resolved with hint: \u0026#34;git add/rm \u0026lt;conflicted_files\u0026gt;\u0026#34;, then run \u0026#34;git rebase --continue\u0026#34;. hint: You can instead skip this commit: run \u0026#34;git rebase --skip\u0026#34;. hint: To abort and get back to the state before \u0026#34;git rebase\u0026#34;, run \u0026#34;git rebase --abort\u0026#34;. Could not apply 953b627... active 拉取远程分支1 git pull origin test:test 拉取远程分支2 git checkout -b test \u0026lt;name of remote\u0026gt;/test\n遇到这种情况的处理方式\n处理冲突，然后执行 git rebase --continue 放弃更新，回到之前的版本git rebase --abort 跳过代码冲突，以远程分支为主，忽略本地代码git rebase --skip git rebase \u0026ndash;skip 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 commit faf2313d3ae7ca5af1f6011d73fc32db99bb81a9 (HEAD -\u0026gt; release, origin/release) Author: nickname \u0026lt;user@example.com\u0026gt; Date: Sat Jan 14 13:41:57 2023 +0000 Update App.vue origin release commit 880816383efc00551ffbf4aa30d217ab1aba3e56 Author: nickname \u0026lt;user@example.com\u0026gt; Date: Sat Jan 14 08:36:15 2023 +0000 删除msg commit cd32eff64d2a60cfdda784de472d415d97c55191 (origin/test, origin/master, test, master) Merge: 6e2e9cc c77e957 ","date":"2023-01-14","permalink":"/posts/git-rebase-notes/","title":"Git rebase 与冲突处理记录"},{"categories":["tools"],"content":"1. router-link 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 1. 不带参数 \u0026lt;router-link :to=\u0026#34;{name:\u0026#39;home\u0026#39;}\u0026#34;\u0026gt; \u0026lt;router-link :to=\u0026#34;{path:\u0026#39;/home\u0026#39;}\u0026#34;\u0026gt; //name,path都行, 建议用name // 注意：router-link中链接如果是\u0026#39;/\u0026#39;开始就是从根路由开始，如果开始不带\u0026#39;/\u0026#39;，则从当前路由开始。 2.带参数 \u0026lt;router-link :to=\u0026#34;{name:\u0026#39;home\u0026#39;, params: {id:1}}\u0026#34;\u0026gt; // params传参数 (类似post) // 路由配置 path: \u0026#34;/home/:id\u0026#34; 或者 path: \u0026#34;/home:id\u0026#34; // 不配置path ,第一次可请求,刷新页面id会消失 // 配置path,刷新页面id会保留 // html 取参 $route.params.id // script 取参 this.$route.params.id \u0026lt;router-link :to=\u0026#34;{name:\u0026#39;home\u0026#39;, query: {id:1}}\u0026#34;\u0026gt; // query传参数 (类似get,url后面会显示参数) // 路由可不配置 // html 取参 $route.query.id // script 取参 this.$route.query.id 2. this.$router.push() (函数里面调用) 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 1. 不带参数 this.$router.push(\u0026#39;/home\u0026#39;) this.$router.push({name:\u0026#39;home\u0026#39;}) this.$router.push({path:\u0026#39;/home\u0026#39;}) 2. query传参 this.$router.push({name:\u0026#39;home\u0026#39;,query: {id:\u0026#39;1\u0026#39;}}) this.$router.push({path:\u0026#39;/home\u0026#39;,query: {id:\u0026#39;1\u0026#39;}}) // html 取参 $route.query.id // script 取参 this.$route.query.id 3. params传参 this.$router.push({name:\u0026#39;home\u0026#39;,params: {id:\u0026#39;1\u0026#39;}}) // 只能用 name // 路由配置 path: \u0026#34;/home/:id\u0026#34; 或者 path: \u0026#34;/home:id\u0026#34; // 不配置path ,第一次可请求,刷新页面id会消失 // 配置path,刷新页面id会保留 // html 取参 $route.params.id // script 取参 this.$route.params.id 4. query和params区别 query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在 params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失 3. this.$router.replace() (用法同上,push) 4. this.$router.go(n) 代码 1 2 3 4 this.$router.go(n) 向前或者向后跳转n个页面，n可为正整数或负整数 ps : 区别\nthis.$router.push 跳转到指定url路径，并想history栈中添加一个记录，点击后退会返回到上一个页面 this.$router.replace 跳转到指定url路径，但是history栈中不会有记录，点击返回会跳转到上上个页面 (就是直接替换了当前页面) this.$router.go(n) 向前或者向后跳转n个页面，n可为正整数或负整数 ","date":"2022-11-17","permalink":"/posts/vue-router-navigation-notes/","title":"Vue Router 跳转与参数传递笔记"},{"categories":["tools"],"content":"常用的 JavaScript 模块化规范有 CommonJS、AMD、CMD 和 ES Modules。\n闭包与模块化 闭包函数 JavaScript 1 2 3 (function() { var flag = true; })(); 使用闭包组织模块 使用模块化作为出口，将需要对外提供的变量和方法放入对象，再返回这个对象。\nJavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 var ModuleA = (function() { // 1. 定义一个对象 var obj = {}; // 2. 在对象内部添加变量和方法 obj.flag = true; obj.myFunc = function(info) { console.log(info); }; // 3. 将对象返回 return obj; })(); CommonJS 导出模块 JavaScript 1 2 3 4 5 6 7 8 9 module.exports = { flag: true, test(a, b) { return a + b; }, demo(a, b) { return a * b; } }; 导入模块 使用 require() 导入模块，并通过解构获取变量和方法：\nJavaScript 1 let { test, demo, flag } = require(\u0026#39;moduleA\u0026#39;); 也可以先获取整个模块对象，再分别读取属性：\nJavaScript 1 2 3 4 let _mA = require(\u0026#39;moduleA\u0026#39;); let test = _mA.test; let demo = _mA.demo; let flag = _mA.flag; ES Modules：export / import 直接导出函数或类 JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 export function test(content) { console.log(content); } export class Person { constructor(name, age) { this.name = name; this.age = age; } run() { console.log(this.name + \u0026#39;在奔跑\u0026#39;); } } 先定义，再统一导出 JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function test(content) { console.log(content); } class Person { constructor(name, age) { this.name = name; this.age = age; } run() { console.log(this.name + \u0026#39;在奔跑\u0026#39;); } } export { test, Person }; 默认导出：export default 某些情况下，一个模块包含的功能不需要指定导出名称，而是由导入者自行命名。这时可以使用 export default。\n在 info.js 中默认导出一个函数：\nJavaScript 1 2 3 4 // info.js export default function () { console.log(\u0026#39;default function\u0026#39;); } 在 main.js 中导入并调用。这里的 myFunc 是导入时自行指定的名称：\nJavaScript 1 2 3 // main.js import myFunc from \u0026#39;./info.js\u0026#39;; myFunc(); 注意：同一个模块只能有一个 export default。\n","date":"2022-11-17","permalink":"/posts/javascript-module-notes/","title":"JavaScript 模块化学习笔记"},{"categories":[],"content":"hugo\n使用文档: https://gohugo.io/documentation/\n主题配置：https://stack.jimmycai.com/\n从入门到放弃： https://olowolo.com/post/hugo-quick-start/\n研究分类布局 https://github.com/olOwOlo/hugo-theme-even/blob/master/exampleSite/config.toml\nhttps://olowolo.com/post/hugo-quick-start/\nhttps://jinli.cyou/p/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99%E7%9A%84%E5%BB%BA%E7%AB%8B%E8%BF%87%E7%A8%8B%E4%B8%89hugo%E4%B8%BB%E9%A2%98stack%E7%9A%84%E4%BD%BF%E7%94%A8%E4%B8%8E%E4%BC%98%E5%8C%96/\n修改主题样式 css\n","date":"2022-11-14","permalink":"/posts/hugo-framework/","title":"Hugo Framework"},{"categories":["tools"],"content":"Git 常用命令与工作流程笔记 以下记录日常使用的 Git 命令、分支协作与认证配置。\nGit 安装 Bash 1 2 yum install git # centos sudo apt-get install git # ubuntu Git 基础使用 创建版本库 Bash 1 git init # 初始化所在目录为Git仓库 初始化的目录可以不为空\n添加文件到版本库 Bash 1 2 3 git add \u0026lt;file\u0026gt; ... # 添加文件到暂存区（stage） -f \u0026lt;file\u0026gt; ... # 强制添加到暂存区（可用于添加忽略文件） git commit -m \u0026#34;提交说明\u0026#34; # 从暂存区提交到版本库 git add 命令可多次执行，然后commit一次。\n工作区和暂存区 工作区: 当前所在目录\n暂存区: git add 命令执行后文件所在的位置\n管理修改 查看状态、差异 Bash 1 2 3 4 git status # 查看仓库当前状态 git diff [file] # 比较工作区和暂存区的差异 git diff --cached [file] # 比较暂存区和版本库的差异 git diff HEAD -- [file] # 比较工作区和版本库的差异 版本切换 Bash 1 2 3 4 5 6 7 8 9 git log # 查看提交历史 git log -1 # 查看最后一次提交信息（-2 则是最后两次） git log --pretty=oneline # 单行格式显示提交历史 --graph # 显示分支合并图 --abbrev-commit # 简写的commit_id git reflog # 查看所有操作记录，包括删除的commit记录 git reset --hard HEAD^ # 回退到上一版本 # HEAD 表当前版本， HEAD^ 表上一版本，HEAD^^ 表上两版本，HEAD~99 表上99版本。 git reset --hard commit_id # 切换到指定版本 Git跟踪管理的是修改，而非文件\n撤销修改 代码 1 2 3 4 5 git checkout -- \u0026lt;file\u0026gt; # 撤销工作区的修改 git reset HEAD \u0026lt;file\u0026gt; # 撤销暂存区的修改 git reset --hard origin/master # 删除本地代码，回滚到远程master分支 git reset --hard xxx # 回滚到某个节点，并删除代码 get reset --soft xxx # 回滚到某个节点，不删除代码，遇到代码异常，但不想回滚所有代码，可以使用soft，保留代码 若已commit但没提交到远程库，可用版本回退进行撤销\n已经提交了修改，发现提交记录错误，仅修改最近一次\n代码 1 git commit --amend -m \u0026#34;add link\u0026#34; https://cloud.tencent.com/developer/article/1730774\n删除文件 方法一：工作区删除文件，然后正常提交 代码 1 2 3 rm \u0026lt;file\u0026gt; ... #工作区删除 git add \u0026lt;file\u0026gt; ... #将修改提交至暂存区 git commit -m \u0026#34;说明\u0026#34; #提交到版本库 方法二：直接命令删除工作区和暂存区，然后提交版本库 代码 1 2 git rm \u0026lt;file\u0026gt; ... # 删除工作区和暂存区文件 git commit -m \u0026#34;说明\u0026#34; # 提交到版本库 删除、增加文件也都属于修改\n远程仓库 创建SSH Key Bash 1 ssh-keygen -t rsa -C \u0026#34;user@example.com\u0026#34; #生成的Key在家目录.ssh文件夹里面，pub后缀是公钥，另一个是私钥。-C 指定秘钥的名称(可以不加该参数) 添加远程库 Bash 1 2 3 4 5 git remote add origin git@server-name:path/repo-name.git #添加远程仓库 git remote #查看远程库信息 git remote -v #显示详细信息 git push -u origin \u0026lt;branch\u0026gt; #推送并关联指定分支到远程库 git remote prune origin # 删除远程不存在的仓库 除第一次关联，之后push不用加-u选项\n从远程库克隆 Bash 1 2 3 git clone git@server-name:path/repo-name.git #将远程仓库克隆到当前目录 git pull #拉取远程仓库内容 git remote add upstream git://github.com/user/repo_name.git # 添加远程fork仓库 拉取远程分支并创建本地分支 Bash 1 git checkout -b feature_v1.5.10 origin/feature_v1.5.10 # git checkout -b 本地分支名 origin/远程分支名 分支管理 分支基础命令 Bash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 git branch \u0026lt;branch\u0026gt; # 创建分支 git checkout \u0026lt;branch\u0026gt; # 切换到指定分支 git checkout -b \u0026lt;branch\u0026gt; # 创建并切换到该分支 git branch # 查看现有分支 git branch -r # 查看所有远程分支 git branch -d \u0026lt;branch\u0026gt; # 删除指定分支 git branch -D \u0026lt;branch\u0026gt; # 强制删除指定分支 git branch --set-upstream \u0026lt;branch_local\u0026gt; \u0026lt;branch_remote\u0026gt; # 指定本地分支与远程分支的链接 git merge \u0026lt;branch\u0026gt; # 合并指定分支到当前分支 --no-ff \u0026lt;branch\u0026gt; # 禁用快速合并 git merge --no-ff -m \u0026#34;提交说明\u0026#34; \u0026lt;branch\u0026gt; #普通方式合并，并附提交说明 git stash # 保存当前工作环境(包括工作区和暂存区，用于处理临时新建bug分支，git stash/ git stash pop) git stash list # 查看保存的工作列表 git stash apply [stash@{X}] # 恢复工作状态，但不删除stash内容 git stash pop [stash@{X}] # 恢复工作状态，并删除stash内容 git stash drop [stash@{X}] # 删除stash内容 git branch -D \u0026lt;branch\u0026gt; # 强制删除分支（常用于未合并的分支） git remote prune origin # 删除远程已经删除过的分支 git rm --cached -rf themes/hugo-theme-stack # 删除已添加的缓存 HEAD不是直接指向提交点，而是指向分支，分支再指向提交点\n解决冲突 代码 1 2 Git用\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;，=======，\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; 标记出不同分支的内容。 git log --graph --pretty=oneline --abbrev-commit # 查看分支的合并情况 多人协作 Bash 1 2 3 ##error: failed to push some refs to ... 1. git pull 远程库 2. 解决冲突（若有），再push 分支管理策略图 多人协作主要思想就是，各自开发对应的功能，然后汇总\n具体到分支上的提现：各自在对应的功能分支上开发，然后汇总dev分支，最后合并到master分支上生产\n标签管理 标签命令 Bash 1 2 3 4 5 6 7 8 9 10 git tag # 查看现有标签 git tag \u0026lt;tag_name\u0026gt; # 给当前所在的commit打标签 git tag \u0026lt;tag_name\u0026gt; \u0026lt;commit_id\u0026gt; # 给指定commit打标签 git tag -a \u0026lt;tag_name\u0026gt; -m \u0026#34;标签说明\u0026#34; \u0026lt;commit_id\u0026gt; # 给指定commit打标签，并附说明 -s \u0026lt;tag_name\u0026gt; -m \u0026#34;标签说明\u0026#34; \u0026lt;commit_id\u0026gt; # 用gpg私钥签名 -d \u0026lt;tag_name\u0026gt; # 删除标签 git show \u0026lt;tag_name\u0026gt; # 显示标签信息 git push origin \u0026lt;tag_name\u0026gt; # 推送标签到远程库 git push origin --tags # 推送所有未推送的标签到远程库 git push origin :refs/tags/\u0026lt;tag_name\u0026gt; # 删除远程标签（先删除本地，再使用该命令删除） 自定义Git Config基础命令 Bash 1 2 3 4 5 6 git config --global user.name \u0026#34;you_name\u0026#34; # 设置全局用户名 git config --global user.email \u0026#34;user@example.com\u0026#34; # 设置全局邮箱 git config --global color.ui true # 设置全局颜色显示 git config --global alias.\u0026lt;alias_name\u0026gt; \u0026lt;\u0026#39;command_name\u0026#39;\u0026gt; # 设置别名 git config (--global) --list # 查看全局配置 git config --global -edit # 编辑命令 配置优先级 代码 1 2 3 4 5 6 1、仓库级别 local 【优先级最高】 2、用户级别 global【优先级次之】 3、系统级别 system【优先级最低】 查看仓库级的config, 默认文件位置 .git/.config, 命令：git config –local -l 查看全局级的config, 默认文件位置 ~/.gitconfig, 命令：git config –global -l 查看系统级的config, 默认文件位置(默认不创建) /etc/gitconfig, 命令：git config –system -l 忽略特殊文件 工作区创建.gitignore文件 内容举例，如下： 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #Windows: Thumbs.db ehthumbs.db Desktop.ini #Python: *.py[cod] *.so *.egg *.egg-info dist build #My configurations: db.ini deploy_key_rsa 忽略文件配置命令 Bash 1 2 git check-ignore -v \u0026lt;file\u0026gt; # 查看忽略该文件的规则 git check-ignore -v ./* # 查看当前文件夹下哪些文件被忽略 规则有错时常用上述命令查找定位\n配置别名列表 Bash 1 2 3 4 5 6 7 8 # git config --global alias.confg \u0026#39;config --global\u0026#39; git config --global alias.st status git config --global alias.co checkout git config --global alias.ci commit git config --global alias.br branch git config --global alias.unstage \u0026#39;reset HEAD\u0026#39; git config --global alias.last \u0026#39;log -1\u0026#39; git config --global alias.lg \u0026#34;log --color --graph --pretty=format:\u0026#39;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)\u0026lt;%an\u0026gt;%Creset\u0026#39; --abbrev-commit\u0026#34; 搭建Git服务器 教程地址 PS: 一般公司系统中使用gitlab、gogs等第三方免费软件管理(附带权限)\nGit 进阶 https://gitee.com/liaoxuefeng/learn-java/raw/master/teach/git-cheatsheet.pdf\nGit 官方命令参考\nhttp://git-scm.com/\nGit 内部原理图解\ngithub https 认证问题 使用https方式连接github是需要通过凭证进行用户认证的(这个后续可以抓包看下)\nGit Credential Manager (GCM) is another way to store your credentials securely and connect to GitHub over HTTPS. With GCM, you don\u0026rsquo;t have to manually create and store a personal access token, as GCM manages authentication on your behalf, including 2FA (two-factor authentication).\nGit Credential Manager （GCM） 是另一种安全存储凭据并通过 HTTPS 连接到 GitHub 的方法。使用 GCM，您不必手动创建和存储个人访问令牌，因为 GCM 会代表您管理身份验证，包括 2FA（双因素身份验证）。\n需要安装证书管理\nBash 1 brew install --cask git-credential-manager github 会连接本地这个地址访问\n代码 1 http://127.0.0.1:53180/?code=7e41b00000000005293b\u0026amp;state=aad92890000000000de08458c5e48cb7 然后跳转浏览器登录github，进行二次认证，最后识别。\n可以在git config 里配置\nBash 1 git config credential.username \u0026#34;your username\u0026#34; 在config配置文件里能看见\n代码 1 2 [credential] username = credential-username 在下次登录的时候就可以直接登录了\n","date":"2021-01-10","permalink":"/posts/git-command-notes/","title":"Git 常用命令与工作流程笔记"},{"categories":["python"],"content":"想搞清楚flask源码\n需要搞清楚 flask 逻辑\n需要wsgi_app 怎么实现\nPython 1 2 3 4 from werkzeug.serving import run_simple try: run_simple(host, port, self, **options) self表示Flask 实例化app = Flask(__name__)\nhttps://segmentfault.com/a/1190000010756866\n这个流程可以参考\n还有一个问题，wsgi_app(environ, start_response)里的start_response为什么是个方法\n里面直接返回\nPython 1 2 3 4 5 6 7 8 9 10 11 from eventlet import wsgi import eventlet def application(environ, start_response): start_response(\u0026#39;200 OK\u0026#39;, [(\u0026#39;Content-Type\u0026#39;, \u0026#39;text/html\u0026#39;)]) body = \u0026#39;\u0026lt;h1\u0026gt;Hello, %s!\u0026lt;/h1\u0026gt;\u0026#39; % (environ[\u0026#39;PATH_INFO\u0026#39;][1:] or \u0026#39;web\u0026#39;) return [body.encode(\u0026#39;utf-8\u0026#39;)] wsgi.server(eventlet.listen((\u0026#39;\u0026#39;, 8080)), application) start_response('200 OK', [('Content-Type', 'text/html')])怎么理解\nhttp://liaoph.com/python-wsgi/\nsocket编程方式\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import socket # 创建一个socket: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 建立连接: s.connect((\u0026#39;www.sina.com.cn\u0026#39;, 80)) # 发送数据: s.send(b\u0026#39;GET / HTTP/1.1\\r\\nHost: www.sina.com.cn\\r\\nConnection: close\\r\\n\\r\\n\u0026#39;) # 接收数据: buffer = [] while True: # 每次最多接收1k字节: d = s.recv(1024) if d: buffer.append(d) else: break data = b\u0026#39;\u0026#39;.join(buffer) # 关闭连接: s.close() header, html = data.split(b\u0026#39;\\r\\n\\r\\n\u0026#39;, 1) print(header.decode(\u0026#39;utf-8\u0026#39;)) # 把接收的数据写入文件: with open(\u0026#39;sina.html\u0026#39;, \u0026#39;wb\u0026#39;) as f: f.write(html) 创建一个socket，建立连接，获取html，写入本地文件\n先根据 start_response('200 OK', [('Content-Type', 'text/html')]) http://liaoph.com/python-wsgi/\n理清思路，WSGI Server 实现纯手动写server\nwsgi协议\n再根据https://segmentfault.com/a/1190000010756866 将流程梳理\n","date":"2021-01-10","permalink":"/posts/flask-wsgi-socket-learning-notes/","title":"从 Flask WSGI 到 socket 的学习记录"},{"categories":["python"],"content":"Flask 上下文管理 内容回顾:\n1. Linux命令 2. 面向对象： 特殊方法 1. `obj['x'] = 123` 2. `obj.x = 123` 3. `obj + 123` functools\nPython 1 2 3 4 5 6 7 def func(a1, a2, a3): return a1 + a2 + a3 v1 = func(1, 2, 3) new_func = functions.partial(func, 111, 2) new_func(3) 你认识的装饰器？应用场景？\n应用： flask：路由、before_request django： csrf、缓存、用户登录 Flask\nthreading.local(跟flask 没有关系) 1.\n上下文管理\n数据库连接池\n\\r\\n\n请求分开\n把 \\r\\n 分开\nrequest wsgi 把原生字符串进行分割\n在源码中分析上下文管理 第一阶段: 将ctx(request, session)放到地上（local对象） 第二阶段: 视图函数导入: request/session 第三阶段: 请求处理完毕 获取session并保存到cookies 将ctx删除 在LocalProxy 中有各种python 双下划线用法\nPython 1 2 3 4 request = LocalProxy(partial(_lookup_req_object, \u0026#34;request\u0026#34;)) session = LocalProxy(partial(_lookup_req_object, \u0026#34;session\u0026#34;)) # partial 表示简约传参的方式，省略传参，不需要传入所有值 # _lookup_req_object 对象方法，反射 getattr 方法 视图函数引入包，LocalProxy代理，_loopup_req_object 反代，最后到localstack中的local找到对应的内容进行操作\n","date":"2021-01-10","permalink":"/posts/flask-context-source-notes/","title":"Flask 上下文与 LocalProxy 学习笔记"},{"categories":["python"],"content":"模板搜索引擎 自定义函数 自定义函数模板\nPython 1 2 3 from flask import Markup return Markup(\u0026#34;\u0026lt;input value=\u0026#39;%s\u0026#39; /\u0026gt;\u0026#34; % value) 在每一个模板里都使用自定义函数\n使用特殊的装饰器(全局)\nPython 1 2 3 4 5 6 7 8 9 10 11 12 @app.template_golbal() def sbbbbbb(a1, a2): \u0026#34; 每个模板中可以调用的函数 :param a1: :param a2: :return \u0026#34; return a1 + a2 # \u0026lt;h1\u0026gt;{{k5(99)|safe}}\u0026lt;/h1\u0026gt; 这里的safe也是一个函数 模板继承(与django相同)\n","date":"2021-01-10","permalink":"/posts/flask-template-notes/","title":"Flask 模板与自定义函数笔记"},{"categories":["python"],"content":"设置session\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from flask import Flask, session app = Flask(__name__) import os app.secret_key = os.environ[\u0026#39;FLASK_SECRET_KEY\u0026#39;] @app.route(\u0026#39;/x1\u0026#39;) def index(): session[\u0026#39;k1\u0026#39;] = 123 return \u0026#34;index\u0026#34; @app.route(\u0026#39;/x2\u0026#39;) def order(): print(session[\u0026#39;k1\u0026#39;]) return \u0026#34;order\u0026#34; Python 1 obj[\u0026#39;xxxx\u0026#39;] = 123 # __setitem__方法 流程分析\n源码解读\n源码解读二，分析类\n","date":"2021-01-10","permalink":"/posts/flask-session-source-notes/","title":"Flask session 使用与源码笔记"},{"categories":["python"],"content":"\n闪现 Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from flask import Flask, session, flash, get_flashed_messages app = Flask(__name__) import os app.secret_key = os.environ[\u0026#39;FLASK_SECRET_KEY\u0026#39;] app.config.from_object(\u0026#39;settings.dev\u0026#39;) @app.route(\u0026#39;/x1\u0026#39;, methods=[\u0026#39;GET\u0026#39;, \u0026#39;POST\u0026#39;]) def login(): flash(\u0026#39;hello world1\u0026#39;, category=\u0026#39;x1\u0026#39;) flash(\u0026#39;hello world2\u0026#39;, category=\u0026#39;x2\u0026#39;) return \u0026#39;login!\u0026#39; @app.route(\u0026#39;/x2\u0026#39;, methods=[\u0026#39;GET\u0026#39;, \u0026#39;POST\u0026#39;]) def index(): # data1 = get_flashed_messages() # print(\u0026#39;=========\u0026#39;, data1) data = get_flashed_messages(category_filter=[\u0026#39;x1\u0026#39;]) print(data) return \u0026#39;index!\u0026#39; if __name__ == \u0026#39;__main__\u0026#39;: app.run() ","date":"2021-01-10","permalink":"/posts/flask-flash-notes/","title":"Flask flash 消息使用笔记"},{"categories":["django"],"content":"restful view 视图处理 第一次封装\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 \u0026#34;\u0026#34;\u0026#34; APIView视图 使用 APIView 的写法 两个类，5个视图函数 get(所有的数据) create 创建数据 get(单条数据) patch/put(修改单条数据) delete(删除单条数据) \u0026#34;\u0026#34;\u0026#34; class BookView(APIView): def get(self, request): query_set = Book.objects.all() book_ser = BookSerializer(query_set, many=True) return Response(book_ser.data) def post(self, request): query_set = request.data book_ser = BookSerializer(data=query_set) if book_ser.is_valid(): book_ser.save() return Response(book_ser.validated_data) else: return Response(book_ser.errors) class BookEditView(APIView): def get(self, request, id): query_set = Book.objects.filter(id=id).first() book_ser = BookSerializer(query_set) return Response(book_ser.data) def patch(self, request, id): query_set = Book.objects.filter(id=id).first() book_ser = BookSerializer(query_set, data=request.data, partial=True) if book_ser.is_valid(): book_ser.save() return Response(book_ser.validated_data) else: return Response(book_ser.errors) def delete(self, request, id): query_set = Book.objects.filter(id=id).first() if query_set: query_set.delete() return Response(\u0026#34;\u0026#34;) else: return Response(\u0026#34;删除的书籍不存在\u0026#34;) Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 \u0026#34;\u0026#34;\u0026#34; 根据上面的，提取两个类方法中的公共内容，然后将几个方法各自抽出 两个类，5个视图函数中，都有queryset和serializer，所以可以提取出来 然后5个对应的方法单独写自己的功能 get -\u0026gt; ListModelMixin create -\u0026gt; CreateModelMixin get -\u0026gt; RetrieveModelMixin patch/put -\u0026gt; UpdateModelMixin delete -\u0026gt; DestroyModelMixin \u0026#34;\u0026#34;\u0026#34; class GenericAPIView(APIView): queryset = None serializer_class = None def get_queryset(self): return self.queryset.all() def get_serializer(self, *args, **kwargs): return self.serializer_class(*args, **kwargs) class ListModelMixin(object): def list(self, request, *args, **kwargs): queryset = self.get_queryset() serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) class CreateModelMixin(object): def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.validated_data) else: return Response(serializer.errors) class RetrieveModelMixin(object): def retrieve(self, request, id, *args, **kwargs): book_obj = self.get_queryset().filter(pk=id).first() book_ser = self.get_serializer(book_obj) return Response(book_ser.data) class UpdateModelMixin(object): def update(self, request, id, *args, **kwargs): book_obj = self.get_queryset().filter(pk=id).first() book_ser = self.get_serializer(book_obj, data=request.data, partial=True) if book_ser.is_valid(): book_ser.save() return Response(book_ser.validated_data) else: return Response(book_ser.errors) class DestroyModelMixin(object): def destroy(self, request, id, *args, **kwargs): queryset = self.get_queryset() try: queryset.get(pk=id).delete() return Response(\u0026#34;\u0026#34;) except Exception as e: return Response(\u0026#34;信息有误\u0026#34;) 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # 我们把公共的部分抽出来 这样不管写多少表的增删改查都变的很简单 # 这样封装后我们的视图会变成这样 \u0026#34;\u0026#34;\u0026#34; 需要自己写get post 等方法，没有太大意义，且有点繁琐 \u0026#34;\u0026#34;\u0026#34; class BookView(GenericAPIView, ListModelMixin, CreateModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer def get(self, request, id, *args, **kwargs): return self.retrieve(request, id, *args, **kwargs) def patch(self, request, id, *args, **kwargs): return self.update(request, id, *args, **kwargs) def destroy(self, request, id, *args, **kwargs): return self.delete(request, id, *args, **kwargs) 所以再加一层封装， 把这几个get post patch 方法帮你写好，就是普通的返回，免去无意义的 CV\n在 generics.py 模块下面的 xxxAPIView,都有写好，直接继承即可\n下面是源码中的几个模块，复制过来看看，就是帮你写了return\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 class CreateAPIView(mixins.CreateModelMixin, GenericAPIView): \u0026#34;\u0026#34;\u0026#34; Concrete view for creating a model instance. \u0026#34;\u0026#34;\u0026#34; def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class ListAPIView(mixins.ListModelMixin, GenericAPIView): \u0026#34;\u0026#34;\u0026#34; Concrete view for listing a queryset. \u0026#34;\u0026#34;\u0026#34; def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) class RetrieveAPIView(mixins.RetrieveModelMixin, GenericAPIView): \u0026#34;\u0026#34;\u0026#34; Concrete view for retrieving a model instance. \u0026#34;\u0026#34;\u0026#34; def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) class DestroyAPIView(mixins.DestroyModelMixin, GenericAPIView): \u0026#34;\u0026#34;\u0026#34; Concrete view for deleting a model instance. \u0026#34;\u0026#34;\u0026#34; def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) class UpdateAPIView(mixins.UpdateModelMixin, GenericAPIView): \u0026#34;\u0026#34;\u0026#34; Concrete view for updating a model instance. \u0026#34;\u0026#34;\u0026#34; def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs) class ListCreateAPIView(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView): \u0026#34;\u0026#34;\u0026#34; Concrete view for listing a queryset or creating a model instance. \u0026#34;\u0026#34;\u0026#34; def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class RetrieveUpdateAPIView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, GenericAPIView): \u0026#34;\u0026#34;\u0026#34; Concrete view for retrieving, updating a model instance. \u0026#34;\u0026#34;\u0026#34; def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs) 最后的封装，就是讲url再高度集成，但大多情况不需要，除非无脑的 CRUD ,要加上业务逻辑只能自己写\n所以个人角度总结 代码 1 2 3 4 5 6 7 8 9 CreateAPIView ListAPIView RetrieveAPIView DestroyAPIView UpdateAPIView ListCreateAPIView RetrieveUpdateAPIView RetrieveDestroyAPIView RetrieveUpdateDestroyAPIView 然后根据业务逻辑自己再重写相应的方法\n","date":"2020-11-28","permalink":"/posts/drf-generic-view-refactoring-notes/","title":"DRF 视图从 APIView 到通用视图的封装"},{"categories":["django"],"content":"Django基础 一、Django 基础操作 创建Django项目 Bash 1 2 # 命令行方式创建项目 django-admin startproject devops Django项目目录结构介绍 代码 1 2 3 4 5 6 |-- manage.py `-- devops |-- __init__.py |-- settings.py |-- urls.py `-- wsgi.py 最外层的devops/目录只是你项目的一个容器 manage.py 一个实用的命令行工具，可让你以各种方式与该 Django 项目进行交互 内层 devops/ 目录是你项目中的实际Python包。通过它你可以导入它里面的任何东西 devops/__init__.py: 一个空文件，告诉Python该目录是一个Python包 devops/settings.py: 该Django项目的配置文件 devops/urls.py: 该Django项目的 URL 声明 devops/wsgi.py: 一个WSGI兼容的Web服务器的入口 启动(运行)项目 起动服务 Bash 1 2 cd devops python manage.py runserver 可以直接通过python3 manage.py查看命令\n指定端口起动服务 Bash 1 python manage.py runserver 8000 指定ip与端口起动服务 Bash 1 python manage.py runserver 0.0.0.0:8000 新建Django app 新建一个名为dashboard的app Bash 1 python manage.py startapp dashboard 配置url Python 1 2 3 4 5 from django.conf.urls import include, url urlpatterns = [ ... url(r\u0026#39;^dashboard/\u0026#39;, include(\u0026#34;dashboard.urls\u0026#34;)), ] 激活app Python 1 2 3 4 INSTALLED_APPS = ( ... \u0026#39;dashboard\u0026#39;, ) Hello world step 1: 编写视图 Python 1 2 3 from django.http import HttpResponse def index(request): return HttpResponse(\u0026#34;Hello world !!!\u0026#34;) step 2: 配置url Python 1 2 3 4 5 from django.conf.urls import include, url from . import views urlpatterns = [ url(r\u0026#39;^$\u0026#39;, views.index, name=\u0026#39;index\u0026#39;), ] Django 之模板 加载模版\ndjango.template.loader 这个模块提供了两种方法加载模板\nPython 1 get_template(template_name, using=None) 加载指定模板并返回Template对象\nPython 1 select_template(template_name_list, using=None) 它与get_template类似，它尝试每个名称并返回第一个存在的模板\n从文件加载内容\nPython 1 2 3 4 5 from django.template import Context, loader def index(request): t = loader.get_template(\u0026#34;test.html\u0026#34;) context = {\u0026#34;name\u0026#34;: \u0026#34;hello reboot !!!\u0026#34;} return HttpResponse(t.render(context, request)) 快捷方式：render()\nPython 1 2 3 4 from django.shortcuts import render def index(request): context = {\u0026#39;name\u0026#39;: \u0026#34;reboot\u0026#34;} return render(request, \u0026#39;test.html\u0026#39;, context) 二、Django 配置 配置静态文件 Python 1 2 3 4 STATIC_URL = \u0026#39;/static/\u0026#39; STATICFILES_DIRS = ( os.path.join(BASE_DIR, \u0026#34;static\u0026#34;), ) 配置时区 Python 1 TIME_ZONE = ’Asia/Shanghai’ 配置数据库 opsweb/settings.py是一个普通的python模块，每项配置都是一对key/value\n数据库的配置是以dict的形式存放在这个模块中，key名为：DATABASES\nPython 1 2 3 4 5 6 7 8 9 10 DATABASES = { \u0026#39;default\u0026#39;: { \u0026#39;ENGINE\u0026#39;: \u0026#39;django.db.backends.mysql\u0026#39;, \u0026#39;NAME\u0026#39;: \u0026#39;django\u0026#39;, \u0026#39;USER\u0026#39;: \u0026#39;root\u0026#39;, \u0026#39;PASSWORD\u0026#39;: os.environ.get(\u0026#39;MYSQL_PASSWORD\u0026#39;, \u0026#39;\u0026#39;), \u0026#39;HOST\u0026#39;: \u0026#39;127.0.0.1\u0026#39;, \u0026#39;PORT\u0026#39;: 3306, } } 日志配置 待续\u0026hellip;\n三、Django Http相关 对象 Django的对象流程，先获取url，创建wsgi对象，request\na. HttpRequest对象 由django创建\n属性：\nPython 1 2 3 4 5 6 7 8 HttpRequest.scheme HttpRequest.body HttpRequest.path HttpRequest.method HttpRequest.encoding HttpRequest.GET HttpRequest.POST HttpRequest.META 方法：\nPython 1 2 3 4 5 HttpRequest.get_host() HttpRequest.get_port() HttpRequest.get_full_path() HttpRequest.is_secure() HttpRequest.is_ajax() b. HttpResponse对象 传递一个字符串作为页面的内容到 HttpResponse 构造函数\nPython 1 2 3 \u0026gt;\u0026gt;\u0026gt; from django.http import HttpResponse \u0026gt;\u0026gt;\u0026gt; response = HttpResponse(\u0026#34;Here\u0026#39;s the text of the Web page.\u0026#34;) \u0026gt;\u0026gt;\u0026gt; response = HttpResponse(\u0026#34;Text only, please.\u0026#34;, content_type=\u0026#34;text/plain\u0026#34;) 属性：\nPython 1 2 3 4 HttpResponse.content HttpResponse.charset HttpResponse.status_code HttpResponse.reason_phrase 方法：\nPython 1 HttpResponse.__init__(content=”, content_type=None, status=200, reason=None, charset=None) c. JsonResponse 对象 Python 1 JsonResponse(data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs) 示例：\nPython 1 2 3 4 5 \u0026gt;\u0026gt;\u0026gt; from django.http import JsonResponse \u0026gt;\u0026gt;\u0026gt; response = JsonResponse({\u0026#39;foo\u0026#39;: \u0026#39;bar\u0026#39;}) \u0026gt;\u0026gt;\u0026gt; response.content b\u0026#39;{\u0026#34;foo\u0026#34;: \u0026#34;bar\u0026#34;}\u0026#39; \u0026gt;\u0026gt;\u0026gt; response = JsonResponse([1, 2, 3], safe=False) GET与POST请求\nGET请求与传参 POST请求与数据提交 QueryDIct对象 在HttpRequest 对象中，GET 和POST 属性是django.http.QueryDict 的实例，它是一个自定义的类似字典的类，用来处理同一个键带有多个值。这个类的需求来自某些HTML 表单元素传递多个值给同一个键 request.POST 和request.GET 的QueryDict 在一个正常的请求/响应循环中是不可变的。若要获得可变的版本，需要使用.copy()。 实例化QueryDict\nPython 1 QueryDict.__init__(query_string=None, mutable=False, encoding=None) 示例\nPython 1 2 \u0026gt;\u0026gt;\u0026gt; QueryDict(\u0026#39;a=1\u0026amp;a=2\u0026amp;c=3\u0026#39;) \u0026lt;QueryDict: {\u0026#39;a\u0026#39;: [\u0026#39;1\u0026#39;, \u0026#39;2\u0026#39;], \u0026#39;c\u0026#39;: [\u0026#39;3\u0026#39;]}\u0026gt; 通过fromkeys实例化QueryDict\nPython 1 classmethod QueryDict.fromkeys(iterable, value=”, mutable=False, encoding=None) 示例\nPython 1 2 \u0026gt;\u0026gt;\u0026gt; QueryDict.fromkeys([\u0026#39;a\u0026#39;, \u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;], value=\u0026#39;val\u0026#39;) \u0026lt;QueryDict: {\u0026#39;a\u0026#39;: [\u0026#39;val\u0026#39;, \u0026#39;val\u0026#39;], \u0026#39;b\u0026#39;: [\u0026#39;val\u0026#39;]}\u0026gt; QueryDict方法：\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 QueryDict.get(key, default=None) QueryDict.setdefault(key, default=None)[source] QueryDict.update(other_dict) QueryDict.items() QueryDict.values() QueryDict.copy() QueryDict.getlist(key, default=None) QueryDict.setlist(key, list_)[source] QueryDict.appendlist(key, item) QueryDict.setlistdefault(key, default_list=None) QueryDict.lists() QueryDict.pop(key) QueryDict.popitem() QueryDict.dict() QueryDict.urlencode(safe=None) 四、Django用户配置使用 配置django数据库 同步数据 Django API 创建用户 执行用户登录 深入解析用户登录过程 同步数据 使用django的命令行工具同步数据库\nBash 1 python manage.py migrate 创建用户 使用django shell 创建普通用户：创建users最直接的方法是使用create_user()辅助函数\nPython 1 2 \u0026gt;\u0026gt;\u0026gt; from django.contrib.auth.models import User \u0026gt;\u0026gt;\u0026gt; user = User.objects.create_user(\u0026#39;demo_user\u0026#39;, \u0026#39;demo@example.com\u0026#39;, \u0026#39;\u0026lt;set-your-own-password\u0026gt;\u0026#39;) 创建管理员 Bash 1 python manage.py createsuperuser --username=user1 --email=user@example.com 修改密码 Django不会在user模型上存储原始的（明文）密码，而只是一个哈希。因为这个原因，不要尝试直接操作user的password属性。这也是为什么创建一个user时要使用辅助函数。\nPython 1 2 3 4 \u0026gt;\u0026gt;\u0026gt; from django.contrib.auth.models import User \u0026gt;\u0026gt;\u0026gt; u = User.objects.get(username=\u0026#39;user1\u0026#39;) \u0026gt;\u0026gt;\u0026gt; u.set_password(\u0026#39;\u0026lt;set-your-own-password\u0026gt;\u0026#39;) \u0026gt;\u0026gt;\u0026gt; u.save() 用户登陆 执行用户登陆 用户登陆过程详解 ","date":"2020-11-28","permalink":"/posts/django-basics-notes/","title":"Django 基础操作与请求响应对象笔记"},{"categories":["django"],"content":"serializer笔记\n本文前提，你已经学习Django，并了解restful的基本概念\n序列化的定义:\n在数据储存与发送的部分是指将一个对象存储至一个存储介质，例如文件或是存储器缓冲等，或者透过网络发送数据时进行编码的过程，可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这程序被应用在不同应用程序之间发送对象，以及服务器将对象存储到文件或数据库。相反的过程又称为反序列化。(维基百科) 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。(百度百科) 在Python的Django框架中，解释就很明显了。将ORM对象(queryset)等转化为JSON或者XML编码格式的数据。反序列化与之相反。\n同样，为什么需要序列化？\nrestful，请求进来(经过视图层)，获取数据库中需要的数据，然后转为json格式，那样可以给前端，或者其他程序调用。或者请求数据以JSON格式进来，转化为数据库能接收的形式，存到数据库(反序列化)。\nSerializers Serializers 官方快速入门示例\n准备数据库\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from django.db import models from pygments.lexers import get_all_lexers from pygments.styles import get_all_styles LEXERS = [item for item in get_all_lexers() if item[1]] LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS]) STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()]) class Snippet(models.Model): created = models.DateTimeField(auto_now_add=True) title = models.CharField(max_length=100, blank=True, default=\u0026#39;\u0026#39;) code = models.TextField() linenos = models.BooleanField(default=False) language = models.CharField(choices=LANGUAGE_CHOICES, default=\u0026#39;python\u0026#39;, max_length=100) style = models.CharField(choices=STYLE_CHOICES, default=\u0026#39;friendly\u0026#39;, max_length=100) class Meta: ordering = [\u0026#39;created\u0026#39;] 数据库的migrate\nPython 1 2 python manage.py makemigrations snippets python manage.py migrate Creating a Serializer class 创建序列化类\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from rest_framework import serializers from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES class SnippetSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(required=False, allow_blank=True, max_length=100) code = serializers.CharField(style={\u0026#39;base_template\u0026#39;: \u0026#39;textarea.html\u0026#39;}) linenos = serializers.BooleanField(required=False) language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default=\u0026#39;python\u0026#39;) style = serializers.ChoiceField(choices=STYLE_CHOICES, default=\u0026#39;friendly\u0026#39;) def create(self, validated_data): \u0026#34;\u0026#34;\u0026#34; Create and return a new `Snippet` instance, given the validated data. \u0026#34;\u0026#34;\u0026#34; return Snippet.objects.create(**validated_data) def update(self, instance, validated_data): \u0026#34;\u0026#34;\u0026#34; Update and return an existing `Snippet` instance, given the validated data. \u0026#34;\u0026#34;\u0026#34; instance.title = validated_data.get(\u0026#39;title\u0026#39;, instance.title) instance.code = validated_data.get(\u0026#39;code\u0026#39;, instance.code) instance.linenos = validated_data.get(\u0026#39;linenos\u0026#39;, instance.linenos) instance.language = validated_data.get(\u0026#39;language\u0026#39;, instance.language) instance.style = validated_data.get(\u0026#39;style\u0026#39;, instance.style) instance.save() return instance Declaring Serializers 序列化是将Class转换为Python原生的数据类型(例如: dict,list)，下面先创建一个叫Comment的Class，再创建comment对象\nPython 1 2 3 4 5 6 7 8 9 from datetime import datetime class Comment(object): def __init__(self, email, content, created=None): self.email = email self.content = content self.created = created or datetime.now() comment = Comment(email=\u0026#39;user@example.com\u0026#39;, content=\u0026#39;foo bar\u0026#39;) Serializing objects 创建序列化类(序列化器)，继承serializer就可以了，然后写入需要序列化的字段。这里的字段一般直接对应数据库的字段。如果加上属性配置，则可以在反序列化是进行条件约束。\nPython 1 2 3 4 5 6 from rest_framework import serializers class CommentSerializer(serializers.Serializer): email = serializers.EmailField() content = serializers.CharField(max_length=200) created = serializers.DateTimeField() 序列化的参数是一个对象，例如上面生成的comment对象。序列化的类跟Django的Form类用法相似，serializer对象中有很多属性跟Form一样。其中serializer.data中保存的就是序列化后的python普通数据类型，下面的例子中是一个字典。\nPython 1 2 3 serializer = CommentSerializer(comment) serializer.data # {\u0026#39;email\u0026#39;: \u0026#39;user@example.com\u0026#39;, \u0026#39;content\u0026#39;: \u0026#39;foo bar\u0026#39;, \u0026#39;created\u0026#39;: \u0026#39;2016-01-27T15:17:10.375877\u0026#39;} 序列化后的数据放在serializer.data中，此时的结果serializer.data已经被转化为Python原生数据类型，再将数据渲染为JSON\nPython 1 2 3 4 5 from rest_framework.renderers import JSONRenderer json = JSONRenderer().render(serializer.data) json # b\u0026#39;{\u0026#34;email\u0026#34;:\u0026#34;user@example.com\u0026#34;,\u0026#34;content\u0026#34;:\u0026#34;foo bar\u0026#34;,\u0026#34;created\u0026#34;:\u0026#34;2016-01-27T15:17:10.375877\u0026#34;}\u0026#39; 这里，我们已经包得到一个标准的json数据了，然后就是返回请求结果了。这就是一个最简答的序列化过程了。如果需要对应我们的一些特殊需求，restful也提供了一些方法。通过覆盖默认方法来实现我们的需求。\nDeserializing objects 反序列化跟序列化类似，不过一般都是通过网络传输。所以传给我们的数据都是一个二进制流形式的数据(我也不知道这么描述对不对)\nPython 1 2 3 4 5 import io from rest_framework.parsers import JSONParser stream = io.BytesIO(json) data = JSONParser().parse(stream) 这里的data已经是一个python的默认数据格式了(字典)。\n这里的JSONParser暂且叫JSON解析器吧。在看restful源码中，会发现有这个。就是这个道理(废话)。需要将流数据需要处理为python能处理的JSON数据类型。转换为JSON类型就好办了，转为Python默认类型\nPython 1 2 3 4 5 serializer = CommentSerializer(data=data) serializer.is_valid() # True serializer.validated_data # {\u0026#39;content\u0026#39;: \u0026#39;foo bar\u0026#39;, \u0026#39;email\u0026#39;: \u0026#39;user@example.com\u0026#39;, \u0026#39;created\u0026#39;: datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)} Saving instances 这个标题是saving instance，翻译保存实例，其实说白了就是反序列化把数据存入数据库里。如果我们希望能够返回基于验证数据的完整对象实例，我们需要实现其中一个或全部实现.create()和update()方法。例如：\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 class CommentSerializer(serializers.Serializer): email = serializers.EmailField() content = serializers.CharField(max_length=200) created = serializers.DateTimeField() def create(self, validated_data): return Comment(**validated_data) def update(self, instance, validated_data): instance.email = validated_data.get(\u0026#39;email\u0026#39;, instance.email) instance.content = validated_data.get(\u0026#39;content\u0026#39;, instance.content) instance.created = validated_data.get(\u0026#39;created\u0026#39;, instance.created) return instance 涉及两个方法，.create() 和 .update()，这两个方法是表示对应的http方法，post =\u0026gt; create, put =\u0026gt; update，根据传给serializer的参数对象(instance)是否为真做判断。简单讲，就是post方法最后就会走到create方法。create就等于是把数据存入数据库了，前提是数据校验好了。具体的校验看下面Validation\n.create()和.update()方法都是可选的。你可以根据你序列化器类的用例不实现、实现它们之一或都实现。\npytnon 1 comment = serializer.save() serializer已经是反序列化类CommentSerializer生成的对象，调用了save方法。\n在BaseSerializer基类中，说明要定义这两个方法create update，并且save方法说明调用这两种方法的情况\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class BaseSerializer(Field): #... def save(self, **kwargs): #... if self.instance is not None: self.instance = self.update(self.instance, validated_data) assert self.instance is not None, ( \u0026#39;`update()` did not return an object instance.\u0026#39; ) else: self.instance = self.create(validated_data) assert self.instance is not None, ( \u0026#39;`create()` did not return an object instance.\u0026#39; ) #... 你可以通过在调用.save()时添加其他关键字参数来执行此操作。例如：\nPython 1 serializer.save(owner=request.user) 另外一个save强大的功能，例如在我们可能不会创建新的实例，而是发送电子邮件或其他消息。\n在这些情况下，你可以选择直接重写.save()方法，因为那样更可读和有意义。\n例如：\nPython 1 2 3 4 5 6 7 8 class ContactForm(serializers.Serializer): email = serializers.EmailField() message = serializers.CharField() def save(self): email = self.validated_data[\u0026#39;email\u0026#39;] message = self.validated_data[\u0026#39;message\u0026#39;] send_email(from=email, message=message) 在上述情况下，我们现在不得不直接访问serializer的.validated_data属性。为了拿到数据进行操作。\nValidation 做过form表单都知道数据验证，在拿到表单数据后，都会进行数据验证。\n在尝试访问经过验证的数据或保存对象实例之前，总是需要调用 is_valid()。如果发生任何验证错误，.errors 属性将包含表示结果错误消息的字典。例如：\nPython 1 2 3 4 5 serializer = CommentSerializer(data={\u0026#39;email\u0026#39;: \u0026#39;foobar\u0026#39;, \u0026#39;content\u0026#39;: \u0026#39;baz\u0026#39;}) serializer.is_valid() # False serializer.errors # {\u0026#39;email\u0026#39;: [u\u0026#39;Enter a valid e-mail address.\u0026#39;], \u0026#39;created\u0026#39;: [u\u0026#39;This field is required.\u0026#39;]} 当数据验证失败，使用is_valid()结果显示false\n引发无效数据的异常 (Raising an exception on invalid data) .is_valid() 方法使用可选的 raise_exception 标志，如果存在验证错误，将会抛出 serializers.ValidationError 异常。\n这些异常由 REST framework 提供的默认异常处理程序自动处理，默认情况下将返回 HTTP 400 Bad Request 响应。\nPython 1 2 # Return a 400 response if the data was invalid. serializer.is_valid(raise_exception=True) 字段级别验证 (Field-level validation) 您可以通过向您的 Serializer 子类中添加 .validate_\u0026lt;field_name\u0026gt; 方法来指定自定义字段级的验证。这些类似于 Django 表单中的 .clean_\u0026lt;field_name\u0026gt;方法。\n这些方法采用单个参数，即需要验证的字段值。\n您的 validate_\u0026lt;field_name\u0026gt;方法应该返回已验证的值或者抛出 serializers.ValidationError 异常。例如：\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 from rest_framework import serializers class BlogPostSerializer(serializers.Serializer): title = serializers.CharField(max_length=100) content = serializers.CharField() def validate_title(self, value): \u0026#34;\u0026#34;\u0026#34; Check that the blog post is about Django. \u0026#34;\u0026#34;\u0026#34; if \u0026#39;django\u0026#39; not in value.lower(): raise serializers.ValidationError(\u0026#34;Blog post is not about Django\u0026#34;) return value 注意：如果在您的序列化器上声明了 \u0026lt;field_name\u0026gt; 的参数为 required=False，那么如果不包含该字段，则此验证步骤不会发生。\n对象级别验证 (Object-level validation)，对多个字段关联比较 要执行需要访问多个字段的任何其他验证，请添加名为 .validate() 的方法到您的 Serializer 子类中。此方法采用单个参数，该参数是字段值的字典。如果需要，它应该抛出 ValidationError 异常，或者只返回经过验证的值。例如： Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from rest_framework import serializers class EventSerializer(serializers.Serializer): description = serializers.CharField(max_length=100) start = serializers.DateTimeField() finish = serializers.DateTimeField() def validate(self, data): \u0026#34;\u0026#34;\u0026#34; Check that the start is before the stop. \u0026#34;\u0026#34;\u0026#34; if data[\u0026#39;start\u0026#39;] \u0026gt; data[\u0026#39;finish\u0026#39;]: raise serializers.ValidationError(\u0026#34;finish must occur after start\u0026#34;) return data 验证器 (Validators) 需要自定义字段的验证逻辑，(与指定字段验证),例如： Python 1 2 3 4 5 6 7 def multiple_of_ten(value): if value % 10 != 0: raise serializers.ValidationError(\u0026#39;Not a multiple of ten\u0026#39;) class GameRecord(serializers.Serializer): score = IntegerField(validators=[multiple_of_ten]) ... 序列化器类还可以包括应用于完整字段数据集的可重用验证器。 组合字段唯一。将两个关联字段绑定。宾馆，同一天同一个房间只能给一个用户。通过在内部 Meta 类上声明来包含这些验证器，如下所示：\nPython 1 2 3 4 5 6 7 8 9 10 11 class EventSerializer(serializers.Serializer): name = serializers.CharField() room_number = serializers.IntegerField(choices=[101, 102, 103, 201]) date = serializers.DateField() class Meta: # Each room only has one event per day. validators = UniqueTogetherValidator( queryset=Event.objects.all(), fields=[\u0026#39;room_number\u0026#39;, \u0026#39;date\u0026#39;] ) Dealing with multiple objects 序列化多个对象 (Serializing multiple objects) ORM查询后的queryset是多条记录。需要在实例化序列化器时传递 many = True 标志。\nPython 1 2 3 4 5 6 7 8 queryset = Book.objects.all() serializer = BookSerializer(queryset, many=True) serializer.data # [ # {\u0026#39;id\u0026#39;: 0, \u0026#39;title\u0026#39;: \u0026#39;The electric kool-aid acid test\u0026#39;, \u0026#39;author\u0026#39;: \u0026#39;Tom Wolfe\u0026#39;}, # {\u0026#39;id\u0026#39;: 1, \u0026#39;title\u0026#39;: \u0026#39;If this is a man\u0026#39;, \u0026#39;author\u0026#39;: \u0026#39;Primo Levi\u0026#39;}, # {\u0026#39;id\u0026#39;: 2, \u0026#39;title\u0026#39;: \u0026#39;The wind-up bird chronicle\u0026#39;, \u0026#39;author\u0026#39;: \u0026#39;Haruki Murakami\u0026#39;} # ] Including extra context ModelSerializer ModelSerializer 类提供了一个快捷方式，可以自动创建具有与模型字段对应的字段的 Serializer 类。\nModelSerializer 类与常规 Serializer 类相同，不同之处在于：\n它将根据模型自动为您生成一组字段。 它将自动为序列化器生成验证器，例如 unique_together 验证器。 它包含默认简单实现的 .create() 和 .update() 方法。 ModelSerializer 例子如下所示：\nPython 1 2 3 4 class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = (\u0026#39;id\u0026#39;, \u0026#39;account_name\u0026#39;, \u0026#39;users\u0026#39;, \u0026#39;created\u0026#39;) 最下面的 fields 表示指定要包含的字段 (Specifying which fields to include)\nInspecting a ModelSerializer 因为 ModelSerializer 是自动生成的 Serializer ，所以我们不确定生成了哪些字段和验证器。\n可以通过 python manage.py shell 打开 Django shell 的方式，然后导入序列化器类，实例化它，打印出来。\nPython 1 2 3 4 5 6 7 \u0026gt;\u0026gt;\u0026gt; from myapp.serializers import AccountSerializer \u0026gt;\u0026gt;\u0026gt; serializer = AccountSerializer() \u0026gt;\u0026gt;\u0026gt; print(repr(serializer)) AccountSerializer(): id = IntegerField(label=\u0026#39;ID\u0026#39;, read_only=True) name = CharField(allow_blank=True, max_length=100, required=False) owner = PrimaryKeyRelatedField(queryset=User.objects.all()) Specifying which fields to include 除了上面说的方式，可以使用 '__all__' 的方式制定所有字段\nPython 1 2 3 4 class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = \u0026#39;__all__\u0026#39; 反选，排除的方式 exclude\nPython 1 2 3 4 class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account exclude = (\u0026#39;users\u0026#39;,) Specifying nested serialization 当有外键的时候，可以使用下面的depth方式。\nPython 1 2 3 4 5 class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = (\u0026#39;id\u0026#39;, \u0026#39;account_name\u0026#39;, \u0026#39;users\u0026#39;, \u0026#39;created\u0026#39;) depth = 1 这种方式有缺点，就是比较死板。假设你有个字段是通过外键的形式。那指定 depth = 1 则就能显示关联外键字段的具体内容了。要是你外键还有外键。那就 depth = 2。不过也确实看出来这种方式很死板，除非很简单，一般还是不用这种方式。\nSpecifying fields explicitly 可以通过重写字段的方式，将需要修改的字段，重新修改。不用 ModelSerializer 提供的字段方式。就像写 Serializer 的字段。\nPython 1 2 3 4 5 6 class AccountSerializer(serializers.ModelSerializer): url = serializers.CharField(source=\u0026#39;get_absolute_url\u0026#39;, read_only=True) groups = serializers.PrimaryKeyRelatedField(many=True) class Meta: model = Account Specifying read only fields 通过指定 read_only_fields 的方式指定多个字段，而不需要在每个字段里写属性 read_only=True\nPython 1 2 3 4 5 class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = (\u0026#39;id\u0026#39;, \u0026#39;account_name\u0026#39;, \u0026#39;users\u0026#39;, \u0026#39;created\u0026#39;) read_only_fields = (\u0026#39;account_name\u0026#39;,) Additional keyword arguments 额外的属性。例如指定字段的属性，或者报错内容等\nPython 1 2 3 4 5 class CreateUserSerializer(serializers.ModelSerializer): class Meta: model = User fields = (\u0026#39;email\u0026#39;, \u0026#39;username\u0026#39;, \u0026#39;password\u0026#39;) extra_kwargs = {\u0026#39;password\u0026#39;: {\u0026#39;write_only\u0026#39;: True}} BaseSerializer BaseSerializer 是 Serializer 继承的基类。可以很简单的用来替代序列化和反序列化的样式。而 BaseSerializer 继承 Field 类\n该类实现与Serializer类相同的基本API：\n.data - 返回传出的原始数据。 .is_valid() - 反序列化并验证传入的数据。 .validated_data - 返回经过验证后的传入数据。 .errors - 返回验证期间的错误。 .save() - 将验证的数据保留到对象实例中。 它还有可以覆写(重写覆盖)的四种方法，具体取决于你想要序列化类支持的功能：\n.to_representation() - 重写此方法来改变读取操作的序列化结果。 .to_internal_value() - 重写此方法来改变写入操作的序列化结果。 .create() 和 .update() - 重写其中一个或两个来改变保存实例时的动作。\n同样 Serializer或ModelSerializer 这两个也有这些接口可以用来覆盖。\n序列化转为 JSON 前的最后一步 to_representation，看下面的例子： Python 1 2 3 4 5 6 7 8 9 10 11 12 13 class CabinetSerializer(serializers.Serializer): idc = serializers.PrimaryKeyRelatedField(many=False, queryset=Idc.objects.all()) name = serializers.CharField(required=True) def to_representation(self, instance): idc_obj = instance.idc ret = super(CabinetSerializer, self).to_representation(instance) ret[\u0026#34;idc\u0026#34;] = { \u0026#34;id\u0026#34;: idc_obj.id, \u0026#34;name\u0026#34;: idc_obj.name } return ret 反序列化前的第一步to_internal_value，如下例子： Python 1 2 3 4 5 6 7 8 9 10 class CabinetSerializer(serializers.Serializer): idc = serializers.PrimaryKeyRelatedField(many=False, queryset=Idc.objects.all()) name = serializers.CharField(required=True) def to_internal_value(self, data): \u0026#34;\u0026#34;\u0026#34; 反序列化第一步：拿到的是提交过来的原始数据: QueryDict =\u0026gt; request.GET, request.POST \u0026#34;\u0026#34;\u0026#34; print(data) return super(CabinetSerializer, self).to_internal_value(data) 小结 序列化 拿到 queryset 将 queryset 给序列化类 Python 1 2 serializer = IdcSerializer(idc) serializer = IdcSerializer(queryset, many=True) 转JSON Python 1 JSONRenderer().render(serializer.data) 反序列化 Python 1 2 3 4 data = JSONParser().parse(BytesIO(content)) serializer = IdcSerializer(data=data) serializer.is_valid() serializer.save() 与之相对应的对应的view视图伪代码\nPython 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class something_list(APIView): def get(self, request, *args, **kwargs): \u0026#34;\u0026#34;\u0026#34;返回所有数据\u0026#34;\u0026#34;\u0026#34; pass def post(self, request, *args, **kwargs): \u0026#34;\u0026#34;\u0026#34;创建一条数据\u0026#34;\u0026#34;\u0026#34; pass class something_detail(APIView): def get(self, request, *args, **kwargs): \u0026#34;\u0026#34;\u0026#34;返回指定数据\u0026#34;\u0026#34;\u0026#34; pass def put(self, request, *args, **kwargs): \u0026#34;\u0026#34;\u0026#34;修改指定记录\u0026#34;\u0026#34;\u0026#34; pass def delete(self, request, *args, **kwargs): \u0026#34;\u0026#34;\u0026#34;删除这条记录\u0026#34;\u0026#34;\u0026#34; pass Restful 源码解析流程链接\n","date":"2020-11-28","permalink":"/posts/drf-serializer-notes/","title":"Django REST framework Serializer 学习笔记"}]