JoyLau's Blog

JoyLau 的技术学习与思考

关于joylau-mybatis的说明

  • 该项目来源自 https://github.com/abel533/Mapper 详细信息和源代码可fork查看
  • 我封装之后项目地址 https://github.com/JoyLau/joylau-mybatis
  • 我自己整合通用Mapper,分页,以及排序功能,使用起来无缝结合,丝般顺滑
  • 我对其封装了所有的通用mapper,并整合本项目添加了自己的方法,详细请查看下文或者在线查看api文档: http://api.joylau.cn/
  • 文档你主要需要查看function的类注释
  • 下面我来逐一介绍:

BaseController

继承FunctionController,目前有2个抽象方法,getSession()和getContextPath(),一看就知道是干嘛的,不多说。想要扩展很简单,继续写自己的方法即可

BaseMapper

  • 集成了MySQL所使用的绝大部分通用Mapper,包括BaseMapper,ExampleMapper,RowBoundsMapper,MySqlMapper,IdsMapper…等等,详细可查看API文档,或者下载源码查看
  • 所有的单表及简单的多表操作都在这里面啦,基本上你是不需要扩展啦,好不好用,敲起mapper再点一下你就知道了

BaseService

  • 得益于Spring项目的强大支持,在Spring4.x后,支持泛型注入,这使得我们封装的更加简单了
  • 现在,不必再调用到Mapper层,现在在Service层就可以完美使用,封装了3个插入方法,4个更新方法,5个删除方法,13个查询方法
  • 内容涵盖了单条记录CRUD;根据ID或者属性或者条件CRUD;批量删除,插入;分页查询
  • 说下分页查询怎么使用:调用selectPage可以进行单表分页查询,调用selectPageByExample可以进行条件分页查询

BaseServiceImpl

  • 继承的FunctionServiceImpl已经实现了上述所有的通用CURD方法
  • 在继承的FunctionServiceImpl类里我提供了获取mapper的方法,由此方法,可以进行很方便的扩展,你懂得~~

BaseModel

  • 添加每个实体都会用到的id属性
  • 添加了createTime和updateTime属性,虽然在业务上可能没有什么用处,但是对于开发和运维的作用相当大,谁用谁知道

