charlemaznable / blog Goto Github PK
View Code? Open in Web Editor NEWWhen I was young, I used to think that money was the most important thing in life, now that I am old, I know it is.
License: MIT License
When I was young, I used to think that money was the most important thing in life, now that I am old, I know it is.
License: MIT License
从Java转向Kotlin, 就像从Objective-C转向Swift一样.
在go中连接Oracle, 需要使用OracleClient提供的ODPI: Oracle Database Programming Interface
库.
在macOS中安装ODPI:
参考文档: https://oracle.github.io/odpi/doc/installation.html#macos
下载压缩包并解压, 下载地址:
https://www.oracle.com/technetwork/topics/intel-macsoft-096467.html
向$HOME/lib
或/usr/local/lib
添加link:
$ ln -s unzipped/path/to/libclntsh.dylib ~/lib/
$ cp unzipped/path/to/{libclntsh.dylib.11.1,libnnz11.dylib,libociei.dylib} ~/lib/
127.0.0.1 XXX.local
其中XXX.local
为/系统偏好设置/共享
中的本机访问地址, 例如
$ go get gopkg.in/goracle.v2
oracle://username:password@[//]host[:port][/service_name][:server][/instance_name]
缓存不是多多益善,它属于有利有弊,是真正到必须使用时才考虑的解决方案。
如果查询的数据在数据源中根本不存在的话,缓存里自然也不会有,这类请求的流量每次都不会命中,每次都会触及到末端的数据源,缓存就起不到缓解压力的作用了,这种查询不存在数据的现象被称为缓存穿透。
为了解决缓存穿透,通常会采取下面两种办法:
如果缓存中某些热点数据忽然因某种原因失效了,譬如典型地由于超期而失效,此时又有多个针对该数据的请求同时发送过来,这些请求将全部未能命中缓存,都到达真实数据源中去,导致其压力剧增,这种现象被称为缓存击穿。
要避免缓存击穿问题,通常会采取下面的两种办法:
不是针对单个热点数据的大量请求,而是由于大批不同的数据在短时间内一起失效,导致了这些数据的请求都击穿了缓存到达数据源,同样令数据源在短时间内压力剧增。
要避免缓存雪崩问题,通常会采取下面的三种办法:
缓存污染是指缓存中的数据与真实数据源中的数据不一致的现象,即缓存和数据源间无法保证最终的一致性。
为了尽可能的提高使用缓存时的一致性,已经总结不少更新缓存可以遵循设计模式,譬如 Cache Aside、Read/Write Through、Write Behind Caching 等。其中最简单、成本最低的 Cache Aside 模式是指:
其中在写入缓存时,有必要专门强调两点:
Cache Aside 模式也会出现缓存中回填的内容与数据源的实际数据不一致的情况。但这种情况的概率是很低的,所以其仍然是以低成本更新缓存,并且获得相对可靠结果的解决方案。
最近在写一个小玩意, 需要一个FiFo的队列, 其大小是一定的, 往队列中插值时, 如果队列已经填满了, 则移出队列的第一个元素: 即所谓的有限大小的队列.
maven依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
注意事项:
maven依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
注意事项:
这是一个有趣的事情: Java的注释会影响代码的编译
这是一种特殊的情况:
编译器会解析Unicode字符,可能导致代码会在编译时报错
public class CompileError {
public static void main(String[] args) {
// \u000d 运行编译报错
}
}
CompileError.java:3: error: not a statement
// \u000d 运行编译报错
^
CompileError.java:3: error: ';' expected
// \u000d 运行编译报错
^
2 errors
Error: Could not find or load main class CompileError
Caused by: java.lang.ClassNotFoundException: CompileError
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = YourConfiguration.class)
@WebAppConfiguration // 标注为WebApp测试
@TestInstance(Lifecycle.PER_CLASS) // 为在@BeforeAll方法中使用自动注入的Bean, 方法需为非static, 测试类需添加此注解
public class YourTest {
// ...
}
private static MockMvc mockMvc;
@BeforeAll
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(yourController) // 可添加多个Controller
.addMappedInterceptors(new String[]{"/**"}, yourInterceptor)
.addFilters(yourFilter).build();
}
@Test
public void testSample() {
val response = mockMvc.perform(MockMvcRequestBuilders.get("/your-req-path")
.param("key", "value"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn().getResponse();
// some assertions
}
老大 @bingoohuang又提了个问题:
玩转JVM从实际需求出发
有两个版本的class,相同包名,相同类名(exactly full qualified long name)
暂且称之为v1和v2两个版本,怎么让他们在一个jvm实例**存,使得方法1调用v1,方法2调用v2
@zhangcunxin提出:
只要classloader不一样就可以让同名类共存
动手实现, 在这里分享一下答案.
今天写测试用例的时候, 忽然产生了一个疑问: Java里的注解到底是什么?
首先, 注解都是继承自java.lang.annotation.Annotation
接口的@interface
, 那么它是一个类吗? 或者是一个接口?
注解看起来更 像 一个接口, 因为可以定义一个类去implements
一个注解, 实现注解定义的方法和一个特殊的方法:
Class<? extends Annotation> annotationType();
而extends
一个注解, 编译则会报错.
那么, 注解是一个 继承了Annotation接口的接口 吗?
好像又不太对, 因为不能定义一个注解继承其他的接口了, 编译器是如何限制这样一个特殊的接口只能被单继承的呢?
或者换一个问题: 能不能不使用@interface
关键字, 仅使用常规的定义语法, 来定义一个注解呢?
把这个问题记在这里, 念念不忘, 必有回响.
golang 13 changed the order of the test initializer
添加代码
var _ = func() bool {
testing.Init()
return true
}()
或直接在flag.Parse()
前添加
testing.Init()
1948年,博士毕业后就在贝尔实验室里研究通讯技术的电子工程师克劳德 • 香农(Claude Shannon, 1916-2001)在《贝尔系统技术杂志》(Bell System Technology Journal)上分两期发表了他一生中也许是最有名的一篇论文:《通讯的数学理论》(A mathematical theory of communications,1948),引入了一条全新的思路,震撼了整个科学技术界,开启了现代信息论研究的先河。在这一伟大的贡献中,他引进的“信息熵”之一般概念举足轻重:它在数学上量化了通讯过程中“信息漏失”的统计本质,具有划时代的意义。
香农最初并没有借用“熵”这个词汇来表达他关于信息传输中的“不确定性”的度量化。他甚至都不太知晓他所考虑的量与古典热力学熵之间的类似性。他想把它称为“information(信息)”,但又认为这个名词太过大众化,已被普通老百姓的日常话语用滥了。他又考虑过就用单词“uncertainty(不确定性)”,但它却更像抽象名词,缺乏量化的余地,确实难于定夺。终于有一天,他遇见了天才的数学家冯 • 诺依曼(John von Neumann, 1903-1957)。真是找对了人!冯·诺依曼马上告诉他:
就叫它熵吧,这有两个好理由。
一是你的不确定性函数已在统计物理中用到过,在那里它就叫熵。
第二个理由更重要:没人真正理解熵为何物,这就让你在任何时候都可能进能退,立于不败之地。
作者:返朴
链接:https://www.zhihu.com/question/22178202/answer/667876061
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
泛型使用extends
和super
的规则:
PECS: producer-extends, consumer-super
-- 《Effective Java》
Source Code Security Audit (源代码安全审计)
官网地址: http://cobra.feei.cn
由于开发人员的技术水平和安全意识各不相同,导致可能开发出一些存在安全漏洞的代码。攻击者可以通过渗透测试来找到这些漏洞,从而导致应用被攻击、服务器被入侵、数据被下载、业务受到影响等等问题。“源代码安全审计”是指通过审计发现源代码中的安全隐患和漏洞,而Cobra可将这个流程自动化。
对于一些特征较为明显的可以使用正则规则来直接进行匹配出,比如硬编码密码、错误的配置等。对于OWASP Top 10的漏洞,Cobra通过预先梳理能造成危害的函数,并定位代码中所有出现该危害函数的地方,继而基于Lex(Lexical Analyzer Generator, 词法分析生成器)和Yacc(Yet Another Compiler-Compiler, 编译器代码生成器)将对应源代码解析为AST(Abstract Syntax Tree, 抽象语法树),分析危害函数的入参是否可控来判断是否存在漏洞(目前仅接入了PHP-AST,其它语言AST接入中)。
疑行无名,疑事无功。
且夫有高人之行者,固见非於世;
有独知之虑者,必见敖於民。
愚者闇於成事,
知者见於未萌。
民不可与虑始而可与乐成。
论至德者不和於俗,
成大功者不谋於众。
是以圣人苟可以彊国,不法其故;
苟可以利民,不循其礼。
——《史记·商君列传》
决定了解一下Hazelcast
, 是因为Vert.x
.
受到Stay Hungry, Stay Foolish
的毒害, 没事总想找点新玩意, 本来想学习一下一直不敢直视的Spring Cloud
, 半路杀出个Vert.x
, 最后终于越走越远了.
以下是Vert.x中定义概念跟其它框架和语言定义概念的比较,同一行中的概念可被认为是相似的:
Vert.x | Akka | Spring | EJB | Node.js | Go |
---|---|---|---|---|---|
Standard Verticle | - | - | - | Reactor | - |
Worker Verticle | - | - | Stateless Session Bean | - | - |
Multiple ThreadedWorker Verticle | - | Bean(Singleton) | - | - | - |
Handler | Actor | - | - | - | - |
Coroutine | - | - | - | - | Goroutine |
Vert.x
最令人兴奋的特点之一, 就是它开箱即用的集群化与高可用能力, 通过可插拔的集群管理器实现集群, 其默认的集群管理器就是采用Hazelcast
.
于是我念了两句诗(并没有), 就拐个弯开始学习Hazelcast
了.
看到Hazelcast的分布式数据结构介绍中, 提到了Cardinality Estimator:实现了HyperLogLog算法的数据结构
, 于是顺便了解一下
什么是HyperLogLog.
HyperLogLog是针对大数据统计存储应用场景下的知名算法。
HyperLogLog是在大数据的情况下关于数据基数的空间复杂度优化实现。
一些只保存数据的类。
DTO类:Data Transfer Object,数据传输对象类,泛指用于展示层与服务层之间的数据传输对象。
VO类:VO有两种说法,一个是ViewObject,一个是ValueObject。
PO类:Persisent Object,持久对象。它们是由一组属性和属性的get和set方法组成。PO是在持久层所使用,用来封装原始数据。
BO类:Business Object,业务对象层,表示应用程序领域内“事物”的所有实体类。
DO类:Domain Object,领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
等等。
这些我们统称为领域模型中的实体类。
名为多继承, 其实是利用<scope>import</scope>
向目标项目(子项目)导入一批dependencyManagement
管理的可选依赖, 从而实现多继承的效果.
<!-- 省略不重要的内容 -->
<groupId>source.group</groupId>
<artifactId>source-artifact</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>utils.group</groupId>
<artifactId>utils-artifact</artifactId>
<version>0.2.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 省略不重要的内容 -->
<groupId>target.group</groupId>
<artifactId>target-artifact</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- 添加需要"继承"的依赖 -->
<!-- 可缺省依赖坐标的版本号 -->
<!-- 即: 使用源项目(父项目)中指定的依赖版本号 -->
<dependency>
<groupId>utils.group</groupId>
<artifactId>utils-artifact</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<!-- 添加依赖管理 -->
<!-- 注意: type为pom, scope为import -->
<dependencies>
<dependency>
<groupId>source.group</groupId>
<artifactId>source-artifact</artifactId>
<version>0.1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
keySet()
for (String key : map.keySet()) {
System.out.println(key + " : " + map.get(key));
}
entrySet()
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
keySet()
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
System.out.println(key + " : " + map.get(key));
}
entrySet()
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + " : " + entry.getValue());
}
分别对四种遍历方式进行10W次迭代,比较用时。
增强for循环,keySet迭代 -> 14 ms
增强for循环,entrySet迭代 -> 7 ms
迭代器,keySet迭代 -> 9 ms
迭代器,entrySet迭代 -> 3 ms
增强for循环,keySet迭代 -> 12 ms
增强for循环,entrySet迭代 -> 11 ms
迭代器,keySet迭代 -> 9 ms
迭代器,entrySet迭代 -> 7 ms
增强for循环,keySet迭代 -> 18 ms
增强for循环,entrySet迭代 -> 18 ms
迭代器,keySet迭代 -> 8 ms
迭代器,entrySet迭代 -> 4 ms
增强for循环,keySet迭代 -> 15 ms
增强for循环,entrySet迭代 -> 12 ms
迭代器,keySet迭代 -> 9 ms
迭代器,entrySet迭代 -> 5 ms
分别对四种遍历方式进行100W次迭代,比较用时。
增强for循环,keySet迭代 -> 32 ms
增强for循环,entrySet迭代 -> 35 ms
迭代器,keySet迭代 -> 33 ms
迭代器,entrySet迭代 -> 18 ms
增强for循环,keySet迭代 -> 32 ms
增强for循环,entrySet迭代 -> 23 ms
迭代器,keySet迭代 -> 24 ms
迭代器,entrySet迭代 -> 34 ms
增强for循环,keySet迭代 -> 31 ms
增强for循环,entrySet迭代 -> 24 ms
迭代器,keySet迭代 -> 27 ms
迭代器,entrySet迭代 -> 25 ms
增强for循环,keySet迭代 -> 32 ms
增强for循环,entrySet迭代 -> 27 ms
迭代器,keySet迭代 -> 28 ms
迭代器,entrySet迭代 -> 26 ms
分别对四种遍历方式进行1000W次迭代,比较用时。
增强for循环,keySet迭代 -> 151 ms
增强for循环,entrySet迭代 -> 92 ms
迭代器,keySet迭代 -> 101 ms
迭代器,entrySet迭代 -> 83 ms
增强for循环,keySet迭代 -> 108 ms
增强for循环,entrySet迭代 -> 74 ms
迭代器,keySet迭代 -> 102 ms
迭代器,entrySet迭代 -> 150 ms
增强for循环,keySet迭代 -> 266 ms
增强for循环,entrySet迭代 -> 269 ms
迭代器,keySet迭代 -> 140 ms
迭代器,entrySet迭代 -> 124 ms
增强for循环,keySet迭代 -> 175 ms
增强for循环,entrySet迭代 -> 145 ms
迭代器,keySet迭代 -> 114 ms
迭代器,entrySet迭代 -> 119 ms
参考文档: Java map 详解 - 用法、遍历、排序、常用API等
其中关于Map遍历的结论:
总结
1. 增强for循环使用方便,但性能较差,不适合处理超大量级的数据。
2. 迭代器的遍历速度要比增强for循环快很多,是增强for循环的2倍左右。
3. 使用entrySet遍历的速度要比keySet快很多,是keySet的1.5倍左右。
多次实测结果表明: for循环遍历Map虽的确略慢于迭代器遍历, 但是实际相差并没有其结论那么大.
今天偷闲, 在公司内部的SonarQube上看看Bugs/漏洞/坏味道, 碰见简单的就顺手改改.
改了一堆诸如:
printStackTrace
转log.error
private constructor
duplicated constant string
正要犯困, 忽然眼前一亮, 看见一个新奇的Bug:
SimpleDateFormat sdf = new SimpleDateFormat("YYYYMMdd");
提示信息如下:
Make sure that Week Year "YYYY" is expected here instead of Year "yyyy".
详细的提示信息中, 引用Javadoc的内容如下:
A week year is in sync with a WEEK_OF_YEAR cycle.
All weeks between the first and last weeks (inclusive) have the same week year value.
Therefore, the first and last days of a week year may have different calendar year values.
For example, January 1, 1998 is a Thursday.
If getFirstDayOfWeek() is MONDAY and getMinimalDaysInFirstWeek() is 4 (ISO 8601 standard compatible setting), then week 1 of 1998 starts on December 29, 1997, and ends on January 4, 1998.
The week year is 1998 for the last three days of calendar year 1997.
If, however, getFirstDayOfWeek() is SUNDAY, then week 1 of 1998 starts on January 4, 1998, and ends on January 10, 1998; the first three days of 1998 then are part of week 53 of 1997 and their week year is 1997.
简而言之, 在一年中的大部分时间里, Y
所表示的week year
和y
所表示的year
是相同的, 使用这样的格式化字符串不会有问题.
但是, 在年初或者年末的时候, 有可能存在这样的问题:
week year
为前一年的年份week year
为后一年的年份在这种情况下格式化的时间字符串, 就会发生提前一年或延迟一年的错误.
接下来, 用实践检验一下:
public class DateFormatterTest {
@SneakyThrows
@Test
public void testWeekYearBug() {
val originDateString = "2015/12/31";
val errorDateString = "2016/12/31";
val parsedDate = new SimpleDateFormat("yyyy/MM/dd").parse(originDateString);
val formatString = new SimpleDateFormat("YYYY/MM/dd").format(parsedDate);
assertNotEquals(originDateString, formatString);
assertEquals(errorDateString, formatString);
}
}
测试通过, 说明:
使用"yyyy/MM/dd"解析"2015/12/31"获取Date,
再使用"YYYY/MM/dd"格式化Date,
获得的字符串为"2016/12/31"
昨天晚上, 老大 @bingoohuang提了个问题:
玩转JVM从实际需求出发
提取java中的ArrayList类的源代码
在add方法中,添加最大容器数量限制I(比如最大1万个),超过则抛出异常
替换系统类库中的实现
略微研究, 浅尝辄止, 在这里分享一下答案.
浅尝辄止
汉语成语,拼音是qiǎn cháng zhé zhǐ,意思是略微尝试一下就停下来。
指不深入钻研。
出自清·彭养鸥《黑籍冤魂》第二十四回:
“此物非不可尝,苟文人墨客,浅尝辄止,用以悦性陶情,有何不可?”
在使用SonarQube
检查使用Jmockit
进行单元测试的Java代码时, 报出了一个Code Smell和一个Bug, 分别是:squid:S1171(Only static class initializers should be used)
和squid:S3599(Double Brace Initialization should not be used)
, 原因是使用了Jmockit
的录制功能:
@Test
public void testInstanceMockingByExpectation() {
AnOrdinaryClass instance = new AnOrdinaryClass();
// 直接把实例传给Expectations的构造函数即可Mock这个实例
new Expectations(instance) {
{
// 尽管这里也可以Mock静态方法,但不推荐在这里写。静态方法的Mock应该是针对类的
// mock普通方法
instance.ordinaryMethod();
result = 20;
// mock final方法
instance.finalMethod();
result = 30;
// native, private方法无法用Expectations来Mock
}
};
Assert.assertTrue(AnOrdinaryClass.staticMethod() == 1);
Assert.assertTrue(instance.ordinaryMethod() == 20);
Assert.assertTrue(instance.finalMethod() == 30);
// 用Expectations无法mock native方法
Assert.assertTrue(instance.navtiveMethod() == 4);
// 用Expectations无法mock private方法
Assert.assertTrue(instance.callPrivateMethod() == 5);
}
搜索了一下Double Brace Initialization
, 记录在此.
参考: Java:Double Brace Initialization
REP
复用/发布等同原则
复用/发布等同原则(Reuse/Release Equivalency Principle
):软件复用的最小粒度应等同于其发布的最小粒度。
直白地说,就是要复用一段代码就把它抽成模块。
CCP
共同闭包原则
共同闭包原则(Common Closure Principle
):为了相同目的而同时修改的类,应该放在同一个模块中。
对大部分应用程序而言,可维护性的重要性远远大于可复用性,由同一个原因引起的代码修改,最好在同一个模块中,如果分散在多个模块中,那么开发、提交、部署的成本都会上升。
CRP
共同复用原则
共同复用原则(Common Reuse Principle
):不要强迫一个模块依赖它不需要的东西。
相信你一定有这种经历,集成了模块A,但模块A依赖了模块B、C。即使模块B、C 你完全用不到,也不得不集成进来。这是因为你只用到了模块A的部分能力,模块A中额外的能力带来了额外的依赖。如果遵循共同复用原则,你需要把A拆分,只保留你要用的部分。
复用原则竞争关系
REP
、CCP
、CRP
三个原则之间存在彼此竞争的关系。REP
和 CCP
是黏合性原则,它们会让模块变得更大,而 CRP
原则是排除性原则,它会让模块变小。
遵守 REP
、CCP
而忽略 CRP
,就会依赖了太多没有用到的模块和类,而这些模块或类的变动会导致你自己的模块进行太多不必要的发布;
遵守 REP
、CRP
而忽略 CCP
,因为模块拆分的太细了,一个需求变更可能要改n个模块,带来的成本也是巨大的。
优秀的架构师应该能在上述三角形张力区域中定位一个最适合目前研发团队状态的位置,例如在项目早期,CCP
比REP
更重要,随着项目的发展,这个最合适的位置也要不停调整。
曾经的拜占庭国土辽阔,为了抵御来自各个方向的敌人,军队之间分隔很远,他们之间只能通过信使互相传递消息。
一场新的战役即将爆发,有5支拜占庭军队要共同进退,5个将军都是平级的,他们要怎么达成一起进攻或者一起撤退的共识呢?
最简单的办法就是投票,每个将军都派出信使将自己的意见发给其他四个将军。
对每个将军来说,算上自己的票数,如果进攻票超过2票就会发起进攻,如果少于或者等于2票就撤退。
这是最简单的情况,很合逻辑。那假如是下面的情况呢?
1. 5个将军中有一个是奸细,其他4个将军有两个赞成进攻,2个反对,这个将军给其中2个发去了进攻的意见,给另外2个却是撤退,结果是2支军队进攻,2支军队撤退,没有达成共识。
2. 可能有一个或者多个信使被暗杀,或者被策反。
在这两种情况下,投票的结果不能代表大多数将军的意见。
以上,可以总结出拜占庭将军问题:在可能有叛徒的情况下,其余忠诚的将军如何不受其影响达成一致的协议?
不动点组合子(Fixed-point combinator, 或不动点算子, 使用FIX
表示)是计算其他函数的一个不动点的高阶函数.
不动点算子具有以下特性, 对于任何函数f
都有:
FIX f = f (FIX f)
不动点组合子中最有名的(也可能是最简单的)是Y
组合子:
Y = λf. (λx. f (x x)) (λx. f (x x))
另一个常见不动点组合子是图灵不动点组合子, 即Θ
组合子:
Θ = (λx. λy. (y (x x y))) (λx.λy.(y (x x y)))
实现不动点组合子时用到的类型:
| 名称 | Type |
| | |
| 递归参数 | T |
| 递归返回 | R |
| 递归函数 | Func<T, R> |
| 单步函数 | Func<Func<T, R>, Func<T, R>> |
| 不动点组合子 | Func<Func<Func<T, R>, Func<T, R>>, Func<T, R>> |
不动点组合子:
一个以单步函数为参数的返回递归函数的高阶函数
最近做了个小玩意儿, 要在后端的Java应用里嵌码埋点日志.
手动埋点日志比较简单, 思路就是创建一个独立的logback-LoggerContext, 获取一个干净的logger, 单独配置其Appender处理日志, 只要按slf4j的接口封装出API, 就能基本满足大部分场景的日志需求.
void log(org.slf4j.event.Level level, String msg);
void log(org.slf4j.event.Level level, String format, Object arg);
void log(org.slf4j.event.Level level, String format, Object arg1, Object arg2);
void log(org.slf4j.event.Level level, String format, Object... argArray);
void log(org.slf4j.event.Level level, String msg, Throwable t);
难点在于自动埋点.
第一时间想起的是Instrumentation, 之前也尝试过, [#9 浅尝Java Instrumentation], 只要配合ASM或者Javassist就能实现.
但是有两点不爽的: 一是目标应用启动时要添加agent参数, 而且附带需要分发埋点工具的jar包; 二是已经玩过了, 炒冷饭有点难受.
这时候就想起来一直很好奇的lombok, 它可以SneakyThrows, 可以Cleanup, 还可以Slf4j, 它是如何完成那些神奇的功能的呢?
还是要多学多练.
$ export GOPROXY="https://goproxy.io"
的确好用, golang.org/x/...
这些包都能go get
到了.
release了新版本, 却怎么都go get
不到
$ go: get ...... unexpected end of JSON input
各种尝试, 一顿搜索, 最后:
$ export GOPROXY=""
终于能go get
到了.
墙才是最终的辣鸡;
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.