CDN源站与搜索引擎收录

问题

在Google上进行搜索时发现除了正常的域名blog.gelu.me,CDN源站cdn-blog.gelu.me也被收录进去。

使用的是阿里云CDN,当时的配置是

  • 源站类型为源站域名,cdn-blog.gelu.me
  • 回源host也是该值
  • 源站服务器上nginx配置了server_name: cdn-blog.gelu.me

解决

进行了如下修改

  • 源站类型改为IP,值为服务器IP地址
  • 回源host改为blog.gelu.me,即加速的域名,不填即为加速域名(未测试)
  • 源站服务器上nginx配置修改为 server_name: blog.gelu.me

配置完成后问题解决。

总结

CDN配置的关键流程

  • 域名解析绑定CDN的CNAME
  • 配置源站类型为IP,填写服务器的IP地址
  • 如果服务器上部署了多个网站(仅通过IP无法定位数据)时,配置回源Host为要加速域名,当然加速域名在源站服务器要能访问才行(如nginx配置server_name之类的)

此时请求到CDN且需回源时,CDN会根据IP找到源站服务器,源站服务器会根据携带来的回源host定位到对应网站,从而返回数据。

Spring Boot使用Gson替换Jackson

参考网上的一些测评,得知Gson对小文件处理比较快,Jackson处理大文件比较好,而系统主要处理小文件请求,因此打算使用Gson替换默认的Jackson。
注意项目使用Maven进行项目管理,依赖的版本号均在parent POM或import POM中维护,因此下面的Maven配置无版本号。

通常情况(排除jackson依赖)

在依赖中排除所有jackson相关依赖, 如:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>

注意所有jackson-core,jackson-databind依赖均需要排除,可使用 mvn dependency:tree查看项目依赖。
依赖排除完成的同时加入gson依赖。

1
2
3
4
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>

查看spring-webmvc.jar中WebMvcConfigurationSupport.java源码

1
2
private static final boolean jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", WebMvcConfigurationSupport.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", WebMvcConfigurationSupport.class.getClassLoader());
private static final boolean gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", WebMvcConfigurationSupport.class.getClassLoader());
1
2
3
4
5
6
7
8
9
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
...
if(jackson2Present) {
objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build();
messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper));
} else if(gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
}
}

因此系统找不到jackson依赖时,Spring MVC便会使用GsonHttpMessageConverter而不是MappingJackson2HttpMessageConverter。

特殊情况(无法排除jackson依赖)

若无法排除jackson依赖(如其他依赖需要jackson),参考上面的源码得知Spring MVC在jackson和gson依赖都存在的情况下优先使用MappingJackson2HttpMessageConverter,因此需要代码中进行调整。

1
2
3
4
5
6
7
8
9
10
@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(httpMessageConverter -> httpMessageConverter instanceof MappingJackson2HttpMessageConverter); // 删除MappingJackson2HttpMessageConverter
converters.add(new GsonHttpMessageConverter()); // 添加GsonHttpMessageConverter
}
...
}

总结

以上便是我在Spring Boot中使用Gson替换Jackson的相关配置,同样的仅使用Spring MVC而未使用Spring Boot配置也类似。

Restful接口定义

CRUD分别对应于HTTP方法:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

即:

GET:从服务器取出资源(一项或多项)。

POST:在服务器新建一个资源。

PUT:在服务器更新资源(客户端提供完整资源数据)。

PATCH:在服务器更新资源(客户端提供需要修改的资源数据)。

DELETE:从服务器删除资源。

其中GET,PUT,DELETE为幂等

1
2
3
4
5
6
7
8
9
10
11
12
13

GET /movie 获取电影列表,返回电影列表

GET /movie/1 获取标志为1的电影,返回单个电影

POST /movie 添加电影,返回新生成的电影

PUT /movie/1 提供完整电影数据,更新后会返回完整的电影

PATCH /movie/1 提供部分要修改的数据,更新后会返回完整的电影

DELETE /movie/1 删除标志为1的电影,返回空文档

postgresql常用命令与查询

配置

修改最大连接数(Ubuntu, postgresql版本9.5)
postgresql配置文件 /etc/postgresql/9.5/main/postgresql.conf