我的接口解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 保存一个实体,null的属性也会保存,不会使用数据库默认值
*/
int insert(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 保存一个实体,null的属性不会保存,会使用数据库默认值
*/
int insertSelective(T model);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 批量插入,支持批量插入的数据库可以使用,另外该接口限制实体包含`id`属性并且必须为自增列
*/
int insertList(List<T> list);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键更新实体全部字段,null值会被更新
*/
int updateByPrimaryKey(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键更新属性不为null的值
*/
int updateByPrimaryKeySelective(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件更新实体`model`包含的全部属性,null值会被更新
*/
int updateByExample(T model, Object example);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件更新实体`model`包含的不是null的属性值
*/
int updateByExampleSelective(T model, Object example);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体属性作为条件进行删除,查询条件使用等号
*/
int delete(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体id删除
*/
int deleteById(int id);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件删除数据
*/
int deleteByExample(Object example);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键字符串进行删除,类中只有存在一个带有@Id注解的字段
*
* @param ids 如 "1,2,3,4"
*/
int deleteByIds(String ids);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键字段进行删除,方法参数必须包含完整的主键属性
*/
int deleteByPrimaryKey(Object key);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体中的属性值进行查询,查询条件使用等号
*/
List<T> select(T model);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体中的id查询实体
*/
T selectById(int id);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 查询全部结果
*/
List<T> selectAll();

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件进行查询
*/
List<T> selectByExample(Object example);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据example条件和RowBounds进行分页查询
*/
List<T> selectByExampleAndRowBounds(Object example, RowBounds rowBounds);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键字符串进行查询,类中只有存在一个带有@Id注解的字段
*
* @param ids 如 "1,2,3,4"
*/
List<T> selectByIds(String ids);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
*/
T selectByPrimaryKey(Object key);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体中的属性查询总数,查询条件使用等号
*/
int selectCount(T model);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件进行查询总数
*/
int selectCountByExample(Object example);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
*/
T selectOne(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体属性和RowBounds进行分页查询
*/
List<T> selectByRowBounds(T model, RowBounds rowBounds);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 单表分页查询
*/
PageInfo selectPage(int pageNum, int pageSize, T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件进行分页查询
*/
PageInfo selectPageByExample(int pageNum, int pageSize, Object example);

怎么使用?

很简单

  • 你的Mapper继承BaseMapper
  • 你的Service继承BaseService
  • 你的ServiceImpl实现你的Service借口,再继承BaseServiceImpl
  • 你的Model继承BaseModel

来试一下

  • 在你的ServiceImpl里点一下方法试试? 是不是很棒???
  • 在你的Mapper里再点一下方法试试?? 6666…

最后

  • 能想到的我都写了,BaseMapper和BaseServiceImpl基本上不需要扩展了,有不明白的可以联系我
  • 欢迎指正,共同学习

Redis-Benchmark

说明

  • redis默认提供了性能测试的工具
  • 在linux下文件是redis-benchmark
  • 在windows下文件是redis-benchmark.exe

参数查看

  • redis-benchmark -h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-
    k <boolean>]

    -h <hostname> Server hostname (default 127.0.0.1)
    -p <port> Server port (default 6379)
    -s <socket> Server socket (overrides host and port)
    -a <password> Password for Redis Auth
    -c <clients> Number of parallel connections (default 50)
    -n <requests> Total number of requests (default 100000)
    -d <size> Data size of SET/GET value in bytes (default 2)
    -dbnum <db> SELECT the specified db number (default 0)
    -k <boolean> 1=keep alive 0=reconnect (default 1)
    -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD
    Using this option the benchmark will expand the string __rand_int__
    inside an argument with a 12 digits number in the specified range
    from 0 to keyspacelen-1. The substitution changes every time a command
    is executed. Default tests use this to hit random keys in the
    specified range.
    -P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline).
    -q Quiet. Just show query/sec values
    --csv Output in CSV format
    -l Loop. Run the tests forever
    -t <tests> Only run the comma separated list of tests. The test
    names are the same as the ones produced as output.
    -I Idle mode. Just open N idle connections and wait.

    Examples:

    Run the benchmark with the default configuration against 127.0.0.1:6379:
    $ redis-benchmark

    Use 20 parallel clients, for a total of 100k requests, against 192.168.1.1:
    $ redis-benchmark -h 192.168.1.1 -p 6379 -n 100000 -c 20

    Fill 127.0.0.1:6379 with about 1 million keys only using the SET test:
    $ redis-benchmark -t set -n 1000000 -r 100000000

    Benchmark 127.0.0.1:6379 for a few commands producing CSV output:
    $ redis-benchmark -t ping,set,get -n 100000 --csv

    Benchmark a specific command line:
    $ redis-benchmark -r 10000 -n 10000 eval 'return redis.call("ping")' 0

    Fill a list with 10000 random elements:
    $ redis-benchmark -r 10000 -n 10000 lpush mylist __rand_int__

    On user specified command lines __rand_int__ is replaced with a random integer
    with a range of values selected by the -r option.

开始测试

  • 搞清了参数的含义,可以进行测试了
  • 本次配置为Redis的默认配置,默认的配置项已经有足够好的性能表现了,不需要调优
  • redis-benchmark -h joylau.cn -p 6379 -a XXX -t get,set -n 1000 -c 400 -q
    我是模仿了我自己现在公司的业务需求,测试了我直接服务器上的Redis,向redis服务器发送1000个请求,每个请求附带400个并发客户端,以静默显示
    redis-joylau-test-q
    可以看到,set操作每秒处理17241次,get操作每秒处理17543次
  • redis-benchmark -h joylau.cn -p 6379 -a XXX -t get,set -n 1000 -c 400
    同上,以标准格式显示
    redis-joylau-test
    可以看到,set操作每秒处理17857次,get操作每秒处理18518次
  • 我自己也开了本地的服务器做测试,每秒操作次数可达100000次

一些参数说明

  • -t : 可以选择你需要运行的测试用例
  • -r : 设置随机数来SET/GET/INCR
  • -P : 一次性执行多条命令,记得在多条命令需要处理时候使用 pipelining。

陷阱和错误的认识

第一点是显而易见的:基准测试的黄金准则是使用相同的标准。 用相同的任务量测试不同版本的 Redis,或者用相同的参数测试测试不同版本 Redis。 如果把 Redis 和其他工具测试,那就需要小心功能细节差异。

  • Redis 是一个服务器:所有的命令都包含网络或 IPC 消耗。这意味着和它和 SQLite, Berkeley DB, Tokyo/Kyoto Cabinet 等比较起来无意义, 因为大部分的消耗都在网络协议上面。
  • Redis 的大部分常用命令都有确认返回。有些数据存储系统则没有(比如 MongoDB 的写操作没有返回确认)。把 Redis 和其他单向调用命令存储系统比较意义不大。
    简单的循环操作 Redis 其实不是对 Redis 进行基准测试,而是测试你的网络(或者 IPC)延迟。想要真正测试 Redis,需要使用多个连接(比如 redis-benchmark), 或者使用 pipelining 来聚合多个命令,另外还可以采用多线程或多进程。
  • Redis 是一个内存数据库,同时提供一些可选的持久化功能。 如果你想和一个持久化服务器(MySQL, PostgreSQL 等等) 对比的话, 那你需要考虑启用 AOF 和适当的 fsync 策略。
  • Redis 是单线程服务。它并没有设计为多 CPU 进行优化。如果想要从多核获取好处, 那就考虑启用多个实例吧。将单实例 Redis 和多线程数据库对比是不公平的。

影响 Redis 性能的因素

有几个因素直接决定 Redis 的性能。它们能够改变基准测试的结果, 所以我们必须注意到它们。一般情况下,Redis 默认参数已经可以提供足够的性能, 不需要调优。

  • 网络带宽和延迟通常是最大短板。建议在基准测试之前使用 ping 来检查服务端到客户端的延迟。根据带宽,可以计算出最大吞吐量。 比如将 4 KB 的字符串塞入 Redis,吞吐量是 100000 q/s,那么实际需要 3.2 Gbits/s 的带宽,所以需要 10 GBits/s 网络连接, 1 Gbits/s 是不够的。 在很多线上服务中,Redis 吞吐会先被网络带宽限制住,而不是 CPU。 为了达到高吞吐量突破 TCP/IP 限制,最后采用 10 Gbits/s 的网卡, 或者多个 1 Gbits/s 网卡。
  • CPU 是另外一个重要的影响因素,由于是单线程模型,Redis 更喜欢大缓存快速 CPU, 而不是多核。这种场景下面,比较推荐 Intel CPU。AMD CPU 可能只有 Intel CPU 的一半性能(通过对 Nehalem EP/Westmere EP/Sandy 平台的对比)。 当其他条件相当时候,CPU 就成了 redis-benchmark 的限制因素。
  • 在小对象存取时候,内存速度和带宽看上去不是很重要,但是对大对象(> 10 KB), 它就变得重要起来。不过通常情况下面,倒不至于为了优化 Redis 而购买更高性能的内存模块。
  • Redis 在 VM 上会变慢。虚拟化对普通操作会有额外的消耗,Redis 对系统调用和网络终端不会有太多的 overhead。建议把 Redis 运行在物理机器上, 特别是当你很在意延迟时候。在最先进的虚拟化设备(VMWare)上面,redis-benchmark 的测试结果比物理机器上慢了一倍,很多 CPU 时间被消费在系统调用和中断上面。
  • 如果服务器和客户端都运行在同一个机器上面,那么 TCP/IP loopback 和 unix domain sockets 都可以使用。对 Linux 来说,使用 unix socket 可以比 TCP/IP loopback 快 50%。 默认 redis-benchmark 是使用 TCP/IP loopback。 当大量使用 pipelining 时候,unix domain sockets 的优势就不那么明显了。
  • 当大量使用 pipelining 时候,unix domain sockets 的优势就不那么明显了。
  • 当使用网络连接时,并且以太网网数据包在 1500 bytes 以下时, 将多条命令包装成 pipelining 可以大大提高效率。事实上,处理 10 bytes,100 bytes, 1000 bytes 的请求时候,吞吐量是差不多的

我想说

  • 我分别测试我之前在腾讯云上的Redis服务器 (Windows Server 2008R2) 和现在在阿里云上的服务器 (Linux CentOS 7.2) 及 局域网下同事的Redis服务器,和本机Redis的服务器
    速度最快的本机服务器,其次是同事的服务器,再次是阿里云上的服务器,最后是腾讯云上的服务器
  • 测试差异之大除了在硬件上的差别外,最客观的因素在网络带宽上,我自己2个位于云上的服务器都是1M的带宽,如此测试,正如上面所说,远远不达不到数据传输所需要的带宽值

最后

Docker

  • 安装: yum install docker
  • 卸载: yum remove docker
  • 启动: systemctl start docker
  • 开机自启: systemctl enable docker

Dockerfile

1
2
3
4
5
FROM java:8
MAINTAINER joylau
ADD joyalu-0.0.1-SNAPSHOT.jar joylau.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/joylau.jar"]

镜像

  • 编译镜像: docker build –t joylau/docker .
  • 查看镜像: docker images
  • 删除镜像: docker rmi name/id

容器

  • 运行: docker run –d --name joylau –p 8080:8080 joylau/docker
  • 停止容器: docker stop id/name
  • 查看运行中的容器 : docker ps
  • 查看所有容器: docker ps -a
  • 删除容器: docker rm id/name

2018-07-05 16:05:00 更新

拉取docker镜像

docker pull image_name
查看宿主机上的镜像,Docker镜像保存在/var/lib/docker目录下:

docker images

删除镜像

docker rmi docker.io/tomcat:7.0.77-jre7 或者 docker rmi b39c68b7af30
查看当前有哪些容器正在运行

docker ps
查看所有容器

docker ps -a
启动、停止、重启容器命令:

docker start container_name/container_id
docker stop container_name/container_id
docker restart container_name/container_id
后台启动一个容器后,如果想进入到这个容器,可以使用attach命令:

docker attach container_name/container_id
删除容器的命令:

docker rm container_name/container_id
删除所有停止的容器:

docker rm $(docker ps -a -q)
查看当前系统Docker信息

docker info
从Docker hub上下载某个镜像:

docker pull centos:latest
docker pull centos:latest
查找Docker Hub上的nginx镜像

docker search nginx
执行docker pull centos会将Centos这个仓库下面的所有镜像下载到本地repository。

2018-07-09 14:02:25 更新

docker search xxx : 在docker仓库查找镜像
docker images | grep xxx : 在本地仓库查找镜像

2018-07-12 09:44:40 更新

进入容器: docker exec -it 容器的ID或者NAME /bin/bash

2020-04-12 12:30:25 更新

查找自定义属性

1
docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Status}}"

SpringBatch

组成部分

  • JobRepository: 用来注册Job的容器
  • JobLauncher: 用来启动Job的接口
  • Job : 我要实际执行的任务,包含一个或多个Step
  • Step : Step-步骤包含ItemReaderItemProcessorItemWrite
  • ItemReader : 用来读取数据的接口
  • ItemProcessor : 用来处理数据的接口
  • ItemWrite : 用来输出数据的接口

整合

SpringBoot 整合 SpringBatch 只需要引入依赖并注册成Spring 的 Bean 即可,若是想开启批处理的支持还需要在该配置类上添加 @EnableBatchProcessing

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
<!-- SpringBatch会自动加载hsqldb,我们去除即可 -->
<exclusions>
<exclusion>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</exclusion>
</exclusions>
</dependency>

来看代码 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Bean
public JobRepository jobRepository(DataSource dataSource, PlatformTransactionManager transactionManager)
throws Exception {
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setDataSource(dataSource);
jobRepositoryFactoryBean.setTransactionManager(transactionManager);
jobRepositoryFactoryBean.setDatabaseType("oracle");
return jobRepositoryFactoryBean.getObject();
}

@Bean
public SimpleJobLauncher jobLauncher(DataSource dataSource, PlatformTransactionManager transactionManager)
throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository(dataSource, transactionManager));
return jobLauncher;
}

