Kang's BlogThe personal space used by Kang to notehttps://blog.kaiyikang.com/A brief introduction to LLM and RAGhttps://blog.kaiyikang.com/posts/2024/a-brief-introduction-to-llm-and-rag/https://blog.kaiyikang.com/posts/2024/a-brief-introduction-to-llm-and-rag/Fri, 26 Jan 2024 00:00:00 GMT<h2>1. Abbreviation Explanation</h2>
<p>Here are the abbreviations that will be mentioned afterwards, you don't need to know the exact meaning, just what it probably expresses:</p>
<ul>
<li>LLM: Large Language Model</li>
<li>RAG: Retrieval Augmented Generation</li>
<li>Mate: An application that can query Confluence or Bitbucket</li>
</ul>
<h2>2. Metaphor of LLM and RAG</h2>
<p>We have all taken important exams. To prepare, students must study the textbook, attend lectures, and research any questions they have. After reviewing the material, it is time to take the exam. Exams come in two types: <strong>closed-book</strong> and <strong>open-book</strong>.</p>
<p>During closed book exams, students must read the questions and conditional hypotheses, search their memory for relevant knowledge, and then write their answers in the space provided at the back. In contrast, during open book exams, students are allowed to refer to their books or notes, and write down the relevant information and what they have memorised.</p>
<p>The student is an LLM and learning knowledge means pre-training of the model. As professors or examiners, we ask the LLM questions and expect the answers. <strong>Closed book</strong> means that the answer does not rely on any external knowledge, but rather than on the parameters stored in the brain. On the other hand, RAG is an <strong>open-book exam</strong>, where the student LLM acquires knowledge related to the question through a vector query before answering.</p>
<p>All processes related to data training or fine-tuning fall under the umbrella of LLM. This includes vector queries, vector database extraction, prompt construction, and more. RAG, on the other hand, is more engineering-related. The core of LLM is wrapped within the big concept of RAG.</p>
<h2>3. LLM</h2>
<p>For a clear and comprehensive explanation of LLM, I highly recommend watching the video <a href="https://www.youtube.com/watch?v=zjkBMFhNj_g&t=1s">(1hr Talk) Intro to Large Language Models</a>. The video covers all the essential aspects of the topic and is the best resource I have come across.</p>
<h3>3.1 Understanding of Transformer</h3>
<p>The paper 'Attention is all you need' introduced the Transformer model, which sparked the GPT boom. You can find the paper at this link: <a href="https://arxiv.org/abs/1706.03762">Attention Is All You Need</a>.</p>
<p>To comprehend the Transformer, I recommend perusing this straightforward explanation titled "<a href="http://jalammar.github.io/illustrated-transformer/">The Illustrated Transformer</a> " rather than paper. The blog post presents a clear and concise overview of how the Transformer operates.</p>
<p>It is important to note that LLM and Transformer are not equivalent, despite the common assumption due to the success and popularity of the latter. Furthermore, while GPT is a variant of the decoder component of Transformer, it is not an exact equivalent; for simplicity, it could be thought of as <code>Transformer ≈ GPT ≈ LLM</code>.</p>
<p>To enhance understanding of GPT, watch this Youtube video by Andrej Karpathy <a href="https://www.youtube.com/watch?v=kCc8FmEb1nY">Let's build GPT: from scratch, in code, spelled out</a> and check out the corresponding open source code <a href="https://github.com/karpathy/minGPT">minGPT</a> on GitHub. In the video, the author takes a code perspective as he builds a GPT model from scratch.</p>
<h3>3.2 Understanding of LLama</h3>
<p>OpenAI is the publisher of ChatGPT. However, despite its name, the model is not open source. Therefore, the open-source community has put in a lot of effort, particularly with the Llama model, which has been trained and open-sourced by Meta. The Llama model comes in sizes 7B, 13B, and 70B.</p>
<p>Meta has made almost everything related to Llama-Series open source:</p>
<ul>
<li><a href="https://ai.meta.com/resources/models-and-libraries/llama-downloads/">Pre-trained model</a></li>
<li><a href="https://github.com/facebookresearch/llama">Model structure</a></li>
<li><a href="https://ai.meta.com/llama/get-started/">Detailed tutorials on use and fine-tuning</a></li>
</ul>
<p>Because Llama is open source, the community has fine-tuned and structured many models based on it. One of the most famous is the series of models from Mistral (France), according to their <a href="https://mistral.ai/news/mixtral-of-experts/">official website</a>, the performance of the <code>Mixtral 8x7B</code> is similar to that of the GPT-3.5.</p>
<p>Most open source models are available on Huggingface with model parameters and free downloads. Meanwhile, Huggingface provides a <a href="https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard">leaderboard</a> to test the performance of these models. However, it is important to note that public lists may not be entirely trustworthy due to <a href="https://twitter.com/bindureddy/status/1750327486460908006">the contamination issue</a> . For a more persuasive list, please refer to the <a href="https://huggingface.co/spaces/lmsys/chatbot-arena-leaderboard">🏆 LMSYS Chatbot Arena Leaderboard</a>, which is voted on by humans.</p>
<h3>3.2 Usage of Model</h3>
<p>Although training the model requires significant GPU resources, expensive hardware is not necessary to load and use it. The model can also be run directly from a MacBook.</p>
<p>To run Llama2, for example, we can download the model files based on Huggingface: <a href="https://huggingface.co/meta-llama/Llama-2-7b-chat-hf/tree/main">Download</a>, and use the <a href="https://huggingface.co/docs/transformers/index">Transformers</a> library to load and run the model directly. However, both loading and running the model can be very slow, as confirmed by actual testing. Part of the reason is that the default parameter precision of Huggingface is set to <code>float32</code> (as shown in the <a href="https://huggingface.co/docs/transformers/main_classes/model">Description</a> ) and it cannot utilize the GPU on the MacBook.</p>
<p>To speed up model loading and usage, the open-source community has developed tools such as <a href="https://github.com/ggerganov/llama.cpp">llamacpp</a> , <a href="https://github.com/ollama/ollama">Ollama</a> or <a href="https://github.com/vllm-project/vllm">vllm</a>. There tools are written in CPP and can read models in quantized gguf format (<a href="https://huggingface.co/TheBloke">more quantized models can be found here</a>). This format is fast and efficient, and also takes advantage of GPU performance. The backend core service can be invoked locally using Bash commands or APIs. The return format is in OpenAI-specific JSON format, which is designed to facilitate easy connectivity to different front-ends.</p>
<p>The tools mentioned above are only suitable for loading and using specific model types. Additionally, Apple has released a tool called <a href="https://github.com/ml-explore/mlx">mlx</a> for Apple Silicon memory, which effectively utilises the unified memory within the M-series chip.</p>
<h3>3.3 Fine-tuning</h3>
<p>As previously mentioned, fine-tuning is only applicable to models. By providing the model with specific data for training, the model acquires the corresponding knowledge and style of expression. This approach also addresses the issue of the model's limited token acceptance.</p>
<p>Although fine-tuning with small data is effective, it can still be costly in terms of GPU resources. I attempted to fine-tune the Llama2-7b model using Google Colab, which is more affordable, but due to hardware limitations, the trained model cannot be exported for use in a production environment. Please refer to the following steps for the experiment: <a href="https://mlabonne.github.io/blog/posts/Fine_Tune_Your_Own_Llama_2_Model_in_a_Colab_Notebook.html">Fine-Tune Your Own Llama 2 Model in a Colab Notebook</a>. The article <a href="https://huyenchip.com/2023/04/11/llm-engineering.html">Building LLM applications for production</a> also compares the difference between Prompt and Fine-tuning.</p>
<p>Fine-tuning Mate at this stage is challenging due to cost and data security constraints.</p>
<h2>4. RAG</h2>
<p>RAG stands for</p>
<ul>
<li>Extract Information</li>
<li>Augment Information</li>
<li>Generate Results.</li>
</ul>
<p>It is like an open-book exam where we provide relevant information to the LLM so that it can give less illusory answers. RAG is an abstract conceptual framework and does not imply a concrete implementation. Therefore, we can choose any module we need, depending on the requirements. It is a very efficient way to do this, even if <a href="https://www.youtube.com/watch?v=ahnGLM-RC1Y">OpenAI has a very positive opinion about it</a>.</p>
<p>The LLM can provide more accurate answers when given relevant information, similar to an open-book exam. RAG is a conceptual framework and does not require a specific implementation. Modules can be chosen based on specific requirements. OpenAI has <a href="https://www.youtube.com/watch?v=ahnGLM-RC1Y">expressed a positive opinion</a> about this efficient approach.</p>
<p>A simple architecture diagram is shown below, which comes from the RAG open source framework LlamaIndex, which is also used as the application framework by Mate.</p>
<p><img src="https://docs.llamaindex.ai/en/v0.9.48/_images/basic_rag.png" alt="" /></p>
<p>To understand the RAG concepts, it is recommended to read the original article, which provides a detailed explanation of the diagram and the concepts. The article can be found at <a href="https://docs.llamaindex.ai/en/stable/getting_started/concepts.html">here</a>.</p>
<p>The diagram below is detailed and clearer than the flowchart above. It comes from the Copilot group on Github, who also share their experience in making LLM apps in their <a href="https://github.blog/2023-10-30-the-architecture-of-todays-llm-applications/">blog</a>:</p>
<p><img src="https://github.blog/wp-content/uploads/2023/10/LLMapparchitecturediagram.png?resize=4088%2C2148?w=2048" alt="" /></p>
<h3>4.1 RAG's journey</h3>
<p>The journey begins with a user's question or query.</p>
<p>The user inputs a question/query into the GUI interface of the front-end (MS Teams for Mate). The back-end (Python code deployed in a VM) receives the query and converts it into a vector using an embedding model. For more information on embedding models, please refer to this article: <a href="https://huggingface.co/blog/getting-started-with-embeddings">https://huggingface.co/blog/getting-started-with-embeddings</a>.</p>
<p>The query vector is then inputted into a pre-prepared vector database, which retrieves the most relevant chunks. Various vector databases are available, and this article on <a href="https://docs.llamaindex.ai/en/latest/module_guides/storing/vector_stores.html#">Vector Stores</a> compares their performance. Compared to a standard database, this type of database can store extremely long arrays of vectors. It also supports various algorithms for comparing similarities between vectors, such as calculating a cosine value to extract the text that is most similar to a query. This process is known as <strong>Retrieval</strong>.</p>
<p>After extracting the relevant chunks from the database, it has to undergo additional processes, such as filtering and reordering. This step corresponds to the <strong>Augmented</strong> stage in RAG.</p>
<p>We now have the user's query in one hand and the relevant chunks from the database in the other. Next, we will input them into the prompt template so that the LLM can detect them correctly.</p>
<p>It is important to note that different LLMs use various system prompts for training, for example, the Llama2 model uses <code><s>[INST] <<SYS>></code> (<a href="https://huggingface.co/blog/llama2">reference</a>), while the dolphin model uses <code>< system> <human></code> (<a href="https://huggingface.co/TheBloke/MPT-30B-Dolphin-v2-GGML">ref</a>). The system prompts greatly affect the results.</p>
<p>Finally, the LLM processes all the prompts and returns a response to the user.</p>
<h2>5. About LLMOps</h2>
<p>According to <a href="https://www.databricks.com/glossary/llmops">databricks</a>, LLMOps primarily focuses on improving and deploying models to enhance the value of the core LLM for the product. For instance, a better LLM in the case of RAG enables the output to extract information from chunks in a more logical and precise way.</p>
<p>LLMOps is appealing to both medium and large teams or companies due to their possession of ample private data and financial resources. They aim to enhance the effectiveness of their products through a stable and efficient process.</p>
<p>In contrast, LLMOps may not be as attractive to smaller teams. The reason for this is that the open source community has provided models with sufficient performance, while more and more important work has focused on processing the input information, e.g. how to get the chunks correctly, how to generate useful meta-information, or how to use better prompts.</p>
<p>For a project that is progressively growing from small to large, it is necessary to first improve performance outside of LLM. If a bottleneck in system performance is reached, such as LLM not providing correct feedback, generating hallucinations, or requiring subtle optimizations such as changes in LLM's speech tone, it is necessary to address these issues in order to build an efficient LLMOps.</p>
<h2>6. Summary</h2>
<p>This short article introduces the development and use of LLM, showing the structure of RAG and how it relates to LLM. The length of the article is limited, for more detailed information please refer to the videos and links in the article.</p>
Dahab - 颠簸的峡谷https://blog.kaiyikang.com/posts/2025/dahab_3%E9%A2%A0%E7%B0%B8%E7%9A%84%E5%B3%A1%E8%B0%B7/https://blog.kaiyikang.com/posts/2025/dahab_3%E9%A2%A0%E7%B0%B8%E7%9A%84%E5%B3%A1%E8%B0%B7/Sat, 12 Apr 2025 00:00:00 GMT<p>我们在 Dahab 的行程是分开的,她安排了自由潜水的训练和考核,而我没有那么硬核,只报名了潜水的初级入门课,一天就能结束,因此错开的时间我可以自由探索,随意安排。</p>
<p>抵达的第二天,她早早开始了训练,我则租了辆自行车,在镇上闲逛。到了晚上,大家聚在一起交流白天的心得经验时,她见我说不出什么奇异精彩,便强烈推荐我去峡谷一日游。一不做二不休,我立刻在 hostel 群里招呼起来,管理员 Z 君反应迅速,马上联系了房主 M。房主 M 在这里朋友众多,几乎所有项目都有特别的门路,不出几个小时,我们就收到了价格和行程安排。我心想第二天也没什么特别安排,就欣然报名。住在其他房间的纪录片创作者 A 君看到群里的消息,临时起意,也决定一同前往。</p>
<p>在 Dahab,许多活动或一日游大多围绕周边展开,车程一到两个小时,距离不算远,但由于语言不通且社会环境不同,自己安排几乎不可能,因此让旅行社安排是最好的选择。市中心海岸边有许多旅行社,即便你有意忽略,蹲在门口的小哥也会极其主动地靠近你,递上传单。走一路,问一路,竞争之激烈可想而知。</p>
<p>游玩的活动规格虽有差异,但内容大同小异,因此和旅行社砍出一个好价格才是关键。我们有幸直接省略了这一步,毕竟每次都能让 hostel 帮忙联系,获得最合适的价格。每次想到这里,我都会感慨,幸亏当初选择了正确的住处,而不是破旧的度假酒店,否则真是亏大了。</p>
<p>第二天一早,外头刮起了猛烈的东北风,虽不利于浮潜,却丝毫不妨碍我们去峡谷徒步。清晨,我们早早收拾好行李,坐在客厅里等待出发。时间一到,Z 君就匆匆走来通知车已到。我们于是起身,踏上了今天的行程。</p>
<p>车是一辆改装的大面包车,后排座椅全部拆除,靠近窗户的位置安装了两排坐凳。没有固定座位,也没有安全带,我觉得自己像货物一样,被搬上了后备厢。车子出发了,在城内穿梭,为了接载后续的旅行者。</p>
<p>车门第一次开启时,一位女性上了车。她穿着现代且略显浮夸,进了车厢就大声用英文与我们搭话,说自己来自开罗,目的是来这里旅行。清晨时分,我精神不足,便轻声应和,敷衍了事。车开了一会儿,又有一位女性走来,热情地与刚上车的那位打招呼,看来她们是结伴而行的好友。只见她手里端着纸壳,里面盛着两杯冰咖啡。咖啡在塑料杯中随着颠簸晃动,仿佛随时会洒出来。看着她们张扬浮夸且不拘小节的性格,冥冥之中,我预感会有些麻烦事发生。</p>
<p>正当我盯着那快要洒出的咖啡思考时,她踩着车边缘上车,不巧没踩稳,手中的杯子脱手,咖啡正应验了我的感召,还真就洒了出来。靠近车尾的地方全是咖啡。见状,她们连忙拿出纸巾开始擦拭,还用阿拉伯语彼此念叨着什么。司机和导游似乎没多问,跑到旁边树下的阴凉处聊天,时不时回头望向车内,像是在等她们解决问题。咖啡没溅到我这边,却溅到了对面法国小哥的裤腿上。她们连忙道歉,他少言寡语,安静地说没事。虽嘴上如此,我却从他的表情中看出一丝疲倦和无奈。</p>
<p>咖啡擦拭干净后,我们终于踏上路途,开始今天的行程。人的性格总包含各种特殊属性,人们因相似性格结为朋友,而某种特定性格的人,做事风格也往往统一且和谐。咖啡事件刚平息,车刚驶出镇子,那位女性就拿出随身携带的 JBL 小音箱,大声询问谁想听歌。见无人回应,她便自顾自地播放起带有现代韵味的阿拉伯歌曲。车尾的小曲没放几首,开车的小哥似乎察觉了什么,右手一边切换档位,一边用手指在操作盘上戳戳点点,前排忽然传来悠扬的古典阿拉伯歌曲。这两个人仿佛走路一瘸一拐,完全没有任何默契和协调。我们夹在中间,像肉片被夹在面包汉堡里,精神受到了折磨和历练。不知过了多久,前后的音乐最终逐渐消失,我们紧绷的神经也慢慢舒缓下来。</p>
<p>旅途中,另一件关于音乐的奇事让 A 君心烦意乱。车行驶在荒漠途中,我听到音乐不断切换,每隔几秒就换一首歌。起初我以为是车载音响的技术故障,因此没太在意。过了一阵,A 君私下告诉我,这全是司机的缘故。他指向司机的位置,让我看他的右手。只见司机的右手不停点击按钮,每点一下,音乐就切换下一首。我恍然大悟,原来一切都是司机在暗中操作。A 君被声音烦扰,不停用英文提醒司机和导游,试图让他们安静下来。我没听清他们对话的细节,只见司机嘴里不断咀嚼着阿拉伯语,手却一刻不停地操作。A 君与对方沟通无果,只好转头向我诉苦。我没有被音乐折磨,只把它当作一场人类特殊行为的奇观。本着好奇心和观察研究的态度,我更愿意理解为司机像个严格的 DJ,有着自己的特殊品味和准则。他严苛到能在音乐开始前几秒内判断该曲是否合耳,结果几乎所有音乐都被他切掉,而唯一保留的一两首,从我的角度看,也没什么特别动听之处。我安慰 A 君,司机有自己的追求和想法,只需平和心态,顺从罢了。</p>
<p>最后一件关于音乐的小插曲发生在走出峡谷时,我们遇到一片辽阔的荒漠。我走在前面,后面同行的一对旅伴带着一个小音箱。正当我欣赏沙漠风光时,身后传来磅礴的曲调,咚咚咚,像什么影视剧的原声。等到进入了主旋律,响起一段女性唱段「哈——啊——诶——」,我这才恍然大悟,原来是《Dune》的主题曲。我有些想笑,又有些无奈。这个曲目选择倒很符合氛围,但为何明明身处荒郊野外,却依然无法彻底摆脱人工制品的影响,可见它对我们的影响已深入骨髓。</p>
<p>离开小镇大约一小时左右,只见车缓缓离开主路,驶入荒漠中。手机收不到信号,地图软件中也找不到方位,文明生活慢慢沉睡,自然与野性的另外一面逐渐苏醒过来。抵达目的地,我们纷纷下了车。导游用简单地英文告诉我们大致行程:下到峡谷,就是一条单程路,包放在车里,手里拿上水,一直往前走即可。</p>
<p><img src="assets/2025/L1090680.JPG" alt="" /></p>
<p>下峡谷的路算不上困难,但的确陡峭。观光的游客众多,岩石被踩出了非常明显的落脚点。一条缆绳垂到谷底,手里紧握绳子,左一步,右一步,我们听从导游的指示,很快就下到谷底。巨大的白色岩壁围绕在身体周围,向上延伸,遮盖了部分天空和阳光。没有了阳光的照射,我忽然感受到一丝凉意。奶白色的岩石圆滚滚的,表面带有磨砂的质感,手指轻轻摸上去,很容易就能从岩石上带下白色的细沙,感觉有些不太牢固。而由于长时间的积淀,不同的岩石层层叠加,不同的颜色和质地一条一条的。流线型的形态,像是不同的丝线在墙壁上飘忽游走。身处其中像是进入了哈哈镜迷宫,惊喜到处都有,一点都不无聊。</p>
<p>我们以不一致的步调持续向前走。阳光被岩壁遮盖,洒下斑斑驳驳的光影,中间的沙地和稀疏植物被阳光晒得发亮。仔细观察,还能看到时不时有甲虫路过。这些少见的活物,仿佛行走在一幅画框中,让人感觉像是在观赏一部高保真的沙漠纪录片。</p>
<p><img src="assets/2025/L1090809.JPG" alt="" /></p>
<p>峡谷的道路并非一帆风顺,有时会遇到岔路口,我们需要在原地等待导游的提示才能继续前行。导游是一位贝都因人,头上绑着红色的头巾,身着 T 恤衫和布裤,脚下踏着一双简单的凉鞋。他没带任何特殊装备,最多就是手里拿了一瓶水。他非常尽责,不时沿着队伍来回跑动,确保没有人掉队或迷路。</p>
<p><img src="assets/2025/L1090837.JPG" alt="" /></p>
<p>这位导游皮肤黝黑,脸上因岁月流逝而出现的沟壑宛如岩壁纹理,既古老又充满智慧,仿佛与这片土地融为一体。我从远处看到 A 君和他攀谈,之后 A 君兴奋地向我讲述了导游传递出的智慧。我感觉一切都很和谐自然,或许正是因为他一直生活在这片充满巨大能量的土地上,才能拥有如此朴素且充满力量的智慧。</p>
<p><img src="assets/2025/L1090817.JPG" alt="" /></p>
<p>峡谷的终点是一片绿洲,那里充满着当地人生活的痕迹。导游带领我们进入一个棚子下休息。棚子的顶棚由棕榈叶搭成,下面铺满了阿拉伯地毯和坐垫,但因时间过去太久,使用的人太多,毯子早已灰乱破旧不堪。光线透过稀疏的棕榈叶照到地面,随着风缓慢摇曳,一切如梦似幻。棚子的旁边有个篝火坑,另外一位贝都因人蹲在那里帮我们煮茶,只见他缓慢地抬起煮开水的茶壶,异常平稳地将茶水倒进一个个水杯中。</p>
<p><img src="assets/2025/L1090938.JPG" alt="" /></p>
<p>我们接过茶杯,连忙向他表示感谢。红茶十分美味。我一边啜饮茶水,一边坐在棚子的角落与 A 君闲聊。因为之前已经与他有过接触,加之身处这片奇幻的地方,我稍微放开了些表达的内容。用非母语表达深刻的事情对我一直是个挑战,可以说我从未有过类似经历,用外文表达自己所遵循的生活哲学和洞察。每次到达这个十字路口,我都会原路返回,或闭口聆听别人发表看法,或找个理由起身离开。能力在逐渐萎缩,但想法始终存在并悸动。我很感激他尝试倾听并追随我模糊不清的想法,并以客气的方式表示理解。我觉得自己像一位刚学乒乓球的菜鸟,努力尝试与对方打个来回。我能感受到,他在用更强的能力维持球的流动。我十分感谢他的努力,并会一直记住那个奇妙的时刻。愿我未来有足够的能力应对这种挑战。</p>
<p><img src="assets/2025/L1090942.JPG" alt="" /></p>
<p>午餐是导游提前准备好的,虽然看起来非常简单,都是些素菜,但闻起来很香,味道非常自然,吃完后也感到十分舒服。吃喝结束,稍作休息后,我们又踏上了行程,前往彩色峡谷。</p>
<p>本以为车辆会一直行驶在平直的柏油马路上,没想到司机一打方向盘,直接开进了荒漠。还没等我反应过来,身体已经随着路面开始颠簸。我们乘坐的是一辆朴素的面包车,没有任何减震等高级功能,脚下就是一块大铁盘,四个轮胎遇到任何颠簸,都会被放大,传递到身体上。</p>
<p>一辆辆车行驶在坚硬的石头路上,压出了相互交织的车辙印。虽然司机努力控制车辆沿着既有轨迹驾驶,但难免出界,于是我们被迫穿梭在不同的时光之中,努力寻找此刻的印记。每一次穿梭,都是一次颠簸。车辆刚开始颠簸时,我还能感受到刺激,想着这一趟玩得很值,竟然还附带了过山车项目。颠簸持续了很久,且异常激烈,以至于我只能将全部心思放在防止自己飞出去上。我的肌肉仿佛消失了,只剩下一堆坚硬的骨骼在来回碰撞。</p>
<p>我无心再和坐在旁边的 A 君分享感受。为缓解晕车,我只好向窗外看去,被迫接受着路面和司机硬塞给我的抖动画面。车还在疾驰,同行的另外一辆车也从侧面飞了过来。我盯着另外茫茫大荒漠上的吉普车,仍觉得自己还沉浸在荒野纪录片中。</p>
<p><img src="assets/2025/L1090961.JPG" alt="" /></p>
<p>霎时间,荒漠成了大海。颠簸的路面是大风煽动的海浪,而砾石则是被激起的浪花。我们就像是坐在一艘小船上,行驶在苍茫无人的大海中央。任凭地势的引领,把我们带向神秘的彼岸。</p>
<p>到达彼岸的路途并非一帆风顺。当车要翻越沙丘时,由于角度过大,无论怎样调整,车轮都无法获得足够的抓力。司机尝试了多次仍未成功,就跳下车,开始调试轮胎。在烈日炙烤下,他身上的白袍刺眼耀眼。其实我并不清楚他具体在做什么,只听见呲呲的声音,也许是在放气。他蹲在那里捣鼓了几分钟,随后重新发动了汽车。车轮在松软的沙丘上呈 S 形,像一条蛇般上下窜动。开了一会儿,终于翻过了沙丘。</p>
<p>这里的司机总会遇到各种各样的问题,除了环境的影响,还有车辆自身的故障。我女友也曾遇到过一次类似的情况。那次她乘坐出租车前往城镇的北边进行泳池训练,途中车辆突然熄火。她们正商量是否换车时,司机毫不犹豫地跑到车前开始捣鼓。据她描述,司机先用两根线反复接触测试,未见反应。随后不知从哪里掏出一段导线,用留出的长指甲剥开线的塑胶外皮,再将亮晶晶的铜线缠绕在旧线上。司机一言不发,不知具体操作了什么,只知道折腾了一会儿,随着引擎轰鸣,车竟然重新启动了。</p>
<p>一望无际的荒漠和公路,逼得你不得不相信并依赖司机,即使他们看上去多么不可靠。他们总会遇到各种问题,也总以粗犷而原始的方式解决。我们习惯青睐可靠的事物和人,对不确定性心存退避,可这些司机似乎总在不确定性的边缘徘徊。我们与他们之间仿佛绑着一根绳子,当他们安全时,我们松了口气;当他们险些坠落悬崖时,我们也会心惊胆战,不知未来将如何。只有安全抵达目的地,关上车门的那一刻,我们悬着的心才终于落下。</p>
<p>在车辆的疾驰下,我们抵达了彩色峡谷。</p>
<p>相比白色峡谷的纯白,我更愿称彩色峡谷为红色峡谷。山谷的岩壁仿佛被染上了色彩,展现出比白色峡谷更为丰富的质感。我承认它的美丽,但上午的行程和车上的颠簸让我异常疲惫,难以静心欣赏,只想着如何能尽快穿越,抵达终点。</p>
<p><img src="assets/2025/L1090973.JPG" alt="" /></p>
<p>即使如此匆忙,岩壁的形状依然给我留下了极为深刻的印象和丰富的想象。由于风化作用,顶部的岩壁出现了许多空隙。随着时间推移,这些空隙被撕扯、扩大,内部形成了完整的通道,外部只剩下瘦弱的石柱支撑。拖着疲惫的身体,我抬头望去,竟觉得这些石柱和通道极像庙宇的走廊。迷你的僧人寄宿在石块之间,在天顶的石柱走廊中穿梭,传递精神,吟诵诗歌,仿佛试图将我召唤到另一个神秘的领域。我怔了怔,稍作停顿,才回过神来,继续向前走。</p>
<p>我们抵达终点,坐回车里。先前喧闹的游客也不再折腾手中的音箱,唯有司机仍不停地点按切歌按钮,但我们已累得无暇理会。</p>
<p>车辆从土路重新驶入柏油路,行驶得安静如同一张松软的床垫。我靠在车窗旁,背脊虽被迫挺直,却还是坐着睡着了。我没有做任何梦,也许梦境遗落在了峡谷里。等醒来时,就已经回到了镇子上。</p>
<p><img src="assets/2025/L1090985.JPG" alt="" /></p>
Dahab - 抵达凌晨的山峦https://blog.kaiyikang.com/posts/2025/dahab_1%E6%8A%B5%E8%BE%BE%E5%87%8C%E6%99%A8%E7%9A%84%E5%B1%B1%E5%B3%A6/https://blog.kaiyikang.com/posts/2025/dahab_1%E6%8A%B5%E8%BE%BE%E5%87%8C%E6%99%A8%E7%9A%84%E5%B1%B1%E5%B3%A6/Tue, 08 Apr 2025 00:00:00 GMT<p>凌晨 4 点下了飞机,经历了一整夜的飞行,我们已经身心俱疲。机场不大,设施看上去很陈旧。欢迎入口处,代表埃及的图腾象征性地堆在一起,旁边零散的矗立着假椰子树,配合分辨率不高的打印背景墙,整体塑料感十足。我们饶有兴致的撇了一眼,便匆匆随着人流走到边检,拿到行李并换了些钱,没走几步路就出了机场大门。</p>
<p>起初我们十分担心,凌晨拖着疲惫的身子到达一个全然陌生的地方,如果没有预先安排好,会不会衍生出很多问题。为防止出错,我还特别向联络人详细问过「有什么步骤?需要什么信息?」等问题,结果对方却十分豪爽的说,什么都不需要,一切都安排好了,你们来就是了。虽然仍战战兢兢,但我姑且妥协了。</p>
<p>事实上,的确是我们想的太多。出了机场大门,尽管时间已经是凌晨,但广场上灯火通明,两旁站满了人。我从左到右快速扫视一圈,立刻就发现了负责接送的司机。他手中攥着一张薄纸,上面书写「Dahab Kang」。虽然字迹十分脆弱,但非常容易分辨。</p>
<p>这是他们处理事情的风格,原始且直接。有时我会深刻体到,现代城市生活对我行事风格太深,但凡做任何事情,都需考虑并遵循来自外界的程序以及限制,同时我也并不清楚这些程序的意义和作用是什么,由于太过模糊,以至于对程序错误所造成的结果有莫名其妙的担心。你也许明白,当想要在城市中处理什么事情,如果缺少了一些特定文件,你就需要来回跑来跑去,搭出很多时间和精力。相互一对比,更凸显出他们处理问题的方式有多么直接,这几乎贯穿了当地生活的方方面面。我忽然自惭形秽,感到有太多有的没的事情侵占了我生命的时间。</p>
<p>我们和他友好地打了招呼。他没有太多的寒暄,就领着我们穿过人群,走向停车场。路不平整,坑坑洼洼的,路旁的马路牙子边还有裸露的砂石。路灯时好时坏,让光线变得支离破碎。</p>
<p>司机走到一辆小车前,示意这就是我们要坐的车。坐进车内,古朴的装潢让我想到了北京 90 年代的出租车。前挡风玻璃下铺着红色绒毯,上面点缀着黄色碎花。车上很多部件都铺着透明塑料纸,也不知道是从未拆封,还是说为防灰尘而特地罩上去的。我坐在后排左侧,白色胶带缠着的车门扶手令我印象深刻,以至于我下次乘车是靠它才辨认出这是同一辆车。后挡风玻璃盖着一层黑色塑料网,我想作用应该和前面的红毯差不多,都是为了当车辆在沙漠太阳中暴晒时,尽量给车内降降温。</p>
<p>车开了,嘟嘟嘟的发动机显然说不上太健康。随着出租车缓缓驶出,从玻璃窗中放眼望去,几乎没有什么好车停在停车场上。好车,指的并不是牌子很高级的车,而是从外观看上去健全的车。正如路面和陈旧的设施一般,这里的车大多都十分破旧。我不清楚这些车都经历了什么大风大浪,表面的漆没有一块是完整的,金属的梁柱也变形,保险杠也多一块少一块,大灯能亮全就算是很不错了。</p>
<p>这里大部分车都被打回了原形——一个带有四个轮子的载人工具。工具这个词描述的恰如其分,车辆不再具备特别的身份属性,豪华的配置和功能在荒漠中也显得十分苍白无力,倒是那四个轮子以及带有阴凉的空间,才是当地人们最需要的东西。</p>
<p><img src="assets/2025/IMG_3219.JPG" alt="" />
<em>较为完整的出租车</em></p>
<p>于是,我们看到了皮实且耐用的日产车满大街的跑来跑去。标记着 ISUZU 品牌的小型皮卡是行走在荒漠和城镇之间的神器,拉人拉货都堪称完美,在当地随处可见。空间更大一些的小型面包车,后面常被改装成纵向长椅,能够装下八个人,非常适合做小型的 road trip。比这个再好一些的车我们也乘坐过,外观功能完整,还带有舒适的空调和座椅,但坐惯了上面说的「破车」,再坐好车甚至还有些不适应。走在路上,但凡看到一些所谓高端的好牌子,我们都会调侃,这种车可太娇嫩了,真不适合在这种恶劣的环境里开。</p>
<p>每每看见这些车的时候,我总会想起「话糙理不糙」这句话。「话」是表面功夫,而「理」则是背后的概念和哲学。生活在这里的人们一直在诠释并贯彻这句话的意涵,虽然车辆表面十分粗糙,但其隐含的功能却完全够人们使用了。除此之外的任何冗余,最终都会被大海和荒漠彻底打磨直至消失。</p>
<p>在机场周围还有些零散的路灯,等出了机场向北,灯就完全不见了。路上几乎没有其它车,柏油路上,隔开车流的线完全失去了作用。路的走势大开大合,直线很长,弯也算不上急,旁边的交通警示牌稀稀疏疏,在车灯的照射下反射出孤寂的光。牌子上的阿拉伯文也没有英文翻译,根本不懂它想表达什么意思。</p>
<p><img src="assets/2025/IMG_3223.JPG" alt="" />
<em>外面一片漆黑</em></p>
<p>车灯伴随着月光,勾勒出两旁山峦的轮廓。它们的身体黑黢黢的,轻微抬头,伴着天空,才能够勉强看到山顶的形状,随着车辆快速驶过,悄然融化在黑夜中,再也找不见踪影。</p>
<p>我们身心俱疲,一路上随着车辆的颠簸醒醒睡睡。司机十分腼腆,话不多,声音也很轻,兴许是为了不打搅我们休息,也没有开任何的音乐和广播。夜变得更静谧,我们三人仿佛身处在一个特殊的时空之中,在向前极速地飘荡。</p>
<p><img src="assets/2025/IMG_3628.JPG" alt="" />
<em>天亮了,行驶在荒漠中</em></p>
<p>迷迷糊糊不知过了多久,天空逐渐被点亮,睁开眼睛就已看到我们早已被群山环抱。到处是荒漠,没有一点植被,远处的山非常裸露和突兀。这是荒漠的日常景象,但对习惯了高楼和青山的我们来说却特别新鲜。坐在后排,我们彼此相视一笑,眼睛反射出凌晨的微光,诉说看到山峦异景的好奇和兴奋。</p>
<p><img src="assets/2025/IMG_3224.JPG" alt="" />
<em>车窗外的荒漠</em></p>
<p>随着车驶出山区,我们逐渐接近了进入小镇的哨卡。哨卡虽然不大,但都处于关键位置的出入口,刚才出机场的时候也有,但夜晚太黑,所以不是很明显。天亮了以后,看着就更加真切。哨卡的路上摆着铁栅栏,旁边有一两个小屋子,里面坐着警察和官兵。一旁的武装车和他们手中的荷枪实弹,看上去颇有威慑力,气氛立刻就不一样了。</p>
<p>这样的哨卡遍布在半岛的各个区域,只要乘车去往不同的城镇,就一定会遇到他们。在路上,我们需要随时准备护照,以应对对方查验。至于标准也是大相径庭,有时几乎不会查,司机说上几个词就能放行(不知这几个词到底是意味着什么,有什么特殊的魔力),而有时却要特地把车停下,等上几分钟才能放行。很难想象,不会阿拉伯语的人应该如何在城镇之间移动。自驾是不可能的,本地人的帮助是必要的。城镇之间被粗暴区隔开了,每当出城的时候,我都会有些恍惚,镇子中的繁华热闹与外界冷静的对比太过明显,连续的旅程也被分割成一段一段的。</p>
<p>通过了哨卡,沉默良久的司机对着我们轻声说道「Welcome to Dahab」。我们透过车窗看去,附近的墙上张贴着宣传语,表示过了这里就进入了城镇。囿于旁边荷枪的军队,我们也没敢拿出相机拍照留念,只好转转头,尝试让这个颇有纪念意义的墙壁在眼睛里多驻足几秒钟。</p>
<p>车继续行驶,宽阔的大海从远处随着漫长的大道逐渐下降展开,我感到有些心跳加速,熟悉的景色开始有了新的转机,大海慢慢地插入天空与山石之间。旅途迎来了它开始的标志,经历了漫长的熬夜和奔袭,我们终于抵达了 Dahab 小镇。</p>
三十https://blog.kaiyikang.com/posts/2025/%E4%B8%89%E5%8D%81/https://blog.kaiyikang.com/posts/2025/%E4%B8%89%E5%8D%81/Fri, 28 Feb 2025 00:00:00 GMT<p>我已经确认多次,时刻的感受几乎都是平的,只有将时间线拉的巨长时,差距对比才会变得显著。我也丝毫不怀疑,当我快要老到将死时,感受也不会发生太大变化,为所作出的行为改变而喝彩,脑中依旧残留些想做却做不了的想象。</p>
<p>你可以感叹往昔,但也似乎持续不久,通常是碰触了那些变化差异巨大的人或事,才会有些感慨,而这劲儿一过,又继续该吃吃该喝喝,过那些日子。它也就是个瞬时的冲击,却伤不到巨大名为感受的海绵的丝毫。</p>
<h2>亲密关系</h2>
<p>这其中充满着好滋味,我能从和爱人的相处中察觉到。她那种近乎天才般的灵动怪感,让我欲罢不能。我想在同她经历并体验更多的事情,那弥足珍贵的记忆和触感,成为我回忆的一部分,更成为我的一部分,也是我喜欢的那部分。</p>
<h2>到处都是智能</h2>
<p>因为其它技术进展缓慢,所以近乎所有人都挤到了同一个主题上来,道路变得无比拥挤。即便每天都会刷新的新闻,看多了也会感到麻木和倦怠,仿佛在没有什么新鲜事了。</p>
<p>兴许人们的信仰消耗殆尽,于是尝试自己动手造「智能」,然后让它尝试去解答信仰的问题。这会是可能的么?我觉得问题在于,它是人们知识和表达均匀后的产物,先有人类的知识创意及表达,然后再出现所谓的智能。你问它问题,你感觉它很非凡,这简直是太对不过的事情,因为它就是如此这样理解并设计的。如果它出了偏差,叙述出了些人类不认可乃至不理解的表达,反倒值得惊奇。</p>
<p>我稍微了解 AI 背后的机理,所以每次看它或使用的时候,无不是带着使用工具的视角。我在想,如果有一群人去故意全然隐匿其背后的算法逻辑,只暴露出一个接受输入和返回输出的 API,那剩下的那群人就真的可能会被蒙蔽,从而觉得,哇,背后蹲着个如同神一般的存在。实际上,它就是个工具,和我们手边的电脑或锤子没什么本质区别,即便能创造奇迹,那也得看是谁在使用它。</p>
<p>绝不低估人类的力量。</p>
<p>还有一点我感到忌惮的是,它是圆逻辑方面的天才,可以说无比「正确」的废话,同时,它无法告诉你它所不知道的事情。在我们使用它的时候,会感觉舒服,但无法在现实中验证它所说的反馈。代码或数学题目可以被验证,但其他的内容,像是算命之类的,就很难被印证。</p>
<p>像它这样表达的氛围,像极了工作中上下级。我们是上级领导,虽然不懂项目,但却觉得下级的报告非常有道理,从而升起一种以未知为基础的信任感。它圆滑得很,不停使我们感到满意。</p>
<p>结合上面所说,模型是由人类训练出来的,带着很具人类味道的互动方式,因此大概率也映射着人类在潜意识中的互动方式,圆滑,权力关系。当然,我是不愿意这样相信的。</p>
<h2>做事</h2>
<p>我感觉「事业」这个词有些油腻,总会让我想到那些在酒桌上指点江山的人,同时在无形中带来一种束缚,仿佛这辈子有了事业就有了一切。</p>
<p>我会希望有自己能够做的事,不是围绕着公司雇主或产业展开,而是围绕着自身的生活或相似的人。这是其中的一个方向,另外的一个方向则和性格有分不开的关系。有些人善于观察外界,总结规律以后,再展开行动。而有些人善于观察自己,总结自己的特征与喜好,并通过行动放大它,使它能够波及到同自身相似的人。我倾向后者,因为它会让我更安心,更有把握去做事。</p>
<p>德国对开展副业不太友好。如果有想法,还需要通告当前雇主,这样势必会给当前主业带来负面的影响,因此不得不全职开展副业,这就和「副」一词背道而驰。</p>
<p>我喜欢程序开发,不停的去思考解决方案,激活思维的火花让我感觉到了活力。它包含工程实践的同时,却仍能够涵盖美学和哲学,增加了无穷的探索深度。另外,工作内容依赖于二进制的世界,与现实的世界解耦,这样我的肉身与心灵变得自由灵活。</p>
<p>My current challenge is communication and mindset. As German and English are not my mother tongues, the expression of the way of thinking is somethings blocked and there is little transparency between the inner and the outer world. This frustrates me. Anyway, I have to pay my time and energy for the flexibility, but overall it is worth it.</p>
<h2>生活</h2>
<p>过量的信息以及极具成瘾机制的算法使我疲倦,相比以前,是愈来愈来严重了。有时我会感觉过去的时光也不错,回看过去的十年,上一个整数的时候,我第一次步入德国的土地,学习然后漫游。(竟然已经快十年了,都没有怎么意识到,时光飞逝呀)</p>
<p>反思并写作真的很好,将信息保存了下来,留给了十年之后的我。读上去,思维还是类似的思维,但浓度不再那么高了,大概都均匀给了生活。小学的时候,博客空间还非常的盛行,曾有一位熟络的网友感叹说我想的太多,太远,没有什么太大必要。我觉得他说对了一半,必要性要有,但太多太远的确没有必要。我的经验似乎被倒置过来了,一般人是先经验,然后在接受生活的乏味后,迷茫再反思。我迷茫不多,但反思不少,本应最早的经验反倒放在了现在,怪哉。</p>
<p>漂浮在行为决策背后的幽灵正在变淡,打开电脑工作,拽取一个接一个 ticket,参加一场又一场的会议,期待每一个周末的懒觉,难得的随机事件是晴朗的天空和暖洋洋的阳光。此时我正身处一个 loop 中,用自我催眠的方式来描述就是来休养生息,等待厚积薄发,换种方式描述就是,丝毫不带有个人风格的迈入平庸且乏味的生活。</p>
<p>决策消耗了脑子的精力,于是再没有多去想为什么,怎么了之类的问题,即使想了又能怎样呢,又能得到怎么样的结果呢?但我既然曾拥有过,那么我就不想丢掉,只不过我现在更加贪心,不仅要想,更要去行动。赞美伟大的行动家。</p>
<p>就看看我能走到什么地方。</p>
<h2>死亡</h2>
<p>生命一端连接着死亡,所有人落在这道弧光的某个位置上,从左向右不间断的移动,时间是动力源。思索太多的死亡,会侵占生命的时间,得不偿失,我们应该将活着的时间和精力都留给生命,死亡是将死的时候才应该考虑的事情。</p>
<p>年龄数字的翻新会提醒我它的存在,而它越是这样跳脱出戏,就越应当罔顾且淡然,不然我活了些什么?我到底是否在成长变大?竟然让它侵占我的生命,这是荒谬且不能接受的。</p>
<p>心无旁骛,专心利用好活着的时光。</p>
Dahab - 尚未自由潜https://blog.kaiyikang.com/posts/2025/dahab_4%E5%B0%9A%E6%9C%AA%E8%87%AA%E7%94%B1%E6%BD%9C/https://blog.kaiyikang.com/posts/2025/dahab_4%E5%B0%9A%E6%9C%AA%E8%87%AA%E7%94%B1%E6%BD%9C/Mon, 14 Apr 2025 00:00:00 GMT<p>自由潜水不能算是休闲活动,而是一项运动和体育项目,且极富技巧。这些技巧既包括通常意义上的「看得见的技巧」,也包括「看不见的技巧」。后者通过海洋独特的环境,显化并放大,使其成为一种甚至决定性的因素。</p>
<p>如果达不到特定的技术要求,就无法入门。而我现在正是在这个门口来回徘徊,犹豫不决,不确定未来是否有一天能迈入这扇大门。</p>
<p>接下来,我会向你详细讲述我的经历。</p>
<p><img src="assets/2025/IMG_3439.JPG" alt="" /></p>
<p><em>这是 <a href="https://www.linyue-zou.com">她</a></em></p>
<p>Dahab 算不上什么旅游名胜,但凡来到这里的人,要么带着明确的目的,要么是被忽悠过来的,几乎没有中间状态。在这里游玩的规律是,几乎所有项目都有一定门槛。换句话说,想参与有趣的项目,要么天赋异禀,要么有特别的勇气和精力,要么拥有纯熟的技巧。几乎不存在那种不费吹灰之力就能体验的项目,类似参观名胜或博物馆,只需一双脚即可。</p>
<p>经过几天的体验,我深刻感受到,Dahab 一点也不谄媚。它大方地展示蓝洞、峭壁、潟湖,如果你想玩,就可以来;至于你有没有能力,那是你的事,不是它的。它不会为了让你轻松玩耍而降低门槛和身段。这种主动性的气质在众多旅游目的地中独树一帜——要么你投身岩壁和海洋,要么你躺在阴凉处吐槽。因此,我的建议是,来之前请充分发挥想象力,明确自己想参与哪些运动,以避免无聊。</p>
<p><img src="assets/2025/DJI_20250418125714.jpg" alt="" /></p>
<p><em>鱼</em></p>
<p>我报名了自由潜水体验课,属于 AIDA 体系的一级。简单来说,潜水分为两大类:一种是我们日常所说的「潜水」,即带着氧气瓶装备下水;另一种是不带任何装备,仅凭肉体下水,这也是「自由」一词的含义。前者较为知名的是 PADI 系统,后者则有 AIDA 或 Molchanovs 系统。这些名字其实不重要,随手一查就有大量资料。虽然我加入了系统,课程也有对应等级,但我心里很清楚,这并不代表个人能力,仅是个到此一游的证明。我没有抱有特殊期待,也不愿过多宣传所谓的证书。</p>
<p>由于当天风大,水面不平静,我们将一天的课程拆成两天,半天理论,半天实践。教练住在城镇北路,从住所出发步行约十几分钟。因为没有门牌号,我只能大致沿着微信定位走。到达后,我四处张望,分不清哪里是废墟,哪里是住宅,只见一丛黄花树投下阴影,还能闻到浓郁的羊粪味。</p>
<p>教练见我到了,下来给我开门。我沿楼梯上去,和他进了屋。屋内装修朴素,像国内老房子,功能性强。地上铺着地毯,旁边放着教练的各种潜水的装备和教具,虽然叫不出名字,但看起来非常专业。因为是初级课程,内容不复杂,多为介绍性质。核心是安全,其次才是技术。课程结束后,我和教练闲聊了一会儿,时间差不多便离开了。</p>
<p>课程中提到,自由潜水是一场「mental game」(精神游戏)。这句话让我印象深刻。一般来说,游戏是一个大框架,适用于大多数活动。它考验人们各种能力,如忍耐力、技巧、灵活性、记忆力等。我对不同能力的印象也会与相应活动挂钩,比如记忆力像带眼镜的书生,灵活性像瘦高的运动员,后者是前者的显化。至于精神能力强的人是什么样,精神显化后又如何,我说不准,因此更好奇,想亲自 " 玩玩这个游戏 ",体验精神力的强弱。</p>
<p>另一个让我印象深刻的是「buddy 模式」,即每次自由潜水必定两人及以上同行,以确保安全。因此,自由潜水不仅对个体心智有要求,还要求人与人之间有连结。这两者结合,像创世的元素,一个向内,一个向外,内外合一才算完整。两人、生命、海洋,许多元素自然且和谐地融合在自由潜水中。</p>
<p>回程路上,我一直在品味这些奇妙的概念。当然,理论终归是理论,最终还是要看实践如何。</p>
<p><img src="assets/2025/DJI_20250418144003.jpg" alt="" /></p>
<p><em>鱼和珊瑚</em></p>
<p>几天后,风停了。我们选择一个天气晴朗的上午,体验第一次下水。</p>
<p>小镇南边有座灯塔,是镇上最热闹的地方。得益于得天独厚的地理优势,这里是潜水最佳地点,近处浅海适合浮潜,稍远则可进行水肺或自由潜水。</p>
<p>我们找了家咖啡厅,教练从后院搬出浮标。我打听得知,教练给别人上课时,要么与潜店合作,要么自己单干。他只需提前和咖啡店打好招呼,把装备暂时存放那里,每次上课时点些吃喝,算是双方互利的交易。</p>
<p>我先穿上湿衣。湿衣摸起来像化纤材质,有厚度,按上去软乎乎的,似乎中间填充了保温材料。穿湿衣要全身包裹严实,从头到脚。即便太阳长时间照射,海水依旧冰冷,若穿得不严实,身体容易失温。此外,因海水浮力大,为了下沉,身体还需绑额外配重。最后戴上面镜和呼吸管,拿起长脚蹼就可出发。自由潜装备虽不少,但比水肺潜水轻便太多,后者甚至需要推小车搬运装备。</p>
<p>下水后,浅滩很浅,印象中水深只到大腿。教练指导我做几个简单动作,如吐水、趴着放松、如何用脚蹼等。其中呼吸放松最有趣,趴在水面上,扫描全身,放松身体,然后在最放松时屏息。身体被海水托起,一动不动漂浮,唯一能动的似乎只有大脑神经。思绪激荡,一会儿飞扬,一会儿沉寂。飞扬时五彩缤纷,沉寂时脑袋空空,只剩纯粹感觉。身体夹在天空与大海之间,变得薄如蝉翼,融化在交界面,随海浪飘荡。</p>
<p>人们用各种方式平息跃动的神经,教材上写可以想象一支燃烧的蜡烛。有人想象动物,有人想象植物。我经验不足,平息毫无章法,甚至搬出「心经」救场,但效果不佳,因背诵时忘词,惹得我恼火,心态更不稳。最终我屏息一分半钟,算普通成绩,相比那些能憋气好几分钟的专业人士,差距明显。</p>
<p>完成简单训练后,我们向稍远处前进。深度约七八米,海水清澈透亮,可见海底。教练固定浮漂后,我尝试下潜。</p>
<p>我倒立,扶着绳子轻轻向下拉,耳朵压力骤变,疼痛随之而来。疼痛由面状逐渐变尖锐。每次下潜,我都会尝试平压技巧,但无论陆地训练多好,被海水包裹后立刻失效。即使冷静,仔细揣摩喉部、声带、咽部和面部肌肉,海浪一飘过,感觉又混淆在一起。海水压力来自四面八方,极致且无死角的包裹,没有定形流体会钻入任何可能的缝隙。深度越深,压力越贴合且紧密,疼痛随之加剧。压力如因果律般无条件发生,面对它,除了正确技巧别无他法。那种无力感在水下被放大,要么做不好,要么做不成,一点办法都没有。我没有怨言,仅下潜一两米便迅速返回水面。</p>
<p>我尝试多次倒立下潜,均无功而返。挂在五米处的黄色网球鲜亮如海市蜃楼,向我招手呼唤,但可惜我无法满足它的期待和召唤。水压和技术成了拦路虎。</p>
<p><img src="assets/2025/DJI_20250418144205.jpg" alt="" /></p>
<p><em>更多的鱼和珊瑚</em></p>
<p>在海中待了一个多小时,身体开始发冷,我告诉教练可以结束。回岸上,阳光强烈,但身体仍不自觉颤抖。我回想这次下水,体验新鲜感占大部分,另一部分是不甘心,像临门一脚未成。虽不完满,但能接受,不至于让我放弃潜水。好消息是,截止到写作的时候,耳朵平压技巧已掌握,但仍需实际下水尝试和调整。</p>
<p>后来和女朋友聊起这次经历,她说她的教练看到我们训练位置离岸距离,评价那个位置和深度非常「神奇」且「微妙」。</p>
<p>我非常赞同,感同身受。完全不潜水的人会呆在岸边,想入门的人会离岸更远,而我正处于两者中间,既不算入门,也不算不入门,这非常准确地描述了我在学习潜水路上的矛盾徘徊。</p>
<p>自由潜似乎本身带有矛盾属性。顺利下潜既需完成特定动作,又要保持绝对放松。大多数情况下,两者相互矛盾:动作牵拉肌肉使其紧张,放松则抵抗紧张使动作停滞。这种矛盾在特殊环境中被彻底激发。我相信,每位技艺高超的自由潜水者,都是追求平衡的大师。他们懂得在紧张与放松间找到平衡,像鱼般灵活游走于动与静的缝隙,追求最大深度和最深层的自我。</p>
Dahab - 清晨的小镇https://blog.kaiyikang.com/posts/2025/dahab_2%E6%B8%85%E6%99%A8%E7%9A%84%E5%B0%8F%E9%95%87/https://blog.kaiyikang.com/posts/2025/dahab_2%E6%B8%85%E6%99%A8%E7%9A%84%E5%B0%8F%E9%95%87/Wed, 09 Apr 2025 00:00:00 GMT<p>我们乘坐出租车驶入小镇,离开柏油马路,拐入小径。</p>
<p>小径路面尘土飞扬,低矮的建筑林立在两旁,说是建筑,不如说是由破碎墙壁和门板围成的方形区域。门口堆满着石料和塑料,远看还是近看都像是垃圾,像是即将开工的工地。路面十分原始,就是单纯的沙土地,上面零星布满着许多东西,最常见的是狗和羊的粪便,还有些塑料制品。粪便经过了阳光和高温的炙烤,变得像石头一样坚硬,没有什么味道,苍蝇也不来光顾。</p>
<p><img src="assets/2025/L1090619.jpg" alt="" /></p>
<p><em>土路</em></p>
<p>我尝试将这些随处可见的土路与建筑和我的经验互相融合,最终得到的结论是,它更像是中国的小区,或德国的 30km/h 区域,不过是埃及版本。通过将不同的印象相互结合,我才能逐渐接受这种全新形式的住宅区。这样的住宅非常极端,究极简陋且不包含任何设计,但却无比实用且非常符合当地的气候和人文条件。长期生在这片土地的人们,各个都是「形式追随功能」的大师,不过全是功能,没有一点形式罢了。</p>
<p>每次走在土路上,都是一场微型的冒险。白天,墙头忽然会冒出的花朵和小猫,同时还能闻到阵阵的羊屎芬芳。夜晚,趁着夜色和微弱的灯光,我在沙地上灵巧的躲避地上的粪便和垃圾。习惯了之后,其实也还好,别有一番风味。</p>
<p>当然,并非所有地方的建筑都这样,像我们选择的 hostel 就不是上面描述的样子。它是个小别墅楼,外表方正,窗户也不大。外墙和内院干净整洁,墙内种植的花束和绿植长得高过了头顶,四处散开,相隔老远就能看到。类似的小楼在小镇上也不少,但大多都被经营成了 Airbnb 或是 hostel。它们多数集中在镇中心,虽然设施都不是最新的,但胜在功能齐全,能看到不少现代的影子。</p>
<p>规模大点的度假村都坐落在城镇边缘。我们都不太喜欢那些地方,每次路过门口,往里面张望,尽是萧条。栏杆和设施被海风严重侵蚀,虽然房间众多,但也不见得有多少人住在里面,冷冷清清的。总之,如果你有机会来到这里,我极力推荐 hostel 或 Airbnb,而不是度度假酒店。</p>
<p><img src="assets/2025/L1090630.jpg" alt="" /></p>
<p><em>有些小院的门口就比较精致</em></p>
<p>下了车,内敛却用心的司机帮助我们把行李搬下车。因为我们抵达太早,所以不得不反复敲叩击大门,并用电话将人叫醒。过了几分钟,负责人 Z 君开了门,一脸睡眼惺忪,向我们问好。我们和司机表示感谢后,将行李放进了大厅的走廊,Z 君简单地向我们介绍了些青旅的设施和使用规则。</p>
<p>Z 君是名埃及青年,身材高大且皮肤黝黑,常穿着一身运动装,负责青旅的大小事务。每次清晨下楼经过客厅,都能看到他躺在沙发上惬意的睡觉或听歌。但据可靠消息称,这并不是在悠闲偷懒,而是他每天都会早起健身,等完成了一系列运动之后回来后,才躺在沙发上休息。刚起床的我们所见到的,其实是忙碌完后的他。</p>
<p>除此之外,最令我们感谢的,是他万事通的能力。上到青旅内部事务,下到各类活动组织,还有小到像是在哪里可以打印东西等等问题,他都知道,且能很快提供回复。在这个网络信息闭塞的地方,地图 App 只能算勉强够用,但更新非常不及时。而第一手的最新信息,还得向他询问。我们很庆幸能选择这个青旅,结实友善且靠谱的当地人,为这趟旅程润滑不少。</p>
<p>hostel 的客厅中,正门边上就是台饮水机,我最喜欢的是里面免费的冰水,每次回来都会喝上几杯。客厅中间是几个沙发,还有电视机,再往里面走是厨房和长桌。厨房中的红茶是免费的,而其他的冲泡饮品则要自助付费。</p>
<p><img src="assets/2025/L1090611.jpg" alt="" /></p>
<p><em>清晨的 hostel</em></p>
<p>埃及红茶是一种冲泡的茶粉,常见的被称作 El Arosa Tea,用很低的价格就能买上一大包。每次饮用的时候,只需挖一小半勺粉末,不需任何滤纸,直接用热水冲对,等待热水被茶粉染成透亮的夕阳红,就可以直接饮用。与红茶相对,欧洲常见的咖啡在这里看上去并不是主流饮品,起码这几天以来,我自己喝到的咖啡都算不上好喝,也缺乏咖啡的香气,反倒是红茶却喝的十分过瘾。</p>
<p>hostel 的后院十分惬意,有舒适的沙发和地垫,以及长桌长椅,旁边的绿植增加不少自然气息。四周墙壁遮挡住了光照,即使是阳光强烈的时候,躲在阴凉下也能感受到一丝凉意。</p>
<p><img src="assets/2025/L1090618.jpg" alt="" /></p>
<p><em>院子墙</em></p>
<p>旅居在这里的人们常会惬意地躺在院子里,一边吃东西,一边喝茶聊天。我也是如此,看到有人在时,会尝试和他们攀谈几句,不过我并不擅长这样做,尤其当我看到对方正埋头看电子屏幕,或是彼此之间正讨论的热火朝天。对闲谈而言,我似乎从未找到任何一种理论和技术去处理它的开始和结束。它就像是一场场梦,每次回忆起来的时候都没有开头和结尾。</p>
<p>我像是一条鱼,不太有主见的游来游去,有话题聊了就说上几句,想不到新的点子或表达不出复杂的想法,就找个理由窜到旁边的岩石缝中去。社交沟通的挑战大于享受,想到话题,插入讨论,表达自己观点同时还要尽可能不得罪他人。使用非母语会放大这种挑战,使之成为一种非常消耗精力的活动。</p>
<p>充满矛盾的是,它算是一种习惯和技能,你越使用它,你就会越熟练,反之则退化,状况变得越来越糟。因此,外界环境推动我自身改变就变得尤为重要,我感觉生活在德国,有时很难支撑这种正面的进化,每当我使用蹩脚的德语尝试交谈时,总会收获对方或沉默的反馈哦。我知道,其实对方并没有任何恶意,但我无法获得反馈,也就从未知道自身想法是否正确地传递给了对方,还仅仅就是我单纯的自说自话。与之相对,那些来自阳光充沛地带的人,总是会给予我更加积极且正面的反馈,即便我们的语言其实并不怎么想通,但就像太阳的能量散发给所有人一样,我能感知到对方的情绪,理解与体谅。</p>
<p>交谈就像是水流。你可以在平静的时候去激发水流,或是顺势加入已经存在的流中,不论是逆流还是顺流都可以,关键在于看清水的运动方式,然后找到契机加入或退出。我很开心,通过这次旅程让我有机会和不同的人发生交流和互动,一来增加了我的自信心,二来让我真正感知到了人和人之间交往的水流。</p>
<p>因为我们抵达太早,眼见离 check-in 的时间还很久,为避免打扰他人休息,我们从客厅退到后院,找了一张柔软的垫子,就地平躺了下来。在此时,一声轻盈的响动引起了我们的注意,只见一只瘦削的三花猫从后院高墙上跳下来,灵巧地围到我们身边。作为爱猫人士,必然是逮着机会就要出手,对它的身子一阵上下其手,贪图一个爽快。</p>
<p>后来 Z 君告诉我们,它的名字是 Orca,和其它的埃及小猫一样,最大的爱好是觅食。通过几日的观察发现,它常会蹲在后院的玻璃门旁,等看到有人拿着盘子和杯子进出时,立刻围上去,用灵巧的身子和脑袋蹭对方的大腿,等待对方坐下后,又会拿头使劲蹭椅子和桌子的角落,嗲里嗲气地喵喵向人叫嚷。我开始以为,它在对我表示欢喜。后来才意识到,它就是个势利鬼,只喜欢接近有食物的人。它的势利还体现在得到食物的时候,等真尝到了甜头,他的贪婪尽显,攻势变得愈加猛烈,不仅叫嚷的更凄厉凶狠,还拿尖锐的爪子去抓挠对方,真就成了一只「Orca」。我们自从知道了它的这种性格后,接近时候就多留了些心眼儿,留出更多空间,也不会无脑地接受它可爱的诱惑了。</p>
<p><img src="assets/2025/L1090633.jpg" alt="" /></p>
<p><em>虎鲸在行走</em></p>
<p>Dahab 的猫狗众多,大街上随处可见。狗的体型十分巨大,聚集时总会喧闹地吠叫和争斗,我已经见过好多次狗咬狗的场景。与之相对,猫就瘦弱灵巧许多,一般不会争斗和聚集,除非是为了吃东西。除此之外,猫确实很爱干净。有一次我亲眼看到一只小猫在沙滩上拉了几泡屎,盖上沙土,优雅地蹭蹭屁股离开了。它的优雅,却让我时常感到忐忑,每次赤脚去沙滩的时候都,会增加些提防,生怕踩到新鲜的地雷。</p>
<p>虽说都是猫咪,但相比土耳其猫的慵懒,这里的猫就显得野性许多,为了食物总会不择手段。有些性格强势的猫咪甚至会直接跳到桌子和盘子边上,此刻我们必须要狠下心来,装作拍打它们的样子将其驱散,倘若心软下来,势必会遭到它的进犯。当然,也有那些性格与脾气更加温和的猫咪,它会默默地在一旁看着我们吃饭,直到再无觅食的可能便悄悄离开。对这些乖巧的小猫,我们自然会提供奖励,或是鸡肉或是牛肉,以表彰它优秀的餐桌礼仪。</p>
<p>顺带一提,当地的猫咪有自己的方言,与普通喵喵声不一样,「Psst」对它们而言才意味着「过来」。这是当地人教我的,如果你想让埃及的猫咪靠近到你身边,可以直接对它 Psst 几声。</p>
<p>Orca 性格自由懒散,被摸了会儿,沿着墙边爬走了。我们躺在软垫上,用帽子盖着眼睛,半醒半睡迷迷糊糊的。这里的苍蝇经过多代进化,都成了精,时不时的飞过来趴在皮肤上,普通有规律的晃动已经没用,必须加大幅度且随机摆动,才会不情愿的飞走。我们不堪其扰,反正也睡不着,决定干脆上街溜达一圈,领略下小镇清晨的风光。</p>
<p>从住所到海边的直线距离其实不远,海岸边上的道路也十分平直,但一旁全是紧挨的建筑,找到一条从中穿过的小路其实不太容易,七拐八拐,我们终于是来到了海岸边。清晨,太阳才刚刚升起,风不大,海面很平静,气温十分舒服。</p>
<p><img src="assets/2025/L1090667.jpg" alt="" /></p>
<p><em>咖啡店街的几个角落</em></p>
<p>街上不见太多人,只有零星的几个人在晨跑,还有些人穿着湿衣,两三成群说说笑笑。街边咖啡店和潜水店林立,多数都还没开张,只有寥寥数个人,在玻璃窗户后面打扫卫生。咖啡店多是靠海而建,临沙滩的,会直接铺上地毯和垫子,再摆上个小茶几,算作是一桌。靠近浅滩的,会摆上一般的桌椅和沙滩躺椅,方便人们下海游泳或晒太阳。</p>
<p>我们初来乍到,没见过这种类型的咖啡店,也不清楚消费用刷卡和付现金,再加上来得时间太早,每家店都冷冷清清,找不到人询问。我们摸不定主意,在海岸线上反复徘徊犹豫,后来实在累的不行,见到有两人躺在沙滩旁的地毯上睡觉,就一不做而不休选定这家,躺在海边,同他们一起睡觉。</p>
<p>我们和老板打了声招呼,点了两杯冰咖啡,然后一股脑地躺在遮阳伞下的地毯上。等上少许片刻,老板拿了两杯塑料杯端了过来。接过了杯子,有气无力地表示感谢,嘬了一口咖啡,咿,真难喝,罢了,睡觉要紧,把小包揣在怀里,听着海浪的声音就睡着了。</p>
<p>沙滩上的垫子积满了灰尘,没有一块是完整的。破洞处的布线一缕一缕的,灰色的棉质碎屑裸露出来,到处乱飘。地毯很久没有擦洗了,上面布满了各种颜色的污渍,掩盖了最初的红色花纹。我想,也许是因为当地淡水资源珍贵,清洗地毯会消耗太多水,性价比太低。或是由于当地的环境,地毯和垫子每天都会被强烈的阳光炙烤,这是天然的杀菌消毒,人们无需担心生出的细菌会让人生病。起初我以为只有一两家店会这样,去多了却发现大部分的餐厅咖啡店都是这样,即便如此,没人会觉得这是个问题,人们进了店,买了吃喝,依旧该坐坐,该躺躺。</p>
<p>我习惯了城市生活。城市中的人们会理所应当地认为,任何东西都需要保持干净,如果有了污渍,需要立刻及时清洗,等变得干净了,才有资格继续使用。你看看各种广告,不都是以高效净白为卖点的么?「没有污渍」成了一种属于都市的属性,是使用物品的常识,也没有人会去质疑,为什么物品要保持洁净?而为什么只有干净的东西才能被使用?相比于埃及的地毯,脏不脏,破不破,从不是人们去考虑的事情,最重要的,恰恰是有没有。只要有,就能席地而坐,好好休息,没有,就起身来离开,寻找下一个能栖身的地方。</p>
<p><img src="assets/2025/L1090640.jpg" alt="" /></p>
<p><em>路上的羊</em></p>
<p>重回城市,我骑车路过高端家具店铺。硕大的玻璃橱窗中,展示着各式各样的高端沙发和座椅,材料不同,形态各异。看着如此多的变体,突然就想到了在埃及的地毯和垫子,它们看着又脏又破,且只提供坐和躺两种姿势。两相对比,我感到莫名的羞愧。后者和自然融合共生,而前者更像是一种迎合,去迎合人类在城市中构建出的模拟生态,生态中包含了钢筋水泥以及各种人工的行为和活动。什么环境,什么场合,就要用什么样的沙发或椅子,要是不契合,保准就会有人眉头一皱,愤然离场。</p>
<p>不知道过了多久,我再次睁开惺忪的睡眼。周围仍如刚才一般平静,倒有几只野生的大狗聚集在旁边。面对他们,我不敢多动,只任凭它们来回嗅闻,<em>呈现出了五狗绕康的奇观</em>。它们呆了几分钟,见没找有食物,就一窝蜂的跑走了。</p>
<p>躺在前方睡觉的两位大哥也醒了过来,其中一位在用阿拉伯语和老板攀谈,看样子应该是本地人。不久,只见他熟练地端来一盏水烟壶,摆放在他们喝剩的啤酒旁。清晨刚醒,便烟酒交融,我在心里不由得对他心生佩服。</p>
<p>他环顾四周,见四下无他人,便多走了几步,来到我们身边站定,主动与我们攀谈。我们简单做了自我介绍,随即聊起一些有的没的,具体的内容我早已记不清,大概是关于刚抵达这个小镇的第一印象与感受。</p>
<p>只记得在聊到换钱的时,他提到自己从事反洗钱相关的工作,今天一早刚下夜班,便直奔海边来放松。他用着算不上流利的英文提醒我们,别在黑市换钱,那些汇率坑得厉害;最稳妥的方式,是直接去银行找人工窗口换。</p>
<p>眼看时间差不多,我们向他道谢并准备结账离开。临走前,他热情地与我们一一握手,还不忘补上一句:" 今晚附近会有 techno 音乐,欢迎来玩。"</p>
<p>从小我便被教导要提防陌生人,尤其是那些主动示好的,总被告诫「有所图谋」。于是面对街头那些带着笑意靠近的身影,无论男女老少,我总会本能地保留几分戒备。而在德国的生活经历,也似乎一再验证了这种观念:不是上门推销网络的陌生人,就是在火车站向人讨钱的过客。久而久之,我几乎遗忘了,人之初的热情,其实是一种最质朴也最温暖的性格特质,它原本拥有拉近彼此距离的力量。</p>
<p>我并不想指责那些假借热情行图利之事的人。我只是感到一丝悲伤——那份本应纯真的人性,被慢慢异化为一把锋利的工具,而它真正的珍贵与温柔,却很少有人愿意细细体会与珍惜。</p>
<p>在我们停留的这几天里,类似的闲聊和对话几乎每天都会上演。无论对方是熟悉的朋友,还是素未谋面的陌生人,我都能真切地感受到那份来自人心的善意与温情。从个体的视角看,也许每个人都只是芸芸众生中平凡的一员,但正是这些不期而遇的交谈与相识,让每一个平凡的瞬间都散发出独特的光芒,使每一个人,在此刻,都显得那样与众不同,绽放出令人惊艳的非凡之光。</p>
<p><img src="assets/2025/L1090665.jpg" alt="" /></p>
<p><em>不认识的当地年轻人,但却给他们拍了照</em></p>
<p>趁着太阳还没升得太高,我们绕回到了 hostel。虽然还没到 check-in 时间,Z 却告诉我们,房间已经提前收拾好,可以直接入住。我忍不住伸出大拇指,用力对他表示了感谢,实际来的刚刚好。</p>
<p>进了屋,我们把行李随处一丢,顾不上满身尘土与汗气,径直扑向了刚收拾好的床铺。眼睛一闭一睁,才发现时间不过正午,可身体与意识却仿佛已跨越了好几个世纪。那一刻疲惫袭来,却也在提醒着我们——这段旅程,才刚刚拉开序幕。</p>
Race Conditionhttps://blog.kaiyikang.com/posts/2024/what-is-race-condition/https://blog.kaiyikang.com/posts/2024/what-is-race-condition/Sat, 20 Apr 2024 00:00:00 GMT<p>In the meeting, my colleague presented an overview of the spike in question, outlining the necessary steps for handling user consent.</p>
<p>It is the case that different system platforms send requests to calculate user privacy patterns to our service at the same time. The different platforms may be customers or third parties.</p>
<p>However, due to the asynchronous communication involved, a <strong>race condition</strong> would occur when the new requests are received.</p>
<h2>What is Race condition ?</h2>
<p>A <strong>race condition</strong> or race hazard is the condition of an electronics, software, or other <a href="https://en.wikipedia.org/wiki/System">system</a> where the system's substantive <strong>behavior is dependent on the sequence or timing of other uncontrollable events</strong>, leading to unexpected or inconsistent results. It becomes a <a href="https://en.wikipedia.org/wiki/Software_bug">bug</a> when one or more of the possible behaviors is undesirable.</p>
<p>Source: <a href="https://en.wikipedia.org/wiki/Race_condition">Race condition - Wikipedia</a></p>
<h2>Understanding from Code perspective</h2>
<p>A race condition arises when two or more threads attempt <strong>to modify shared data simultaneously</strong>. Given that the thread scheduling algorithm can switch between threads at any point, it is <strong>impossible to predict the order in which threads will access shared data</strong>. Consequently, the outcome of the data modification is contingent upon the thread scheduling algorithm, whereby both threads are essentially engaged in a race to access or alter the data.</p>
<pre><code>if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
}
</code></pre>
<p>In order <strong>to prevent race conditions</strong> from occurring, you would typically <strong>put a lock around the shared data</strong> to ensure only one thread can access the data at a time. This would mean something like this:</p>
<pre><code>lockX(); // Obtain lock for x
if (x == 5)
{
y = x * 2; // Now, nothing can change x until the lock is released.
// Therefore y = 10
}
unlockX(); // release lock for x
</code></pre>
<p>Source: <a href="https://stackoverflow.com/questions/34510/what-is-a-race-condition">multithreading - What is a race condition? - Stack Overflow</a></p>
Kafka Event in Integration Testshttps://blog.kaiyikang.com/posts/2024/kafka-event-in-integration-tests/https://blog.kaiyikang.com/posts/2024/kafka-event-in-integration-tests/Thu, 17 Oct 2024 00:00:00 GMT<h2>What is the requirement?</h2>
<p>When introducing a new feature to the system, it is imperative to develop and implement a comprehensive suite of tests, including Unit Tests, Integration Tests, and Acceptance Tests.</p>
<p>This post will primarily focus on the Integration Test (IT) phase. Integration Tests are designed to verify the functionality of the service on a local machine environment. It is crucial to ensure that all external systems and dependencies are properly configured and operational before commencing the testing process. Integration Testing serves as the penultimate stage of validation before proceeding to Acceptance Testing.</p>
<p>The current requirement is as follows: Upon successful completion of the primary process, the service is tasked with triggering a Kafka event. This event serves as a notification mechanism, informing other services of the successful execution. This signalling mechanism must be thoroughly validated through rigorous integration testing procedures.</p>
<h2>Start Integration Test</h2>
<p>There are few steps before run the code test:</p>
<ol>
<li>Clean up the docker containers and images.</li>
<li><code>mvn clean install</code>: clean files, compile and package the current source code.</li>
<li><code>mvn clean verify [with special options for validation]</code>: verify step is one of the step in install command, but the validation can be realised with different options.</li>
<li><code>-P</code> parameters : activate specified Maven profiles. Like open daemon service to block the main services exits after involking.</li>
<li><code>-D</code> parameters: sets system properties.</li>
<li><code>-rf service</code>: Resume the build from the specified module.</li>
</ol>
<p>Files ending names with "ITCase" or "IT" are designated as integration tests. This is a standard naming convention. Maven is configured to automatically identify and execute these files as integration tests based on the presence of "IT" in their names.</p>
<p>Note: The code snippet is provided for illustrative purposes only. It is not a functional example and cannot be used in a real-world context.</p>
<h2>Basic Code Structure</h2>
<p>The basic structure for ITCase is comprised of four elements:</p>
<ul>
<li>Define constants, class/instance variables</li>
<li>Define <code>@BeforeAll</code>: it marks a method to be run once before ALL test methods in the class.</li>
<li>Define <code>@BeforeEach</code>: Marks a method to be run before EACH test method in the class.</li>
<li>Define test: Implementing custom annotations to tag use case IDs enhances documentation traceability and improves test case management.</li>
</ul>
<pre><code>public class ServiceITCase {
// define variables;
private static final STATIC_VALUE = VALUE;
private InstanceDefinedInAtBeforeAll instance;
@BeforeAll
static void setUp() throws Exception {
// ...
}
@BeforeEach
static void setUpBeforeEach() throws Exception {
// …
}
@Test
@Verifies("TraceId_useCase")
void TestMethod() {
// Given
// ...
// When
// ...
// Then
// ...
}
}
</code></pre>
<h2>Configure Kafka Event</h2>
<p>Properties is a class that represents a persistent set of properties and is a part of the <code>java.util</code> package. <code>getProperty()</code> and <code>setProperty()</code> are common methods.</p>
<p>This code example configures properties for Kafka consumer.</p>
<pre><code>private static Properties setKafkaConsumerWith(final String groupId) {
final Properties properties = new Properties();
properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaBootServer());
properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, groupId);
properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
properties.setProperty(ConsumerConfig.CLIENT_ID_CONFIG,
ServiceITCase.class.getSimpleName() + System.currentTimeMillis());
properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, CustomEventDeserializer.class.getName());
return properties;
}
</code></pre>
<p>The rough model of Kafka is as follows: The <code>producer</code> sends messages to <code>Kafka Brokers</code> with <code>topics</code>, which then send them to the <code>consumer</code>. Here is a brief description of each line:</p>
<ol>
<li><code>BOOTSTRAP_SERVERS_CONFIG</code>: the Kafka broker addresses. It provides the initial points of contact for a related client (producer, consumer, or admin) to connect to the Kafka cluster.</li>
<li><code>GROUP_ID_CONFIG</code>: defines the consumer group id. Consumers with the same group Id work together as a single logical consumer.</li>
<li><code>AUTO_OFFSET_RESET_CONFIG</code>: optional, but recommended. Define where to start reading messages if no <em>offsets</em> is found.</li>
<li><code>CLIENT_ID_CONFIG</code>: Define a unique identifier for the client. In this case, it is this class.</li>
<li><code>KEY_DESERIALIZER_CLASS_CONFIG</code>: specifies the deserialiser for message keys.</li>
<li><code>VALUE_DESERIALIZER_CLASS_CONFIG</code>: specifies the deserialiser for message value.</li>
</ol>
<h3>What is <em>Topic</em>, <em>Partition</em> and <em>Offset</em>?</h3>
<p>Before we proceed further with our code exploration, it is beneficial to familiarize ourselves with some key technical terminology. If you're already well-versed in these terms, feel free to advance to the subsequent chapter.</p>
<p><strong>Topic</strong>: Kafka topics organize related events. For example, we may have a topic called 'logs', which contains logs from an application.</p>
<p><strong>Partitions</strong>: Topics are broken down into a number of partitions. A single topic may have more than one partition.</p>
<p><strong>Offsets</strong> represent the position of a message within a Kafka Partition. It is an integer value, and each message in a given partition has a unique offset.</p>
<p>Brief Summary: Kafka structures data into <strong>topics</strong>, which are segmented into partitions. Each partition contains messages with unique, sequential identifiers known as offsets. These offsets enable consumers to monitor their progress and resume reading from specific points within the message stream, facilitating efficient data processing.</p>
<h2>Main Test</h2>
<p>Let's move on to the central part of the test.</p>
<pre><code> @Test
@Verifies("TraceId_useCase")
void TestMethod() {
// Given
prepareSomething = prepare();
final Properties properties = setKafkaConsumerWith("groupId");
try (final Consumer<String, CustomEvent> kafkaConsumer = new KafkaConsumer<>(properties)) {
kafkaConsumer.subscribe(Collections.singleton("custom_topic"));
kafkaConsumer.poll(java.time.Duration.ofSeconds(1));
// When
// This method will send the event
var result = doSomethingAndGetResult();
// Then
assertNotNull(result);
assertThat(result.contains("value")).isTrue();
await().atMost(3, TimeUnit.SECONDS)
.untilAsserted(() -> assertTestMethod(kafkaConsumer));
}
}
</code></pre>
<p>The Consumer interface uses two type of parameters to specify the type of the key and value in Kafka messages.</p>
<pre><code>final Consumer<String, CustomEvent> kafkaConsumer = new KafkaConsumer<>(properties)
</code></pre>
<p>The next two lines of code are crucial for setting up and operating a consumer:</p>
<pre><code>kafkaConsumer.subscribe(Collections.singleton("custom_topic"));
kafkaConsumer.poll(java.time.Duration.ofSeconds(1));
</code></pre>
<p><code>subscribe()</code> registers the consumer to specific Kafka topic, specifying which data streams to monitor for incoming messages.</p>
<p><code>poll()</code> fetches messages from the subscribed topic. The duration parameter (1 second in this case) defines the maximum wait time for new messages before the method returns.</p>
<p>The first <code>poll()</code> call serves to establish a connection with the Kafka cluster and retrieve initial offsets. This initial call also allows for early detection of potential connection errors.</p>
<p>Complementing the standard <code>assertThat()</code>, we can leverage <code>await()</code> from <code>org.awaitility.Awaitility</code>. This code snippet demonstrates an asynchronous testing methodology:</p>
<pre><code>await().atMost(3, TimeUnit.SECONDS).untilAsserted(() -> assertTestMethod(kafkaConsumer));
</code></pre>
<p>This line of code:</p>
<ol>
<li>waits for a condition to be met</li>
<li>has a timeout of 3 seconds</li>
<li>repeatedly checks the assertion that will be introduced in the subsequent chapter</li>
<li><strong>Passes</strong> if the assertion becomes true within the timeout</li>
<li><strong>Fails</strong> if the timeout is reached before the assertion is true</li>
</ol>
<h2>Assertion</h2>
<p>The Assertion of record does not require any specialized procedure. Step 2 is an optional phase designed to verify the accuracy of the record obtained from the broker.</p>
<pre><code> private void assertTestMethod(final Consumer<String, CustomEvent> kafkaConsumer) {
// 1. Poll records from broker
final ConsumerRecords<String, CustomEvent> consumerRecords =
kafkaConsumer.poll(java.time.Duration.ofSeconds(1));
// 2. (Optional) Verify the records
final Optional<ConsumerRecord<String, CustomEvent>> serviceRecord = StreamSupport
.stream(consumerRecords.spliterator(), false)
.filter(consumerRecord -> {
final CustomEvent event = consumerRecord.value();
return event.getSomething().equals(testCase.getSomething().toString());
}).findAny();
assertThat(serviceRecord).isPresent();
// 3. Assert Record
final ConsumerRecord<String, CustomEvent> record = serviceRecord.get();
assertThat(record.key()).isEqualTo(testCase.getKey().toString());
// 4. (Optional) Debug
for (Header header : record.headers()) {
System.out.println("Header Key: " + header.key() +
", Value: " + new String(header.value()));
}
}
</code></pre>
<p>Regarding step 4, it's important to note the presence of headers in the record. Headers are a crucial feature in Kafka, enabling the attachment of metadata to messages without altering the message payload itself. These headers consist of key-value pairs, where the key is a String and the value is a byte array. To illustrate, here's how one can add headers to a ProducerRecord:</p>
<pre><code>ProducerRecord<String, String> record = new ProducerRecord<>(
"topicName",
null, // partition
"key",
"value",
Headers headers = new RecordHeaders();
headers.add("header1", "value1".getBytes());
headers.add("header2", "value2".getBytes());
);
</code></pre>
<h2>Extended Topic: Perceptual Decoupling</h2>
<p>Before concluding, I'd like to share my thoughts on method design. These are just observations and experiences, not definitive guidelines.</p>
<p>In the "Basic Code Structure" chapter, I mentioned tests are divided into three parts: <code>@BeforeAll</code>, <code>@BeforeEach</code>, and <code>@Test</code>. They not only execute in sequence but also have a hierarchical structure logically. <code>@BeforeAll</code> can be understood as global, with all tests and helper methods able to access variables defined there. <code>@BeforeEach</code> is for each test, where the test environment and state can be reset.</p>
<p>Once variables are defined, we can use them in <code>@Test</code>. At this point, when creating different methods based on this test, a divergence appears: whether to use these defined variables in sub-methods or pass them as arguments. This difference highlights the concept of decoupling.</p>
<p>As the project has been ongoing for a long time, many test cases exist. For new tests, I can combine different methods through copy and paste to complete testing for new features. I've found that the former method using arguments is clear and explicit, while with the other, I need to frequently check the <code>@BeforeAll</code> and <code>@BeforeEach</code> annotated functions.</p>
<p>Here is an example.</p>
<p><!--
在结束之前,我想聊一聊对如何设计方法的感想。当然,这仅仅是感想和经验,而非明确的指导。</p>
<p>在代码结构一章中,我提到测试分成三个部分:<code>@BeforeAll</code>, <code>@BeforeEach</code>,<code>@Test</code>。它们不仅是随时间顺序执行,同时在逻辑上也具有上下级的结构。<code>@BeforeAll</code> 可以理解成是全局的,所有的测试和辅助方法都能访问在该方法中定义的变量。<code>@BeforeEach</code> 则是针对每次测试,测试环境和状态都可以在该函数中被重置。</p>
<p>当变量在不同的部分被定义后,我们可以在 <code>@Test</code> 中使用。此时,如果我们在基于该测试创建不同的方法时,分歧就出现了,即是否要在子方法中使用上述定义的变量,或通过 argument 的方式使用变量。这其中不同,凸显出了解藕的概念。</p>
<p>因为项目持续了很久,所以存在很多测试用例。针对新测试,我可以通过复制和粘贴的方式,组合不同方法,从而完成新 feature 的测试。我发现,前者使用 argument 方法清晰且明确,而后者我需要频繁检查 <code>@BeforeAll</code> 和 <code>@BeforeEach</code> 修饰的函数。</p>
<p>举个例子: --></p>
<pre><code>private static HttpClient client;
@BeforeEach
void setUp(){
client = createHttpClient(USER_NAME,PASSWORD);
}
@Test
void test() {
useClient1();
useClient2(client);
}
void useClient1() {
client.toTarget("www.abc.com").request().post()
}
void useClient2(HttpClient server) {
server.toTarget("www.abc.com").request().post()
}
</code></pre>
<p>When I copy <code>useClient2()</code>, I clearly know it requires an <code>HttpClient</code>. I also know that <code>HttpClient</code> is defined in the <code>@BeforeEach</code> block above.</p>
<p>When I copy <code>useClient1()</code>, the <code>client</code> is hidden and not explicitly defined. So I need to spend more effort to find the corresponding keyword.</p>
<p>These two approaches represent different levels of coupling with the test. Of course, I'm not saying <code>useClient2()</code> is better than <code>useClient1()</code>, as we can see <code>useClient2()</code> contains more code, making the overall code more verbose.</p>
<p>How to choose between these two styles? My current answer is that it depends on the project style. If the project is mature with many similar code blocks, the decoupled approach might be better. But if this test is unique and likely to appear only here, the first approach is better.</p>
<p><!--
当我复制 <code>useClient2()</code> 时,我明确知道它需要 <code>HttpClient</code>. 同时我也明确知道,<code>HttpClient</code> 在更上一层的 <code>@BeforeEach</code> 中被定义。</p>
<p>而当我复制 <code>useClient1()</code> 时,<code>client</code> 被隐藏了,并非显示定义。所以我需要花费更多的精力去寻找对应的关键词。</p>
<p>两种方式对应着和测试不同程度的耦合。当然,我这里并不是说 <code>useClient2()</code> 比 <code>useClient1()</code> 好,毕竟我们可以看到,<code>useClient2()</code> 包含更多的代码,会让整体代码显得冗长。</p>
<p>如何选择这两种风格?当前我的答案是按照项目风格而定的。如果项目比较成熟,有很多相似功能的代码块,那么解藕的方式会更好,但如果这个测试非常独特并且可能就只会在这里出现,那么第一种方式更好。 --></p>
分离业务和控制逻辑https://blog.kaiyikang.com/posts/2025/%E5%88%86%E7%A6%BB%E4%B8%9A%E5%8A%A1%E5%92%8C%E6%8E%A7%E5%88%B6%E9%80%BB%E8%BE%91/https://blog.kaiyikang.com/posts/2025/%E5%88%86%E7%A6%BB%E4%B8%9A%E5%8A%A1%E5%92%8C%E6%8E%A7%E5%88%B6%E9%80%BB%E8%BE%91/Thu, 15 May 2025 00:00:00 GMT<p>核心逻辑是,将程序中业务,领域知识的部分(domain or business logic)与负责流程控制、协调和决策的部分(control logic)分开,提高代码的可维护性,可测试性和复用性。</p>
<p>在 DDD 中,业务逻辑对应着领域逻辑,控制逻辑对应着「应用层」或「服务层」。</p>
<p><strong>Business Logic</strong></p>
<ul>
<li>内容:业务规则、验证数据、计算公式</li>
<li>职责:确保系统行为符合业务需求和规范</li>
<li>示例:
<ul>
<li>计算订单的总价</li>
<li>判断用户的权限</li>
<li>业务状态的转换</li>
</ul>
</li>
</ul>
<p><strong>Control Logic</strong></p>
<ul>
<li>内容:流程控制、调用顺序、条件判断</li>
<li>职责:组织和管理业务逻辑的执行,保证系统流程正确</li>
<li>示例:
<ul>
<li>处理多步骤业务流程的状态切换</li>
<li>事务开始和提交控制</li>
</ul>
</li>
</ul>
<h2>示例</h2>
<h3>用 Python</h3>
<h4>普通写法</h4>
<pre><code>class UserService:
def __init__(self):
self.users = []
def register_user(self, username, password):
# 1. validate username
if username in self.users:
return {"success": False, "message": "Username already exists"}
# 2. validate password length
if len(password) < 6:
return "Password too short"
# 3. store user
self.users.append(username)
return {"success": True, "message": "User registered successfully"}
</code></pre>
<h4>分离后写法</h4>
<pre><code>class UserRepository:
def __init__(self):
self.users = {}
def exists(self, username):
return username in self.users
def save(self, user):
self.users[user["username"]] = user
# Domain Logic
class UserDomainService:
def __init__(self, user_repository):
self.user_repository = user_repository
def is_username_available(self, username):
# Check if the username is already taken
return self.user_repository.exists(username)
def validate_password(self, password):
# Validate the password according to your criteria
return len(password) >= 6
def create_user(self, username, password):
user = {"username": username, "password": password}
self.user_repository.save(user)
return user
# Application Logic
class UserApplicationService:
def __init__(self, user_domain_service):
self.user_domain_service = user_domain_service
def register_user(self, username, password):
# 1. Check if the username is available
if self.user_domain_service.is_username_available(username):
return {"success": False, "message": "Username already taken."}
# 2. Validate the password
if not self.user_domain_service.validate_password(password):
return {"success": False, "message": "Password does not meet criteria."}
# 3. Create the user
user = self.user_domain_service.create_user(username, password)
return {"success": True, "user": user}
repo = UserRepository()
domain_service = UserDomainService(repo)
app_service = UserApplicationService(domain_service)
print(app_service.register_user("john_doe", "password123")) # Should succeed
print(
app_service.register_user("john_doe", "pass")
) # Should fail due to password criteria
print(
app_service.register_user("john_doe", "newpassword")
) # Should fail due to username already taken
</code></pre>
<ul>
<li><strong>UserDomainService</strong> 只关注业务规则(用户名是否存在、密码校验、创建用户)。</li>
<li><strong>UserApplicationService</strong> 负责流程控制(调用顺序、返回结果)。</li>
</ul>
<h3>用 Java</h3>
<h4>普通写法</h4>
<pre><code>import java.util.HashSet;
import java.util.Set;
public class UserService {
private Set<String> users = new HashSet<>();
public Result register(String username, String password) {
// 1. Check if the username is available
if (users.contains(username)) {
return new Result(false, "Username is already in use");
}
// 2. Validate the password
if (password.length() < 6) {
return new Result(false, "Password must be at least 6 characters");
}
// 3. Create the user
users.add(username);
return new Result(true, "success");
}
public static class Result {
private boolean success;
private String message;
public Result(boolean success, String message) {
this.success = success;
this.message = message;
}
public boolean isSuccess() {return success;}
public String getMessage() {return message;}
}
}
</code></pre>
<h4>分离后写法</h4>
<pre><code>import java.util.HashMap;
import java.util.Map;
// Domain Logic
class UserDomainService {
private UserRepository userRepository;
public UserDomainService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public boolean isUsernameTaken(String username) {
return userRepository.exists(username);
}
public boolean validatePassword(String password){
return password != null && password.length() >= 6;
}
public void createUser(String username, String password) {
User user = new User(username, password);
userRepository.save(user);
}
}
// Persistence Layer
class UserRepository{
private Map<String, User> users = new HashMap<>();
public boolean exists(String username){
return users.containsKey(username);
}
public void save(User user){
users.put(user.getUsername(), user);
}
}
// User Entity
class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
}
class Result {
private boolean success;
private String message;
public Result(boolean success, String message) {
this.success = success;
this.message = message;
}
public boolean isSuccess() {return success;}
public String getMessage() {return message;}
}
// Application Logic
class UserApplicationService {
private UserDomainService userDomainService;
public UserApplicationService(UserDomainService userDomainService) {
this.userDomainService = userDomainService;
}
public Result register(String username, String password) {
if(userDomainService.isUsernameTaken(username))
return new Result(false, "Username taken");
if(!userDomainService.validatePassword(password))
return new Result(false, "Invalid password");
userDomainService.createUser(username, password);
return new Result(true, "success");
}
}
public class scratch_46 {
public static void main(String[] args) {
UserRepository userRepository = new UserRepository();
UserDomainService userDomainService = new UserDomainService(userRepository);
UserApplicationService userApplicationService = new UserApplicationService(userDomainService);
Result r1 = userApplicationService.register("alice", "12345");
System.out.println(r1.getMessage());
Result r2 = userApplicationService.register("alice", "123456");
System.out.println(r2.getMessage());
Result r3 = userApplicationService.register("alice", "1234567");
System.out.println(r3.getMessage());
}
}
</code></pre>
<ul>
<li><strong>UserDomainService</strong> 负责业务规则(用户名是否存在、密码校验、创建用户)。</li>
<li><strong>UserApplicationService</strong> 负责控制流程(调用顺序、返回结果)。</li>
<li><strong>UserRepository</strong> 模拟数据存储。</li>
<li><strong>User</strong> 领域实体。</li>
</ul>
<h2>提示与建议</h2>
<ol>
<li>从简单方法开始构思</li>
<li>简单方法转换到逻辑控制层</li>
<li>分离出业务逻辑</li>
</ol>
往返于法兰克福和慕尼黑之间https://blog.kaiyikang.com/posts/2025/%E5%BE%80%E8%BF%94%E4%BA%8E%E6%B3%95%E5%85%B0%E5%85%8B%E7%A6%8F%E5%92%8C%E6%85%95%E5%B0%BC%E9%BB%91%E4%B9%8B%E9%97%B4/https://blog.kaiyikang.com/posts/2025/%E5%BE%80%E8%BF%94%E4%BA%8E%E6%B3%95%E5%85%B0%E5%85%8B%E7%A6%8F%E5%92%8C%E6%85%95%E5%B0%BC%E9%BB%91%E4%B9%8B%E9%97%B4/Sun, 30 Mar 2025 00:00:00 GMT<p>我们从未认真商量过具体几点起床,几点出发,只是估计了个上午九十点钟。等着起了床,简单收拾了行李,看着时间都快到了 11 点,才悻悻的出发前往停车场。</p>
<p>从家出发走到停车场,我们路过火车站,看到行人们错落的站在站台上,听到喇叭里面的信息广播。景象和声音召唤着我的大脑回路,仿佛下一秒我就要岔开路,走向站台,但随即转念一想,有了车,大部分情况下,我们就无需光临德铁的车厢了。霎时间,竟然生出了些许感慨,但一想到德铁延误给我造成的糟糕感受,感慨也就烟消云散了。</p>
<p>去的路途全是由她完成的。我曾询问她是否需要换着来开,她似乎状态很好,说着不用,就一直开完了全程。</p>
<p>从 A5 一路向南,到了斯图加特掉转方向进入 A8 就能直接抵达慕尼黑。</p>
<p>还记得一路上的天气由阴转阳,开始是乌云密布,小雨淅淅沥沥的,随着向南驾驶,大片的云散开,露出了太阳。我坐在副驾驶,看着变换的风景从眼前飞过,尤其是从狭窄到宽阔处,令我的印象尤其深刻。单向是宽阔的四车道,即便是在最右侧也可以疾驰到 130 以上,两旁是绿树林,远处有些恍惚的小山,天上的云则是格外的肥厚。一会儿是清淡飘逸的,一会儿又是浓密厚重的,天气也随着速度盘飘忽不定。</p>
<p>去程的时候,我们没有特地的准备音乐,只是上了高速以后才慢慢甄选。我们以「印象派」的方式进行选取,印象深的排在最前面,而后是浅的。最深的,自然要数《超高音质 - 车载必备金曲》。只记得每个大叔大妈的车里都会有多张类似的 CD 碟片,专辑没有固定的名字,但必然是这些形容词的任意排列组合,而且最后歌手那个条目也必然是「艺术家」、「群星」之类的,不明所以的称呼。味道真是冲,但也真的醇。</p>
<p>「内蒙古」,「草原」,「西藏」等等元素似乎更受老一辈的青睐,「纠结的情爱」都要往后靠一靠,要么是中年男人浑厚的嗓音,要么是奉为国宝艺术家的女高音,各类杂七杂八的气质都会汇聚在一张 CD 中。小时候听多了,听烦了,自己开上车以后竟然格外的带感,难道这就是所谓的老一辈的智慧?「诶嘿~嘿诶~」突然一阵俏皮的声音出现在了山歌中,把我一股股得抽回到了儿时的国内记忆中。看来,这也不失为一个感受回国气息的好方法。</p>
<p>高速公路上,每过一段间隔,都会遇到休息站。站点的排布似乎非常有讲究,因为只要下体的膀胱开始抽动,等再开上十几分钟,就能见到快意释放的地方,所以,我大胆假设,这些站点的排布距离,也许就是根据成年人的平均膀胱尺寸和忍耐时间综合计算出来的,所以才会如此之精妙,让我们坐在车中啧啧称赞。</p>
<p>休息站的消费感非常浓重,只要停下来,就让你不得不消费。除了昂贵的油费,还体现在厕所和食物的消费方式上。如厕要一欧元,投入硬币就会吐出一张一欧元的代金券,店内消费的时候,就可以使用它减免。感觉商店中的物价是平常的 1.5 倍,再加上代金券,价格其实还好,但购买一件东西才能使用一张券,因此多出来的券只好自己存着,筹划着下次再用。如厕是必须的,因此拿到券也是必然的,为了让自己感觉「值一点」,所以券也得用出去,这不又得买一杯咖啡,完后喝完坐车里继续等着咖啡因催尿,从而实现下一个休息站的如厕和消费循环。这些人,精得很,也坏得很。</p>
<p>大概开了 5 个小时,终于到了慕尼黑,她累到沉睡过去。我没打扰,心里联想到了欧盟长途安全规定,说的是司机开长途,每次驾驶 4.5 小时后都要休息 45 分钟。这很合理,毕竟开车确实消耗精力,但对比国内却有不同。记忆中,在中国似乎不会有大巴车会司机会停下休息,又类似于土耳其的司机,穿个白褂子,中途还会停靠揽行人,休息最多就是喝口咖啡或是茶水。他们都像是大仙,盘着手里的巨大方向盘,在曲折的山路里各种游走,好不自在,但赶来赶去说白了还是为了能多赚些钱,多点休息的时间,为此在无形之中修炼并精进了各种驾驶能力。令人钦佩,但也确实是辛苦。</p>
<p>因为我们已经领略过了 A5 的狭窄,所以回程我选择从 A9 转 A3,先是向北,再向西。实际体验下来也确实更好一些,道路更加宽阔,沿途的设施也更齐全。</p>
<p>有了上次的长途驾驶经验后,我们两人有了经验,只要要提前存储好歌单,以免手忙脚乱,不知道听些什么。因为要检索自己最喜欢的歌曲,所以一不小心就鉴赏到了凌晨,最后整理出来了一张长长的列表。我们几乎没有添加重复的歌曲,蹊跷的是,唯独「套马杆」被添加了两次,兴许是草原和骏马的魅力,让我们的选择不谋而合,做得好!</p>
<p>相对于她的稳扎稳开,我驾驶则稍微激进些。指示牌,车,我,三者的最低限决定了速度的上限,习惯以后,我能从超车中感受到了些快乐,当然,一切还要以安全为准。</p>
<p>天气这次是反着的,由阴转阳。太阳逐渐从云中冒出来,光线照在手臂和身上,暖呼呼的。得益于宽阔的道路和丰富的植被,视野和景观非常好。看着山上的小城逐渐出现,再逐渐隐去,脑子里也会幻想着他们截然不同的生活方式,不以围绕着繁华中心而活的生活方式。</p>
<p>开了两个多小时,我们找到了一个膀胱休息站,停靠了,上过厕所没有去买什么吃的,而是从袋子里拿出了她做的简单鱼汉堡。太阳被云雾笼罩着,天气变得阴阴的,风也在刮着。我们两人坐在车里,听着财经播客,一边吃着汉堡一边点评,口干了再嘬两口咖啡,还要防止汉堡渣渣掉进干净的车里。也许是因为开车不熟练的缘故,眼睛感到胀胀的,因此就把驾驶的宝座交给了她,让她负责之后的行程。</p>
<p>熟练了以后,她开的更好了,车速控制得当,超车也更从容了。从中我们还获得了一个经验,就是遇到什么事情,先不要惊呼出来,而是稍微冷静的直接说明。因为惊呼会让驾驶的人感到慌张,而一慌张操作就会变形,车开不好,还闹的情绪不佳,所以抑制住呼喊的冲动,保持平和且镇静,对大家都好。</p>
<p>一路上风平浪静,进了城,驶入了停车场。身体似乎没有之前的那样疲惫,也逐渐了习惯了开车和坐车的感觉,往后大概会越来越频繁了。</p>
快慢打字,深深深https://blog.kaiyikang.com/posts/2025/%E5%BF%AB%E6%85%A2%E6%89%93%E5%AD%97%E6%B7%B1%E6%B7%B1%E6%B7%B1/https://blog.kaiyikang.com/posts/2025/%E5%BF%AB%E6%85%A2%E6%89%93%E5%AD%97%E6%B7%B1%E6%B7%B1%E6%B7%B1/Mon, 13 Jan 2025 00:00:00 GMT<h2>慢写字,快打字</h2>
<p>就在刚才尝试用笔写了一些字。不仅书写速度很慢,字在脑子里还要思维很久才能成型,而对比电脑打字,就更加缓慢了。</p>
<p>因为长时间没有书写过文字,这种对比另我惊讶。哆哆嗦嗦的手像老年人一样写字,眼睛看着手,脑子站在一旁干着急,想写的话已经在脑子中重复了好几遍,手还没写完前几个字。</p>
<p>我不知道是不是,但感觉上,缓慢的写字更符合自然的规律,也符合大脑的规律。极速的拼音输入法,一目十行,乃至思维,也飞速奔驰,完全停不下来。于是,人们嫌弃手写,也不再尝试手写。当人们长大了,从学校中出来,似乎再也不会在桌子上留下纸与笔,除了简单的速记,也不会写太长太长的。</p>
<p>这种符合自然天性的缓慢,经过了时光的结晶,成了书信和贺卡,漂流进每个人都最珍爱的宝盒中。字迹变得模糊,纸张变得发黄,但质感和情绪依旧在那里。</p>
<p>这是真实写字的独特魅力。</p>
<h2>深深深</h2>
<p>前有 Cal 的 Deep Work,后有 David 的 Depth Year。这就像是常年身处都市的人,去购买性能完全冗余的户外冲锋衣。我爱人她也问过我,我说这就像是一种向往,虽然自己没能达到,但是购买了,穿上了,就仿佛自己有了这种能力,愿景也变得稍微真实了一点点。</p>
<p>对于 Deep 这个概念,我有类似的想法,很迷人,但是非常难做到。我会涉猎相关的主题,有一搭没一搭的,读着他们的概念,我就开始想象自己在做事的时候,也能变得十分 deep。而直到现在,我仍然觉得自己没有达到我所理解的 deep 状态,因此看着爱人在专心做事的时候,我会觉得尤其充满魅力。弱的时候,房间内的能量向她聚集,强的时候,宇宙能量向她发射高浓度射线。作为一个能量代理人,她只是投身在想做的事情上,而全然不会顾忌能量积聚这件事上。原初且纯粹,真是令人着迷。</p>
<p>关于潜水,爱人是 freedriver,其中一个目标就是要潜水足够 deep。因此,这个意象会忽然不自主的和先前所提到的概念相互联系起来。我觉得它们具有一种感性上的关联。当我在尝试深入一件事情,而非真正深入意见事情的时候,三心二意会将我拉回到现实中,会让我重新感知到我在做一件不能中断的重要事情。简单的想象是,我在做一件重复的劳动,这样的劳动,每一次都需要伴随着惯性,要足够精确,要足够耐心,一旦分心,动作出现了偏差,那么劳动的结果就不会令人如意。我有明确的感觉,可能开始的几分钟我完全投入了,思维也完全沉浸或放空了,但坐着坐着,脑子突然升起了大的思维,将我从惯性抽离出来,然后我不再变得沉浸,反倒感觉恐惧,害怕自己的中断,担心事情无法顺利的执行下去。惶恐就像是正反馈电路一样,不断加强信号,从而把我彻底丢入到上空,直到无法生存的宇宙中。</p>
<p>如果说上面的 deep 是时间上的和动作事物上的,还有一种则是精神上的。这点我同样也有感触,类似于背诵快忘没忘的单词,做一个还有些许记忆的代码题目,或读一本已经有模糊记忆的书籍。当我再次遇到他们的时候,我感觉自己在接触记忆或精神上的鼻涕。有沾上了一点,但也不多,牵拉一下还能成丝线,我也不知道什么时候回中断,有轻微的恐惧感,但更多是嫌弃。不够干脆清爽,不像是用一个带油的勺子用过了洗洁精,带着阻力的愉悦感。每当这种感觉袭来的时候,我会有些打退堂鼓,但次数不算多,很多时候是一咬牙一跺脚把那一大坨东西拿过来,让它变得轮廓清晰分明。一旦克服了,就会有别样的愉悦感和成就感。</p>
<p>做的要够慢且够深,我觉得也算是一种做事智慧。</p>
Choosing a Clear Namehttps://blog.kaiyikang.com/posts/2025/choosing-a-clear-name/https://blog.kaiyikang.com/posts/2025/choosing-a-clear-name/Fri, 28 Mar 2025 00:00:00 GMT<p>'Whitelist configuration' is a technical term, but the associated context and the resulting consequences are what truly matter, and this information should be documented.</p>
<p>Specifically, when the 'whitelist' is modified, the system reads it during initialization. When the system reaches the 'ServiceInfoCalculation' phase, it triggers the 'ServiceStatusChangedDetector' to check for differences between the new and old 'ServiceInfo'. These changes are then sent out and broadcast through various channels. Crucially, only changes related to services listed in the 'whitelist' can actually be sent.</p>
<p>In other words, the 'whitelist' is merely one component in the message sending process, indicating that this particular technique is employed. Other techniques, such as using a 'hardcode array', could also serve the purpose. For documentation purposes, the primary focus should be on the use case—the impact and consequences of applying the technology.</p>
<p>Describing requirements solely centered around the term 'whitelist' can confuse readers or users, making it an unsuitable approach. Ultimately, describing the overall process should be the main focus.</p>
命名:起一个明白的名字https://blog.kaiyikang.com/posts/2025/%E8%B5%B7%E4%B8%80%E4%B8%AA%E6%98%8E%E7%99%BD%E7%9A%84%E5%90%8D%E5%AD%97/https://blog.kaiyikang.com/posts/2025/%E8%B5%B7%E4%B8%80%E4%B8%AA%E6%98%8E%E7%99%BD%E7%9A%84%E5%90%8D%E5%AD%97/Fri, 28 Mar 2025 00:00:00 GMT<p>配置白名单,是一个技术术语,与它关联的上下文以及所造成的结果才是最重要的。这些信息应该被归档。</p>
<p>具体而言,当我们修改了白名单后,系统会在初始化的时候读取白名单。当系统进行到 ServiceInfoCalculation 的环节时,会触发 ServiceStatusChangedDetector 以检查新的 ServiceInfo 和旧的区别。这些变化将通过不同方式被发送,被广播。而其中只有在 whitelist 中的 Service,它的变化才能够被广播出去。</p>
<p>换句话说,白名单只不过是消息发送其中的一个环节,代表该处使用了这个技术,而其他的技术也可以被使用,比方说使用 hardcode array。对文档来,最重要的是 use case,是使用该技术所产生的影响和后果。</p>
<p>如果单纯以白名单作为中心,描述需求,会让读者或使用者感到困惑,是一种不合适的方式。最终,我们都应以描述整体作为关键。</p>
蹦床https://blog.kaiyikang.com/posts/2025/%E8%B9%A6%E5%BA%8A/https://blog.kaiyikang.com/posts/2025/%E8%B9%A6%E5%BA%8A/Sat, 01 Mar 2025 00:00:00 GMT<h2>小时候的蹦床</h2>
<p>在我 30 岁生日的时候,爱人提出了几个建议供我参考,其中一个是去玩电动卡丁车,另一个是玩蹦床。我琢磨了下,之后买了车可以肆意的开,不需要再特地花钱去开卡丁车,因此选择了前者去跳蹦床。</p>
<p>我小时候就喜欢跳蹦床。那时长辈会带我去地坛公园玩耍,记得是从安定门桥下来的一个门进入公园,走过一段植满树木的路,就能到游乐园。游乐园虽然特别小,也很简陋,但我也很小,对比之下它就显得非常大。</p>
<p>游乐园没有一个所谓的大门,各种玩乐设施随意的散落在各个角落,而在门口处的钓鱼和再往里面的蹦床是我的最爱。钓鱼远没有名字听上去的高大上,只记得就一个老头儿坐在低矮的板凳上,面前铺着塑料布,上面摆着钓到鱼以后的奖励玩具,旁边是个不大不小的「池塘」。充气的泳池里灌满着水,里面漂浮着五彩的塑料小鱼。小鱼一直在随着流动的水游动,至于水如何是如何变活的,我记忆模糊了。</p>
<p>我常会在水池边上钓得入迷,每次不拿到一个塑料小恐龙之类的玩具,决不罢休返回。</p>
<p>让我沉迷的不仅仅是钓鱼,往里走的蹦蹦床更是我的挚爱。为了保护小孩的安全,蹦床是封闭的,依稀的记得它有一道小铁门,孩子钻进去以后,老板会从外面关上,如果想要出去,就叫嚷一声,他就会起身上来给我们开门。</p>
<p>蹦床似乎不止有一层,入口进去就是蹦床层,下面的一层铺着塑料小球,上面则是用网编织的通道,小孩可以从中来回穿梭。当然,我呆的最久的还是蹦床。蹦床的网编织的并不致密,透过方形的孔可以向下望,有时我蹦累了,就会趴在网上,透过小洞向下观望。五颜六色的弹性绳灰突突的相互重叠在一起,长久的使用痕迹显得蹦床很陈旧,但并不影响我跳跃的心情。我只记得身体很轻盈,每跳一下都像是飞了起来,为了让自己不停的飞,我会不停的跳。跳的时候,我会摆弄身体,让它在那一瞬间呈现出奇妙且怪异的形状。我觉得我成了异形。</p>
<p>跳到尽兴,我就往周围一圈的软垫靠上去,头向着护栏外张望,找寻着长辈,想知道他们正在做什么,如果找不到踪影或仍聊的尽兴,我就会放松一口气,等待片刻又会重新回到蹦床的中央。</p>
<p>从蹦床出来以后,脚上还有刚刚弹软的触感,走在坚实的大地上还觉得有些不适应。沉溺在上一刻的身体,反倒觉得脚下的坚实才是幻觉,于是不自主地用力一跃,等着脚底心和大地结结实实地碰撞在一起,我这才意识到,原来这是在回家的路上。</p>
<h2>长大后的蹦床</h2>
<p>一片宽阔的室内场地被分割成不同的区域,在区域中散落着切成矩形的蹦床。蹦床的材质编织的非常致密,看不清任何孔隙。</p>
<p>时隔多年再跳上蹦床,脚下的坚实触感消失不见了,身体被抛在了半空中,虽然很短暂,但对身体的感知和体察却变得全方位起来。我只觉得身子变得巨大和沉重,前几日健身所造成的肌肉阵痛还没有消退,飘在空中,任何一个身体的扭动和造型都需要付出大量的能量。</p>
<p>我对这样的变化感到有些吃惊,不过大概是身体还没有得到充分热身,这样的疲倦在蹦跳过一阵后就逐渐缓解,身体也变得越来越灵活。</p>
<p>有几个场地布置了跳跑的障碍物,就是在蹦床之间加入了高低和形状不同的障碍物,灵巧的人可以迅速的翻越,像是一个专业的跑酷者。而不太熟练的人则需要在每个蹦床之间做停歇,上下跳动,寻找一个合适的契机再翻越。</p>
<p>我做了些许尝试。也许是不知道正确的跳落和衔接技巧,身体到了半空中,盯着眼前的障碍物会不知所措。「哦!那个障碍物突然出现了,我应该立刻做些什么?它太高,太远了,我够不到…现在高度合适,我可以跳了!….不,我错过了那个最佳的时机,要等下一次跳跃了。」内心需要足够的准备才能开始,但那一瞬间只会在身体到达最高点才会出现,转瞬即逝间,身体就会掉落,即使意识准备的再充分,不去真正的跳跃也是无济于事。</p>
<p>这成了勇敢者的游戏。身体升到最高点,意识快速的准备,勇气是绳索和抓手,让我迅速能够把握这快速的一瞬做出真正的反应,我朝着皮质软垫撞了上去。着陆的姿态有些狼狈,但我还是抓住了障碍物,手脚并用的爬上了它,不太利索的通过了第一个障碍。</p>
<p>类似的过程要重复数次,直至意识心对此感到麻木,不再需要太多时间的思索和准备,身体和勇气已经提前做出了判断。经过多次跳跃,我总结出经验,心中突然出现的胆小鬼会让自己受到伤害。因为一切都已经就位了,如果身体突然卸力平衡了蹦床的弹力,身体就会留在地面上然后径直撞向障碍物。这可不是好迹象。</p>
<p>心思越少,玩的越好,你会看到在场的许多小孩子都会玩得无比轻松和写意。</p>
<p>临走前,我和爱人得出的结论是,蹦床应该是越年轻玩越好,等身体变得不再那么灵巧了,虽然也可以玩,但却少了很多的乐趣。身体漂浮在半空中,只有短暂的瞬间,这像是童年和青年的时光,顷刻之间就成了过往的回忆。</p>
Dahab - Arriving at the Mountains at Dawnhttps://blog.kaiyikang.com/posts/2025/dahab-1-arriving-at-the-mountains-at-dawn/https://blog.kaiyikang.com/posts/2025/dahab-1-arriving-at-the-mountains-at-dawn/Tue, 08 Apr 2025 00:00:00 GMT<p>Landing at 4 AM after an overnight flight, we were completely exhausted. The airport was small with visibly aging facilities. At the welcome entrance, Egyptian totems were symbolically arranged alongside scattered fake palm trees against a low-resolution printed backdrop—giving the whole setup a distinctly plastic, artificial feel. We glanced at it with curious interest before hurriedly following the crowd through immigration, taking our luggage, exchanging some currency, and exiting the airport within minutes.</p>
<p>Initially, we had been quite concerned about arriving in a completely unfamiliar place in the early morning hours with our energy depleted. Wouldn't this create problems if we hadn't made proper arrangements? To prevent any issues, I had specifically asked our contact detailed questions like "What are the procedures? What information will we need?" The response was surprisingly casual—nothing was needed, everything was arranged, just show up. Though still apprehensive, I reluctantly accepted this answer.</p>
<p>In fact, we overthink everything. Outside the airport gates, despite the early hour, the square was brightly lit with people lining both sides. Scanning quickly from left to right, I immediately spotted our driver. He was holding a thin piece of paper with "Dahab Kang" written on it. Though the writing was delicate and fragile, it was perfectly legible.</p>
<p>This exemplifies their approach to handling matters—primitive yet direct. I often realize how deeply modern urban life has influenced my behavior. For any task, I feel compelled to consider and follow external procedures and restrictions, without truly understanding their purpose. This vagueness creates irrational anxiety about the consequences of procedural errors. You probably understand that in city life, missing a specific document means running back and forth, wasting considerable time and energy. By comparison, their direct problem-solving approach, which permeates nearly every aspect of local life, stands out starkly. I suddenly felt embarrassed, realizing how many unnecessary concerns had been occupying my time.</p>
<p>We greeted the driver cordially. Without much conversation, he led us through the crowd toward the parking lot. The path was uneven and potholed, with exposed gravel along the curbs. The streetlights flickered inconsistently, fragmenting the illumination.</p>
<p>The driver stopped at a small car, indicating it was our ride. Inside, the rustic decor reminded me of 1990s Beijing taxis. A red soft cloth decorated with yellow speckles covered the dashboard beneath the windshield. Many parts of the car were covered with transparent plastic sheets, which I couldn't tell whether they were original wrappings never removed or deliberately added to protect against dust. Sitting on the left side of the back seat, I was struck by the door handle wrapped in white tape, which became my identifying marker when we rode the same car later. The rear windshield was covered with black plastic mesh, likely serving the same purpose as the red cloth up front—reducing interior temperatures during desert sun exposure.</p>
<p>The car started with a sputtering engine that clearly wasn't in prime condition. As we slowly drove away, looking through the windows revealed almost no well-maintained vehicles in the parking lot. By "well-maintained," I don't mean luxury brands, but simply cars that appeared intact externally. Like the roads and facilities, most vehicles here showed significant wear. I couldn't tell what storms these cars had weathered—not a single patch of paint remained intact, metal frames were warped, bumpers had pieces missing here and there, and having all headlights working was considered quite an achievement.</p>
<p>Most cars here had been stripped back to their original form—simply tools with four wheels for carrying people. The word "tool" describes them perfectly; these vehicles no longer carried status symbols, and luxury features seemed meaningless in the desert environment. Instead, those four wheels and a shaded interior were precisely what locals needed most.</p>
<p><img src="assets/2025/IMG_3219.JPG" alt="" /><em>A relatively intact taxi</em></p>
<p>Consequently, sturdy and durable Nissan vehicles dominated the streets. Small ISUZU pickup trucks served as versatile transportation between desert and towns, perfect for carrying both people and cargo, visible everywhere locally. Slightly larger minivans, often retrofitted with longitudinal benches in the back to accommodate eight passengers, were ideal for small road trips. We occasionally rode in better-maintained vehicles with functioning air conditioning and comfortable seats, but after becoming accustomed to these "broken cars," the nicer ones felt almost uncomfortable. Walking around, whenever we spotted high-end brands, we would joke that such delicate vehicles were completely unsuited for this harsh environment.</p>
<p>These cars always remind me of the saying "rough words but sound reasoning." The "words" represent surface appearances, while the "reasoning" embodies the underlying concept and philosophy. Local residents continuously interpret and implement this principle—though their vehicles appear rough externally, the essential functions remain completely adequate. Any superfluous elements eventually get polished away by the sea and desert until they disappear completely.</p>
<p>Around the airport, scattered streetlights provided some illumination, but heading north, darkness enveloped everything. The road was nearly empty of other vehicles, and the lane markings on the asphalt had lost all practical purpose. The road layout featured bold, sweeping designs with long straight sections and gentle curves. Sparse traffic signs reflected lonely light under our headlights. The Arabic text lacked English translations, leaving their messages incomprehensible to us.</p>
<p><img src="assets/2025/IMG_3223.JPG" alt="" />
<em>It's completely dark outside</em></p>
<p>Our headlights, complemented by moonlight, outlined the silhouettes of mountains on both sides. Their forms appeared pitch black, and only by looking upward along with the sky could we barely discern the mountaintops. As we sped past, these shapes silently dissolved into the night, vanishing completely.</p>
<p>Physically and mentally drained, we drifted in and out of sleep with the vehicle's movements. Our shy driver spoke little, his voice soft—perhaps intentionally avoiding disturbing our rest—and played no music or radio. The night grew increasingly serene, and we three seemed to exist in a special dimension, rapidly floating forward.</p>
<p><img src="assets/2025/IMG_3628.JPG" alt="" />
<em>Dawn broke, driving in the desert</em></p>
<p>After an indeterminate period of drowsiness, the sky gradually brightened. Opening my eyes, I realized we were surrounded by mountains. Desert stretched everywhere without vegetation, and distant mountains stood starkly bare and abrupt. While this represents typical desert scenery, for those accustomed to skyscrapers and verdant mountains, it felt remarkably fresh. From the back seat, we exchanged smiles, our eyes reflecting the faint dawn light, silently communicating our curiosity and excitement at these unusual mountain vistas.</p>
<p><img src="assets/2025/IMG_3224.JPG" alt="" />
<em>Desert outside the car window</em></p>
<p>As we exited the mountainous region, we approached the checkpoint at the town entrance. Though not large, these checkpoints occupied all key entry and exit points. We had passed one when leaving the airport, though darkness had obscured it. In daylight, everything appeared clearer. Iron barriers blocked the road at the checkpoint, with small huts housing police and soldiers. Armored vehicles and armed personnel created an intimidating atmosphere, immediately changing the mood.</p>
<p>Such checkpoints dot various areas across the peninsula. Traveling between towns by car inevitably means encountering them. We needed to keep our passports readily accessible for verification. The inspection standards varied dramatically—sometimes the driver could pass with just a few words (whose meaning and special significance remained mysterious to me), while other times they would stop the car for several minutes before allowing passage. It's difficult to imagine how non-Arabic speakers could navigate between towns independently. Self-driving seemed impossible; local assistance was essential. The towns existed as distinct, separated entities, and leaving one always created a sense of disorientation. The contrast between town vibrancy and external stillness was striking, fragmenting our continuous journey into discrete segments.</p>
<p>After clearing the checkpoint, our long-silent driver softly announced, "Welcome to Dahab." Through the car windows, we noticed promotional slogans on nearby walls marking the town boundary. Constrained by the armed military presence, we didn't dare photograph this memorable moment, instead turning our heads to mentally capture the significant landmark for a few extra seconds.</p>
<p>As we continued driving, the vast sea gradually came into view in the distance along the descending avenue. My heart rate quickened slightly as familiar scenery began transforming—the sea slowly inserting itself between sky and stone. This marked the true beginning of our journey; after enduring a long night of wakefulness and travel, we had finally reached the small town of Dahab.</p>
Database Security Configuration and Connectivity for Java Applicationshttps://blog.kaiyikang.com/posts/2024/database-security-configuration-and-connectivity-for-java-applications/https://blog.kaiyikang.com/posts/2024/database-security-configuration-and-connectivity-for-java-applications/Wed, 20 Nov 2024 00:00:00 GMT<p>I have an enterprise Java application and want to securely manage database connection configuration.</p>
<p>To address this requirement, this article will briefly describe how to securely manage database credentials with HashiCorp Vault and how to configure JPA for database connectivity in a Kubernetes environment.</p>
<h2>Key management</h2>
<h3>Key storage</h3>
<p>In a Kubernetes environment, we use HashiCorp Vault as the key management tool. The configuration is stored in the `values.yaml' file, which uses the Helm configuration values:</p>
<pre><code># values.yaml
secretList:
- name: database-credentials
vaultPath: /secret/database/prod
</code></pre>
<h3>Key acquisition process</h3>
<ol>
<li>store sensitive information (e.g. database username, password) in the Vault</li>
<li>retrieve credentials information from the Vault via <code>vaultPath</code></li>
<li>use <code>name</code> as a reference identifier (e.g. <code>database-credentials.username</code>)</li>
</ol>
<h2>Configuration File Management</h2>
<p>When the application is deployed, Kubernetes injects the values from <code>values.yaml</code> into the application configuration file, which mainly consists of:</p>
<ul>
<li><code>context.xml</code> (Tomcat data source configuration)</li>
<li><code>persistence.xml</code> (JPA configuration)</li>
<li><code>application.properties/yml</code> (Spring configuration)</li>
</ul>
<p>In the configuration file, we use the placeholder format: <code>^CFG:DATABASE_PASSWORD^</code>.</p>
<h3>Tomcat data source configuration</h3>
<p><code>context.xml</code> is Tomcat's context configuration file for creating and managing data sources and other container-level resources.</p>
<p>Example:</p>
<pre><code><Context>
<Resource
name="jdbc/MainDatabase"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb"
username="^CFG:DATABASE_USERNAME^"
password="^CFG:DATABASE_PASSWORD^"
maxTotal="20"
maxIdle="10"
maxWaitMillis="10000"/>
</Context>
</code></pre>
<p>Detailed reference: <a href="https://tomcat.apache.org/tomcat-9.0-doc/jndi-resources-howto.html">Apache Tomcat 9 (9.0.97) - JNDI Resources How-To</a></p>
<h3>JPA configuration</h3>
<p>JPA (Java Persistence API) is a specification/interface, the most common JPA implementation is Hibernate.</p>
<p><code>persistance.xml</code> is the configuration file for jpa:</p>
<pre><code><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
<persistence-unit name="mainPU" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>java:comp/env/jdbc/MainDatabase</non-jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
</persistence>
</code></pre>
<p>where <code>java:comp/env/</code> is the standard prefix for JNDI namespaces, as explained here: <a href="https://stackoverflow.com/questions/11631839/what-is-javacomp-env">jakarta ee - what is java:comp/env? - Stack Overflow</a></p>
<p>You can find the corresponding Resource by using it.</p>
<h2>Application startup process</h2>
<p>Now let's string together the above configuration files.</p>
<ol>
<li><strong>Kubernetes initialization</strong></li>
</ol>
<ul>
<li>Reading <code>values.yaml</code></li>
<li>Get keys from Vault</li>
<li>Replace the placeholders in the configuration file</li>
</ul>
<ol>
<li>**Tomcat startup</li>
</ol>
<ul>
<li>Load the <code>context.xml</code></li>
<li>Initialize the data source connection pool</li>
</ul>
<ol>
<li>**Application deployment</li>
</ol>
<ul>
<li>Reads <code>persistence.xml</code>.</li>
<li>Hibernate initialization</li>
<li>Create database connection</li>
</ul>
<h2>Code example</h2>
<p>By importing the JPA specification and its implementation, Hibernate, as well as the Spring framework, we can properly manipulate the data in the database.</p>
<pre><code>@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
// getters and setters
}
@Service
@Transactional
public class UserService {
// Specify the persistence-unit with the name attribute.
@PersistenceContext(unitName = "mainPU")
private EntityManager entityManager;
public User findById(Long id) {
return entityManager.find(User.class, id);
}
public void save(User user) {
entityManager.persist(user);
}
}
</code></pre>
<h3>Summary</h3>
<p>We have simply implemented a complete database security configuration scheme:</p>
<ol>
<li>key management with HashiCorp Vault</li>
<li>configuration injection via Kubernetes</li>
<li>Combining Tomcat data sources and JPA for database connectivity.</li>
</ol>
<p>This solution ensures security and maintains good maintainability, which is suitable for the development and deployment of enterprise-level Java applications.</p>
<p><!--</p>
<p>我有一个企业级 Java 应用,希望安全地管理数据库连接配置。</p>
<p>针对该需求,本文将简单的介绍如何在 Kubernetes 环境下,通过 HashiCorp Vault 实现数据库凭证的安全管理,以及如何配置 JPA 实现数据库连接。</p>
<h2>密钥管理方案</h2>
<h3>密钥存储</h3>
<p>在 Kubernetes 环境中,我们使用 HashiCorp Vault 作为密钥管理工具。配置信息存储在 <code>values.yaml</code> 文件中:</p>
<pre><code># values.yaml
secretList:
- name: database-credentials
vaultPath: /secret/database/prod
</code></pre>
<h3>密钥获取流程</h3>
<ol>
<li>将敏感信息(如数据库用户名、密码)存储在 Vault 中</li>
<li>通过 <code>vaultPath</code> 从 Vault 获取凭证信息</li>
<li>使用 <code>name</code> 作为引用标识符(如 <code>database-credentials.username</code>)</li>
</ol>
<h2>配置文件管理</h2>
<p>在应用部署时,Kubernetes 会将 <code>values.yaml</code> 中的值注入到应用配置文件中,主要包括:</p>
<ul>
<li><code>context.xml</code>(Tomcat 数据源配置)</li>
<li><code>persistence.xml</code>(JPA 配置)</li>
<li><code>application.properties/yml</code>(Spring 配置)</li>
</ul>
<p>配置文件中,我们使用占位符格式:<code>^CFG:DATABASE_PASSWORD^</code>。</p>
<h3>Tomcat 数据源配置</h3>
<p><code>context.xml</code> 是 Tomcat 的上下文配置文件,用于创建和管理数据源以及其他容器级别的资源。</p>
<p>示例:</p>
<pre><code><Context>
<Resource
name="jdbc/MainDatabase"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb"
username="^CFG:DATABASE_USERNAME^"
password="^CFG:DATABASE_PASSWORD^"
maxTotal="20"
maxIdle="10"
maxWaitMillis="10000"/>
</Context>
</code></pre>
<p>详细参考:</p>
<p>https://tomcat.apache.org/tomcat-9.0-doc/jndi-resources-howto.html</p>
<h3>JPA 配置</h3>
<p>JPA (Java Persistence API) 是一个规范/接口,最常用的 JPA 实现是 Hibernate。</p>
<p><code>persistance.xml</code> 是 jpa 的配置文件:</p>
<pre><code><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
<persistence-unit name="mainPU" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>java:comp/env/jdbc/MainDatabase</non-jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
</persistence>
</code></pre>
<p>其中 <code>java:comp/env/</code> 是 JNDI 命名空间的标准前缀,这里有解释:https://stackoverflow.com/questions/11631839/what-is-javacomp-env</p>
<p>通过它可以找到对应的 Resource。</p>
<h2>应用启动流程</h2>
<p>现在我们将上述的配置文件串联起来。</p>
<ol>
<li>
<p><strong>Kubernetes 初始化</strong></p>
<ul>
<li>读取 <code>values.yaml</code></li>
<li>从 Vault 获取密钥</li>
<li>替换配置文件中的占位符</li>
</ul>
</li>
<li>
<p><strong>Tomcat 启动</strong></p>
<ul>
<li>加载 <code>context.xml</code></li>
<li>初始化数据源连接池</li>
</ul>
</li>
<li>
<p><strong>应用程序部署</strong></p>
<ul>
<li>读取 <code>persistence.xml</code></li>
<li>Hibernate 初始化</li>
<li>建立数据库连接</li>
</ul>
</li>
</ol>
<h2>代码示例</h2>
<p>通过导入 JPA 规范和其实现 Hibernate,以及 Spring 框架,我们可以正确操作数据库中的数据。</p>
<pre><code>@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
// getters and setters
}
@Service
@Transactional
public class UserService {
// 通过name属性指定persistence-unit
@PersistenceContext(unitName = "mainPU")
private EntityManager entityManager;
public User findById(Long id) {
return entityManager.find(User.class, id);
}
public void save(User user) {
entityManager.persist(user);
}
}
</code></pre>
<h3>总结</h3>
<p>我们简单实现了一个完整的数据库安全配置方案:</p>
<ol>
<li>使用 HashiCorp Vault 进行密钥管理</li>
<li>通过 Kubernetes 进行配置注入</li>
<li>结合 Tomcat 数据源和 JPA 实现数据库连接</li>
</ol>
<p>这种方案既保证了安全性,又保持了良好的可维护性,适合企业级 Java 应用的开发部署。</p>
<p>--></p>
Dahab - Early Morning in the Townhttps://blog.kaiyikang.com/posts/2025/dahab-2-early-morning-in-the-town/https://blog.kaiyikang.com/posts/2025/dahab-2-early-morning-in-the-town/Wed, 09 Apr 2025 00:00:00 GMT<p>We took a taxi into the small town, leaving the asphalt road behind and turning onto a dusty path.</p>
<p>The path was covered in dust, flanked by low, densely packed buildings. Calling them buildings might be generous—they were more like square enclosures made from broken walls and door panels. The entrances were cluttered with stones and plastic, which from any distance looked like piles of trash or a construction site about to begin. The road itself was very basic—just sandy soil scattered with various debris, most commonly dog and sheep droppings, along with some plastic waste. The droppings had been baked hard by the sun and heat, turning almost stone-like, odorless, and flies didn’t bother to come.</p>
<p><img src="assets/2025/L1090619.jpg" alt="" />
<em>Dirt road</em></p>
<p>I tried to reconcile these ever-present dirt roads and buildings with my own experiences, and my conclusion was that this place resembled a Chinese "XiaoQu" (residential compound) or a German 30km/h residential zone—but in an Egyptian style. By blending these impressions, I gradually came to accept this new form of neighborhood. The housing here is extremely basic—utterly simple and devoid of design—but highly practical and perfectly suited to the local climate and culture. People who have lived here for generations are masters of "form follows function," but here, it’s all function with no form.</p>
<p>Walking these dirt roads always feels like a small adventure. During the day, flowers and kittens suddenly appear on the walls, and the air carries the faint scent of sheep droppings. At night, under dim light and darkness, I carefully dodge the droppings and trash scattered on the sandy ground. Once you get used to it, it’s not so bad—it has its own unique charm.</p>
<p>Of course, not all buildings here are like that. The hostel we stayed at was quite different. It was a small villa, square and neat, with small windows. The exterior walls and courtyard were clean and tidy, with flowers and greenery growing taller than our heads, spreading widely and visible from afar. There are quite a few similar small buildings in town, but most have been converted into Airbnbs or hostels. These are mostly clustered in the town center. Though the facilities aren’t the newest, they’re fully functional and show many modern touches.</p>
<p>Larger resorts are located on the outskirts of town. We didn’t like those places much. Every time we passed by and peeked inside, they felt deserted. Railings and facilities were badly corroded by the sea breeze. Despite having many rooms, few people seemed to live there—it was cold and empty. In short, if you ever come here, I highly recommend staying in a hostel or Airbnb rather than a resort hotel.</p>
<p><img src="assets/2025/L1090630.jpg" alt="" />
<em>Some courtyards have more refined doorways</em></p>
<p>After getting out of the car, the reserved but attentive driver helped us carry our luggage. Since we arrived too early, we had to knock repeatedly and call to wake people up. After a few minutes, the person in charge, Mr. Z, opened the door, looking sleepy but greeting us warmly. After thanking the driver, we left our luggage in the hallway. Mr. Z briefly introduced the hostel’s facilities and rules.</p>
<p>Mr. Z is a tall, dark-skinned Egyptian youth, usually dressed in sportswear, responsible for all hostel affairs. Every morning when passing through the living room, we’d see him lying on the sofa, either napping or listening to music. But reliable sources say this isn’t laziness -- he works out early every day and only rests on the sofa after finishing his routine. What we saw was him relaxing after a workout.</p>
<p>What we appreciated most was his encyclopedic knowledge. From hostel management to organizing activities, even small questions like where to print documents, he knew the answers and responded quickly. In this place with limited internet access, map apps barely work and are often outdated. For the latest firsthand info, you have to ask him. We felt lucky to choose this hostel —- a solid, friendly, and reliable local who made our trip smoother.</p>
<p>In the hostel’s living room, right by the main door, there’s a water dispenser. I especially liked the free ice water and drank several cups every time I returned. The living room had several sofas and a TV. Further inside was the kitchen and a long table. The red tea in the kitchen was free, but other instant drinks required self-payment.</p>
<p><img src="assets/2025/L1090611.jpg" alt="" /></p>
<p><em>Morning at the hostel</em></p>
<p>Egyptian red tea is a powdered tea called "El Arosa Tea," sold cheaply in large bags. To make it, you scoop about half a spoon of powder, no filter needed, pour hot water over it, and wait for the water to turn a bright sunset red before drinking. Compared to red tea, coffee—which is common in Europe—doesn’t seem to be popular here. At least, the coffee I’ve had these days was mediocre and lacked aroma, while the red tea was very satisfying.</p>
<p>The hostel’s backyard is very pleasant, with comfortable sofas, mats, and long tables and benches. Nearby greenery adds a natural touch. The surrounding walls block sunlight, so even on hot days, the shade feels cool.</p>
<p><img src="assets/2025/L1090618.jpg" alt="" /></p>
<p><em>Courtyard wall</em></p>
<p>People staying here often lounge in the courtyard, eating, drinking tea, and chatting. I do the same. When I see someone, I try to strike up a conversation, though I’m not very good at it—especially when they’re focused on screens or engaged in lively discussions. I’ve never figured out how to start or end casual chats smoothly. They feel like dreams—no clear beginning or end when recalled.</p>
<p>I’m like a fish, drifting without strong opinions, chiming in when there’s a topic, and slipping away when I can’t think of new ideas or express complex thoughts. Socializing is more challenging than enjoyable—thinking of topics, joining discussions, expressing opinions while trying not to offend. Doing this in a non-native language makes it even more exhausting.</p>
<p>Paradoxically, socializing is a skill—the more you do it, the better you get; if you don’t, you get worse. So the environment pushing me to change is crucial. I feel that living in Germany sometimes makes it hard to sustain this positive evolution. When I speak broken German, I often get silence or indifferent responses. I know there’s no malice, but without feedback, I never know if I’m understood or just talking to myself. In contrast, people from sunny places give me more positive, encouraging responses. Even if our languages don’t fully connect, like the sun’s energy spreading to all, I can sense their emotions and empathy.</p>
<p>Conversation is like water flow. You can stir it when calm or join an existing current, upstream or downstream. The key is to observe the flow and find the right moment to join or leave. I’m glad this trip gave me chances to interact with different people, boosting my confidence and letting me truly feel the flow of human connection.</p>
<p>Since we arrived early and check-in was still hours away, to avoid disturbing others, we moved from the living room to the backyard, found a soft mat, and lay down. Then a light sound caught our attention—a skinny calico cat jumped down from the high backyard wall and nimbly came over. As a cat lover, I couldn’t resist petting it all over, enjoying the moment.</p>
<p>Later, Mr. Z told us its name was Orca. Like other Egyptian kittens, its favorite pastime is scavenging. After a few days’ observation, we noticed it often waits by the glass door in the backyard. When someone comes in or out with plates or cups, it immediately approaches, rubbing against their legs. Once they sit, it rubs its head against chairs and table corners, meowing coquettishly. I thought it was showing affection to me, but later realized it’s just a snob, only interested in people with food. Its greed shows when it gets food—its meows become louder and more aggressive, and it scratches with sharp claws, truly living up to its name "Orca." Knowing this, we became more cautious, giving it space and resisting its cute temptations.</p>
<p><img src="assets/2025/L1090633.jpg" alt="" />
<em>Orca walking</em></p>
<p>Dahab has many cats and dogs roaming the streets. Dogs are large and noisy, often barking and fighting. I’ve witnessed many dog fights. Cats, in contrast, are slimmer and more agile, rarely fighting or gathering unless food is involved. They’re also very clean. Once I saw a kitten poop on the beach, cover it with sand, elegantly rub its butt, and walk away. Its grace makes me wary; I’m always cautious walking barefoot on the beach, afraid of stepping on fresh "landmines."</p>
<p>Compared to the lazy Turkish cats, these cats are wilder and will do anything for food. Some bold cats even jump onto tables and plates. At those times, we have to be firm and shoo them away; if we’re soft, they’ll take over. Of course, some cats are gentler, quietly watching us eat and leaving when no food is left. We reward these well-behaved cats with chicken or beef to commend their good manners.</p>
<p>By the way, local cats have their own dialect. Unlike ordinary meows, "Psst" means "come here." Locals taught me this. If you want Egyptian cats to approach, just say "Psst" a few times.</p>
<p>Orca, lazy and free-spirited, after being petted, climbed along the wall and left. We lay on the soft mat, hats over our eyes, half asleep. The flies here have evolved into experts over generations. They occasionally land on skin, and normal, rhythmic swatting doesn’t work. You have to wave vigorously and unpredictably to make them fly off. Annoyed and unable to sleep, we decided to take a walk and enjoy the town’s early morning atmosphere.</p>
<p>The distance from our place to the sea is short, and the coastal road is straight, but buildings are tightly packed, making it hard to find a path through. After many twists and turns, we finally reached the shore. Early morning, the sun had just risen, the wind was light, the sea calm, and the temperature comfortable.</p>
<p><img src="assets/2025/L1090667.jpg" alt="" />
<em>A few corners of the coffee shop street</em></p>
<p>The street was quiet, with only a few joggers and some people in wetsuits chatting in small groups. Coffee shops and dive shops lined the street, most still closed, with only a few cleaning behind glass windows. The coffee shops were mostly seaside, facing the beach, with carpets and mats laid out and small tea tables set up as seating. Near the shallow water, there were regular tables, chairs, and beach loungers for swimming or sunbathing.</p>
<p>Being new, we didn’t know how to pay card or cash and it was too early to find anyone to ask. We hesitated along the coast, then, exhausted, saw two people sleeping on a beach carpet and decided to join them, lying down by the sea.</p>
<p>We greeted the owner, ordered two iced coffees, and lay down under the umbrella. After a while, the owner brought two plastic cups. We took them and weakly thanked him. The coffee tasted awful, but sleep was more important. Holding our bags, we fell asleep to the sound of waves.</p>
<p>The beach mats were dusty and worn, none intact. The fabric at holes was frayed, with gray cotton fibers exposed and floating. The carpets hadn’t been cleaned for a long time, stained with various colors, obscuring the original red patterns. I guessed that fresh water is scarce here, so washing carpets is costly and inefficient. Or maybe the strong sunlight sterilizes them naturally, so people don’t worry about bacteria. At first, I thought only a few shops were like this, but after visiting more, I found most restaurants and coffee shops were the same. Still, no one sees it as a problem. People come in, buy food and drinks, and sit or lie down as usual.</p>
<p>I’m used to city life, where people expect everything to be clean. If something is stained, it must be cleaned immediately before use. Advertisements all promote cleanliness and whitening. "No stains" is a city norm, common sense for using things, and no one questions why things must be clean or why only clean things can be used. Compared to Egyptian carpets, whether dirty or torn isn’t a concern. The key is whether it exists. If it does, you can sit or lie down and rest. If not, you get up and find another place.</p>
<p><img src="assets/2025/L1090640.jpg" alt="" />
<em>Sheep on the road</em></p>
<p>Back in the city, I rode past a high-end furniture store. The huge glass window displayed various luxurious sofas and chairs, made from different materials and shapes. Seeing so many options, I suddenly thought of the Egyptian carpets and mats—dirty, worn, and only offering sitting or lying positions. Comparing the two, I felt a strange shame. The carpets blend naturally with their environment, while the furniture caters to the artificial ecology humans build in cities, with concrete and man-made activities. Different environments and occasions require different sofas or chairs. If they don’t fit, people frown and leave.</p>
<p>I don’t know how long I closed my eyes before waking again. The surroundings were still calm. A few wild big dogs gathered nearby. I dared not move, letting them sniff around, creating <em>a curious spectacle of five dogs circling "Kang" (me)</em>. After a few minutes, finding no food, they ran off in a pack.</p>
<p>Two men sleeping ahead woke up. One chatted with the owner in Arabic, apparently a local. Soon, he skillfully brought a hookah and placed it beside their leftover beer. Just waking up, mixing smoke and alcohol—I couldn’t help but admire him inwardly.</p>
<p>He looked around, saw no one else, walked over to us, and started a conversation. We introduced ourselves briefly and chatted about various things, though I don’t recall details—probably first impressions of the town.</p>
<p>I remember when discussing currency exchange, he said he worked in anti-money laundering, just off night shift, and came straight to the beach to relax. Using imperfect English, he warned us not to exchange money on the black market because the rates are terrible. The safest way is to go to the bank’s teller window.</p>
<p>Seeing the time, we thanked him and prepared to leave. Before parting, he warmly shook our hands and added, "There will be techno music nearby tonight, you’re welcome to come."</p>
<p>Since childhood, I was taught to be wary of strangers, especially those who approach warmly, always warned "they have ulterior motives." So when smiling strangers approach on the street, regardless of age or gender, I instinctively keep my guard up. Life in Germany has reinforced this: strangers are either salespeople or beggars. Over time, I almost forgot that genuine warmth is a simple, kind trait that can bring people closer.</p>
<p>I don’t blame those who fake friendliness for gain. I just feel a little sad—that pure humanity has been twisted into a sharp tool, and its true value and gentleness are rarely appreciated.</p>
<p>During our stay, similar chats happened almost daily. Whether with friends or strangers, I truly felt the kindness and warmth from people’s hearts. From an individual perspective, we may all be ordinary, but these unexpected conversations make every moment shine uniquely, making everyone here feel special, glowing with extraordinary light.</p>
<p><img src="assets/2025/L1090665.jpg" alt="" />
<em>Unknown local youths, but I took their photo</em></p>
<p>Before the sun rose too high, we returned to the hostel. Though it wasn’t check-in time, Z told us the room was ready, so we could move in early. I couldn’t help but give him a thumbs-up and sincerely thanked him; the timing was just right.</p>
<p>Inside, we tossed our luggage aside, ignoring dust and sweat, and collapsed onto the freshly made bed. Opening and closing my eyes, I realized it was only noon, but my body and mind felt like they’d crossed centuries. That wave of exhaustion reminded us—this journey had only just begun.</p>
<hr />
<p>Here's the contact info for <a href="https://www.instagram.com/tranquilohosteldahab?utm_source=ig_web_button_share_sheet&igsh=ZDNlZDc0MzIxNw==">hostel tranquilo</a>, I think you'll like it there :D</p>
Dahab - Bumpy Canyonshttps://blog.kaiyikang.com/posts/2025/dahab-3-bumpy-canyons/https://blog.kaiyikang.com/posts/2025/dahab-3-bumpy-canyons/Sat, 12 Apr 2025 00:00:00 GMT<p>Our schedules in "Dahab" were separate. She had arranged freediving training and certification, while I wasn't quite that intense and only signed up for a beginner's scuba diving course that could be completed in a day. So during the staggered times, I was free to explore and make my own plans.</p>
<p>On the second day after we arrived, she started her training early, while I rented a bike and wandered around town. That evening, when everyone gathered to share their experiences from the day, she noticed I had nothing particularly exciting to say and strongly recommended I join a one-day canyon tour. Without hesitation, I immediately posted in the hostel group. The manager Mr. Z responded quickly and contacted the landlord Mr. M.</p>
<p>Mr. M had many friends here and special connections for almost every activity. Within a few hours, we received the price and itinerary. Since I had no special plans the next day, I happily signed up. Documentary filmmaker Mr. A, staying in another room, saw the message and spontaneously decided to join us.</p>
<p>In "Dahab", most activities or day trips revolve around nearby areas, usually within one or two hours' drive. The distances aren't far, but due to language barriers and different social environments, arranging things on your own is nearly impossible. So using a travel agency is the best option. There are many travel agencies along the city's coastal center. Even if you try to ignore them, the young men stationed at the doors will eagerly approach you, handing out flyers. The competition is fierce, as you can imagine.</p>
<p>Though the activity options vary somewhat, the content is largely similar. So the key is to negotiate a good price with the travel agency. We were lucky to skip this step entirely since the hostel helped us contact them and secure the best rates. Every time I think about it, I'm grateful I chose the right accommodation instead of a rundown resort hotel—otherwise, it would have been a big loss.</p>
<p>Early the next morning, a strong northeast wind was blowing. Though not ideal for snorkeling, it didn't stop us from hiking the canyon. We packed up early and waited in the living room for departure. When the time came, Mr. Z hurried over to tell us the vehicle had arrived. We got up and set off for the day's adventure.</p>
<p>The vehicle was a modified large van with all the rear seats removed and two rows of benches installed near the windows. There were no fixed seats or seat belts. I felt like I was being treated like cargo in the back of the van. The van started and we drove through town to pick up other travelers.</p>
<p>When the door opened the first time, a woman got on. She was dressed in modern, somewhat flashy clothes and immediately started chatting loudly in English, saying she was from Cairo and here to travel. Early in the morning, I was low on energy and responded quietly and politely. After a while, another woman boarded, warmly greeting the first. They seemed to be friends traveling together. She carried a cardboard tray holding two cups of iced coffee. The coffee in plastic cups jostled with every bump, looking like it might spill any second. Watching their flamboyant and carefree personalities, I had a feeling some trouble was coming.</p>
<p>Sure enough, as I watched the coffee about to spill, the woman stepped onto the van's edge but lost her footing. The cup slipped from her hand, and coffee spilled just as I had feared. The rear of the van was covered in coffee. They quickly grabbed tissues and started wiping, muttering to each other in Arabic. The driver and guide didn't ask much and moved to chat in the shade under a nearby tree, occasionally glancing back as if waiting for them to finish cleaning up. The coffee didn't splash on my side but landed on the pants of a French guy sitting opposite me. They apologized quickly, and he, quiet and reserved, said it was fine. Though he said so, I could see a hint of fatigue and resignation in his expression.</p>
<p>Once the coffee was cleaned up, we finally set off. People's personalities always have unique traits. Friends form through similar temperaments, and certain personality types tend to have consistent and harmonious ways of doing things. Just after the coffee incident was resolved and the van left town, the woman pulled out a small JBL speaker and loudly asked who wanted to listen to music. When no one responded, she played modern Arabic songs on her own. After a few songs from the back, the driver seemed to notice. While shifting gears with his right hand, he poked at the control panel and suddenly a melodious classical Arabic song came from the front speakers. The two music sources clashed like two people walking with a limp, completely out of sync. We were stuck in the middle like the filling in a sandwich, mentally exhausted and tested. After some time, the music faded away, and our nerves slowly relaxed.</p>
<p>During the trip, another music-related oddity annoyed Mr. A. Driving through the desert, I noticed the music kept switching every few seconds. At first, I thought it was a technical glitch with the car's audio system, so I didn't pay much attention. Later, Mr. A told me privately it was the driver's doing. He pointed to the driver's right hand, which kept clicking buttons, each click switching to the next song. I realized the driver was secretly controlling everything. Mr. A was irritated by the noise and kept asking the driver and guide in English to quiet down. I didn't catch the details but saw the driver muttering in Arabic while his hand never stopped working. Mr. A gave up trying to communicate and complained to me instead. I wasn't bothered by the music and saw it as a curious human behavior. Out of curiosity and observation, I preferred to think of the driver as a meticulous DJ with his own tastes and rules. He was so strict he judged songs within seconds and cut almost all of them. The one or two songs he kept weren't particularly pleasant to me. I reassured Mr. A that the driver had his own preferences and we just needed to accept it calmly.</p>
<p>The last music episode happened when we left the canyon and entered a vast desert. I walked ahead while two companions behind carried a small speaker. As I admired the desert scenery, a powerful tune came from behind—"dong dong dong"—like a film soundtrack. When the main melody started with a female vocal "ha—ah—ei—", I realized it was the theme from "Dune". I wanted to laugh and felt a bit helpless. The choice fit the atmosphere perfectly, but it was ironic that even in the wilderness, we couldn't escape the influence of man-made things. It shows how deeply ingrained they are.</p>
<p>About an hour after leaving town, the van slowly left the main road and entered the desert. There was no phone signal, and the map app couldn't locate us. Civilization slowly faded, and the wild side of nature gradually awoke. When we arrived, we all got out. The guide explained the itinerary in simple English: the canyon trail was one-way. Leave your bags in the van, take water with you, and just keep walking forward.</p>
<p><img src="assets/2025/L1090680.JPG" alt="" /></p>
<p>The path down the canyon wasn't difficult but was steep. Many tourists had passed, and the rocks had clear footholds. A rope hung down to the valley floor. Holding it tightly, step by step, we followed the guide's instructions and quickly reached the bottom. Huge white rock walls surrounded us, stretching upward and blocking part of the sky and sunlight. Without sunlight, I suddenly felt a chill. The milky white rocks were round and smooth with a matte texture. Touching them gently, fine white sand came off easily, feeling somewhat fragile. Layers of rock had accumulated over time, with different colors and textures forming stripes. The flowing shapes looked like threads drifting on the walls. Being inside felt like entering a funhouse maze, full of surprises and far from boring.</p>
<p>We walked forward at uneven paces. Sunlight filtered through the rock walls, casting dappled shadows. The sandy ground and sparse plants shone under the sun. Occasionally, beetles passed by. These rare creatures seemed to walk inside a picture frame, making it feel like watching a high-definition desert documentary.</p>
<p><img src="assets/2025/L1090809.JPG" alt="" /></p>
<p>The canyon trail wasn't always smooth. Sometimes we reached forks and had to wait for the guide's instructions. The guide was a Bedouin man wearing a red headscarf, T-shirt, and cloth pants, with simple sandals. He carried no special gear, just a bottle of water. He was very responsible, moving back and forth to ensure no one was left behind or lost.</p>
<p><img src="assets/2025/L1090837.JPG" alt="" /></p>
<p>His dark skin and weathered face looked like rock textures—ancient and wise, as if part of the land itself. From a distance, I saw Mr. A chatting with him, then Mr. A excitedly shared the wisdom the guide conveyed. Everything felt harmonious and natural. Perhaps because he had lived on this powerful land all his life, he possessed such simple yet profound wisdom.</p>
<p><img src="assets/2025/L1090817.JPG" alt="" /></p>
<p>The canyon's end was an oasis filled with signs of local life. The guide led us into a shelter to rest. The roof was made of palm leaves, and underneath were Arabic carpets and cushions. But after long use and many visitors, the carpets were gray, messy, and worn. Light filtered through the sparse palm leaves, swaying gently in the breeze, creating a dreamlike atmosphere. Next to the shelter was a fire pit, where another Bedouin squatted, brewing tea. He slowly lifted the kettle of boiling water and poured tea steadily into cups.</p>
<p><img src="assets/2025/L1090938.JPG" alt="" /></p>
<p>We took the tea and thanked him. The black tea was delicious. While sipping, I sat in a corner chatting with Mr. A. Having interacted before and being in this magical place, I felt freer to express myself. Expressing deep thoughts in a non-native language has always been a challenge for me. I've never had such an experience—sharing my life philosophy and insights in a foreign tongue. Whenever I reach this crossroads, I either retrace my steps, listen quietly to others, or find an excuse to leave. My ability is fading, but my thoughts remain alive and stirring. I'm grateful he tried to listen and follow my vague ideas, politely showing understanding. I felt like a beginner learning table tennis, trying hard to rally. I could feel him using greater skill to keep the ball moving. I deeply appreciate his effort and will always remember that wonderful moment. May I have the strength to face such challenges in the future. (By the way, <a href="https://www.hunchmaker.com">hunchmaker</a> is Mr A's page and there is a lot of interesting content for you to explore.)</p>
<p><img src="assets/2025/L1090942.JPG" alt="" /></p>
<p>Lunch was prepared by the guide in advance. Though simple and vegetarian, it smelled fragrant and tasted natural. After eating, we felt comfortable. After a short rest, we set off again for the color canyon.</p>
<p>I expected the vehicle to stay on a straight asphalt road, but the driver turned sharply and drove straight into the desert. Before I could react, my body began to bounce with the rough terrain. We rode in a basic van with no advanced suspension. Underfoot was a large metal plate. Every bump on the four tires was amplified and transmitted to our bodies.</p>
<p>Cars drove on stone roads, leaving intertwined tire tracks. Though the driver tried to stay on the tracks, he sometimes went off course. We were forced to shuttle through different time tunnels, trying to find our mark. Each time travel was a jolt. At first, I felt excited, thinking this trip was worth it, with a roller coaster ride included. The bouncing lasted a long time and was intense, so I focused solely on not flying out. My muscles seemed to vanish, leaving only hard bones colliding.</p>
<p>I had no energy to share my feelings with Mr. A beside me. To ease motion sickness, I looked out the window, forced to accept the shaking scenes imposed by the road and driver. The van sped on, and another jeep flew past from the side. I stared at the jeep in the vast desert, still feeling immersed in a wilderness documentary.</p>
<p><img src="assets/2025/L1090961.JPG" alt="" /></p>
<p>Suddenly, the desert became an ocean. The bumpy road was waves stirred by strong winds, and the gravel was the foam. We were like on a small boat sailing in a vast, empty sea. Letting the terrain guide us, it took us to a mysterious shore.</p>
<p>The journey wasn't smooth. When the van tried to climb a sand dune, the angle was too steep, and no matter how it adjusted, the wheels couldn't get enough grip. The driver tried many times but failed, then jumped out to adjust the tires. Under the scorching sun, his white robe was dazzling. I didn't know exactly what he did, only heard hissing sounds, maybe releasing air. He squatted fiddling for a few minutes, then restarted the van. The wheels moved in an S shape on the soft dune, like a snake wriggling. After a while, it finally climbed over.</p>
<p>Drivers here always face various problems, from environmental challenges to vehicle breakdowns. My girlfriend once had a similar experience. She took a taxi north of town for pool training session, and the vehicle suddenly stalled. While deciding whether to switch cars, the driver ran to the front and started fiddling without hesitation. According to her, he first tested two wires repeatedly with no response. Then he pulled out a wire, peeled off the plastic with his long fingernail, and wrapped shiny copper wire around the old wire. The driver said nothing and worked silently. After a while, the engine roared back to life.</p>
<p>The vast desert and roads force you to trust and rely on the driver, no matter how unreliable they seem. They always face problems and solve them in rough, primitive ways. We tend to prefer reliability and avoid uncertainty, but these drivers seem to live on the edge of uncertainty. We seem tied to them by a rope. When they're safe, we breathe easy; when they nearly fall off a cliff, we panic, unsure of what's next. Only when we arrive safely and close the door does our anxious heart finally settle.</p>
<p>At speed, we arrived at the color canyon.</p>
<p>Compared to the pure white canyon, I prefer to call this one the red canyon. The rock walls seemed dyed with color, showing richer textures than the white canyon. I admit it was beautiful, but the morning's itinerary and the bumpy ride left me exhausted and unable to appreciate it calmly, only wanting to get through quickly and reach the end.</p>
<p><img src="assets/2025/L1090973.JPG" alt="" /></p>
<p>Even in my hurry, the rock formations left a deep impression and sparked my imagination. Weathering had created many gaps at the top of the walls. Over time, these gaps tore and widened, forming complete passages inside, with only thin stone pillars supporting the outside. Dragging my tired body, I looked up and thought these pillars and passages resembled temple corridors. Miniature monks lodged between stones, wandering the stone pillar corridors above, conveying spirit and chanting poetry, as if trying to summon me to another mysterious realm. I paused briefly, then regained focus and moved on.</p>
<p>We reached the end and got back in the van. The previously noisy tourists no longer fiddled with their speakers, only the driver still kept pressing the skip button, but we were too tired to care.</p>
<p>The van drove from dirt back onto asphalt, moving quietly like a soft mattress. I leaned against the window, my back forced upright, yet I still fell asleep. I had no dreams—maybe they were left in the canyon. When I woke, we were already back in town.</p>
<p><img src="assets/2025/L1090985.JPG" alt="" /></p>
Dahab - Not Yet Freedivinghttps://blog.kaiyikang.com/posts/2025/dahab-4-not-yet-freediving/https://blog.kaiyikang.com/posts/2025/dahab-4-not-yet-freediving/Mon, 14 Apr 2025 00:00:00 GMT<p>Freediving is not a leisure activity; it is a sport that requires a great deal of skill. These skills include both the obvious, "visible skills" and the less obvious, "invisible skills." The latter are revealed and amplified by the ocean's unique environment, making them even a decisive factor.</p>
<p>If you don't meet certain technical requirements, you simply can't get started. Right now, I find myself pacing back and forth at this threshold, hesitant and unsure if I will ever be able to step through that door.</p>
<p>Next, I will share my experience in detail.</p>
<p><img src="assets/2025/IMG_3439.JPG" alt="" /></p>
<p><em>This is <a href="https://www.linyue-zou.com">Her</a></em></p>
<p>Dahab isn't exactly a tourist hotspot. People who come here either have a clear purpose or have been persuaded to come—there's almost no middle ground. The rule here is that almost every activity has a certain threshold. In other words, if you want to take part in something interesting, you either need exceptional talent, special courage and energy, or well-honed skills. There are almost no activities that can be experienced effortlessly, like visiting a scenic spot or museum, which only requires walking.</p>
<p>After spending a few days here, I deeply felt that Dahab is not flattering at all. It generously showcases the Blue Hole, cliffs, and lagoons. If you want to play, you can come; whether you have the ability or not is your own business, not its. It won't lower the bar or make things easier just so you can have fun. This proactive attitude is unique among tourist destinations—you either throw yourself into the cliffs and ocean or lie in the shade complaining. So my advice is to use your imagination fully before coming and be clear about which sports you want to try, to avoid boredom.</p>
<p><img src="assets/2025/DJI_20250418125714.jpg" alt="" /></p>
<p><em>Fish</em></p>
<p>I signed up for a freediving experience course, which is level one in the AIDA system. Simply put, diving falls into two main categories: one is what we usually call "diving," which involves going underwater with oxygen tanks; the other is going underwater without any equipment, relying solely on your body—that's the meaning of "free" in freediving. The former is better known through the PADI system, while the latter includes the AIDA or Molchanovs systems. These names aren't that important—you can find plenty of information with a quick search. Although I joined the system and the course has levels, I know very well that this doesn't represent my personal ability; it's just proof that I've been there. I have no special expectations and do not wish to overly promote the so-called certificate.</p>
<p>Because it was windy and the water surface was rough that day, we split the course into two days: half a day of theory and half a day of practice. The coach lives on the town's north road, about a ten-minute walk from where I was staying. Without a house number, I just roughly followed the Weixin location. When I arrived, I looked around but couldn't tell what was ruins and what was a residence; all I saw was a cluster of yellow-flowered trees casting shadows and smelled a strong scent of sheep dung.</p>
<p>The coach saw I had arrived and came down to open the door for me. I went up the stairs and entered the house with him. The interior was simple, like old houses in China, very functional. There was a carpet on the floor, and next to it were various diving gear and teaching aids. Although I couldn't name them, they looked very professional. Since it was a beginner course, the content was straightforward and mostly introductory. The focus was on safety first, then technique. After the course, I chatted with the coach for a while before leaving.</p>
<p>The course mentioned that freediving is a "mental game." This phrase stuck with me. Generally, a game is a broad concept that applies to most activities. It tests various abilities like endurance, skill, flexibility, memory, and so on. I tend to associate different abilities with certain activities—for example, memory is like a bespectacled scholar, flexibility like a tall, lean athlete, the latter being a manifestation of the former. As for what a person with strong mental ability looks like, or how mental strength manifests, I can't say for sure. That's why I'm curious to "play this game" myself and experience the strength of the mind firsthand.</p>
<p>Another thing that impressed me was the "buddy system", meaning freediving is always done with at least two people to ensure safety. So freediving demands not only individual mental strength but also connection between people. These two combined are like the elements of creation—one inward, one outward—and only when inside and outside unite is it complete. Two people, life, and the ocean: many elements naturally and harmoniously merge in freediving.</p>
<p>On the way back, I kept reflecting on these fascinating ideas. Of course, theory is theory, and in the end, it all comes down to practice.</p>
<p><img src="assets/2025/DJI_20250418144003.jpg" alt="" /></p>
<p><em>Fish and coral</em></p>
<p>A few days later, the wind died down. We chose a sunny morning for my first dive.</p>
<p>There's a lighthouse on the south side of town, the busiest spot around. Thanks to its unique geography, it's the best place to dive. The shallow waters nearby are perfect for snorkeling, while farther out you can do scuba or freediving.</p>
<p>We found a café, and the coach brought out a buoy from the backyard. I learned that when he teaches others, he either partners with a dive shop or works independently. He just needs to notify the café in advance, store his gear there temporarily, and order some food and drinks during classes—a mutually beneficial arrangement.</p>
<p>I first put on a wetsuit. It felt like synthetic fiber, thick and soft to the touch, as if insulated inside. Wearing a wetsuit means covering your entire body, head to toe. Even with long sun exposure, the seawater remains cold, and if you're not properly covered, you risk hypothermia. Also, because seawater is buoyant, you need to wear extra weights to sink. Finally, I put on the mask and snorkel, grabbed the long fins, and was ready to go. Freediving gear is quite a bit, but much lighter than scuba gear, which sometimes requires a cart to carry.</p>
<p>Once in the water, the shallow beach was very shallow—I remember the water only reached my thighs. The coach guided me through some simple moves like spitting out water, lying down to relax, and how to use the fins. The most interesting part was breathing relaxation: lying on the surface, scanning my body, relaxing, and then holding my breath at the most relaxed moment. My body was buoyed by the water, floating motionless, with only my brain seemingly active. Thoughts surged—sometimes colorful and flying, sometimes silent and empty, leaving only pure sensation. My body was suspended between sky and sea, as thin as a cicada's wing, melting at the boundary and drifting with the waves.</p>
<p>People use various methods to calm their racing nerves; the textbook suggests imagining a burning candle. Some imagine animals, others plants. I lacked experience and had no method, even resorting to reciting the "Heart Sutra" to calm myself, but it didn't work well because I forgot the words, which annoyed me and made me more unstable. In the end, I held my breath for one and a half minutes, an average result. Compared to professionals who can hold their breath for several minutes, the gap was clear.</p>
<p>After the basic training, we moved to a slightly deeper spot, about seven or eight meters deep. The water was clear, and I could see the seabed. After the coach secured the buoy, I tried to dive.</p>
<p>I inverted myself, held the rope, and gently pulled down. The pressure in my ears changed suddenly, followed by pain. The pain started broad and then sharpened. Every time I dived, I tried to equalize the pressure, but no matter how well I trained on land, it failed immediately underwater. Even when calm and carefully focusing on my throat, vocal cords, pharynx, and facial muscles, the sensation became confused as waves passed. The seawater pressure came from all directions, tight and unyielding, seeping into every possible gap. The deeper I went, the tighter the pressure and the greater the pain. The pressure was inevitable, like causality; facing it, there was no choice but to use the correct technique. That feeling of helplessness was magnified underwater; either you do it poorly or you can't do it at all, with no solution. I had no complaints but quickly surfaced after only one or two meters.</p>
<p>I tried several times to invert and dive but failed each time. A yellow tennis ball hanging at five meters glowed like a mirage, beckoning me, but sadly I couldn't meet its call. Water pressure and technique were the barriers.</p>
<p>After spending over an hour in the sea, my body started to get cold, so I told the coach we could stop. Back on shore, the sun was strong, but my body still trembled involuntarily. Reflecting on the dive, most of my feelings were excitement about the new experience, but part of me was frustrated, like I had missed the final step. Though imperfect, I could accept it and wasn't ready to give up diving. The good news is that by the time I'm writing this, I've mastered the ear equalizing technique but still need to practice and adjust underwater.</p>
<p><img src="assets/2025/DJI_20250418144205.jpg" alt="" /></p>
<p><em>More fish and coral</em></p>
<p>Later, I talked about this experience with my girlfriend. She said the coach commented on our training spot's distance from shore, calling the location and depth very "magical" and "subtle."</p>
<p>I completely agree and empathize. People who don't dive stay close to shore; those eager to start go farther out. I'm somewhere in between—not quite a beginner, but not a non-diver either. This perfectly describes my conflicted hesitation on the path to learning diving.</p>
<p>Freediving itself seems inherently contradictory. Successful diving requires performing specific actions while maintaining total relaxation. Most of the time, these two contradict each other: actions tense the muscles, while relaxation resists tension and halts movement. This contradiction is fully triggered in this special environment. I believe every skilled freediver is a master of balance. They know how to find equilibrium between tension and relaxation, moving fluidly like a fish in the space between motion and stillness, pursuing the greatest depth and the deepest self.</p>
Separate Business and Control Logichttps://blog.kaiyikang.com/posts/2025/separate-business-and-control-logic/https://blog.kaiyikang.com/posts/2025/separate-business-and-control-logic/Thu, 15 May 2025 00:00:00 GMT<p>The core logic is to separate the parts of the program that handle business and domain knowledge (domain or business logic) from the parts responsible for process control, coordination, and decision-making (control logic), thereby improving code maintainability, testability, and reusability.</p>
<p>In DDD, business logic corresponds to domain logic, and control logic corresponds to the "application layer" or "service layer."</p>
<p><strong>Business Logic</strong></p>
<ul>
<li><strong>Content</strong>: Business rules, data validation, calculation formulas</li>
<li><strong>Responsibility</strong>: Ensure system behavior complies with business requirements and specifications</li>
<li><strong>Examples</strong>:
<ul>
<li>Calculating the total price of an order</li>
<li>Determining user permissions</li>
<li>Business state transitions</li>
</ul>
</li>
</ul>
<p><strong>Control Logic</strong></p>
<ul>
<li><strong>Content</strong>: Process control, call sequence, conditional judgment</li>
<li><strong>Responsibility</strong>: Organize and manage the execution of business logic to ensure correct system flow</li>
<li><strong>Examples</strong>:
<ul>
<li>Handling state transitions in multi-step business processes</li>
<li>Controlling transaction start and commit</li>
</ul>
</li>
</ul>
<h2>Examples</h2>
<h3>Using Python</h3>
<h4>Conventional Approach</h4>
<pre><code>class UserService:
def __init__(self):
self.users = []
def register_user(self, username, password):
# 1. validate username
if username in self.users:
return {"success": False, "message": "Username already exists"}
# 2. validate password length
if len(password) < 6:
return "Password too short"
# 3. store user
self.users.append(username)
return {"success": True, "message": "User registered successfully"}
</code></pre>
<h4>Separated Approach</h4>
<pre><code>class UserRepository:
def __init__(self):
self.users = {}
def exists(self, username):
return username in self.users
def save(self, user):
self.users[user["username"]] = user
# Domain Logic
class UserDomainService:
def __init__(self, user_repository):
self.user_repository = user_repository
def is_username_available(self, username):
# Check if the username is already taken
return self.user_repository.exists(username)
def validate_password(self, password):
# Validate the password according to your criteria
return len(password) >= 6
def create_user(self, username, password):
user = {"username": username, "password": password}
self.user_repository.save(user)
return user
# Application Logic
class UserApplicationService:
def __init__(self, user_domain_service):
self.user_domain_service = user_domain_service
def register_user(self, username, password):
# 1. Check if the username is available
if self.user_domain_service.is_username_available(username):
return {"success": False, "message": "Username already taken."}
# 2. Validate the password
if not self.user_domain_service.validate_password(password):
return {"success": False, "message": "Password does not meet criteria."}
# 3. Create the user
user = self.user_domain_service.create_user(username, password)
return {"success": True, "user": user}
repo = UserRepository()
domain_service = UserDomainService(repo)
app_service = UserApplicationService(domain_service)
print(app_service.register_user("john_doe", "password123")) # Should succeed
print(
app_service.register_user("john_doe", "pass")
) # Should fail due to password criteria
print(
app_service.register_user("john_doe", "newpassword")
) # Should fail due to username already taken
</code></pre>
<ul>
<li><strong>UserDomainService</strong> focuses only on business rules (whether the username exists, password validation, user creation).</li>
<li><strong>UserApplicationService</strong> is responsible for process control (call sequence, return results).</li>
</ul>
<h3>Using Java</h3>
<h4>Conventional Approach</h4>
<pre><code>import java.util.HashSet;
import java.util.Set;
public class UserService {
private Set<String> users = new HashSet<>();
public Result register(String username, String password) {
// 1. Check if the username is available
if (users.contains(username)) {
return new Result(false, "Username is already in use");
}
// 2. Validate the password
if (password.length() < 6) {
return new Result(false, "Password must be at least 6 characters");
}
// 3. Create the user
users.add(username);
return new Result(true, "success");
}
public static class Result {
private boolean success;
private String message;
public Result(boolean success, String message) {
this.success = success;
this.message = message;
}
public boolean isSuccess() {return success;}
public String getMessage() {return message;}
}
}
</code></pre>
<h4>Separated Approach</h4>
<pre><code>import java.util.HashMap;
import java.util.Map;
// Domain Logic
class UserDomainService {
private UserRepository userRepository;
public UserDomainService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public boolean isUsernameTaken(String username) {
return userRepository.exists(username);
}
public boolean validatePassword(String password){
return password != null && password.length() >= 6;
}
public void createUser(String username, String password) {
User user = new User(username, password);
userRepository.save(user);
}
}
// Persistence Layer
class UserRepository{
private Map<String, User> users = new HashMap<>();
public boolean exists(String username){
return users.containsKey(username);
}
public void save(User user){
users.put(user.getUsername(), user);
}
}
// User Entity
class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
}
class Result {
private boolean success;
private String message;
public Result(boolean success, String message) {
this.success = success;
this.message = message;
}
public boolean isSuccess() {return success;}
public String getMessage() {return message;}
}
// Application Logic
class UserApplicationService {
private UserDomainService userDomainService;
public UserApplicationService(UserDomainService userDomainService) {
this.userDomainService = userDomainService;
}
public Result register(String username, String password) {
if(userDomainService.isUsernameTaken(username))
return new Result(false, "Username taken");
if(!userDomainService.validatePassword(password))
return new Result(false, "Invalid password");
userDomainService.createUser(username, password);
return new Result(true, "success");
}
}
public class scratch_46 {
public static void main(String[] args) {
UserRepository userRepository = new UserRepository();
UserDomainService userDomainService = new UserDomainService(userRepository);
UserApplicationService userApplicationService = new UserApplicationService(userDomainService);
Result r1 = userApplicationService.register("alice", "12345");
System.out.println(r1.getMessage());
Result r2 = userApplicationService.register("alice", "123456");
System.out.println(r2.getMessage());
Result r3 = userApplicationService.register("alice", "1234567");
System.out.println(r3.getMessage());
}
}
</code></pre>
<ul>
<li><strong>UserDomainService</strong> is responsible for business rules (whether the username exists, password validation, user creation).</li>
<li><strong>UserApplicationService</strong> is responsible for controlling the process (call sequence, return results).</li>
<li><strong>UserRepository</strong> simulates data storage.</li>
<li><strong>User</strong> is the domain entity.</li>
</ul>
<h2>Tips and Suggestions</h2>
<ol>
<li>Start by designing simple methods.</li>
<li>Move simple methods to the control logic layer.</li>
<li>Separate out the business logic.</li>
</ol>
数字证书的处理流程https://blog.kaiyikang.com/posts/2025/%E6%95%B0%E5%AD%97%E8%AF%81%E4%B9%A6%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B/https://blog.kaiyikang.com/posts/2025/%E6%95%B0%E5%AD%97%E8%AF%81%E4%B9%A6%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B/Mon, 24 Feb 2025 00:00:00 GMT<h2>例子</h2>
<p>为了证明我的身份,让别人可以安全的和我通信,以及防止别人冒充我的身份,我可以申请身份证明:</p>
<ol>
<li>创建自己的秘密(私钥)和公开信息(公钥)</li>
<li>使用自己的信息,填写申请表</li>
<li>将申请表提交给第三方,第三方盖章,证明我是我</li>
<li>最后我将证明和秘密放在一起,保存好</li>
</ol>
<p>使用身份证明的方法是:</p>
<ol>
<li>我使用秘密生成一个签名</li>
<li>对方使用公开信息验证签名</li>
<li>第三方使用签名验证我的身份</li>
</ol>
<h2>流程</h2>
<p>申请数字证书的标准流程如下:</p>
<ol>
<li><strong>密钥对生成</strong></li>
</ol>
<ul>
<li>创建私钥 (需严格保密)</li>
<li>生成对应的公钥</li>
</ul>
<ol>
<li><strong>证书申请</strong></li>
</ol>
<ul>
<li>准备申请者信息</li>
<li>填写证书申请表 (CSR)</li>
</ul>
<ol>
<li><strong>CA 认证</strong></li>
</ol>
<ul>
<li>提交至认证机构 (CA)</li>
<li>CA 验证并签发证书</li>
</ul>
<ol>
<li><strong>证书安装</strong></li>
</ol>
<ul>
<li>合并私钥与证书</li>
<li>安全保存完整证书</li>
</ul>
<h2>流程细节</h2>
<p>使用技术的语言来讲,申请方首先应该创建 P12,它是 PKCS#12 的简称,全称是 Public Key Cryptography Standards,里面包含了:</p>
<ul>
<li>私钥</li>
<li>证书(如果之后有)</li>
<li>证书链(如果之后有)</li>
</ul>
<pre><code>keytool -genkeypair \
-keysize 4096 \
-keystore keystore_file.p12 \
-storetype PKCS12 \
-alias keystore_file \
-dname "CN=keystore_file,OU=dataproxy-services" \
-keyalg RSA \
-storepass abc
</code></pre>
<p>生成的 p12 文件本身就是一种 keystore,可以直接被用作 csr,即 Certificate Signing Request:</p>
<pre><code>keytool -certreq \
-keystore keystore_file.p12 \
-alias keystore_file \
-file certificate.csr \
-storepass abc
</code></pre>
<p>其中包括了从私钥中提取出来的公钥,申请着的信息,以及使用私钥对上述信息的签名。这个文件将会在之后发送给 CA 机构进行签发。</p>
<p>第三方的 CA 签发机构会验证信息,并使用自己的私钥签名 CSR 并且生成证书(链),发送回给申请者。</p>
<p>申请者要将原始的,包含私钥的 p12 合并,完成证书的安装:</p>
<pre><code>keytool -importcert \
-keystore keystore_file.p12 \
-file certificate.p7b \
-alias keystore_file \
-trustcacerts \
-noprompt \
-storepass abca
</code></pre>
<h2>如何使用</h2>
<p>我希望将 p12 和 vault 结合起来使用。</p>
<p>因为 p12 是二进制文件,不适合存储和传输,因此为了存储和使用,首先需要将 p12 转化为 base64:</p>
<pre><code>base64 -i keystore_file.p12 -o p12.base64
</code></pre>
<p>Vault 包含加密存储的功能,而 kv 适合存储证书类型的数据。我们将改文件存储到 Vault 中:</p>
<pre><code>vault kv put custom/internal/certificates/kafkaKeyStore [email protected]
</code></pre>
<p>最后就是使用:</p>
<pre><code>jks:
secretList:
- name: kafkaStore
vaultPath: "custom/internal/certificates/kafkaKeyStore"
</code></pre>
<p>应用启动的时候,就可以直接获得证书了。</p>
Digital Certificates Flowhttps://blog.kaiyikang.com/posts/2025/digital-certificates-flow/https://blog.kaiyikang.com/posts/2025/digital-certificates-flow/Mon, 24 Feb 2025 00:00:00 GMT<h2>Understanding Digital Certificates</h2>
<p>Digital certificates serve three crucial purposes: proving your identity, enabling secure communication, and preventing identity theft.</p>
<p>Here's how the certification process works:</p>
<ol>
<li>Generate your unique cryptographic keys (private and public)</li>
<li>Complete an identity verification application</li>
<li>Get your application validated by a trusted third party</li>
<li>Securely store your credentials together</li>
</ol>
<p>Using these digital credentials involves:</p>
<ol>
<li>Creating a digital signature with your private key</li>
<li>Having others verify your signature using your public key</li>
<li>Allowing third parties to authenticate your identity</li>
</ol>
<h2>The Certification Process</h2>
<p>Getting a digital certificate involves four main steps:</p>
<ol>
<li><strong>Creating Your Key Pair</strong></li>
</ol>
<ul>
<li>Generate a private key (must remain secret)</li>
<li>Create its matching public key</li>
</ul>
<ol>
<li><strong>Applying for Certification</strong></li>
</ol>
<ul>
<li>Compile your identification details</li>
<li>Create a Certificate Signing Request (CSR)</li>
</ul>
<ol>
<li><strong>CA Verification</strong></li>
</ol>
<ul>
<li>Submit to a Certificate Authority (CA)</li>
<li>Receive your verified certificate</li>
</ul>
<ol>
<li><strong>Setting Up Your Certificate</strong></li>
</ol>
<ul>
<li>Combine your private key with the certificate</li>
<li>Store everything securely</li>
</ul>
<h2>Technical Implementation</h2>
<p>The process begins with creating a P12 file (PKCS#12 format), which serves as a secure container for:</p>
<ul>
<li>Your private key</li>
<li>Your certificate (once issued)</li>
<li>The certificate chain (once established)</li>
</ul>
<pre><code>keytool -genkeypair \
-keysize 4096 \
-keystore keystore_file.p12 \
-storetype PKCS12 \
-alias keystore_file \
-dname "CN=keystore_file,OU=dataproxy-services" \
-keyalg RSA \
-storepass abc
</code></pre>
<p>The generated p12 file itself is a type of keystore that can be directly used as a CSR (Certificate Signing Request):</p>
<pre><code>keytool -certreq \
-keystore keystore_file.p12 \
-alias keystore_file \
-file certificate.csr \
-storepass abc
</code></pre>
<p>After the CA verifies and signs your request, you'll need to merge the original p12 containing the private key to complete the certificate installation:</p>
<pre><code>keytool -importcert \
-keystore keystore_file.p12 \
-file certificate.p7b \
-alias keystore_file \
-trustcacerts \
-noprompt \
-storepass abca
</code></pre>
<h2>Usage</h2>
<p>For practical use, I recommend integrating P12 with Vault for enhanced security. Since P12 files are binary and not transmission-friendly, we first convert them to base64 format:</p>
<pre><code>base64 -i keystore_file.p12 -o p12.base64
</code></pre>
<p>Vault includes encrypted storage functionality, and kv is suitable for storing certificate-type data. We store this file in Vault:</p>
<pre><code>vault kv put custom/internal/certificates/kafkaKeyStore [email protected]
</code></pre>
<p>Finally, configure your application to use the certificate:</p>
<pre><code>jks:
secretList:
- name: kafkaStore
vaultPath: "custom/internal/certificates/kafkaKeyStore"
</code></pre>
<p>This setup allows your application to automatically retrieve the certificate at startup, streamlining the secure authentication process.</p>
Slow Writing, Fast Typinghttps://blog.kaiyikang.com/posts/2025/slow-writing-fasttyping/https://blog.kaiyikang.com/posts/2025/slow-writing-fasttyping/Mon, 13 Jan 2025 00:00:00 GMT<h2>Slow Writing, Fast Typing</h2>
<p>I just tried writing some characters by hand. Not only was the writing painfully slow, but it took considerable mental effort to form each character in my mind. Compared to typing on a computer, the difference in speed was striking.</p>
<p>Having not written by hand for so long, this contrast startled me. My hands trembled like those of an elderly person as I wrote, my eyes fixed on the movement, while my mind raced ahead impatiently. The words I wanted to express had already cycled through my thoughts several times before I could finish writing even the first few characters.</p>
<p>I'm not certain, but I feel there's something more natural about the deliberate pace of handwriting that aligns with how our brains are meant to work. In contrast, the lightning-fast pinyin input method allows us to process multiple lines at once, our thoughts racing ahead at breakneck speed, never pausing for breath. As a result, people have grown to dismiss handwriting, no longer giving it a chance. Once we leave school and enter adulthood, pen and paper rarely grace our desks anymore, save for the occasional quick note - gone are the days of lengthy handwritten texts.</p>
<p>This inherently natural slowness has crystallized over time into something precious - handwritten letters and greeting cards that find their way into everyone's cherished keepsake boxes. Though the writing may fade and the paper yellow with age, the tactile quality and emotional resonance remain unchanged.</p>
<p>This is the unique magic of genuine handwriting.</p>
<h2>The Depths of Deep</h2>
<p>There was Cal's Deep Work, and then came David's Depth Year. It reminds me of city dwellers who purchase high-end outdoor gear they rarely need. When my partner questioned this, I explained it as aspirational - though we haven't reached that level, simply owning and wearing such gear makes us feel closer to embodying those capabilities, making our aspirations feel slightly more tangible.</p>
<p>The concept of "Deep" fascinates me, though it remains elusive. I find myself drawn to related topics, casually exploring their theories while imagining myself achieving such profound focus in my own work. Even now, I haven't reached what I consider a truly deep state, which is why I'm captivated when watching my partner work with intense concentration. In those moments, it's as if the room's energy gravitates toward her; at peak times, it's like the universe itself channels its force through her. She simply immerses herself in her work, naturally embodying this state without conscious effort. There's something beautifully primal and pure about it that I find mesmerizing.</p>
<p>My partner is a freediver, where depth is literally the goal. This physical manifestation of depth often connects in my mind with these abstract concepts of deep work and focus. When I attempt to delve deep into a task, rather than achieving true immersion, my wandering thoughts often pull me back to the surface of reality, reminding me of the task's importance and my fear of breaking concentration. It's like performing precise, repetitive work - each motion requires momentum, accuracy, and patience. One moment of distraction can throw everything off balance. I might start fully engaged, my mind either completely focused or peacefully empty, but then larger thoughts emerge, breaking my rhythm. Instead of remaining immersed, I become anxious about losing focus, worried about maintaining the flow. This anxiety amplifies itself, like a cascade of disruption, until I feel completely disconnected, floating in a void of lost concentration.</p>
<p>Beyond the physical and temporal aspects of depth, there's also a mental dimension. I experience this when encountering half-forgotten vocabulary, attempting coding problems I vaguely remember, or revisiting books that exist as shadows in my memory. These encounters feel like grasping at gossamer threads of memory - delicate connections that might break at any moment. There's a slight apprehension, mixed with frustration at the lack of clarity. It's not the clean, satisfying feeling of complete understanding, but rather a resistant kind of engagement. While these moments sometimes make me hesitate, I usually push through, forcing myself to grasp these nebulous concepts until they become clear and defined. Overcoming this resistance brings its own unique satisfaction and sense of achievement.</p>
<p>Perhaps there's wisdom in this approach - taking things slowly and diving deep.</p>
Traveling Between Frankfurt and Munichhttps://blog.kaiyikang.com/posts/2025/traveling-between-frankfurt-and-munich/https://blog.kaiyikang.com/posts/2025/traveling-between-frankfurt-and-munich/Sun, 30 Mar 2025 00:00:00 GMT<p>We never really discussed a specific time to wake up or leave, just vaguely aimed for around nine or ten in the morning. After finally getting up and quickly packing, we saw it was already nearing 11 AM when we reluctantly headed for the parking garage.</p>
<p>On the walk from home to the garage, we passed the train station. The sights and sounds activated familiar pathways in my brain, as if in the next second I would veer off my path and head towards the platform. But then I remembered: with a car, we mostly wouldn't need to take the Deutsche Bahn anymore. A fleeting sense of nostalgia washed over me, but it quickly evaporated when I recalled the frustrations caused by Deutsche Bahn delays.</p>
<p>She drove the entire way there. I asked if she wanted me to take over at some point, but she seemed energetic and said she was fine, so she handled the whole drive.</p>
<p>We took the A5 south, then near Stuttgart, switched to the A8 heading directly towards Munich.</p>
<p>I remember how the weather shifted from overcast to sunny during the drive. It started with dense clouds and a light drizzle, but as we headed south, the clouds broke apart, revealing the sun. Sitting in the passenger seat, I watched the changing scenery fly by. The transition from narrow stretches to wide-open sections was particularly impressive. The road opened up into four wide lanes in our direction, allowing speeds over 130 km/h even in the rightmost lane. Green forests lined both sides, with hazy hills in the distance, and the clouds above were remarkably full-bodied–sometimes light and wispy, other times dense and heavy, the weather shifting as erratically as the needle on the speedometer.</p>
<p>We hadn't prepared any music beforehand, only starting to choose songs once we were on the highway. We picked them in an "impressionistic" manner–tracks that left a strong impression came first, followed by those with lighter ones. The strongest impression, undoubtedly, belonged to playlists titled something like "Super High Quality - Essential Car Anthems". I recall seeing similar CDs in the cars of many older folks ("uncles and aunts"), albums with no set name but always some combination of these descriptive adjectives. The artist was invariably listed as "Various Artists" or some other vague term. The flavor was strong, almost overwhelming, yet genuinely authentic.</p>
<p>Elements like "Neimenggu" (Inner Mongolia), "Caoyuan" (Grassland), and "Xizang" (Tibet) seemed especially popular with the older generation, pushing "tangled love stories" aside. It was either the deep voice of a middle-aged man or the soprano of a "national treasure" female artist; all sorts of disparate styles converged on a single CD. I heard too much of it as a kid and grew tired of it, but driving my own car now, it felt surprisingly energizing. Could this be the wisdom of the elders they talk about? "Ei hei~ hei ei~"–a sudden playful voice emerged from a mountain folk song, pulling me back, wave after wave, to childhood memories in China. It seems this wasn't such a bad way to reconnect with that feeling of home after all.</p>
<p>Along the highway, rest stops appeared at regular intervals. Their placement seemed remarkably deliberate. Just as your bladder started sending signals, you'd find a place for relief within another ten minutes or so of driving. I boldly hypothesize that the spacing of these stops might be calculated based on the average adult bladder capacity and tolerance time–hence their exquisite timing, which had us clicking our tongues in admiration from our seats.</p>
<p>The rest stops had a strong commercial atmosphere; stopping almost forced you to spend money. Besides the pricey fuel, this was evident in how toilets and food were handled. Using the restroom cost one Euro, but inserting the coin dispensed a one Euro voucher usable for purchases in the adjacent shop. The shop prices felt about 1.5 times higher than usual, so with the voucher, the final cost wasn't too bad. However, you could only use one voucher per item, meaning any extra vouchers had to be saved for the next time. Since using the toilet was often necessary, getting a voucher was unavoidable. To feel like you were getting "some value", you felt compelled to use the voucher, which often meant buying another coffee. Then, after drinking it in the car, you'd wait for the caffeine to work its magic, setting up the cycle of needing the toilet and spending money at the next rest stop. These people are sharp, and rather shrewd.</p>
<p>After about five hours of driving, we finally reached Munich. She was exhausted and quickly fell asleep. I didn't disturb her, my thoughts drifting to EU regulations on long-haul driving safety–the rule requiring a 45-minute break after every 4.5 hours of driving. It makes sense, as driving is undeniably tiring, but it contrasts with practices back in China. I recall that buses in China rarely seemed to stop for driver breaks. It reminded me of Turkish drivers too, often in white shirts, who might stop mid-route to pick up more passengers, their breaks consisting of just a quick coffee or tea. They handled those huge steering wheels like wizards, weaving through winding mountain roads with incredible ease, almost nonchalantly. But ultimately, all that rushing was about earning more money and getting more rest time later, invisibly cultivating and refining various driving skills in the process. It's admirable, yet undeniably tough work.</p>
<p>Having experienced the narrow A5, I opted for the A9 to A3 route for the return trip–heading north first, then west. This proved to be a better choice; the roads were wider, and the roadside facilities were more comprehensive.</p>
<p>Learning from our previous long drive, we knew to prepare a playlist beforehand to avoid scrambling for music. The quest for our favorite songs inadvertently turned into a late-night music appreciation session, resulting in a very long list. We barely added any duplicates, except, curiously, for "'Tao Ma Gan'", which appeared twice. Perhaps the allure of grasslands and stallions led us independently to the same choice. Well played!</p>
<p>Compared to her steady approach, my driving style was a bit more spirited. The actual speed limit was dictated by whichever was lowest: the posted signs, the car's ability, or my own comfort level. Once comfortable, I found a certain pleasure in overtaking–always prioritizing safety, of course.</p>
<p>This time, the weather pattern was reversed: it went from overcast to sunny. The sun gradually peeked through the clouds, its rays warming my arms and body. Thanks to the wide roads and abundant greenery, the views were excellent. Watching small towns perched on hills appear and then recede, I imagined their entirely different ways of life, ones not centered around major urban hubs.</p>
<p>After driving for over two hours, we found a "bladder break" rest stop. We parked, used the facilities, but skipped buying food, instead retrieving the simple fish burgers she had made. The sun was veiled by mist, the weather turned grey, and a wind picked up. We sat in the car listening to a finance podcast, munching on our burgers and discussing the content, sipping coffee when thirsty, and carefully trying not to drop crumbs in the clean car. Perhaps because I wasn't fully accustomed to driving yet, my eyes felt strained. So, I relinquished the driver's seat to her for the remainder of the journey.</p>
<p>With practice, her driving had improved noticeably. She managed the speed well and overtook with more confidence. This led us to another realization: when something happens, it's better to state it calmly rather than exclaiming. Shouting can startle the driver, leading to erratic maneuvers, poor driving, and frayed nerves. Suppressing the urge to yell and maintaining composure benefits everyone.</p>
<p>The rest of the trip was uneventful. We entered the city and pulled into the parking garage. I didn't feel as tired as before and was getting more accustomed to driving and being driven. It seems these trips might become more frequent from now on.</p>
像猫一样思考https://blog.kaiyikang.com/posts/2017/%E5%83%8F%E7%8C%AB%E4%B8%80%E6%A0%B7%E6%80%9D%E8%80%83/https://blog.kaiyikang.com/posts/2017/%E5%83%8F%E7%8C%AB%E4%B8%80%E6%A0%B7%E6%80%9D%E8%80%83/Sat, 13 May 2017 00:00:00 GMT<p>我曾经问过同学,说,人能不能像猫一样思考。她和我讲,人怎么知道猫是怎么想的?也许我们永远无法知道猫在想什么,但我们还是可以去尝试「感受」它在想什么。这种感受从结果上来说,是大致相同的,但是如何能够获得感受的能力确实因人而异的,有的人可能看到某些语句就可以获得极大的共鸣,而有的人即使是长时间的耳濡目染,也很难获得这样的体会。</p>
<p>《当下的力量》这本书,如今已经摆在了心灵励志或者是情感的分类货架上的。那时刚上大学的时候看到了这本书,就已经受到了一些震撼,几年过去了,书中的某些语句,例如「像树木和花草一样思考」,「人的思维大多只是冰山上的那一部分」等等,到了至今还时不时的会回忆起来。</p>
<p>那个时候的我,也没有有关的知识,读这些书只是觉得内心很舒坦,而其中的语句也可以让焦躁或者恶劣的心情平静下来。不过至于原理和原因,书中却没有过多的描述,而因为本人是个工科生,本着求实的态度,所以对此还有些耿耿于怀。</p>
<p>在《改善情绪的正念疗法》一书中,我倒是发现了许多有实验结论做支撑的理论依据。</p>
2024 十月阅读https://blog.kaiyikang.com/posts/2024/2024%E5%8D%81%E6%9C%88%E9%98%85%E8%AF%BB/https://blog.kaiyikang.com/posts/2024/2024%E5%8D%81%E6%9C%88%E9%98%85%E8%AF%BB/Sun, 27 Oct 2024 00:00:00 GMT<h2>正文</h2>
<p>最近 mindset 一词出现频率很高。</p>
<p>例如 Joshua Comeau 不断尝试从编程逻辑的视角去理解 CSS。在他的技术演讲中,内容不仅精彩,还拓展了自己的心路历程,并在其中强调了自己在学习中填补 mindset gap 的重要性。</p>
<p>我没有想到中文合适对应的词。mind 有头脑和精神之意,而 set 则是集合。它不单是「思维模式」或「思维心态」,也许是知道编程的缘故,对我而言,它更有些模块的含义。gap 更加活灵活现,也符合对应的气质。人们摆弄着不同的模块,然后把它们以合适的顺序连接起来,从而得以理解一个事物。</p>
<p>我很喜欢这个概念,这和《Soft Skills》中曾强调过的「构建属于自己的 Tutorial」不谋而合。通过学习与实践,把理解一个事物的思维链条打通,使其足够直观且容易理解,这是我想一直践行的。</p>
<p>同时,打通逻辑链也应符合从简的原则。一切应从最符合直觉的简单语句出发,例如动宾短语等,然后逐渐深入,就像用放大镜观察,一层层捕捉其中没有连接上的断点,等到断点被弥合了,再深入,直至功能最终实现。在这个递归流程中,我们能够感觉并理解为什么人们当前所流行的解决方案非常复杂的原因,因为它们存在的目的正是弥补从直观角度上考虑所忽略的事情。那些敲脑袋想出来的解决方案,但在实际中其实并没有办法实现。如果我们时间和精力不足,那么只考虑该模块的输入和输出,而不深入内部构造,也足够能实现想要完成的任务。</p>
<p>面对如此规模的知识爆炸,快速理解并将其引入到当前结构的能力是十分必要的。制造轮子在深入了解某个主题是必要的,但倘若时间有限,拿来就用也是不错的选择。</p>
<hr />
<p>最近几个月都在集中的阅读 Cal Newport 的书,改写版的《Digital Minimalism》,中译本《深度工作》以及原版《Slow Productivity》。</p>
<p>在聊这些书的内容之前,我似乎稍微摸到了畅销效率书的门道,开头先来一个例子:出现了问题,然后在一阵反思,顺着展示出承诺或解决方案,最后再持续循环。至于上面提到的作者,比类似的模版稍微好一些,除了例行公事外还会讲一些自己信奉的主义和哲学,算是给读者喂了「所推崇的方法论有效」的定心丸。</p>
<p>自主性的理念贯穿他所有的著作。《Digital Minimalism》不会排斥社交媒体和最新的技术,而提倡在使用这些技术前,我们要明确的认知,知道我们自己到底想要获得什么。iPhone 的确能运行很多 App,但这不是因此贪嗜的理由,甚至乔布斯在当年最初设计的时候,排斥将应用商店加入手机功能中。此刻的我们,正在使用着别人硬塞给我们的东西,从不去想自己真正想要获得什么。</p>
<p>至于《深度工作》则强调注意力在现代的重要性。他们不仅希望我们可以掏钱,还进一步希望剥夺我们的注意力,从而将大部分的时间投入其中。因此,重新获得专注力,并将它投入到自己想要发展的领域,在当下是尤为重要的。</p>
<p>最后的《Slow Productivity》是 2024 年最新的作品,基本上将上面两本书做了个结合。他总结出的原则是,在快节奏的工作中,我们应该以自然的工作节奏,做更少的事情,并且执着于质量。这本书就像我的认知唤醒器一样,不断的轻轻敲打我应该如何更加主动的做好一件事情。</p>
<p>在讲述执着于质量的章节中,作者提到了审美的重要性,让我印象深刻。以往对于审美的谈论很大程度聚集在个人本身,例如提升修养品味,让精神升华一类的。他提到的却是和所做工作的质量联系在一起的。对这点我感同身受,当我欣赏过精致简约的架构和代码之后,我就再也无法忍受自己写出质量糟糕的代码,并且看到由他人写的,质量并非很好的代码,也会变得无比嫌弃。这里其实假定了,人都是有朝着更好方向迈进的动机的。把握了这层心里,一旦人有了品味和审美以后,再让他退回到之前的状态,是无比困难的。因此,好的品味不光还影响人本身,还会影响人手下的创造之物。</p>
<hr />
<p>另外一本读到的是台版翻译的《Die With Zero》。</p>
<p>作者偏爱风险,同时很富有,因此对书中的内容,我无法完全认同,照单全收,但有些点子确实有启发性,尤其是在于个人塑造方面。</p>
<p>他强调了一个观念是,人的经历和金钱一样重要,会随着时间的变化产生收益。不同点是,钱可以在任何时间挣取出来,但经历会随时间和年龄的变化而无法再来。作者假定了有一些经历是和年龄匹配的,过了这个年龄,再经历味道就不对了。因此如何让对应的年龄,做对应的事情,从而让它的「收益」最大化,这是他在书中要讨论的事情。</p>
<p>我们所知道的绝大部分人都是在生前省吃俭用,死后还留有一大笔未曾使用的财产,而这部分财产则包含着,这个人为了辛苦工作,而丧失掉了享受人生的那部分时间。人的时间大多固定,只要用在了这里,那么就没办法用在那里,正是所谓的「机会成本」。</p>
<p>把握自己真正想要的,从而让自己在有精力的时候去经历这些,才是让生命获得最大价值的方法。以上论述都来自于书中,而现实状况更多是复杂且有出入的,因此我才说不能照单全收。但作者也还是强调了「主动做自己想做的」,这点也和上一节作者所提及的不谋而合。</p>
<p>从中我读到的比较新颖的观点是,人在死后留财产是「不负责的」,只有那些不知道自己如何享受生活和支配金钱的人,才会将选择权交给死亡。相反的那类人,会在生前就处置并分配好所有的财产,不论分配是多少,自己知道自己要花多少,以清醒和理智的态度直面死亡。</p>
<p>我个人觉得有一定的道理,但仍太过绝对。也许等我真的哪天能挣出那些钱,有所谓的资产了,才大概能更深刻地明白作者想聊的理论吧。</p>
<h2>小总结</h2>
<p>不论多少工具,AI 算法的出现,最后还是落在了要专注做事上。用主动且积极的态度去反思自己的日常行为,然后再用主动的心态,花些时间和精力泡在事情中,从简单开始再逐步拓展到复杂。</p>
<p>对自己好些,不要太吝啬,好好享受当下,好好做事。</p>
<hr />
<h2>谵语</h2>
<p>写作有些像梦境,不知道从哪里开头,也不知道在哪里结尾。一行行或一段段的文字流于荧幕上,滚入到眼球,再滚出。回归到现实后,写作以不间断的形式一直在飘荡,从人的出生开始从不间断,直到死亡。</p>
<p>梦和生命也很相似。生命也没有开头。从观者的角度看,生命降生到世界上是有迹可循的。性爱,怀孕,然后在产房生产。观者像读者,实践并感受新生命的降生。读者进入书店,买了本书,然后再拆封,开始阅读书中第一行的道理一样。但从生命自身角度讲,一切非常莫名其妙。自己如何被降生,自己又如何度过了最初在襁褓中的几年,是失去记忆的梦,没人会记得。稀里糊涂的开始上学,然后工作。</p>
<p>结束了写作,结束了阅读,生命死亡了。它们都会让人感到惋惜,记忆随着时间逐渐模糊,渗入了神经中。观者在生活时,部分神经被激活,才会回忆起那时发生的事情。</p>
<p>真实属于他人,只有自己在做梦。这点并不矛盾,不是说你我类似,于是你我都必须是真实的,或都在做梦。它们不是定义,而是描述。真实和做梦的阐述不来源于其自身,而来自你我,是我的经历和对你的观看,共同塑造了这个看似矛盾的架构。反过来,一切都是调和的了。</p>
2024 写作https://blog.kaiyikang.com/posts/2024/2024%E5%86%99%E4%BD%9C/https://blog.kaiyikang.com/posts/2024/2024%E5%86%99%E4%BD%9C/Sun, 14 Jul 2024 00:00:00 GMT<p>标题的数字没有任何含义,不代表 2024 年有什么特别之处,单纯是个作为区分的版本标签。关键想聊的是当下的写作。</p>
<p>说话的我和写作的我,是两个不同的我。在说话的时候,我明显感觉到,思维像是一个沙包,一颗颗豆子被缝在小布兜里,不规则无定形。说出来的话,仿佛小孩子将沙包丢到天空中,到处飞来飞去。写作可大不一样了。豆子还是那些豆子,但我有足够的时间,坐在电脑面前将它们按压碾碎,过筛烹煮,最终把口感绵密的豆沙馅呈现给对此感兴趣的读者。</p>
<p>在创造方面,我是形式感很重的人,这意味着,我的创造应该遵循特定的韵律,文字要有明确的标题和格式,其内容要足够规整和易读。照片要稍微精致一些,并且发布的日期需要定点定时。我想,我没法为这些这种形式感寻找出合适的理由,它混杂了片面的最佳实践,还有个人的性格与审美。也许是做为程序员所带来的职业病,什么创作似乎都脱离不了写代码的影子。虽然代码是严谨的,但面对同一个功能块,不同的人会有不同的实现方式,这也同样包含着创作者的性格,从该角度讲,我将其称为一种艺术。</p>
<p>对形式感的感触是薛定谔式的,看着整齐划一的历史文章和图片,有时觉得充满着秩序的美感,但有时也会觉得有些许的反人性。当我想创造或发布一个作品的时候,脑子里蹦出的第一个问题不是关于作品本身,而是它是否符合先前的各种形式,如果符合了,无事发生,如果不符合,那么也许连创作行为本身都无从谈起。让创作之外的东西影响其本身,我对此实在欢喜不起来。</p>
<p>我时常认为写作是严谨的,要顾及逻辑的同时,更不能胡说八道。但细细想来,这也得看自身和环境,要是它本就不是什么技术论述,单纯是个私人感受的分享,那感受和思维会变的过分拘谨,得不到其应有的释放。而环境的影响也不小,例如有无评论功能,即使是一篇胡言乱语的技术分享文章,下面根本没有评论功能,读者找不到渠道去评论或纠正文章中的错误,这样的胡说八道似乎也招惹不来太多的是非,也仅是落下个江湖骗子的名分。不太重视脸面的作者,也就无所谓要不要严谨了。</p>
<p>我在想是不是自己太过君子,从没有过流氓的念头,或有过类似的念头,但不过是被心中强烈的道德卫兵缠在树上吊起来打。有时候斯文败类就是比纯斯文的要更有魅力,前者不仅多了俩字,同时还消弱了后者所透露出的学究和规训气质。</p>
<p>我想变得有趣,严谨只是一个工具,但最终还是想要有趣。</p>
<hr />
<p>我从去年的每星期一篇,再到每月一篇,最后到了几个月一篇,写作频率是越来越低。专心投入到生活之中是原因之一,而平台则是另外的原因。</p>
<p>比方说微信的公众号。在发布公众号文章前,需要首先在另外的平台排版插图,然后再同步。经过一系列复杂的点选和设置后,还要打开手机扫描二维码,最终才能发布出去。而且文章一经发布,几乎没有修改的可能性。从腾讯的角度角度讲,这逻辑很合理,在如此严苛的环境下,要是真的放开了,让人们撒开了手脚放肆的书写和发布,微信真有可能变得乌烟瘴气。诚然,即便是增加了如此多的束缚,现在的公众号仍活成了我们不喜欢的样子。说回来自己,我不管它是如何考量的,但就我自己的真实体验而言,发布一个公众号就是复杂的,刻板的,极端严肃的。</p>
<p>我不喜欢。</p>
<p>其他的平台也有可能,但着实好不到哪里去。比如豆瓣,丝毫不考虑我作为作者分享的强烈欲望。我辛苦拍的照片,写的文字,就是为了让所有人早早看到,而不是闷在那里,等着管理员蹲在小黑屋里细细筛选完每个字以后,再被无罪释放。我的分享应该是属于所有人的,所有人都应该有同样的资格第一时间阅读到它。(当然,我最亲近的人应该排在所有人之前:D)</p>
<p>切换平台成了重要的事情,因此私人的博客就孕育而生。这个平台,除了底层的技术用到框架技术之外,完完全全都是在我控制范围内的。从域名链接,再到背景颜色,再到每一个字的间距和大小,这些都是可以根据个人喜好定制,而不必再遵循任意平台的限制。</p>
<p>当黑盒子被打开成为白盒子的时候,一切运行的机理将更为透明且可理解。当然,伴随着的阵痛必然是信息过载,有太多不明白的问题亟待解决,以及更多的东西要学习,但幸亏学习的过程是愉悦的,不断的刺激和正面的反馈正在推动着我去不断的思考当前的平台,如何改善它?如何增加新的功能?如何让阅读的体验更加舒服?我相信对我自身成长也是有裨益的。</p>
<hr />
<p>有了态度,有了传递出的平台,最重要的还应该回归内容。内容的形式,长短,该有什么不该有什么,我想了很久,也想了很多。</p>
<p>有天,我曾和母亲提起过,说是如果文章太短会不会显得琐碎,从而没人阅读。她的反馈是,短点儿好,短了才有人来读,毕竟现在社会的节奏这么快。我心想说有道理,于是这才放下对长短的戒备,在此之前,我都会把极短的内容堆积在一起,拖了很久才发布。现在想想,短了,于他人于自己都是有利的。</p>
<p>有另一个我不太确定的好处是,短了反倒更容易让我写的变长。短了怎么又变长了,这不是矛盾么?其实情况是,我写作不光是情绪很严肃,环境和时间更是要遵循一个特定的路径。从频率角度讲,产量最高永远会发生在周日的下午,家旁边的咖啡馆。道理很简单,为了平静的迎接周一的工作日,周日的下午自然不会安排什么太过上火的活动,爬山社会,胡吃海塞都会统统避免。而咖啡馆则是老习惯,这点我记得在几年前的文章还提到过,记得那时还奉星巴克为圭臬,现在已然变成最糟糕的选项了。</p>
<p>现在生活节奏发生了变化,以前的路径也不再合适,创作长文的方式也应该得到调整。虽然时间比较细碎,但我可以利用这个细碎的时间去专注书写些较短的段落,然后通过对这些段落的拼接和延续,逐步积累成一篇长文。相较于之前的「要么全部,要么没有」,短段落更加现实,实现了先把内容铺开来再说的策略。</p>
<hr />
<p>还有是内容的选择。</p>
<p>有了搜索引擎,甚至是大预言模型之后,我发现技术、理论或和方法论相关的内容,都已经屡见不鲜了。很多内容都被整理的很好,很具体。我之前还曾想把自己遇到问题的解决答案总结成博文,分享出来,但发了几篇之后,发现还不如网上搜索到的总结好,看似内容都是自己的东西,实则都是复制粘贴的杂合体。尝试几次后,就完全没了兴致。</p>
<p>在实际中为了解决问题而存在的内容,我愿称其为笔记,更应该随性的为自己记录,保存在本地,至于是否公开,那就得看每个人的意愿。就我自己而言,我在想如何让自己的博客变得更自我一点,不是说从中展现太大的 ego,而更多的是自身性格和对同一片天地的认识。这种偏向于让物种多样性增加的方向,是在这个内容生成时代弥足珍贵的东西。就算大预言模型能够精准产出像我这种风格的写作内容,那我也希望这个循规蹈矩的自己,而不是概率模型。</p>
<hr />
<p>最后想用一句简短的话来总结这篇文章:我希望在自己的平台上,用有趣,简练和带有自我的方式做表达。</p>
什么是同步锁?https://blog.kaiyikang.com/posts/2024/java_%E5%90%8C%E6%AD%A5%E9%94%81/https://blog.kaiyikang.com/posts/2024/java_%E5%90%8C%E6%AD%A5%E9%94%81/Fri, 04 Oct 2024 00:00:00 GMT<h2>同步锁和线程</h2>
<p>Synchronization Lock,同步锁,或称监视锁,互斥锁。</p>
<p>它确保不同线程,安全访问共享资源。</p>
<p>一个线程,想执行以下代码时,需获取该对象的锁。该获取过程,由 JVM 自动控制:</p>
<pre><code>public synchronized void method() {
// 方法体
}
// OR
synchronized(object) {
// do something
}
</code></pre>
<p>多个进程,想同时执行下面的代码,即尝试获取同一个对象的锁,叫线程竞争。</p>
<p>竞争会导致 Blocked 阻塞状态([[Java多线程的故事#线程的状态]])。线程 B 已经持有了锁,线程 A 想尝试获得,但失败。JVM 会将它置于锁池 Lock Pool 中,并改变 A 状态为阻塞。</p>
<p>Lock Pool 中存放着,所有试图获取锁,但没有如愿的线程。</p>
<p>线程 A 在池中,会等待锁的释放,一旦释放,便恢复运行。该过程称为等待机制。</p>
<p>与之相对,持有锁的线程,执行完代码以后会释放锁,JVM 会唤醒等待的线程。称之为唤醒机制。</p>
<h3>例子</h3>
<pre><code>class Scratch {
public static void main(String[] args) {
SharedResource reource = new SharedResource();
Thread threadA = new Thread(() -> {
reource.doSomething("Thread A");
});
Thread threadB = new Thread(() -> {
reource.doSomething("Thread B");
});
threadA.start();
threadB.start();
}
}
class SharedResource {
public synchronized void doSomething(String threadName) {
System.out.println(threadName + " has entered the synchronized method");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName + " has exited the synchronized method");
}
}
</code></pre>
<p>输出:</p>
<pre><code>Thread A has entered the synchronized method
Thread A has exited the synchronized method
Thread B has entered the synchronized method
Thread B has exited the synchronized method
</code></pre>
<h2>关键字 volatile</h2>
<p><code>synchronized</code> 锁的是 object,定义是 is created using a class is said to be an instance of that class。</p>
<p><code>volatile</code> 锁的是 Variables,更轻量。开销小,性能好。</p>
<p>直接的例子:</p>
<pre><code>public class SynchronizedExample {
private boolean flag = false;
public synchronized void setFlag() {
flag = true;
}
public synchronized boolean isFlag() {
return flag;
}
}
</code></pre>
<pre><code>public class VolatileExample {
private volatile boolean flag = false;
public void setFlag() {
flag = true;
}
public boolean isFlag() {
return flag;
}
}
</code></pre>
<p>特性对比:</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>volatile</th>
<th>synchronized</th>
</tr>
</thead>
<tbody>
<tr>
<td>可见性 Visibility</td>
<td>yes</td>
<td>yes</td>
</tr>
<tr>
<td>有序性 Ordering</td>
<td>yes</td>
<td>yes</td>
</tr>
<tr>
<td>原子性 Atomicity</td>
<td><strong>no</strong></td>
<td>yes</td>
</tr>
</tbody>
</table>
<p><strong>可见性 Visibility</strong></p>
<ul>
<li>定义:指当一个线程修改了共享变量的值,其他线程能立即看到。</li>
<li>原因:多核 CPU 和各自缓存之间出现的一致性问题。</li>
<li>例子:线程 A 修改了变量 x 的值,但线程 B 可能仍然看到的是 x 的旧值。</li>
</ul>
<p><strong>有序性 Ordering</strong></p>
<ul>
<li>定义:程序执行的顺序按照代码的先后顺序执行。</li>
<li>原因:为提高性能,编译器和 CPU 会对指令进行重排,导致执行与书写顺序不同。</li>
<li>例子:代码中,语句 1 在语句 2 之前,但实际执行时,颠倒顺序。</li>
</ul>
<p><strong>原子性 Atomicity</strong></p>
<ul>
<li>定义:指一个操作是不可中断。即使在多线程中,操作一旦开始,会让整个操作完成,而不被其他线程干扰。</li>
<li>问题:在多线程环境下,非原子操作可能会导致数据不一致。</li>
<li>例子:i++ 看似是一个操作,实际上包含了「读取 i」、「增加 1」、「写回内存」三个步骤,因此不是原子操作。</li>
</ul>
<h3>什么时候用 volatile?</h3>
<p>适合用在一个线程写,多个线程读的场景,并且写入是简单赋值,不依赖于变量当前值。</p>
<p>适合的例子:</p>
<pre><code>public class FlagHolder {
private volatile boolean flag = false;
public void setFlag() {
flag = true; // 简单赋值,不依赖当前值
}
public boolean isFlag() {
return flag;
}
}
</code></pre>
<p>不适合的例子:</p>
<pre><code>public class Counter {
private volatile int count = 0;
public void increment() {
count++; // 不适合,因为这是复合操作,依赖当前值
}
}
</code></pre>
不协调https://blog.kaiyikang.com/posts/2024/%E4%B8%8D%E5%8D%8F%E8%B0%83/https://blog.kaiyikang.com/posts/2024/%E4%B8%8D%E5%8D%8F%E8%B0%83/Sun, 23 Jun 2024 00:00:00 GMT<h2>人多的好处</h2>
<p>身处大城市中,人多常常暗示着拥挤和等待。</p>
<p>不过这种状况也并非完全。当你在匆忙赶地铁的时候,倘若看到在你要出发的站台上看到了很多人站着等待,这传递出了非常好的信号——车辆即将抵达,你将可以马上离开。</p>
<p>同理,如果你看到站台上等待的人很少,意味着车刚才已经驶离,你不得不在站台上等待一个完整的周期,并看着人们逐渐的在身边聚集。此时此刻,「人多」所传递出的,不再是快速的离开,而是拥挤与等待。</p>
<h2>衣领子</h2>
<p>某天我在服装店里闲逛,忽然看到假人模特身上,套着一件领子格外别致的衣服。由于领口没有缝合线,尖尖的领子从脖子位置平整地延展上去,自然且松弛地翻垂下来。我对它的德语名称产生了好奇,通过手机戳戳点点,查到领子被称为「Kragen」。为了记住这个词,我在脑子里不停地重复它。</p>
<p>当天是德国的自豪日,我刚从店里推门出来,一股汹涌的人潮就向我的面门奔涌而来。人们着装各异,有遮盖乳房,尝试露出上半身的「男人」;有性感穿搭,穿着吊带小裙的「女人」。各类要素随意排列组合,毫无章法地倾泻了出来。</p>
<p>我看着形形色色的人,又想到「Kragen」,心中不由得去对比这两个概念,遂而隐约察觉到,人的个体作为概念的渺小与无力,以及「Kragen」相对更加「永恒」一些。</p>
<p>比方说,我的名字是 YK(用缩写举例)。假设它不重名,那么 YK 作为一个概念,是有能力指代并且描述我,「YK 是怎样的」或「这个有 YK 的感觉」等说法也都成立。把我作为个体抽象为 YK 与「Kragen」比较,无论站在什么视角,都无法与其竞争。从时间上,领子的德文早在 1600 年就出现了。从地理上,它广泛传播在大德语区,每当你同当地人讲「Kragen」的时候,对方都能明白,这代表着领口的那片布。</p>
<p>我作为 YK 的缔造者和诠释者之一,颇有些无力与挫败感。原以为,人可以更强大些,但其实作为普通人,名声都比不过衣领子。从另外一个角度出发,有些人的名声却能流芳百世,比方说「Transformation de Fourier」。</p>
<p>即便说了这些,一般人也无太大必要始终和抽象概念过意不去。毕竟衣领子只能被摆在塑料假人身上,没办法像人那样,吃穿住行,并感受着欢喜和悲伤。</p>
<h2>时间的格式</h2>
<p>我始终没有在苹果手表上寻找到带有合适功能的表盘。</p>
<p>我的需求很简单,数字钟作为主体,并附有四个附加功能,日期,天气,气温和倒计时。我尝试换了不同的表盘,要么表盘不显示数字,要么附加功能不完全。</p>
<p>时常,我就不得不在智能手表上使用传统机械时钟的表达方式,去作为日常赶车的参考。经常用地图或软件查车的人都知道,所有时间都会精确到阿拉伯数字,而现代公交的管理系统也是根据根据的具体的数字时间。因此这就产生了冲突,模糊的我,匹配不上清晰的它。</p>
<p>机械时间指针给我带来的是模糊的质感。有时虽然我欣赏并享受时间呈现的闪烁其词,但每当它是这样的时刻,我都不需要知道精确的数字,因为在那时,我更关心檀香的长度或天空色彩的变化。反过来,如果我需要精确数字的时刻,例如赶车,但它没有及时出现时,模糊性只会放大内心中的不踏实和不安的感觉。</p>
<p>这是种皮笑皆非的错位感。发车的时间和智能手表,本应通过精确的数字对齐。但智能手表就是要模拟出表盘时间,就是要通过确凿但复杂的算法,把本就精确的数字模拟成粗大的像素点,从而在人工干预的情况下,给我们的使用造成障碍。</p>
<p>通过电子模拟出来的浪漫和怀旧,还请在首先实现基础功能的情况下进行,不然总会显得过分谄媚。</p>
Java多线程的故事https://blog.kaiyikang.com/posts/2024/java_%E5%A4%9A%E7%BA%BF%E7%A8%8B/https://blog.kaiyikang.com/posts/2024/java_%E5%A4%9A%E7%BA%BF%E7%A8%8B/Mon, 30 Sep 2024 00:00:00 GMT<h2>线程和进程</h2>
<p>Process 进程是 System 级单位,有独立运行空间,切换会有较大开销。</p>
<p>Thread 线程是处理器 CPU 调度单位,同一类共享代码和数据空间,比 Process 轻量,切换的开销小。</p>
<p>一个 Process 是一个 Java 程序的运行,包含至少一个或 N 个 Thread。</p>
<h2>创建一个线程</h2>
<p>创建一个 Thread,可以直接使用 Thread Class。或者使用以下三种方法自定义:</p>
<ul>
<li>继承 Thread 类:简单,无法继承。重写 run 方法。</li>
<li>实现 Runnable 接口:复杂,可以继承。重写 run 方法。</li>
<li>实现 Callable 接口:同 Runnable。重写 call 方法。</li>
</ul>
<p>其中,继承 Thread 类和实现 Runnable 接口,无法获得执行结果,无法处理执行异常。</p>
<p>但是位于 <code>java.util.concurrnet</code> 中的 <code>Callable</code> 解决了该问题,call 方法是 run 方法增强版,可以在实现时,通过传入泛型的方式,定义返回值。</p>
<p><strong>例子:继承 Thread 类</strong></p>
<pre><code>class Scratch extends Thread{
@Override
public void run() {
System.out.println("Thread is Created");
}
public static void main(String[] args) {
Scratch thread = new Scratch();
thread.start();
System.out.println(Thread.currentThread().getName());
}
}
</code></pre>
<p><strong>例子:实现 Runnable 接口</strong></p>
<pre><code>class MyTask implements Runnable {
public void run() {
System.out.println("Task is running");
}
}
public class Main {
public static void main(String[] args) {
MyTask task = new MyTask();
Thread thread = new Thread(task);
thread.start();
}
}
</code></pre>
<p><code>MyTask</code> 类实现了 <code>Runnable</code> 接口,只定义了可执行的任务,但本身不是线程。<code>Thread</code> 类提供了机制,将任务与线程相互关联。</p>
<p><strong>例子和解读:实现 Callable 接口</strong></p>
<p>接口的定义是:</p>
<pre><code>@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
</code></pre>
<blockquote>
<p>补充知识:<code>@FunctionalInterface</code> 是函数式接口,可以用 Lambda 表达实现:<code>ClassWithFunctionalInterface func = () => System.out.println("ok");</code></p>
</blockquote>
<p>Callable 是 Runnable 的补丁,<code>V</code> 定义了返回值,<code>Exception</code> 定义了异常。</p>
<p>因为最终我们使用 Thread 实现多线程,而它只接受 Runnable,因此需要一个机制,连接 Callable 和 Thread。</p>
<p>解决方案是 <code>FutureTask</code> 类。</p>
<p>该类提供两个构造方法:</p>
<ul>
<li><code>FutureTask(Callable callable)</code>,可以和 Callable 关联。</li>
<li><code>FutureTask(Runnable runnable, V result)</code>,也支持 Runnable。</li>
</ul>
<p>该类是 <code>RunnableFuture</code> 接口的实现,源码是:</p>
<pre><code>public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
</code></pre>
<p><code>Runnable</code> 负责 Thread,<code>Future</code> 负责得到来自 <code>Callable</code> 的返回值。</p>
<p>总结一下逻辑关系是:</p>
<p>Thread -<code>Runnable</code> + Callable - <code>Future</code> -> <code>RunnableFuture</code> -> <code>FutureTask</code></p>
<p>用代码逻辑表示是:</p>
<pre><code>// Callable
Scratch scratch = new Scratch();
// To FutureTask
FutureTask<String> future = new FutureTask<>(scratch);
// To Thread
Thread thread = new Thread(future);
</code></pre>
<p>完整的 <code>FutureTask</code> 使用例子:</p>
<pre><code>class Scratch implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Scratch");
return "OK";
}
public static void main(String[] args) {
// Callable
Scratch scratch = new Scratch();
// To FutureTask
FutureTask<String> future = new FutureTask<>(scratch);
// To Thread
Thread thread = new Thread(future);
thread.start();
try {
// Get result from FutureTask
String result = future.get(5, TimeUnit.SECONDS);
System.out.println(result);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
}
</code></pre>
<h2>线程的不同状态</h2>
<p>通过 <code>new</code>,我们创建了一个 Thread 对象。在从生到死的完整生命周期中,它包括六个状态:new,runnable,blocked,waiting,time_waiting,terminated。</p>
<p><img src="http://www.itheima.com/images/newslistPIC/1615964642254_%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE.png" alt="" /></p>
<p>具体如下:</p>
<ol>
<li><strong>New 新建</strong>:初识状态,不能运行。JVM 为它分配了内存,没有线程生命特征,和其它 Java 对象一样。</li>
<li><strong>Runnable 可运行</strong>:New 状态,调用 <code>start()</code> 进入。它细分成两个状态:
<ol>
<li><strong>Ready 就绪</strong>:它没运行,只是做好准备,等待 JVM 调度。</li>
<li><strong>Running 运行</strong>:真正运行。JVM 或 CPU 开始调度。</li>
</ol>
</li>
<li><strong>Blocked 阻塞</strong>:因某些原因,失去 CPU 执行权,即 CPU 不执行它。一般会因两种情况进入阻塞:
<ol>
<li>[[同步锁]] 被其它线程截胡。</li>
<li>线程运行时,发出 IO 请求。</li>
</ol>
</li>
<li><strong>Waiting 等待</strong>:Running 中的线程,调用了 <code>wait()</code> 或 <code>join()</code> 方法,进入该状态。等待中的线程,必须等其他线程执行<em>特定操作</em>后,才有机会再次争夺 CPU 使用权,然后转为 Running。例如:
<ul>
<li><code>wait()</code>:等待其他线程调用 <code>notify()</code> 或 <code>notifyAll()</code> 唤醒</li>
<li><code>join()</code>:等待其他加入的线程终止。</li>
</ul>
</li>
<li><strong>Timed_waiting 定时等待</strong>:Running 中的线程,调用了 <code>sleep(long millis)</code>、<code>wait(long timeout)</code>、<code>join(long millis)</code> 等方法进入。类似 Waiting 状态。</li>
<li><strong>Terminated 终止</strong>:<code>run()</code>、<code>call()</code> 执行完或者未捕获的 Exception、错误 Error,线程进入终止状态,生命周期结束。</li>
</ol>
<h2>切换不同状态</h2>
<h3>New -> Runnable</h3>
<p>调用 <code>start()</code> 方法,启动线程,然后会自动调用 <code>run()</code>。</p>
<p>单纯调用 <code>run()</code>,无法启动线程,参考下面的例子:</p>
<pre><code>public class StartVsRunExample {
public static void main(String[] args) {
// 创建一个实现了 Runnable 接口的匿名类
Runnable task = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i ++) {
System.out.println(Thread.currentThread().getName() + ": Count " + i);
try {
Thread.sleep(500); // 睡眠500毫秒
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thread thread1 = new Thread(task, "Thread-1");
thread1.start();
Thread thread2 = new Thread(task, "Thread-2");
thread2.run();
// 主线程继续执行
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": Main Count " + i);
try { Thread.sleep(500); }
catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
</code></pre>
<p>输出:</p>
<pre><code>main: Count 0
Thread-1: Count 0
Thread-1: Count 1
main: Count 1
main: Count 2
Thread-1: Count 2
main: Count 3
Thread-1: Count 3
Thread-1: Count 4
main: Count 4
main: Main Count 0
main: Main Count 1
main: Main Count 2
main: Main Count 3
main: Main Count 4
</code></pre>
<p>其中,<code>main: Count 0</code> 在主线程中运行,不在线程 <code>thread2</code> 中。</p>
<h3>Runnable -> Block</h3>
<p>例子:</p>
<p>两个线程,抢通过定义 Object 为 <code>lock</code>,:</p>
<pre><code>class BlockThread extends Thread {
private String name;
private Object lock;
public BlockThread(String name, Object lock) {
this.name = name;
this.lock = lock;
}
@Override
public void run() {
System.out.println("Thread " + name + " State is "+Thread.currentThread().getState());
synchronized (lock) {
System.out.println("Thread " + name + " hold the lock.");
try {
System.out.println("Thread " + name + " State is "+Thread.currentThread().getState());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + name + " released the lock.");
}
}
}
public class Scratch {
public static void main(String[] args) throws Exception{
Object lock = new Object();
BlockThread t1 = new BlockThread("Thread1", lock);
BlockThread t2 = new BlockThread("Thread2", lock);
t1.start();
t2.start();
Thread.sleep(100);
System.out.println("T1 state is "+t1.getState());
System.out.println("T2 state is "+t2.getState());
}
}
</code></pre>
<p>输出:</p>
<pre><code>Thread Thread2 State is RUNNABLE
Thread Thread1 State is RUNNABLE
Thread Thread2 hold the lock.
Thread Thread2 State is RUNNABLE
T1 state is BLOCKED
T2 state is TIMED_WAITING
Thread Thread2 released the lock.
Thread Thread1 hold the lock.
Thread Thread1 State is RUNNABLE
Thread Thread1 released the lock.
</code></pre>
<h3>Runnable -> Waiting</h3>
<p><code>join()</code>:让其它线程进入自身,使用 <code>wait()</code> 机制实现。</p>
<p>例如,当 Thread A 执行 threadB.join() 时,线程 A 会进入 WAITING 状态,等待 ThreadB 结束后,再继续。</p>
<p>例子:</p>
<pre><code>public class JoinExample {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("Thread started");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread finished");
});
thread.start();
System.out.println("Main thread waiting for thread to finish");
thread.join(); // thread join main
System.out.println("Main thread continues");
}
}
</code></pre>
<p><code>wait()</code>:Object 类方法,每个对象都有,必须在 synchronized block 中使用 ([[同步锁]])。使进程进入等待,直到其他线程,调用同一对象 <code>notify()</code> 或 <code>notifyAll()</code>。</p>
<p>使用方法:</p>
<pre><code>synchronized (object) {
while (条件不满足) {
object.wait();
}
// 条件满足,继续执行
}
</code></pre>
<p>它区别 <code>sleep()</code>:</p>
<ul>
<li><code>sleep()</code> 不释放锁,<code>wait()</code> 释放锁。</li>
<li><code>sleep()</code> 暂停执行,<code>wait()</code> 用于进程通信</li>
</ul>
<p>例子:</p>
<pre><code>class Scratch {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread t1 = new Thread(() -> {
try {
resource.use();
} catch (Exception e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
Thread.sleep(2000);
resource.prepare();
} catch (Exception e) {
e.printStackTrace();
}
});
// not ready, waiting...
t1.start();
// ready, notify!
t2.start();
}
}
class SharedResource {
private boolean isReady = false;
public synchronized void prepare() {
isReady = true;
notify();
}
public synchronized void use() throws InterruptedException{
while (!isReady) {
wait();
}
System.out.println("I am ready");
isReady = false;
}
}
</code></pre>
<h3>Waiting -> Runnable</h3>
<p><code>notify()</code>:通知 <code>wait()</code>。</p>
<p>例子见上一子章节。</p>
<h2>参考</h2>
<ul>
<li><a href="https://blog.csdn.net/qq_38721537/article/details/124581201">Java中Thread详解(一篇就够了)_java thread-CSDN博客</a></li>
<li><a href="https://github.com/cosen1024/Java-Interview/blob/main/Java%E5%B9%B6%E5%8F%91/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%80%BB%E7%BB%93%E7%89%88.md">Java-Interview/Java并发/Java多线程总结版.md at main <code>cosen1024/Java-Interview</code> GitHub</a></li>
<li><a href="https://www.itheima.com/news/20221107/102713.html">Java多线程:线程的生命周期的六种状态</a></li>
</ul>
关于涟漪的联想https://blog.kaiyikang.com/posts/2024/%E5%85%B3%E4%BA%8E%E6%B6%9F%E6%BC%AA%E7%9A%84%E8%81%94%E6%83%B3/https://blog.kaiyikang.com/posts/2024/%E5%85%B3%E4%BA%8E%E6%B6%9F%E6%BC%AA%E7%9A%84%E8%81%94%E6%83%B3/Sat, 11 May 2024 00:00:00 GMT<p>人如果是一片水池或一个空间,人的思维,发出的任何声响还是动作,都会让这片水塘泛起涟漪,也会在空间中增加物体。涟漪的复杂,空间中物体的多寡,都会随着人思维和运动的频率不停变化。如果思维太多,太剧烈,空间里的东西会变得细碎,容易扬起沙尘,而涟漪的波浪则会破碎,成为细小的水花。不论是沙尘还是水花,容易随着感受或思维的震荡变得无法保持稳定,升起并在空间中飞扬,生命力与创造力在此刻达到顶峰。水波停止并不在摆动,扬尘被清扫,空间变得重新洁净起来。这看起来像是一种违背活着的状态,活着,意味着运动与创造,只有动起来才会在外部世界留下痕迹,他人也能因此将你识别为活着的状态。不过,他人的话语也仅仅是一家之言,我们能感受自己空间的存在和震颤才更加的重要。顶着要活着的正流,尝试不要去「活」,说是一种矛盾,也同样是一种张力,而这种冲突和对立的力量,正是能够被自己察觉,从而从自身的角度感受到自己在活着,即活的涟漪能够被活自身所识别。于是,人就需要外物的确认,任何的扬尘和涟漪都只能存在于自我的空间中,只是知晓了这一点,那么沙尘和水雾也就成了冗余的事物。</p>
<p>当人意识到矛盾发生的时候,统一也随之来临。自我的内部包裹着自我的外部,然后再次循环起来。人变得整体起来,知道什么是活,也知道了什么是彻底的宁静,从而不再惧怕死亡,因为那是连续的,圆融的,安静且祥和的。</p>
Kaş - 初入卡什https://blog.kaiyikang.com/posts/2024/%E5%8D%A1%E4%BB%80_%E5%88%9D%E8%AF%86%E5%8D%A1%E4%BB%80/https://blog.kaiyikang.com/posts/2024/%E5%8D%A1%E4%BB%80_%E5%88%9D%E8%AF%86%E5%8D%A1%E4%BB%80/Wed, 04 Sep 2024 00:00:00 GMT<p>凌晨一点,我们趁夜色刚降临之际出发,为了追赶最早五点的航班。夜很静,以为最后一班的公交车空空如也,结果上车才发现乘客还真不少。看上去,大家都专注在自己世界中,车里比车外似乎还要安静。</p>
<p>到了换乘车站,站在站台等车,看到铁轨的另外一侧还有工人在做焊接工作,不禁想起德国到处长期停滞的工地,感概道,原来真的还有工人在工作啊。有人在创造秩序,自然也有人在破坏秩序。夜幕下,喝醉的人也不少。不远处就有个人,身着浅色的背带裤,提着瓶酒蹲在站台边上,等了许久才站起身来,摇摇晃晃背着向远处走去。对方的姿态从未变化过,只从屁股缝隙处多出了些棕色的脏污垢。不想去大胆猜测,但又不得不猜测。</p>
<p><img src="assets/2024/IMG_1434.JPG" alt="夜晚的站台" /></p>
<p>到了机场,人稀稀疏疏的,没有想象中的那么空旷和寂寥。登着check-in之前,我们找了一处椅子坐下。机场是全天候开放的,因此也吸引了不少流浪汉,为了防止躺在椅子上,就在座椅中间「特地」加入了栏杆。不锈钢质地的栏杆,防了流浪汉,也防了我们在机场休息的打算。半梦半醒,环顾四周,有人已经睡着,男士们则在看游戏视频,打发时间。</p>
<p>约莫四点,前台的人终于上班了。等待行李托运完,我们随着人流上楼,等待安检开门。左等右等,一个多小时都不见开门的踪影,这时前排的人逮着路过的工作人员问了问,这才知道,大部分人都排错了队伍。我们在D,实际要去B。D口因为没有早航班,没开门,B口早开了。于是,太阳还没亮,一大帮人火急火燎的又冲到B口。</p>
<p>半梦半醒的时候,人的注意力和逻辑是断片的。比如,过边检的时候,我把CH错认成了CN,还以为自己的护照也能走快捷通道,结果门口的阿姨立马把我拦住,叫我去远处的窗口。我这才意识到自己眼花,低下头快步朝着另外一个通道走去。</p>
<p>六点,我们上了飞机。因为订的廉价航空,所以即使要确定座位也要额外加钱。但这里有一个额外小贴士是,如果留意订票过程可以发现,完全可以省略订座的流程,在check-in的时候自动分配座位,这样就是完全免费的。实际上了飞机,后半部分几乎没有什么人,完全可以躺下来睡觉。由此可以看出,廉价航空引诱你花钱的策略还是非常多的。</p>
<p><img src="assets/2024/IMG_1449.JPG" alt="朝阳" /></p>
<hr />
<p>下了飞机,空气很热,但有些许微风吹过,因此并不闷。</p>
<p>沿着下飞机的通道走,右侧是中庭,透过玻璃,能看到楼下的迷你儿童乐园。乐园中的玩具泛着塑料的旧光,颜色虽然浓郁,但部分油漆已经脱落,颇有种国内三四线乃至乡镇大商场的乡土气息。</p>
<p>边检的通道一字排开,坐落在大厅中央,乘客自由地朝着边检口涌去。通常印象中的边检,都是庄严肃穆的警察局窗口,到了这里,却成了火车票卖票口,丝毫没有章法可言。我们特地打印出了签证,为的是防止太严格,不给过关,打印出来的纸递了出去,结果对方都没折开,在护照上盖了戳子,就直接给放行了。</p>
<p>等待行李的大厅也维持了同样格调。等行李期间,我想着去厕所一趟,短暂行个方便。厕所不出所料的很臭,原因自然很简单,每个包间内部都有个巨大的废纸篓,什么样的纸张都在里面堆积着。还记得之前在哪里看见过,说的是一些地方的基建落后,厕所下水管道比较窄,放厕所纸容易堵,因此不得不使用废纸篓。另外还有个发现,是在另外一个隔间有幸发现了蹲厕。这种在欧洲极为罕见,但在亚洲很常见的如厕方式,竟然存在于中亚的交汇之处。正想着要感叹之余,眼票见旁边有根水管子,却也不见得类似的垃圾桶,不由又一阵细思极恐。</p>
<p>拿到行李出了海关,转角是个换汇的地方,欧元对里拉,1比29。这个数看起来很大,熟不知到了市中心,汇率立刻成了1:36。我可算明白机场的心思了,明明可以用抢的,却还额外附加换汇服务,真是用心良苦。相比那些挣来的多余的钱,都用来修缮店面了,难怪硕大的机场,就属这个小窗口最精致现代。</p>
<p>完全站在外面,即使身处阴凉下,不出片刻背后就已经湿透。这让常年呆在寒冷德国的我不得不要额外适应一番,忍住燥热挑拨出的躁动,尝试冷静思考下一步的动作。根据网友信息,出门应该有个轻轨站,但我们当前站的位置,门前是大片的停车场,怎么也不像是有轻轨的地方。于是我们尝试顺着人流逐步摸索,旅行社的宣传亭子首先不需要碰的,再来出租车也是碰不得,毕竟土耳其的出租车在中英文世界似乎都得不到待见。新世界加上高温度的双重夹击下,我们的脑子缩成了单线程,最终采用了最原始朴素的方案,问路。</p>
<p>这一问,就问了一条街。因为很多当地人并不懂英语,相互理解起来非常吃力,所以相同内容,需要反复问不同的人。有门口的保安,铁柱子下休息的工人,还有年轻的小情侣,虽然人各有不同,但都笑憨憨的,也没有什么不耐烦的样子。再经过多同他人多次确认之后,我们得出了结论,只有先乘坐公交大巴或出租车,从2号航站楼乘车到1号航站楼,然后才能坐上大巴。大巴公交车属于市政交通,需要买全票上车,换句话说,机场本身不提供任何中间接驳服务。</p>
<p>在接受了这个事实后,立刻又要接受另外一个事实是,如此重要的车站站点,竟然就孤零地站在大门的一侧,没有提示牌,没有时刻表,仅有的也不过是陈破的广告。所谓大隐隐于市,却没想到以这种方式。看到已经有个小哥坐在了那里,我们才勉强确认这姑且是个在运营的车站。等待之余,我们和小哥闲聊,彼此互报家门,知道他来自俄罗斯,独自过来旅游。聊着聊着听他说,他在这里已经等了一个多小时,我们的心一下子就疲倦了起来。也许是上天喜欢戏耍旅人,我们嘴边的话还没升温,车缓缓的开过来了,刚铺盖卷儿的心,又随着车里空调忽然又重新蹦起来了。</p>
<p>拿信用卡刷了车票,等待片刻,车启动了。车一路跑,我一路盯着手机里的导航看,生怕上错了车,跑到了什么犄角旮旯里去。等屏幕中的小箭头与规划的路线重合了,我悬着的心终于才放下,安安心心朝着下个目的地前进。</p>
<p><img src="assets/2024/IMG_1459.JPG" alt="车站" /></p>
<hr />
<p>城轨车站不算难找,就在航站楼门口不远处。上了电梯,进站口延续了之前车站的乡土气,弱不禁风的闸机,还有孤零零的售票机器。</p>
<p>一位女士站在售票机前忙碌的操作,看到我们过来,仿佛等到了救兵,用着简单的英文向我们求助。这是典型的问道于盲,我们凑过去看屏幕,尝试切换成英文戳戳点点,但遗憾的是,即便每个词都认识,但真的不知道这个票应该如何购买。系统没有额外的提示信息,也没有具体的站名可供选择,只提到说什么5次或11次Usage之类的云云,全然的莫名其妙。</p>
<p>我们几个面面相觑,眼神也往返了几个回合,但买票的事情却一点进展都没有转机,直到一对情侣路人的出现。</p>
<p>这对路人看着年轻,似乎是本地人,它们看了看机器,却也太说不明白买票的操作逻辑,再加上语言沟通不畅,那个男的想不出更好的办法,干脆一不做二不休,拿着自己的交通卡给我们所有人买票,加上他共五个人。印象中,一张票20里拉,虽然总价不贵,但这股热忱却十分珍贵。我本想支付他现金,但现在钱包中一张里拉钞票都没有,见对方也没有想收的打算,只好作罢,嘴里连忙说着感谢。</p>
<p>沿着轨道走一小段,车就在前面等着我们。由于这里是始发站,且线路简单,所以也不必担心做错方向。车厢内人不多,空旷且安静,我们找了个座位坐下,空调开的很足,心和思维也稍微镇定了下来。</p>
<p><img src="assets/2024/IMG_1460.JPG" alt="很现代的轻轨车厢" /></p>
<hr />
<p>长途汽车站坐落在城郊偏西北的方向,周围的建筑稀疏,比不上城中的热闹。我们顺着指示牌走到门口,太阳依旧灼热。车站是个巨大凉亭样子,亭顶带着橙红色的锯齿状,稀疏的钢制框架布满了锈迹和污渍。或棕色或透明的玻璃围绕起来成了大厅,上面有很多胶粘和碎纸的痕迹,想必一些告示已经被反反复复贴了好几回。哪里漏水,哪里地板塌陷,就拿个红白相间的警戒线围住,做个松松垮垮的补丁。</p>
<p>过了如同充话费赠送的安检门,大厅里坐落着不用大巴车公司的柜台和办公室,似乎来的不是时候,里面一半都空无一人。我们要去 Kaş,于是在每个柜台的信息告示处浏览,没一会儿的功夫就找到了。</p>
<p>「Kaş?」,「Kaş!」这几乎就是交易完成所需的全部对话了。</p>
<p>15分钟后离开,前台的大爷和我们说。我们一听时间似乎有些紧张,毕竟身上携带了很多行李的同时,还要简单休整,所以也没货比三家,当即拍板决定了。一切准备妥当后,我们顺着大爷招呼的方向沿着背后的大门走去,那里还有另外的大爷在做接应。我同他确认了目的地后,把拖箱搬进内行李舱。</p>
<p>车厢内前排已经坐了人,大多是土耳其人,游客反倒不算多,我们顺着找了位置坐下。座椅是暗淡的红色,破旧的华丽感。</p>
<p><img src="assets/2024/IMG_1463.JPG" alt="大巴车内" /></p>
<p>嘴上说着15分钟离开,但等了20分钟也不见动静。我们坐在那里观察司机,发现了规律。网上都说土耳其的长途车不准时,也找不到固定的时刻表,原因全在于人。虽然他们说的是土耳其语,但我都可以脑补出来「里面有大座儿啊,去Kaş,还有十分钟出发了,过时不候咯!」。一切的准则都是以填满座位为第一优先,只有等待填得差不多了,才会出发。在我小的时候就已经领略过了类似的氛围,如今在他国的土地上再次经历,颇有一种考古的韵味,而且非常的「国际化」。</p>
<p>司机师傅,不出所料还是一位大爷,是长途大巴另外的一个鲜明标志。白头发,黑眉毛,方正的面孔,虽然因为年龄,脸上带着些皱纹,但眼眉中透露着精神气。他穿着白色的Polo衫,黑色西裤皮带和小皮鞋。褶皱却锃亮的衬衫用料并不高级,里面的白色跨栏背心全都能透出来。</p>
<p>这样的穿着和与作派,无疑都在诉说着这样的特征,老练与专业。像这样的人去开大巴车,速度多快你都不会慌张的。我都能想象到一连串的画面,司机大爷游走在崎岖的山间车道,开了十几年的路像是在后花园遛弯一样,如同打太极般游转着手下的大方向盘,路在颠簸,乘客在遥移,但大爷内心的沉着冷静却是永恒不变的真理。</p>
<p>对这样的角色,我从不会吝惜赞美之词。</p>
<p>大巴车终于开动了,但不能高兴得太早,因为这里要提及大巴车从不准时的第二个理由,是司机中途会拉人。大概开出不到几公里,能感受到车缓缓地停下,似乎交涉了一阵,上来了个人。他看到座位已经满了(对,此时已经满了),司机会从架子上面抽出来个垫子,放在驾驶座旁边,过道一头的位置,像极了被罚学生坐在黑板边上的 VIP 位。人做好,司机就位,车这才继续行驶。</p>
<p>像这样的临时停靠,印象中十个手指都数过来。因此在印象中,我们的旅程变得非常冗长与沉闷,如同车上的空调一般,靠近摸上去在出冷风,但其实一点冷气也感受不到。幸亏我们需要大量的睡眠,睡眼惺忪后,最终抵达了 Kaş。</p>
<hr />
<p>又经历了些反复询问和小的波折后,我们终于在傍晚抵达了在 Kaş 的住处。</p>
<p>虽然仅过了不到一天,从清晨的出发,到傍晚的到达,但却身体和精神却仿佛经历了许多天。新的环境与信息扑面而来,大脑与思维在不停的认识它们,并且解决突如其来的问题。真刺激,也真的足够让我们印象深刻。</p>
<p><img src="assets/2024/IMG_1485.JPG" alt="傍晚抵达卡什" /></p>
Kaş - 浮潜https://blog.kaiyikang.com/posts/2024/%E5%8D%A1%E4%BB%80_%E6%B5%AE%E6%BD%9C/https://blog.kaiyikang.com/posts/2024/%E5%8D%A1%E4%BB%80_%E6%B5%AE%E6%BD%9C/Sun, 08 Sep 2024 00:00:00 GMT<p><img src="assets/2024/R0000002.JPG" alt="石头滩" /></p>
<p>当脚踏着鹅卵石,朝着地势更低走过去,海水会逐渐的浸没足,小腿,膝盖,最后是大腿乃至全身。太阳能量很强烈,乃至皮肤都能感受到纤细的灼热,海水却因为吸收了能量,变得很温和,没有任何寒冷的刺激感。而被水浸过的皮肤,像是糊了一层盔甲,一扫炎热的灼烧感。</p>
<p>身体逐步适应了海水,进一步地,脸乃至整个头部都要扎进海水里。那些与水十分熟悉的人,会丝毫不犹豫地扎进水里,一个转身的功夫已经找不到人影了。与其相反的人,例如我,会将身体泡在水里后,面部朝下,对着海面试探,发呆。澄澈的海水已经足够让我见到水下的地面与岩石,但是否继续朝着它靠近,我不确定,大概是因为需要心理准备和一些胆量。</p>
<p><img src="assets/2024/R0000003.JPG" alt="飘进海里" /></p>
<p>轻微调整了一下游泳面罩,脸部慢慢浸入水中。开始是鼻子与嘴巴,再来是脸颊,然后是耳朵,最后是头。细微体验,会发现皮肤和五官的触感是联动的,仅仅是一小片肌肤进入水中,体验却像是所有的五官都完全进去了,但当然这仅仅是个错觉。不能贸然被这种错觉所吓倒,然后错误的马上把头抬上水面,而要继续向水下挺进,直到所有的五官全部被封闭。</p>
<p>水的温热并不会牵动五官,让我觉得过度慌张。但除了温度与触感的改变外,最大当属听觉上的变化。人类的吵闹声会瞬间被清空,一切变得安静的同时,水浪声会在耳畔徘徊,时不时还能听见微小的气泡声。如果此时戴了呼吸装置,例如浮潜的管子,或是水肺的呼吸管,从身体内部传来的浓厚呼吸声,则是所有声音中最突出的那一轨道。呼吸,还是呼吸。呼吸被尘封在身体盒子中许久,这一刻被海水打开了,逃逸了出来。我的身体退居到了舞台后面,成为搬运气息的帮工,一切灯光都聚集在了呼吸上。</p>
<p>我仍然没有完全适应被重新计算过权重的身体,不清楚如何调整,只好大大地睁开眼睛,盯着水底的石头和沙土,任由呼吸声在四周飘摇。</p>
<p>腿小幅度向前咕涌,头深入水中,直至完全淹没。等到眼前的景色看的差不多了,就得一鼓作气,将双脚向后撤,借用海水的浮力把身体完全抬起。抬起后的身体并非笔直,而是有些摇摆,将四肢弯折呈现出游泳的姿势,开始准备遨游浅水下的世界。</p>
<p><img src="assets/2024/IMG_1931.JPG" alt="海下的石头和脚" /></p>
<p>随着地势和视野的关系,水下的风光十分飘忽,从不同的位置进入,看到的也大相径庭。</p>
<p>从海滩处浮潜,水草不多,多是沉底的沙粒和石子,随着离陆地越来越远,地面越来越深,陆续出现了巨大的礁石。肉眼可见的,光线逐渐被打散开,远处黑黑的,礁石也黑黑的,躲藏在深海的背后。我漂浮在海面,看着愈行愈远的水底产生了错觉,大地仍旧在延续,而我却飞升了起来。远方的巨大黑暗峡谷变大了,恐惧的心也升了起来。我很小,用了很多力量向前游动,肉眼看到的位移却小到可以忽略,能量都被黑暗的深海吞没了。于是我决定停止继续,转身回去爬山,爬回到高地,爬回到沙滩与岸边。</p>
<p><img src="assets/2024/IMG_1806.JPG" alt="海草" /></p>
<p>相比于海滩,从 Boat Trip 途径的海湾处开始浮潜,能看到的景象有些许不同。定是经过船长导游们精心挑选过的地方,海里的元素丰富许多,除了平整的沙地外,还有许多稀疏的水草。礁石的形状更加丰富,除了自然的,还有经过人工雕琢过的,那是些已经沉入水中的遗迹。有处遗迹围城了一个矩形,像是一座水下的泳池,不清楚它的用途,但表面已经崎岖不平。脚踩在上面需要注意,不小心就会被划伤。</p>
<p>也许是位置远离人烟的原因,海中的鱼类变多了。虽然不像在海洋馆看到的,或在宣传册中看到的鱼群那样丰富多彩,大概是滤镜和现实的差别,但有幸见到还是令人愉快的。窸窸窣窣的小鱼穿梭在岩石缝中,如果用手尝试去够,它们会立刻察觉,然后飞速逃开。鱼群是少见的,但我仍见到了一次。似乎是物理中的磁场或电场线。每一条鱼都十分纤细和小巧,身体会随着海洋漂浮并反射出光线。它们是粒子探针,聚集在一起,有节律的闪烁着光线,巧妙地刻画着海洋的流动轨迹。近看颇为精巧且壮观。</p>
<hr />
<p>从海中回到陆地,我立刻怀念起不久前的感觉。</p>
<p>那个世界很宁静,只有海浪和自己的呼吸声,仿佛和自己变得更亲近了,当然和自然也同样是。四肢和躯体可以肆意扭动,旋转,浮力战胜了重力。站在陆地上,身体虽然踏实了,但有些异样沉重的幻觉,像是灵魂的自由没了。耳畔除了海浪与风声,再来就是人类的喧闹声,这定不会让我感到愉快。</p>
<p>大陆不断在延伸,我却要背着它走去,重新过我的生活,骑着小摩托车,吃我的馕饼,我会想念它们的。</p>
<p><img src="assets/2024/R0000001.JPG" alt="海边" /></p>
Kaş - 陌生的交易https://blog.kaiyikang.com/posts/2024/%E5%8D%A1%E4%BB%80_%E9%99%8C%E7%94%9F%E7%9A%84%E4%BA%A4%E6%98%93/https://blog.kaiyikang.com/posts/2024/%E5%8D%A1%E4%BB%80_%E9%99%8C%E7%94%9F%E7%9A%84%E4%BA%A4%E6%98%93/Fri, 06 Sep 2024 00:00:00 GMT<p><img src="assets/2024/R0002207.JPG" alt="租车" /></p>
<p>卡什镇子不大,就有一条主街道,但汽车却不少,而街边剩下的位置,都被小摩托车占据了。在我心目中,小摩托车是个非常适宜的交通工具。它不大,可以穿梭在房子中间;它有动力,能应对上下的山势;它续航足,去偏僻的街区或海边很方便。在这个小镇常见摩托车的身影,我们不打算租汽车,但为了增加活动半径和灵活度,决定试试小摩托车。</p>
<p>在地图上,我们找到了第一家租车店。傍晚散步过去,只见得店门大开,门口稀稀疏疏停着摩托,张望了半天,却也找不到店主的身影。我们踏进门,尝试叫了几声,却也没有回应。因此,我们无功而返,计划着晚饭过后再回来看看。</p>
<p>晚饭过后,我们回来的时候,就见到了店主大爷带着孩子在旁边吃东西。他看到有客人上门,憨憨地跑到跟前向我们介绍。他英文很好,说车是 400 里拉一天,没有押金。如果有事故且是对方的责任,那么对方保险负责,如果是我们的责任,那我们该赔多少就多少。<em>听上去是相当详细的介绍,但爱人的提醒,即时拉住了我这匹即将脱缰的野马。</em></p>
<p>我听见没押金,感觉价格大致能接受,脸上浮现出了便宜到惊讶,想要快速成交的轻松神情,但爱人之前调查过,见网上说是有 150 一天,因此谨慎不少。统一口径后,我们回复大爷,说是再回去想想,如果决定了就第二天来租。</p>
<p>土耳其的交易,多看是不是本地人,看是不是说的本地话。是,则友情价,不是,那就可以靠着绝对的信息差坐地起价。因此作为外国游人,做任何交易的时候,还真应该留个心眼。我反思,像我这种喜形于色的,就是个反面典型,最容易在其中被宰的。</p>
<p>第二天,我们在地图附近标记了三四家,打算一家家挨着过去问。</p>
<p>第一家离港口最近,门前也最为吵闹。店主在忙着和另外的顾客谈事情,见我们过来,就招呼另外一个靠近门口的年轻小哥和我们聊。他不说话,沉默良久,从兜里掏出手机,在翻译软件中颇为熟练地输入土耳其语,翻译过来是你们想要租什么。我们用英文作答,说想要租小摩托车。他回复要只需要 150 里拉,很棒的价格,但条件是需要有经验。本着诚信原则,我们诚实地告诉他说我们没有经验,还能不能租。反反复复「对话」了几轮,结果都是不能。最终我们只好作罢,道谢过后前往下一家。走在途中,有个问题萦绕在我脑中,既然他能听懂英文,为什么还需用翻译软件呢?着实令人百思不得其解。</p>
<p>第二家在不远处,从远处就能看到梳着别致油头的年轻人坐在空调房里,一边闲聊一边玩手机。见我们过来,店主马上放下手机向我们打招呼。开了玻璃门,为了节约时间,我们确认了 150 里拉的价格,开门见山地问没有经验行不行,得到的回答和刚才一致。店主还特别嘱咐了一句,要有非常熟练的驾驶技巧才可以。随即转过话头,马上推荐让我门租车驾驶。<em>听到这句话,爱人露出了怀疑的表情。持有德国的驾照本来可以不论有经验与否在土耳其驾驶小摩托车,这会不会是商家的一种为了引导顾客去租利润更高的小汽车的一种营销手段??</em> 我们依旧无法,因此只好扫兴得离开。</p>
<p>烈日炎炎,我们被高温和连续的拒绝搞得晕头转向,遂决定,如果最后一家也不行,那就转回头问第一家,贵也就贵点儿吧。朝着山坡走不远,我们就来了最后的租车店。这家店虽然评分高,但实际只有几个人评论,我心里有些将信将疑。店面没有豪华的配置,应该说是对比之中最朴素的,昏暗的房间没有空调,破旧的木质桌子和椅子,墙上和玻璃上贴着陈旧的日历和海报,门口似乎也没有几辆像样的摩托车。</p>
<p>进了店,靠在桌子一侧的人和我们打了招呼。同样的话术,我们又强调了我们有允许开小摩托的德国驾照,只不过没有相关经验,并询问能不能教我们开小摩托车。听闻我们的询问,对方报价 400 里拉,也确认没经验没关系,愿意教我们。被两家店折腾之后,我们的心灵仿佛如沐清凉的空调,看到了自由奔驰的希望。简单合计,我们就这样决定了。因为现金不够,我们打算用欧元付,他说没问题,只不过现在手头没有里拉现金,打算之后还车了以后还多出的钱,写字条为证。</p>
<blockquote>
<p>经过市场调研以后,我们也摸清了市场行情,即租小摩托车必须要有当地认可的驾照,例如持有德国B驾照。如果是中国驾照,则需要翻译件或国际驾照。其次,价格分乘有经验和没经验两档,有丰富驾驶经验 150 里拉,没有驾驶经验 400 里拉。最后,虽然小摩托车没有押金,但是也没有任何保险。</p>
</blockquote>
<p>成交之后,他没着急出门带我们看车,而是用土耳其语打了一通电话。我们不解其意,他说等等,车马上就来。我们呆站在门口等了会儿,只见得一个骑着黑色小摩托车的大爷飞驰而来,停稳到店门口,下了车,伸手给我们指,示意这就是我们要的车了。</p>
<p><img src="assets/2024/IMG_1734.JPG" alt="就是这辆" /></p>
<p>车不大,非常破旧,但感觉饱经风霜,看着很皮实。我们交了钱,店主从店里拿出了自带复写纸的表格。他们两人用车椅子当桌子,对着我的驾照和证件一通记录,其间嘴里还说了很多听不懂的土耳其语,感觉,大爷咖位是比店主要大上不少,一通指指点点。等单子填完,他们就指导我开始如何点火,按油门,停车等等操作。可能是因为上两次碰壁的关系,我生怕他们会因为我技艺不佳,导致看了我尝试操作之后,收回租车,所以我用尽浑身解数尝试记忆,并尽可能在下一步的实验环节中复现刚才的操作。结果大概是差强人意的,起码没有在起步的时候就把车挂着碰着,试着在院子里转了个踉跄的小圈圈之后,我拍着摩托车的椅子说,没问题,很好骑!</p>
<p>试验结束,我心想也该结束,然后付钱了,但很可惜没有,店主说,我需要跟着他去去另外一个地方,交钱,然后我在独自把车开回来才行。我懵懂的满口答应,随即坐在了副驾驶。大爷一个箭步就上了主驾,指指我的头盔,示意我戴好,然后熟练地启动了摩托车。</p>
<p>路不平,一路是非常陡峭的上坡,我坐在副驾,身体不自主的向后倒。为了平稳,我自然要扶住大爷的肩膀,Polo 衫和白色跨栏背心,看起来是一套,碰上去的感觉就是另外一套了。我本来想要用双手轻轻扶肩膀,但因为天气炎热,他出了不少汗。为了不捂着大爷,也为了防止怪异的手感,我从一整只手掌,改成只用几个手指做支撑。虽然面对颠簸的路途不算是很牢固,但起码没有飞过去。在经过了十几个弯路过后,我们终于抵达了另外一家店。看到店牌子,我才知道这个大爷是另外一个修车的店主。</p>
<p>他完全不会英文,单纯用手势邀请我坐下。我面对陌生的环境,显得很乖巧,总之顺着对方的意思准没错。之后,我把欧元交给他。他用浏览器查了查当日汇率,并用计算器算了算 400 乘三天的总价,和我确认完数字后,找给了我余下的费用。具体过程的我忘记了,总之非常艰辛,除了最基础的软件翻译,我们用了几乎所有的能表达含义是的方式,嗯嗯啊啊,手势,数字等等,最后还是把这单小生意给敲定下来了。</p>
<p>等我骑车离开前,他还帮我选了顶头盔,是被埋在众多的巨大摩托车头盔之中,最小最秀气的那个。我不知怎么表达感谢,只好连连用大拇指的手势,表示对他的最真挚和热烈的感谢与赞赏。</p>
<p>同他告别后,我骑着小摩托,一边估摸着来时的方向,一边回忆街道标志的含义,开始时颤颤巍巍,等过了几个道口,深觉熟练不少,遂朝着原来的方向,一路自由得奔驰而去。</p>
Kaş - 骑行在半岛https://blog.kaiyikang.com/posts/2024/%E5%8D%A1%E4%BB%80_%E9%AA%91%E8%A1%8C%E5%9C%A8%E5%8D%8A%E5%B2%9B/https://blog.kaiyikang.com/posts/2024/%E5%8D%A1%E4%BB%80_%E9%AA%91%E8%A1%8C%E5%9C%A8%E5%8D%8A%E5%B2%9B/Sat, 07 Sep 2024 00:00:00 GMT<p><img src="assets/2024/R0002196.JPG" alt="" /></p>
<p>kaş 西侧有个半岛,形状犹如手挤出来面团剂子,头窄身宽。从繁华的城中心向北直行,拐过一个分叉口就是入口。一条巨大的环形路环绕着整座半岛,一路向前无需指引,车不多路况很好。</p>
<p>阳光明艳,即便是我戴上了墨镜,给山海蒙上了一层透黑的滤镜,一切事物依旧被照射着无所遁行。我骑着昨日租来的小摩托车,在路上驰骋。路旁时不时的会出现粉红的花,衬着灰黄的草木更显艳丽。左侧是山,临近山脚有酒店公寓。右侧是海,海边是熙熙攘攘的人与遮阳伞。前方的路很是俏皮,弯弯绕绕,时不时的被山所遮挡。</p>
<p><img src="assets/2024/R0002213.JPG" alt="" /></p>
<p>描绘景色不如看照片来得直观,但骑着小摩托在山海之间疾驰的感觉却是拍不出来的。</p>
<p>左手轻靠刹车,右手轻扭油门,眼睛时不时的跳动在前方道路和仪表盘之间,速度约莫在 30 到 40 之间,随着油门起起伏伏。引擎的声音轰鸣,开着开着也逐渐习惯了,直到停车休息时,所有一切都安静了下来,才乎的意识到方才的巨大轰鸣。风是另外一个指向器,风声鲁莽且尖锐的时候,暗示着速度正逐步加快。当风逐渐从皮肤上褪去,阳光射线重新溅射时,速度变得缓慢,乃至彻底静止。</p>
<p>每当我看到一处好风景,都会找个安全的位置,停车并熄灭引擎,刚才的暴躁一下变得乖巧,仔细听听,车尾的排气管还有轻微的震颤声音,不清楚是机械问题,还就是个单纯的物理规律。我面朝大海,拍了几张照,转过头去就能看到它孤零零地矗立在街边,左右既无车,也无人,仿佛身处荒郊野岭。一时间,我忽然感到有些轻微恐慌,担心车子坏掉,被抛弃在灼热的太阳之下,于是便生出些想法,想要变成公路侠客,带着全套的修理装备,掌握不系统却十分实用的修车知识。即便是身边的老伙计出现意外状况,无需他人相助,仅凭自己的本事就能叮咣五四地利索搞定问题。随着一次次地尝试打火,引擎重新轰鸣,旅程又能继续下去了。</p>
<p><img src="assets/2024/R0002142.JPG" alt="" /></p>
<p>还有种异样的感觉。我下半身稳坐在车座上,脚牢固地踩着车,头和上半身却感受着大风在急速的呼啸,暗示着自己在以超乎肉体的能力在向前奔跑。这确实很怪,而上下半身的速度体验不一致也同样很怪。有时,这样的怪异感在不断的刺激我的神经,每当手中的握把稍微懈怠的时候,它总会提醒我,为我纠偏。</p>
<p>即便海风笼罩,我仍然难以躲避太阳的灼烧。草木大多低矮,围绕着山脚,路旁虽然有零星的树,勉强高于人的身高,但它们的影子很难被说成是避难所。地大多是荒的,平的,想要站在上面俯瞰大海与岛屿,就不得不被天空笼罩,与阳光做正面的接触。拍了几处,我暂且还能忍受,但到后来,汗流不止,皮肤刺痛得仿佛在尖声抗议。因此我只好匆忙的结束环岛之旅,狼狈的跑到沙滩边,寻求海洋的慰藉。</p>
<p><img src="assets/2024/R0002237.JPG" alt="" /></p>
吃鸡肉https://blog.kaiyikang.com/posts/2024/%E5%90%83%E9%B8%A1%E8%82%89/https://blog.kaiyikang.com/posts/2024/%E5%90%83%E9%B8%A1%E8%82%89/Thu, 15 Aug 2024 00:00:00 GMT<p>家门口没走几步路就有一个广场。每逢周四就会有各式各样的摊贩,开着特别改装过的移动商店去贩卖新鲜的东西。最常见的是水果蔬菜鲜花,还是肉铺奶酪一类的。但要数最吸引我的,还得是买烤肠和鸡腿的摊位。他们每次出现的位置都大差不差,烤肠在中间,鸡腿则靠近商铺一侧。每次路过都会传来阵阵香气,味道连着鼻子,鼻子连着肚子,而肚子连着钱包。倘若一次性全都买下,一来吃不掉容易撑着,二来挑费太高,钱包会抱怨,但总要选一个,因此不得不在其中来回踱步。</p>
<p>这天我来的早,中午到了广场,只有鸡腿店开张了。车门掀起,鸡的各类部位架在上面被炙烤,散发阵阵肉香,场面颇为诱人。看没什么人,我提了半只鸡,欣喜地往家里走。</p>
<p>即使是半只鸡,配合一些其他的吃食,也足够让我能吃上两顿。要么说生活的重点在于不停地选择,刚刚逃过选香肠还是鸡肉的一劫,现在又不得不对眼前鸡的部位犯了难。鸡有半只,共含了鸡胸和鸡腿两块部位。这两个区域看似都有肉,实则却大相径庭,鸡腿肉带骨头,香而嫩,鸡胸肉虽然多,但又柴又干。这一顿我只能吃其中一个区域,要么香中午柴晚上,叫先甜后苦,要么柴中午香晚上,叫先苦后甜。</p>
<p>要是按照我原有的习惯,那必然是先苦后甜,但转念一想,无论如何调换顺序,似乎总会在香香柴柴之间来回打转。不过是因为有了先后时间顺序,导致感官有了错觉,仿佛它们是有所不同的,这才在隐隐之中让我做出选择犯了难。想到这点以后,面对香喷喷的鸡肉,我继续发出疑惑,该如何调节感官和心灵,并让品味发挥最大的效果。</p>
<p>后来我想清楚了:对于好吃的鸡腿肉,我要关闭外界任何噪音的影响,凝神静气,贪婪地同时,细细品味腿部的精华。色泽香味口感,所有的感官必须要全力以赴,不可以有丝毫的懈怠,但凡有哪个感官溜了号,那都是对最好吃部位的亵渎。而对于口味一般的鸡胸肉,我打开了电子榨菜,本着填饱肚子的打算,囫囵吞枣的将剩下的部位吃干抹净。由于注意力的游移,干柴那部分对感官而言也变得不再重要,这样我既饱餐了一顿,鸡胸肉也得以成功的完成了其使命。</p>
<p>最终,我完整了品味了这半只鸡,即享受到了精华,也体验了平凡。就综合的整体而言,我能够以实现最高意志的方式享受了它,在这一刻,我的灵魂得到了升华,同时鸡也得到了某种意义上的救赎。我们成功规避掉了时间和两难取舍对于人性的考验,并最终获得了究极的体验。</p>
圆筒与礼https://blog.kaiyikang.com/posts/2024/%E5%9C%86%E7%AD%92%E4%B8%8E%E7%A4%BC/https://blog.kaiyikang.com/posts/2024/%E5%9C%86%E7%AD%92%E4%B8%8E%E7%A4%BC/Wed, 27 Mar 2024 00:00:00 GMT<h2>圆筒</h2>
<p>去往站台的路上,我想到些关于人的比喻,似乎这比喻非常形象的描摹了人的形态。我对此窃喜,但因为没什么价值,也只好写出来聊以自慰。</p>
<p>人,不论是在肉体还是精神上,其实就是一个中空的筒子,大的类似中空的水泥柱子,小的类似厕纸用完的纸芯。其实但从肉体角度讲,这个比喻并不夸张,嘴巴是上面的开口,屁股是下面的开口。食物从上面进去,废物从下面出来,这不就是完美的筒状么?至于真实的人,不过是在这个基础上附加了很多精致的结构:脸,胳膊,腿等等。而正由于这些特殊的结构的存在,让原本的筒也有了非常多额外的性能。</p>
<p>如果将一块巧克力从厕纸芯的前端丢进去,你将会立刻看到它会从末端掉出来。巧克力没有变成营养被吸收,也没有变成带有异味的奇怪物质。同时,除了纸芯壁上会蹭一些巧克力的残渣以外,并没有太多显著的变化,没有变胖或是变瘦。这点正是厕纸芯和人的不同之处,前者缺乏了性能,无法吸收外界的物质并作用于自身,而这却是后者与生俱来的能力。</p>
<p>将这个概念推广开,其实人的精神也应该是一个具备吸收变化功能的圆筒。学校里学的,书本上读的知识,或是在参与社会获得的经验,这些都作为食物,被吸收到精神中,即使精神本身它并没有太多意识,就像人会品尝味道,但不会在意下肚以后的事情。这些知识或经历,过了一段时间成了精神的养料。那些因为吸收而不断长大的东西,我想可以称作是灵魂或是智慧。</p>
<p>饮食完毕,如果不运动不做功,营养化为了膘,对身体丝毫没有益处。吸收的知识也是同理,不诉说不写作不实践,堆积起来的污泥堵塞了精神的通道。也许人会因此变得偏执和忧郁,并持续自甘堕落下去,恶性的循环就此形成。</p>
<p>写作或是同他人聊天,没想到竟然也有活血化淤的功效。</p>
<h2>礼或仪式</h2>
<p>礼或仪式有种束缚感,最直观的感受来自穿上白衬衫和西裤的瞬间。白衬衫要平整,但上臂幅度较大就会有褶子,甚至需要 skirt slay 等工具的辅助。而西裤要足够贴合腰部,为了美观,有时甚至更紧。作为男性,我不清楚女性穿着裙子的感受,但相信既美观又舒适的裙子是存在的。不过这种穿着也要看场合,即使在身体物理方面没有束缚,不过穿着的场合和条件却有些许严苛。</p>
<p>从外星人的视角看来,仪式有些时候像是整个人类社会的 SM 活动。人们的身体被束缚,但精神却无比的愉悦。直率的轻松快乐不好么?我们需要为此再过度包装一层么?对此类问题我想到的合适比喻是在炎炎夏日,人们会在闭紧门窗的屋内开足空调冷气,同时还会在身上裹上厚厚的被子,为的了获得一丝温暖,以抵御人造的「严寒」。小的时候,我有类似的经历,享受是有,但更多的还是困惑与不解。</p>
<p>从自动化的角度讲,这又像是一种反馈调节机制,通过不断的折腾人们的身体和动作,从而达到内心的喜悦。其实人人都是系统控制的大师,不过其机制并不被人人所察觉。</p>
<p>通过外界的仪式,以达到获得改善内心的效果,从古至今似乎均是如此。不过礼也在逐渐变化,这种变化不是形式上的,而是从客观逐步向主观过度。换句话说,同一套仪式,也许上一辈的人会认可,但这一辈的人会排斥。它被打散,变得十分细碎,以至于无法成为统一的社会共识,只能被有能力辨别的人所接纳。因此,有些礼虽然你看着像礼,但其实质已经变成了戴着面具的舞台演出。它不是生活和人心本身,而是旧有通行货币的遗风,以一种陈列的方式被放置在博物馆中。</p>
<p>也许有不少人会因此幻想和迷恋,但我其实怀疑他们并不知道自己在迷恋什么。他们的感动,更多是来自于「我们一起经历了件大事」,而不是仪式本身的神圣性和特殊性。这种遮蔽会让仪式的存在更加脆弱,不再那么神圣化了,就像是可替换的非原厂配件,只要能运行起来就足够了。因此就我个人而言,不会对社会上的泛用仪式特别感兴趣,而更加短促和直接的获得其背后实质。这种实质可以很多样,可以是诚挚的话语,也可以是真情的流露。当然,我没有完全一棒子打死它的缘故,也是在于仪式可以像导管一般,能以快捷通道的方式,将人们最直面内心的感情给迅速抽出来,并呈现在所有人的面前。虽然过程复杂了些,但所幸结果是好的。</p>
<p>经此一役,人们或能更深刻的理解外界,或能更真诚的对待对方,应该没有什么比这更令人开心的结果了。</p>
我爱的是人,不是效率https://blog.kaiyikang.com/posts/2024/%E6%88%91%E7%88%B1%E7%9A%84%E6%98%AF%E4%BA%BA%E4%B8%8D%E6%98%AF%E6%95%88%E7%8E%87/https://blog.kaiyikang.com/posts/2024/%E6%88%91%E7%88%B1%E7%9A%84%E6%98%AF%E4%BA%BA%E4%B8%8D%E6%98%AF%E6%95%88%E7%8E%87/Thu, 14 Nov 2024 00:00:00 GMT<p>我熟悉以下类似的场景,人们进入虚拟会议,主持人浏览一圈与会人的头像,见都是德国同事,于是提议会议要不要切成德语。但同时,也有人注意到了我的头像,会特别向我征询意见问,是否要切换语言。而我的回答也几乎相同,请继续使用德语。</p>
<p>后来我反思,自己的回答和选择到底是出于什么目的或动机。其实把视角放宽,时间拉长,我也不总是机械的回复,让大家使用德语。一旦出现了重要的事情,或需要我深度参与或了解的任务,我就会主动请求将语言换成英文。对此,我认为了我选择的标准就是为了效率。如果一场会议,我几乎不需要参与,而主要参与人的母语都是德语,那我坚持德语一定就是为了让这场会议更高效,哪怕提升微乎其微。</p>
<p>我相信,工作场中的交谈都是以效率优先,毕竟人们的时间都有限。当双方同时都可以兼容英文时,那使用英文就是最好的方式。</p>
<p>于此相对,另外一个极端场景是喝酒。</p>
<p>我喝过酒,也见过会英文的同事喝酒。事实上,在酒过三巡之后,没人会在乎对方说的是人话还是鸟话,即使用肢体语言似乎也可以表述世界上的一切事情和感受。亲身体验,酒精会让我获得更多的勇气,尝试使用不擅长的德语交流。这样的交流笨拙且效率低下,但谁在乎呢,大家这会儿可都泡在酒精里面。</p>
<p>有种浅显的矛盾是,常在社会中混,看似没有个正形的人,德语交流往往非常好。非德裔的大爷大妈口音虽然含混且不太正宗,但总能和其他人聊得有来有回。你自然可以说他们英文可能不太好,但同时,我也不会觉得他们之间的闲聊就是为了追求效率。很多时候,他们的对话就像是喝完酒精后的发言,很多时候就是鬼扯,扯到天南地北毫无边界。而在工作场中的人,往往不会怎么不着边际的胡扯,虽然也有些午休时候闲聊,但更多还是在会议中讨论具体的业务和事情。</p>
<p>此前我还会尝试在工作中练习德语的机会,后来察觉到「德语-效率」之间的矛盾后,也就不再强求自己,转而去寻找其它更合适的途径。</p>
<p>看到街边的大爷大妈,我会觉得他们彼此是在和人聊天,或换种说法,在他们聊天中,「人」的浓度非常高。而反观会议中的人们,「效率」的浓度绝对是爆表的。因此在最初我所描述的情景中,我会坚持用德语,绝不是为了那些与会人,而就是单纯为了效率。</p>
<p>我爱的是效率,而不是那些人。如果就如此写下定论,我会很沮丧,所以我想要加一个定语,「只有在稍显冰冷的工作中」,这条定论才会成立。</p>
<p>脱下工作以后的外衣后,我更爱的是人,而不是效率。</p>
缩小比例尺https://blog.kaiyikang.com/posts/2024/%E6%89%A9%E5%A4%A7%E6%AF%94%E4%BE%8B%E5%B0%BA/https://blog.kaiyikang.com/posts/2024/%E6%89%A9%E5%A4%A7%E6%AF%94%E4%BE%8B%E5%B0%BA/Sun, 01 Dec 2024 00:00:00 GMT<p>不久之前,meine Liebe des Lebens 拜访了一户人家后,向我用惊奇的口吻,描述了这段颇为新鲜且魔幻的经历:</p>
<p>这户人家住山上,没有便利的公交,只能开车前往。门铃是个平板,可触摸的选项玲琅满目,菜单藏得很深。进入大门,建筑外面是一片巨型花园,一不小心就容易迷路。与其说建筑是房子,不如说是城堡,里面的设施应有尽有,车库,影院,健身房不一而足。管家有两人,其中一位还是医生。男主人从事法律事务,同时还经营着房地产等投资生意。女主人参与经营的同时,也还有自己的工作。他们两人还有一个专业的赛车车队,算是娱乐也算是投资。</p>
<p>据她描述,她所看到的一切,仿佛只在电影或是纪录片里出现过。我饶有兴致的听着这段短暂的旅程,虽然她只经过了一个下午的时光,但感觉却是到了异域的大草原。所到之处,所见之物,无不是些珍奇和新鲜的玩意儿。</p>
<p>尽管不太重要,但不可否认的是,我们与他人相处时,都会在无意识间和他人比较,或是出身背景,或是成绩,或是财富。但在我听她的讲述时,我忽然意识到,由于被比较的两者差距实在过于巨大,所以失去了任何比较的意义。霎时我产生了一种,见识到「不同物种」之间的错愕感。他们是少数生物,而我们像木头,尽管木头之间也不尽相同,但总归太普通,太常见。</p>
<p>这种比较不仅存在于他人与我们自身,还存在于我们见识到的每个人,换句话说,把每一个遇到的人放在统一的坐标系内。这户人家的特别之处在于,他们已经超越了认知坐标系,远远落在了坐标系之外。他们不仅刷新了系统的 scale 的同时,带来另一个影响是,原先散落在这个坐标系中的坐标全部被「集中」,或说「拍扁」了。当参考被大幅度更新后,原先与身边人的巨大差距,相较之下也变得微不足道了。先前也许我们会觉得,认识的人坐拥一两套房子,实现了财富自由,这已经代表了「富有」。但面对超乎想象的富有,先前的认知就完全不成立了。</p>
<p>关于财富,关于人与人之间的差距,并非是我在这里想聊的。我认为重要的,是他们为我们带来的不同视野和认知。</p>
<p>首先是教育教育方面。这户人家有孩子,而他们对孩子的教育关键词是「礼仪」、「语言」、「实践探索」和「专注力」:</p>
<ul>
<li>礼仪,我觉得不必赘述。我猜测,他们经常会出现在非常正式的场合,因此对应的礼仪是必不可少的。</li>
<li>语言,需要掌握多门,并且是母语的水准。在这里,我再延伸一下,不仅仅是语言本身,表达和社交能力也是必不可少的。</li>
<li>实践探索。孩子的教育并非全来自外部,而更多是源于自己的热爱的和探索,因此男女主人并没有把重心放在精英教育,或说「卷」上,而让孩子自由发挥。比如想尝试钢琴,就买一架钢琴;如果对赛车感兴趣,就直接让孩子学罢了。</li>
<li>专注力。我个人感觉这是现代社会最稀缺的资源,很佩服他们也把握住了这一点。他们让小孩练习绘画,通过长时间将注意力集中在创作事件,从而培养孩子的专注力。</li>
</ul>
<p>以上就是他们所认为重要的部分,随之而来的问题是,他们所关注的和我们有什么关系?虽然在前文中,我有些夸大了观奇的体验,但不能忽略的相似点是,我们本质上都是人类,而只要是人类都会和周围的环境产生互动,不同环境产生的互动大相径庭。于是我们有机会能观察到一个非常难得的现象,即在无限的物质和金钱存在的条件下,人们会想些什么,会做些什么。</p>
<p>首先时间会变得无比重要。对他们而言,单位时间内产生的收益极高,因此将更多的时间投入到自己的事业和生活中是最佳的选择,而不是被一些不重要的琐事纠缠。他们会相对不计成本的投入那些能够让他们不会操心的事情中,例如雇佣管家,或选择时间最近的头等舱等等。</p>
<p>由于物质上的试错成本极低,因此只要是想要的,就可以立刻投入精力和金钱去实践。如果能成,就是一桩美事,如果成不了,也没什么太多损失。相比之下,由于普通人都受到了有限的条件,试错成本非常高,在行动选择上就会更多参考多数人的路径。虽然这条路径没有什么创意,但好在四平八稳,不会出错。基于此,他们的想象力就比普通人更加宽阔,就比如在车队上做广告,也能够称为一种投资。</p>
<p>在听完了她所讲述的见闻后,我也感觉十分新奇。我想到更多的不是财富差距,而是另外一种别样的且可能的生活方式。这里说的「可能」,并不是说赚多少钱,而表示的是已经存在了的生活方式,就如同艺术家,旅行家,政治家等等。基于这样的角色,就会冒出对应方式的思维和看问题的角度,而别样的角度,对我来说才是最重要的,也是最有趣的。</p>
拯救厨子https://blog.kaiyikang.com/posts/2024/%E6%8B%AF%E6%95%91%E5%8E%A8%E5%AD%90/https://blog.kaiyikang.com/posts/2024/%E6%8B%AF%E6%95%91%E5%8E%A8%E5%AD%90/Sun, 08 Dec 2024 00:00:00 GMT<p>有时,做后端开发和维护也会遇到奇妙的事情。时间退回到几周前,当时有个任务是要修改测试环境中的数据库数据,我看是测试环境,并且就修改一个值,感觉并不麻烦,于是就接了下来。受限于业务的熟悉程度,还有访问测试工具的权限,我就叫上了另外一个组的同事 J 一起来处理这个需求。</p>
<p>因为我们想跳过复杂的行政流程,因此就采取了一个讨巧的方法去修改。虽然花了一些时间,解决步骤也比较讨巧,但从中我受益良多。等任务解决了,我就欣然的和对方汇报,说修改完成了。我想,也是因为回复的太过迅速,以至于给对方留下了一个「解决类似问题专业人」的印象,即便并非是我本人直接解决的。</p>
<p>时间继续推进,就在我快把这个事情忘的差不多了,类似的请求又发生了,而且是在最忙碌的时刻。通常,项目以两个星期为一个小周期,多个小周期凑成一个大周期。在大周期的末尾,所有的人都会集中起来,花上两三天的时间制定下个大周期的计划。此时是最忙的时候,因为要不停的开会,听其他项目的同时,制定自己项目的计划。</p>
<p>变更的需求发生在清晨。我刚坐到办公室,打开笔记本,就收到了消息。首先是需求方 K 的消息,再来是一个不太熟识的领导 P 远在印度向我打招呼,然后是组内安排计划的同事 B 和我私聊了这个事情,最后自然免不了上次帮忙的同事 J 的询问。</p>
<p>有这么着急么?我心里念叨着。为打消心里的困惑,还特地向同事 B 确认了一番,问是不是需要忽略近两天忙碌的会议,而去解决今早临时出现的问题。同事 B 和我说,她已经和另外组的同事 J 联系过了,毕竟他们组负责的内容,应由他们来处理。我说好,然后静静等待进展。</p>
<p>随着同事 J 不断的在和我联系,我大概确定了,这事情是非常重要的,看来要今天我是铁定跑不了了。于是我一边开着视频会议,一边打开了文档编辑器,准备数据库变更操作的 SQL 命令。这次的需求,看着描述类似,实则大相径庭:一来是涉及的车辆变得很多,二来是在实际的生产环境,而不是测试环境。前者,意味着我要准备一份长长的,包含许多车辆识别码的脚本,逻辑虽然不复杂,但架不住脚本太长,因此编辑时要格外留神。后者,实际的生产环境意味着任何的改动都需要提前报备,符合正确的流程,然后在选择正确的时间执行。一般来说,需要提前一两个星期的准备时间。(顺带一提,如果真的按照时间表操作,执行时间应该是在明年)我不担心技术层面的执行,但会很在意流程的合规,而这恰恰是程序员无法控制的。</p>
<p>倒是和我一起解决问题的同事 J 还挺淡定的,说让需求方去沟通吧,毕竟对方得给出合适的理由。我连声答应。</p>
<p>从上午到下午,我一边开着会议,一边准备脚本,一边盯着记录流程的网页,看看需求方是否有留言,状态是否更新了。过了一会儿,专门负责流程的组(另外一家公司)倒是不紧不慢的在里面留了言,询问是否有测试环境的变更记录,理由是否已经被描述,等等。</p>
<p>因为需求方也要和我们开会,因此回复也不算及时。又过了一阵子,同事 J 发消息提醒,说是流程已经被许可了,等下开完会,可以一起执行操作。我说好,同时瞟了一眼留言,只见里面赫然出现了一串红色加粗的大字。</p>
<blockquote>
<p><strong>这个变更需要被立刻执行,因为其中有一辆是苹果 CEO Tim Cook 的车!</strong></p>
</blockquote>
<p>太乐呵了,这是我第一反应。第二反应是有些紧张,毕竟问题涉及到的人身份比较特殊,这倒是也解释了他们会这么着急的缘故。</p>
<p>会议还在开,我的脑子就开始飘乎:「厨子(Cook)的车,他竟然会开这种车,竟然不是其它品牌的车?这是他的第几量车?是他的,还是他管家的?我这个操作看来是意义非凡呀。哈哈哈,厨子竟然也有今天,还需要远在大洋彼岸的我来帮忙。就让你感受到来自德国的神秘力量吧…」</p>
<p>一种奇怪的使命感在我心中升起,仿佛是在解决一件了不起的大事件。还没等会议完全结束,我就找了个由头提前离开,立刻和同事 J 取得了联系。简单商量了一下,因为我缺少权限,也没有额外的显示器,所以具体操作还得是由他来执行,我则负责检查和归档。实际操作分两步,第一步更改,第二部刷新。第一步进行得很顺利,但第二步出现了些问题,和我们的预期有些出入。</p>
<p>此时已经过了下班时间,我着急忙慌地给不同的同事留言,说需要额外的帮助。时间一分一秒过去,虽然仅仅过去了几分钟,但感觉却超乎想象的长。最后终于有一位资深的同事还在线,我立刻就将他拽入会议。经过一番理论讲解,以及执行过程中突入其来的小报错,波折后总算是正确的触发了刷新,变更请求也得到了正确的解决。</p>
<p>我将执行的状态和结果全部归档,并提醒请求方检查结果是否正确。我反复思索任务是否全部得到了妥善处理,等全部确认后,悬着的心才算落到地上。</p>
<p>结束以后,精神很疲倦,但脑中回忆起来,整段经历却散发着别样的有趣光彩。从中我也学到了不少,比方说技术与知识的可靠性,这是 junior 和 senior 之间的巨大鸿沟,同时也是当前很难依赖 AI 补足的。技术上,比如纯熟的 database 的应用,或是 psql 命令行的使用。知识上,有对业务系统的理解,也有对技术术语的理解。在紧急关头,不依赖搜索或 AI,而单纯使用一颗大脑去能去迅速定位并且解决问题,这是高手。</p>
<p>我希望成为可靠的人,这样就能「拯救更多的厨子」了。</p>
树与表达https://blog.kaiyikang.com/posts/2024/%E6%A0%91%E4%B8%8E%E8%A1%A8%E8%BE%BE/https://blog.kaiyikang.com/posts/2024/%E6%A0%91%E4%B8%8E%E8%A1%A8%E8%BE%BE/Mon, 01 Jul 2024 00:00:00 GMT<p>最近我和同事一直在聊数据结构,其中有种结构叫树(Tree)。就如同我们平日里能见到的树木一样,它由两个要素组成:枝(Branch)和节点(Node)。</p>
<p>根据不同的位置和关系,根有不同的名字。树木的根就叫做根(Root),由此伸展出的可以加父节点(ParentNode),以及它的下属节点,子节点(ChildNode)。</p>
<p>技术内容的铺垫到此结束,现在我想提及与之相关的糗事。</p>
<p>在和同事聊起时,我虽然知道树结构和节点的概念,但却仅限于中文的语境。在用英文沟通,且无法及时查阅英文文档的状况下,我错误的将中文的「父节点」直译成了英文的「FatherNode」,而不是「父母节点」(ParentNode)。直到会议结束,我浏览文档时,才忽然察觉出之前的错误。</p>
<p>无意中,我感到有些冒犯,说出这些仿佛我是一个极端以男性为主导的人,但事实并非如此,因为这单纯只是翻译上的或是文化上不匹配的错误,而非本人的想法。</p>
<p>这个事情虽然不大,但我触动很深。文化或语言已经以先入为主的态度贯穿了我们的精神,同时我们还无法选择,只得根据环境去习得并适应。直到我们长大了,接触了更多的人和事情,习得了全新的表达方式后,先入为主才会被揭露出来,被我们所察觉。</p>
<p>这让我想到了在社交媒体上看到的例子,大意是说两人同时开始学习语言,一个日语,一个法语。前几个单元,学日语接触到的单词是加班、通勤和上司,而法语则是旅游、度假和约会。当法语已经学到调情上床了,日语的词汇表才刚到置办房地产。</p>
<p>上述例子也同样一种显露。我觉得大部分人,包括我,都仍对此没有太多察觉,而只有当真正投身于其中,甚至是露出洋相后,才甚至其背后巨大的差异和对我们的影响。</p>
浅水消息https://blog.kaiyikang.com/posts/2024/%E6%B5%85%E6%B0%B4%E6%B6%88%E6%81%AF/https://blog.kaiyikang.com/posts/2024/%E6%B5%85%E6%B0%B4%E6%B6%88%E6%81%AF/Sun, 07 Apr 2024 00:00:00 GMT<p>对话有诸多形式,或面对面,或在聊天软件上。后者占据着大部分我们的日常生活,凭我个人的观察,通过软件聊天的时候,我们的说话方式会发生变化,会变得更直接和简练。比方说「我对你描述的故事感觉十分有趣」直接会简化成「哈哈哈」。</p>
<p>内容上的简化,随之而来的是单条消息的信息密度并不太高。一旦想表达的内容变多,势必会被分割成数十个简单的对话气泡,手机的提示音也会弹个不停。以前我一直比较遵循类似的模式,认为信息简洁明了是种环保的美德,久而久之,我便发现单条发送的方式,也影响到了我的思维方式,常常只有发送了第一条消息后,脑子里才会想到,还需发送第二条消息作为补充。下句本是上句内容的补充,它们本可以被囊括在同一个对话泡泡中,但随着割裂的发生,下句无法自然而然的在脑中浮现,而是需要上句被「叮」地发送了,才会想到,原来自己还想再补充一句。</p>
<p>由于信息发送变得廉价了,而沟通的载体是信息,因此沟通本身也就变得廉价起来。</p>
<p>我想到在初中校外补课的经历,那时我使用的还是个诺基亚手机,虽然已经有流量的概念,但通讯软件远不如现在的发达,发送任何消息都需要通过一条条短信,而每条短信,其实都需要一定费用。还记得是个北京夏天的午后,气温很高,脑子昏昏沉沉,教室的墙壁斑驳,课桌和黑板十分老旧,头顶的电扇随着惯性旋转,假装自己还能吹出些风来。我觉得乏味,想要和同伴寒暄,于是偷偷把手机放在椅子上,夹在两腿之间,手指戳在迷你的九宫格键盘上,打出了些话,向大家问好。据事后收到消息的同学们反馈,消息虽然发送成功,可是字却打错了,错成了一个非常生僻的字,因此大家也面面相觑,不知道作何回复。</p>
<p>传递消息像是递水,如果每次传输都需付费,那我自然是希望能够多蓄些水,然后一次性传递得多些。如果传输的费用可以忽略不计,那么蓄水就变得没那么吸引力。一旦脑子里多出些东西,马上就能发送出去,这样的时效性确实是种优点,但并不能说,蓄水变得没有意义。人的大脑虽然会像泉眼一般,不断的流出思维和感受,但只要冒出一点就送出去,冒一点就送出去,积蓄便是无从发生的。而量变会产生质变,浅显的思维经过积蓄和沉淀,势必会孕育出新的思维与灵感,人们还会从中发现各种各样的属于自己的模式与路径,这些也同样是弥足珍贵的东西。</p>
<p>当然,我写下这些,并非也意味着我赞成只要发送消息,就要发送一大坨的内容。对那些本就没有什么逻辑或情感连续性的内容,逐条简单的发送其实是非常有效的,也能节约彼此大量的时间,而至于那些连贯的内容,我倒是更愿意积累在一起阅读,或更愿意在有条件的时候面对面交流。</p>
<p>不要被技术的爆炸所迷惑,我们必须要认识到,信息的传递在自然界本就不是一件容易的事情。同时,更要警惕,不能让消息传递的廉价化,误导我们觉得沟通消息本身就是廉价的。我们大脑和内心是一切事情发生的起点,这则是最为无价的。</p>
湖边与状态转移https://blog.kaiyikang.com/posts/2024/%E6%B9%96%E8%BE%B9%E4%B8%8E%E7%8A%B6%E6%80%81%E8%BD%AC%E7%A7%BB/https://blog.kaiyikang.com/posts/2024/%E6%B9%96%E8%BE%B9%E4%B8%8E%E7%8A%B6%E6%80%81%E8%BD%AC%E7%A7%BB/Sun, 19 May 2024 00:00:00 GMT<p>水面在风的吹拂下抖动,反射着银光,刺动着双眼。能见度很好,山峦带着积雪,在湖遥远的另一侧,徐徐展开。湖水冰冷,尖锐的刺激着皮肤。水中的沙石随机排布,借着重力的影响,对着足底猛烈进攻。我赤着脚尝试踏进湖水,结果痛的滋哇乱叫,仿佛健康出了故障。</p>
<p>爱人则灵活多了,等着阳光将湖水烘热,不紧不慢地在湖岸边游泳,钻入码头下面,又从中穿出,好不自在。</p>
<p>我坐在岸边,身体一半在阳光下,一半在阴凉里,透露着一丝犹豫。爱人问我,是不是不喜欢阳光。我无法笃定地回答是或不是。我不抗拒太阳与它散发的光芒,但因为我对它太生疏,也不了解,缺乏了打交道的常识(比如涂抹防晒),所以无法得出一个论断。</p>
<p>太阳如此强烈,占据着所有曝露着的地方。在这个空间中呆上许久,皮肤就像被带细刺的毛刷拂过,从温暖变得刺囊,忍不住地要挪动为止。而钻入阴影中,又很快会被寒冷的湖水侵蚀,于是不得不再次回到阳光之中。阳光,湖水,阴凉,它们好像占山的大王,各自守护着自己的领地,不断驱赶着无法适应环境的人。</p>
<p>我躺在树荫下,腿脚晒着太阳。树叶的影子在头顶窸窸窣窣的。树叶随着树枝,由里到外生长,根部密集,外侧稀疏,从远处看,就是一团绿丛丛。阳光一照,显出了原形,外面的较薄,闪耀着嫩绿色,内部的较厚,呈现出难以辨析的暗绿色。树叶的形状是锯齿的,从光线到阴影的过度十分锐利且不自然,但因为每个叶丛都遵循着类似的长势,所以整体的光线异常和谐。</p>
<p>透过树叶,云在背后闪躲。爱人喜欢看云,也能辨云,甚至能说得上不同云形状的拉丁文。她同我说了许多术语,冷凝,结晶,温度层等等。我看着云在天空中飘动,实在难以和精确的学术术语结合起来,于是也将这些词打包送上的空中,成为云朵,随着风逐渐散开。</p>
<p>她说的大多数虽然已经忘记,但仍对「状态」一词印象颇深。如果我没理解错误,云是状态集合的体现,这个状态集合包含温度,湿度,气压等等。当这些指标达到了一定的标准,它们就以肉眼可见的形式呈现了出来。我想象,天空是一片巨大的空间,每个位置都十分独特,且被许许多多的状态所统治,当状态合适时,就显露出白色,当状态不合适时,则又变成了透明。坐标点像是个传送轴,定点滚动,而云则是包裹,不断地被传送到远方。</p>
<p>从天空出发,拓展到地面,乃至穹宇。空间中的每个点都有着基本的状态,当它们都具备了,那么在同一个位置,也许显现出的是个人,也许是一只猫,而因为人想回家,猫想觅食,于是这个坐标又变得空无一物起来。</p>
<p>一切的一切都是静止的,但也同样都是在运动的。</p>
社会内外https://blog.kaiyikang.com/posts/2024/%E7%A4%BE%E4%BC%9A%E5%86%85%E5%A4%96/https://blog.kaiyikang.com/posts/2024/%E7%A4%BE%E4%BC%9A%E5%86%85%E5%A4%96/Sun, 11 Aug 2024 00:00:00 GMT<h2>内外</h2>
<p>我发现,当我们讨论 MBTI 的时候,代表内向和外向(Introversion and extroversion)的第一个属性常常背离一贯对他人的了解和认识,经常以惊叹的形式呈现出来,「不可能,你怎么可能是个 I 人呢?」。出现了一次是意外,但次次出现大概就能感觉到背后似乎出现了理解上的偏差。</p>
<p>我在自己是 I 和 E 的判断上出现过分歧,几年前写过篇黑历史,还看似以颇为严谨的态度剖析自己为什么是 E 人而不是 I 人,现在看来也太过滑稽。当然,现在看过去显得幼稚和滑稽并不是坏事,这表示人在进步,而不是逐渐再萎缩。</p>
<p>说回到内外。前几日,我和他人在闲聊,主题各种各样,有工作也有生活,但随着每个人都在对同一主题发表意见和想法的时候,我忽然察觉到了内外泾渭分明的分界线。依照同一个主题,有些人会不由自主地从社会历史或阶级等角度出发,而另外一些人会从自己内心的感受出发,虽然视角会来回穿插,但通过对话能非常明显的判断出这个人内外视角所占的面积有多大,遂能显露出其背后的偏好。</p>
<p>内或外,也可以说是主观和客观。仅仅从日常聊天来判断人的归属是非常武断的,譬如我会在亲人或爱人面前展露内心的感受,但也会客观的与同事交流对项目和产业的看法。从前者看我是个 I 人,后者看我就是个 E 人,虽然人人都是两者兼有的,但如果都这么混沌的去判断的话,似乎也就没有判断的必要了。</p>
<p>对外的性格犹如面具,大抵是可以通过训练等方式人工塑造出来的,但从本心出发的偏好和无意识间占据思维的比重可就没办法被塑造出来了,毕竟没必要自己骗自己玩儿。我斗胆猜测,面对同样的外部世界,人们也都说着同样的话,「面对这种情况,我需要…」。E 人的重音落在前脚的「这种情况」,I 人则落在「我」上,根据不同的重点,思维和感受也由此发生了偏离,并走上了不同的方向。</p>
<p>综上,性格的内向和外向并不是一个很好的判断依据,外向的人可以沉默装高手,内向的人也能大胆开麦当社牛,能够影响的因素太多也太复杂。但面对自己真实的思维和感受却无法装出来,也不需要去装,所以算是一个较为明晰的判断标准吧。</p>
<h2>社会是这样的</h2>
<p>德国汽车行业不景气是人尽皆知的事实,如果没有亲身参与进去,那无非就是从报道的文字中获知了一个不太相关的信息,但作为边缘的相关者,那么它就会影响着我每天的三分之一时间。</p>
<p>我工作不久,但坦诚的讲,虽然常听人说社会无情险恶,但也从未经历过太大的震动。不过最近的公司裁员的确是对我有所触动。</p>
<p>一开始我是那种,即使相处了几个月的同事因为自己状况变了而离开公司,也会忍不住感慨一番的人,直到到后来人员变动太大,来来走走太过频繁,因此纤细敏感的突触也逐渐钝化。可能是社会这个大哥觉得我还是太嫩,想要再拿我开涮,于是降下来裁员这么一遭事情,也许是为了让我有所警惕并恢复危机意识,我在此先谢谢大哥了。上周开大会,老板宣布行业不景气要大家做好准备,这周就为了节省成本开始大刀阔斧的裁员。这一刀不要紧,也砍到了我们刚组建的队伍上。组里负责项目沟通的人不幸落难,遗憾与我们道别,然后就火速下线。</p>
<p>虽然德国是永久工作合同,但特殊情况下为了保这艘大船,该裁还是得裁。于是我便在一星期内经历了极速的库伯勒·罗丝过式山车:「不是吧?不应该呀!能救么?救不了啊…那行吧」。做完了过山车之后,我还和剩下的同事总结了一下自己幸存的缘由,大概是因为太便宜且姑且算个劳动力吧。</p>
<p>闲聊之余仔细去想,人的感情和社会的运行方式很多时候都是冲突的。一方面我们唾弃公司不能如此对人无情,另一方面也不得不承认这是种算是普遍的解决问题的方案,事实就被如此塑造了出来。我不知道对其他人的想法,但就个人而言,尽人事听天命,努力学习提升自己,并以最大的友善祝福他人,也许就是最陈词滥调,但也最有效的应对方式了。</p>
里半生https://blog.kaiyikang.com/posts/2024/%E9%87%8C%E5%8D%8A%E7%94%9F/https://blog.kaiyikang.com/posts/2024/%E9%87%8C%E5%8D%8A%E7%94%9F/Thu, 13 Jun 2024 00:00:00 GMT<p>我有听过「上半生」,「下半生」,「前半生」或「后半生」等之类的说法。</p>
<p>它们的共性在于,前半部分是个描述方位空间的形容词,后者则充满了时间意味。用空间描述时间?仿佛时间是个凝固的对象,通过上下前后去描述本应流动的事物。充满活动色彩的时间,被空间的形容词感染,从而变迟钝了。</p>
<p>以传染性来说,描述时间的传染性似乎强于描述空间的。例如「短暂的空间」和「狭窄的时间」。虽然前者被描述的对象是空间,但我仍旧会联想到时间的角度上。这里,我感觉有抽象与具象是造成传染性不同的原因之一。相比抽象,人天生对具象敏感一些,因此,在面对能被「瞬间」可视化的空间来说,对时间的感知,更像是后知后觉的结果。</p>
<p>同时,上下或前后,似乎也潜藏着一些不平衡的色彩,前者如朝阳,后者则垂暮。至于说我为什么这样感觉,是因为均衡的形容词很难被用在「生」上,例如你从未听过「左半生」或「里半生」等说法。从这个角度看,左右或里外,则要更平衡一些。</p>
<p>当然,以上的论述并不包含任何严肃的语义学讨论,只是单纯和粗浅的感知,属于一耳朵的声音和拍脑门儿的结论。</p>
<p>尽管如此,「我的内半生」或是「我的外半生」等的说法倒是蛮另类的,我喜欢这个表达,听上去就像是那种对人生个性游刃有余的那种人,不把自己交付给他人(例如,「我的下半生就和你一起度过了」等说辞)。一会儿可以当个外生人,一会儿又能钻进内心做个内省者。时间对所有人都是平等的,不与其较劲,而是把自己的这副身子骨和精神气给管理妥帖与明白,想必也是一种不小的成就。</p>