1
max_connections = 100(默认100, 修改后需重启postgres)

常用命令

1
2
-- 重启
sudo service postgresql restart

常用查询

1
2
3
4
5
6
7
8
- 查看postgresql的连接数
select * from pg_stat_activity;

- 查看最大连接数限制
show max_connections;

- 查看为超级用户保留的连接数
show superuser_reserved_connections;

firewalld常用命令

添加开放的端口

1
firewall-cmd --permanent --add-port=5000/tcp

删除开放的端口

1
firewall-cmd --permanent --remove-port=5000/tcp

刷新

1
firewall-cmd --reload

查看

1
firewall-cmd --list-all

工作小记1

就上次的问题消息系统顺序问题,考虑到修改消息系统模块风险大,太多其他模块使用了该模块(由于系统技术债务,系统并没提供扩展写法,对该模块也没有足够的单元测试),最后没有就该思路走下去。
原来的业务问题是APP端有两次请求(一先一后),后台要求有顺序,否则可能执行不成功。至于为什么原系统是这样我就不知道了…两次请求会执行一些简单的逻辑,然后第二次请求会通过消息系统调用后面的服务,此时若第一次请求没有执行完,后面的服务就报错。
最后采取的方案是将操作2(包含参数)存入数据库,然后写定时任务再去调用,从而保证操作2在后面执行。
解决方案不优雅,但也算能解决当前的业务问题。重构好像要提上进程,希望能还些债务。

我的2016

始于一个微信电商项目的快速开发上线。
然后随公司一位老板换了个办公场所专心于一个项目。
7月去上海发展,公司是做Saas类型的企业服务,我也从在小公司要承担各种事务过渡到了只要专心开发,一下轻松了很多。
又经历了从开始的一个轻松的项目组被带到更要费心的另一个组。
工作技能方面也有了进一步的提升。
生活方面没什么值得说的,长进的怕是只有年龄。

设计模式总结

创建类模式

结构型模式

  • 适配器模式
  • 装饰器模式
  • 代理模式
  • 外观模式
  • 桥接模式
  • 组合模式
  • 享元模式

行为型模式

  • 模板方法模式
    模板方法模式
  • 策略模式
  • 观察者模式
  • 责任链模式
  • 迭代子模式
  • 命令模式
  • 备忘录模式
  • 状态模式
  • 访问者模式
  • 中介者模式
  • 解释器模式

其它

  • 并发型模式
  • 线程池模式

git fetch 与 git pull 区别

当远程主机版本库与本地版本库不一致时,可以使用git fetch将远程的更新取回到本地。
git fetch会取回所有分支更新,也可更新指定分支,如:

1
2
git fetch <远程主机名> <分支名>
git fetch origin develop

上面是取回远程develop分支。

其中 使用git remote可查看远程主机。

而git pull是取回远程分支再与本地分支合并。

1
2
$ git pull <远程主机名> <远程分支名>[:<本地分支名>]
$ git pull origin develop:branch1

上面是取回远程分支develop并且与本地branch1分支合并,若与当前分支合并则本地分支名部分可省略,如:

1
2
# 当前分支为branch1,则下面的写法和上面命令效果相同
$ git pull origin develop

git pull相当于是先git fetch再git merge,如:

1
2
$ git fetch origin
$ git merge origin/develop

若本地远程有单一追踪关系,还可省写为:

1
$ git pull

简单的说 git fetch仅将远程仓库的更新取回到本地仓库,不影响当前代码,而git pull除了取回外还会与workspace代码合并,影响当前代码。

单例模式常用写法

方法一:懒汉

在调用取得实例的方法时才会实例化对象

1
2
3
4
5
6
7
8
9
10
public class Singleton {  
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

方法二:饿汉

在单例类被加载时候完成实例化

1
2
3
4
5
6
7
public class Singleton {  
private static final Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

方法三:双重校验锁定

懒汉升级版,JDK1.5后生效,毕竟getInstance上的synchronized多数情况都没有用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {  
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

方法四:枚举

1
2
3
4
5
public enum Singleton {  
INSTANCE;
public void mothed() {
}
}

方法五:静态内部类

1
2
3
4
5
6
7
8
9
public class Singleton {  
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}