@Bean
public Job importJob(JobBuilderFactory jobs, Step s1) {
return jobs.get("importJob")
.incrementer(new RunIdIncrementer())
.flow(s1)
.end()
.listener(csvJobListener())
.build();
}

@Bean
public Step step1(StepBuilderFactory stepBuilderFactory, ItemReader<Person> reader, ItemWriter<Person> writer,
ItemProcessor<Person,Person> processor) {
return stepBuilderFactory
.get("step1")
.<Person, Person>chunk(65000) //1
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}



//接口分别实现

@Bean
public ItemReader<Person> reader() throws Exception {
//
return reader;
}

@Bean
public ItemProcessor<Person, Person> processor() {
//
return processor;
}



@Bean
public ItemWriter<Person> writer(DataSource dataSource) {//1
//
return writer;
}
}

貌似就这么简单的完成了……

扩展

  • 监听Job的执行情况,自定义类实现JobExecutionListener
  • 执行计划任务,在普通的计划任务方法中执行JobLauncher的run方法即可

emoji

准备

  • MySQL5.5.3+
  • mysql-connector-java5.1.13+

有异常

1
2
3
4
5
6
7
8
9
java.sql.SQLException: Incorrect string value: '\xF0\x9F\x92\x94' for colum n 'name' at row 1 
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3593)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3525)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1986)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2140)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2620)
at com.mysql.jdbc.StatementImpl.executeUpdate(StatementImpl.java:1662)
at com.mysql.jdbc.StatementImpl.executeUpdate(StatementImpl.java:1581)

配置项

1
2
3
4
5
6
7
8
9
10
11
    
[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
  • 数据库字符集:utf8mb4 -- UTF-8 Unicode
  • 排序规则:utf8mb4_general_ci

多样的浏览器兼容

1
2
3
4
5
6
7
8
9
<link href="http://cdn.staticfile.org/emoji/0.2.2/emoji.css" rel="stylesheet" type="text/css" />
<script src="http://cdn.staticfile.org/jquery/2.1.0/jquery.min.js"></script>
<script src="http://cdn.staticfile.org/emoji/0.2.2/emoji.js"></script>



var $text = $('.emojstext');
var html = $text.html().trim().replace(/\n/g, '<br/>');
$text.html(jEmoji.unifiedToHTML(html));

Sonatype

说明

  • 个人感觉第一次发布的步骤非常复杂,我在第一次操作的时候来来回回发布了7,8个版本,最后都是校验失败,导致构件不能关闭(因为我遇到了个大坑)
  • 第一次发布成功之后后面的更新和添加新的构件都相对来说要容易一些(groupid不变的情况下)

开始

账户注册

创建并提交工单

创建工单

  • Project和issue Type的填写如上图所示,不能填写错了
  • 创建完成之后就等待网站工作人员的审核就可以了,不知道为什么,我等待的时间非常短,2分钟都不到,工作人员就回复我了,可能是我的运气比较好吧,但是上个星期买房摇号我却没摇到伤心欲绝
  • 当issue 的 state 变为 RESOLVED时就可继续操作了,同时下面的活动区会给你发消息
    Comment

gpg生成密钥对

  • 下载安装:https://www.gpg4win.org/download.html
    安装时注意的是,只安装主体组件和加密解密窗口的组件就可以了,其他的不需要~~~~
  • 查看是否安装成功:gpg --version
    version
  • 生成密钥对:gpg --gen-key
    gpg --gen-key
    gpg --gen-key
  • 之后往下,会让你输入用户名和邮箱,还有一个Passphase,相当于密钥库密码,不要忘记。
  • 查看公钥:gpg --list-keys
  • 将公钥发布到 PGP 密钥服务器
    1
    2
    3
    4
    5
    gpg --keyserver hkp://pool.sks-keyservers.net --send-keys C990D076
    //可能由于网络问题,有点慢,多重试几次

    //查看发布是否成功
    gpg --keyserver hkp://pool.sks-keyservers.net --recv-keys C990D076

配置setting.xml文件和pom.xml文件

  • setting.xml文件

    1
    2
    3
    4
    5
    6
    7
    8
    <servers>
    <server>
    //记住id,需要和pom文件里的id一致
    <id>oss</id>
    <username>username</username>
    <password>password</password>
    </server>
    </servers>
  • pom.xml文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    <!--
    ~ The MIT License (MIT)
    ~
    ~ Copyright (c) 2017 2587038142@qq.com
    ~
    ~ Permission is hereby granted, free of charge, to any person obtaining a copy
    ~ of this software and associated documentation files (the "Software"), to deal
    ~ in the Software without restriction, including without limitation the rights
    ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    ~ copies of the Software, and to permit persons to whom the Software is
    ~ furnished to do so, subject to the following conditions:
    ~
    ~ The above copyright notice and this permission notice shall be included in
    ~ all copies or substantial portions of the Software.
    ~
    ~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    ~ THE SOFTWARE.
    -->

    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.joylau.code</groupId>
    <artifactId>joylau-echarts</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <name>joylau-echarts</name>
    <description>Configure the most attribute for ECharts3.0+ by Gson</description>
    <url>http://code.joylau.cn</url>

    <parent>
    <groupId>org.sonatype.oss</groupId>
    <artifactId>oss-parent</artifactId>
    <version>7</version>
    </parent>

    <licenses>
    <license>
    <name>The Apache Software License, Version 2.0</name>
    <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
    </license>
    </licenses>

    <developers>
    <developer>
    <name>JoyLau</name>
    <email>2587038142@qq.com</email>
    <url>http://joylau.cn</url>
    </developer>
    </developers>

    <scm>
    <connection>scm:git:git@github.com:JoyLau/joylau-echarts.git</connection>
    <developerConnection>scm:git:git@github.com:JoyLau/joylau-echarts.git</developerConnection>
    <url>git@github.com:JoyLau/joylau-echarts</url>
    </scm>
    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
    <dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.5</version>
    <scope>compile</scope>
    <optional>true</optional>
    </dependency>
    </dependencies>

    <build>
    <testResources>
    <testResource>
    <directory>src/test/resources</directory>
    </testResource>
    <testResource>
    <directory>src/test/java</directory>
    </testResource>
    </testResources>
    </build>

    <profiles>
    <profile>
    <id>release</id>
    <build>
    <plugins>
    <!--Compiler-->
    <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>1.7</source>
    <target>1.7</target>
    </configuration>
    </plugin>
    <!-- Source -->
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-source-plugin</artifactId>
    <version>2.2.1</version>
    <executions>
    <execution>
    <phase>package</phase>
    <goals>
    <goal>jar-no-fork</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    <!-- Javadoc -->
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <version>2.9.1</version>
    <executions>
    <execution>
    <phase>package</phase>
    <goals>
    <goal>jar</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    <!-- GPG -->
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-gpg-plugin</artifactId>
    <version>1.6</version>
    <executions>
    <execution>
    <phase>verify</phase>
    <goals>
    <goal>sign</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
    <distributionManagement>
    <snapshotRepository>
    <id>oss</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    </snapshotRepository>
    <repository>
    <id>oss</id>
    <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
    </distributionManagement>
    </profile>
    </profiles>
    </project>

上传构件到 OSS 中

1
2
3
mvn clean deploy -P release
<--! jdk1.8后再生成javadoc时语法较为严格,这时去除javadoc即可 !-->
mvn clean deploy -P release -Dmaven.javadoc.skip=true

在上传之前会自动弹出一个对话框,需要输入上面提到的 Passphase,它就是刚才设置的 GPG 密钥库的密码。
随后会看到大量的 upload 信息,因为在国内网络的缘故,时间有点久,耐心等待吧。

在 OSS 中发布构件

  • 在 OSS 中,使用自己的 Sonatype 账号登录后,可在 Staging Repositories 中查看刚才已上传的构件,这些构件目前是放在 Staging 仓库中,可进行模糊查询,快速定位到自己的构件。
  • 此时,该构件的状态为 Open,需要勾选它,然后点击 Close 按钮。系统会自动验证该构件是否满足指定要求,当验证完毕后,状态会变为 Closed。
    1
  • 最后,点击 Release 按钮来发布该构件,这一步没有截图,将就看吧知道就行:
    2

等待构件审批通过

这个,又只能等待了,当然他们晚上上班,还是第二天看。当审批通过后,将会收到邮件通知。

从中央仓库中搜索构件

这时,就可以在maven的中央仓库中搜索到自己发布的构件了,以后可以直接在pom.xml中使用了!

中央仓库搜索网站:http://search.maven.org/

第一次成功发布之后,以后就不用这么麻烦了,可以直接使用Group Id发布任何的构件,当然前提是Group Id没有变。

以后的发布流程:

a)构件完成后直接使用maven在命令行上传构建;

b)在https://oss.sonatype.org/ close并release构件;

c)等待同步好(大约2小时多)之后,就可以使用了

遇坑记录

  • 安装GPG时候,没有安装弹框组件,导致gpg密码框弹不出来
  • 一开始所有的命令行都在git下操作,每次部署的时候都是提示没有私钥错误,后来发现git生成的gpg密钥对在user更目录下,切换到CMD操作,是生成在AppData下。经查看有私钥,问题解决

引用

有一部分程序员中的老司机,他们善于找各种借口,少干活,少背锅,多拿钱。但是,更多的程序员坦诚、直白、意气用事。
那些年吹过的牛逼都实现了吗?还是随风而去?

这个功能简单,一天就能搞完
Images
程序员拿到一个新功能,心里暗暗发笑,这剧情我见过啊。于是脱口而出,这功能简单,一天就能做完,明天上线肯定没问题。
结果,眼看着到自己设定的截止日期了,还有一部分代码没有写完,怎么办?
很简单啊,又不是生死状,又不要命。解决办法很简单,加班~~~
程序员,那些年吹过的牛逼,最后都自己加班了。

这段代码肯定没bug,我都测试过了
Images
功能开发完了,拿去测试吧,拿去玩耍吧,上线吧,部署吧,发给客户吧,肯定没问题的。
结果,很多时候还没发布。要么测试发现bug,要么产品发现bug,要么老板发现bug。
你的第一反应就是:是特么你们不会用老子开发的功能吧?你乐呵呵的看着bug复现,怎么办呢?
很简单啊,紧急修复bug,重新发布。时间来不及了?加班啊~~~
程序员,那些年吹过的牛逼,最后都自己加班了。

我用的是最现在最流行的技术,某某大公司也用这个
Images
在技术讨论会上,你侃侃而谈,我精心设计的前后端分离的框架,我使用了现在最流行的界面库,我们用的技术某某独角兽公司都在使用,肯定是最好的。
结果呢,使用的技术太新。Github上很少有相关的开源项目,Stack Overflow上很少有这方面的问答。你被一个问题搞的昏天暗地,只能默默的看官方文档,而且是英文的(这是好事儿)。
啊?项目着急上线怎么办呢?加班啊~~~
程序员,那些年吹过的牛逼,最后都自己加班了。

重构代码,很快就能完成
Images

  • 何为Code refactoring

    Code refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behavior.

之前为了快速迭代,忽略了代码的结构和质量。正好最近这两天没有什么新功能开发,我要重构一下现有的代码,绝对没问题。
结果呢,两天的空窗期没搞定。明天就要开发新的功能了,怎么办呢?加班啊~~~
程序员,那些年吹过的牛逼,最后都自己加班了。

向外行介绍程序员工作的复杂程度
在工作中经常能听到这样的话「不就加个按钮么?怎么要做两天时天?」。那么,作为程序员如何解释自己的工作复杂度呢?
如果你的老板是技术出身,那你很庆幸,他能理解你实现一个小小功能,修改一个小小功能所付出的辛苦劳动。
如果你的老板不懂技术,也许你就要无穷无尽的加班了。给你的忠告就是:做正确的事儿,等着被开除。这是一位谷歌工程师说的话。
如果你的产品经理懂技术,那么你既是幸运的也是不幸的。
幸运的是,他可以理解程序员工作的复杂度。但是“不幸”的是,你再也不能为了偷懒找借口。
当产品经理提出一个方案时,你再也不敢坚定地说“技术不可行”。因为你害怕产品经理自己写好了代码给你,那是多么尴尬的境地。

  • 下面是 Channing Walton 的用泡茶的例子来解释,非常形象。
      - 请他们描述泡出一杯茶需要哪些步骤,他们会这么说:
      - 烧水
      - 把茶叶放到茶壶里
      - 水烧开后倒入茶壶
      - 等待5分钟
      - 把茶倒进杯子
      - 加牛奶
      - 喝
      - 现在,有趣的开始了。你要开始问这样的问题:
      - 烧水?
      - 水哪来的?
      - 热水壶在哪里?
      - 你怎么把水倒进热水壶?
      - 你怎么知道热水壶壶里要倒多少水?
      - 如果没有水/热水壶/电怎么办呢?
      - 假如加水传感器失效怎么办?
      - 假如煮水传感器失效怎么办?
      - 茶叶放到茶壶里?
      - 茶壶在哪里,如果没有茶壶怎么办?烧水之前我们应该考虑到这些问题吗?
      - 茶叶在哪里,要用哪一种茶叶?我们是否应该先问清楚,或许如果没有对应的茶叶,我们甚至都不应该开始泡茶?
      - 关于加水和传感器也可以有类似的问题要问
      - 倒开水?
      - 你确定水已经开了么?你怎么能确保“倒水”的机器从热水壶那收到“烧水完成”的信号呢?
      - 你如何确保倒水的机器知道热水壶在哪里?
      - 如果热水壶在倒水的过程翻了怎么办呢?

程序员代码提交中的骂声
正如你工作中看到的,写代码会让你骂骂咧咧,经常爆粗口。
另外有数据统计,写 C++ 程序,会比写 PHP 或 Python 程序所遭到的骂声更多。
Andrew Vos在找一个周末项目,于是决定在 GitHub 上抓取100百万条提交信息(commit),并扫描其中的脏话。
Images   

  • 而且程序员最喜欢的一句是:
      “去TMD,咱们就这样发布。

1
2
3
4
let arr = [1, 1, 2, 2]
arr = Array.prototype.slice.call(new Set(arr))
alert(arr)
//output: 1, 2
0%