MySQL datetime 与时区

问题

最近碰到这样的问题:jdbc 插入 timestamp 类型的数据到 MySQL 的 datetime 类型列时,小时会变成 12 小时制了。比如 2017-04-11 15:24:32 变成 2017-04-11 03:24:32

一开始以为是默认时区设置导致,后面发现不是,在 MySQL client 命令行里直接插入是不存在这样问题的。

如果是时区问题,跟本地时间相差不会是 12 小时的。

绕过方法:
1. 直接使用 MySQL 的 now() 函数;
2. 在 Java 里把 timestamp 转换为 yyyy-MM-dd HH:mm:ss 格式字符串,然后用 MySQL 的 convert 函数进行转换。

时区

CET, Central European Time,欧洲中部时间:比世界标准时早一个小时。冬季时间为 UTC+1,夏季欧洲夏令时为 UTC+2

UTC, Coordinated Universal Time, 世界标准时间或世界协调时间:

GMT, Greenwich Mean Time, 格林尼治标准时间。

CST, China Standard Time, 中国标准时间。

CST 同时可以代表如下 4 个不同的时区:
Central Standard Time (USA) UT-6:00
Central Standard Time (Australia) UT+9:30
China Standard Time UT+8:00
Cuba Standard Time UT-4:00

关系:
CET = UTC/GMT + 1小时
CST = UTC/GMT + 8小时
CST = CET + 9小时

show variables like '%time_zone%';
set time_zone='+8:00';

欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

《Netty in action》 第三章 Netty 组件和设计

第三章 Netty 组件和设计

从高层视角,Netty address 两个等价的关注域:技术和架构
* 首先,它是构建于 Java NIO 上的异步、事件驱动的实现,保证了在高负载下最大的应用性能和伸缩性;
* Netty 体现了一组设计模式,从网络层解耦应用逻辑,简化开发,最大化可测试性、模块化、代码重用。

当我们更细微地学习 Netty 的独立组件时,我们将聚焦于它们是如何协作来支持这些架构最佳实践。通过遵循同样的原则,我们将获得 Netty 能提供的所有好处。

3.1 Channel, EventLoop, ChannelFuture

Channel, EventLoop, ChannelFuture 放到一起,可以代表了 Netty 的网络层抽象:

  • Channel: Sockets
  • EventLoop: 控制流,多线程,并发
  • ChannelFuture: 异步通知

3.1.1 Channel 接口

基本的 I/O 操作(bind(), connect(), read(), write()) 依赖于底层网络传输的提供的原子操作。在基于 Java 的网络里,基础结构是 Socket 类。Netty 的 Channel 接口提供的 API 极大简化了直接操作 Sockets 的复杂工作。

3.1.2 EventLoop 接口

EventLoop 定义了 Netty 处理一个连接生命周期里发生的事件的核心抽象。Channel、EventLoop、EventLoopGroup 之间的关系如下:

  • 一个 EventLoopGroup 包含一个或多个 EventLoop;
  • 一个 EventLoop 在它的生命周期里是绑定到单一线程的;
  • 一个 EventLoop 处理的所有 I/O 事件都是在它的专用线程上进行的;
  • 一个 Channel 在它的生命周期里是注册到到单一的 EventLoop;
  • 单一的 EventLoop 可能被赋给一个或多个 Channel。

注意,在这种设计里,给定 Channel 的 I/O 事件都由同一个线程来执行,事实上消除了对同步的需要。

继续阅读

基于数据库的乐观锁

涉及涉及数据库的高并发解决方案一般都会提到乐观锁。

乐观锁:在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。

利用数据库实现乐观锁的伪代码:

Connection conn = DriverManager.getConnection(url, user, password);
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
// step 1
int oldVersion = getOldVersion(stmt);

// step 2
// 用这个数据库连接做其他的逻辑

// step 3 可用预编译语句
int i = stmt.executeUpdate(
        "update optimistic_lock set version = " + (oldVersion + 1) + " where version = " + oldVersion);

// step 4
if (i > 0) {
    conn.commit(); // 更新成功表明数据没有被修改,提交事务。
} else {
    conn.rollback(); // 更新失败,数据被修改,回滚。
}

注意事项:

  • 前面伪代码的 step 3 和 4 应在存储过程里完成,防止 step 3 完成后,由于 GC 或网络阻塞导致 step 4 延迟执行,此时 乐观锁的记录被数据库锁定,其他请求要进行更新只能等待,被阻塞。
    可以用存储过程来替代后面的两步,或者整个逻辑都用存储过程实现:
delimiter $$

create procedure sp_update_version (newVersion int, oldVersion int) begin
    update optimistic_lock t set t.version = newVersion where t.version = oldVersion;

    if row_count() > 0 then
        commit;
    else
        rollback;
    end if;
end $$

乐观锁的缺点:

  • 会带来大数量的无效更新请求、事务回滚,给DB造成不必要的额外压力。
  • 无法保证先到先得,后面的请求可能由于并发压力小了反而有可能处理成功。

欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

基于Redis实现分布式锁

用单实例的正确实现

命令:set resource_name my_random_value NX PX timeout_millis,在 key resource_name 不存在时(NX选项的作用)设置这个key 的值为 my_random_value、超时时间设为 timeout_millisPX选项的作用)。

删除key的时候用 Lua 脚本来检测key的值是否为 my_random_value,是才允许删除;需要保证这个值在所有客户端里唯一;(借助 Lua 实现一个 CAS 操作)

一些注意点与问题

  • 一个分布式锁必须设置超时时间,否则一旦某个客户端获取锁成功后与 Redis 无法通信,那么它会一直持有这个锁,其他客户端无法获得锁。
    这个过期时间叫做锁的有效时间(lock validity time),客户端必须在这个时间内完成对资源的访问操作。

  • 设置key与超时时间必须在一个命令里完成。如果客户端在设置了key后、设置超时时间前崩溃,那么它会一直持有这个锁。

  • 释放锁时必须检查key对应的值是否与自己持有的一致(通过 Lua 实现CAS),防止误删其他客户端持有的锁。防止出现:客户端A准备释放持有的锁时,检测到值与自己持有的一致,然后由于某种原因被长时间阻塞,锁的超时时间到达后客户端B获取了锁,此时客户端A执行不检查key值的删除操作就会破坏了锁。

继续阅读

2016 年终总结

一、工作

离开顺丰科技

在顺丰待满了刚好两年,选择离开。很感谢顺丰科技,在那里获得一段不错的经历。

加入友金所

离开顺丰后加入了友金所的资产端团队,即友金普惠。友金普惠的贷前贷后的管理系统是从一个厂商购买的,受限很大,进去后主要做一些新系统搭建、业务逻辑重写。

刚进去不久,因为一些定时任务出问题,其实是由于 Quartz 使用不当导致,基本上把 Quartz 关于并发控制的源码都看了遍。

做了个规则引擎,这个引擎是由规则+数据组成,规则由业务人员在网页上自己配置,数据主要是对接的合作方的数据、去第三方网站搜索的数据。

为了第三方网站搜索统一起来,对搜索的流程进行了封装,只要通过配置就可以直接把搜索结果转化为特定的 POJO。为了把网页信息提取为 POJO,基于 WebMagic 做了个组件来基于 XML 配置信息完成这个提取。

其他的主要是一些业务逻辑开发、重写,比如记账、扣款的逻辑等。

二、学习

虽然 RSS 订阅、微信公众号每天看,但这些都不是系统性的、零散,收获不多。

继续阅读

发布组件到 Maven 中央仓库

折腾了几天,终于把一个小组件MyBatis-batch 发布了中央仓库,做个笔记记录下。

  1. 注册 sonatype JIRA 帐号并配置 settings.xml
    我是先在 sonatype 上发布,然后由 sonatype 自动同步到中央仓库的。首先要在 sonatype 注册一个 JIRA 帐号,

$M2_HOME/conf/settings.xmlservers 标签下添加如下配置:

<server>
    <id>sonatype-nexus</id>
    <username>sonatype 登录名</username>
    <password>sonatype 密码</password>
</server>
  1. 在 github 添加 ssh key
    Maven 构建的时候,会自动操作 github,比如创建 tag 。
    用 ssh-keygen 生成一对秘钥,在 https://github.com/settings/keys 页面可以添加 SSH key。

继续阅读

Java finalize 方法与垃圾回收

JVM 对于覆写了 Object.finalize() 方法的类是实例,在垃圾回收时,先放进一个队列里,
然后用一个线程执行这些实例的 finalize 方法,最后才做内存回收。

这个机制的问题是:它会影响垃圾回收,如果实现了 finalize 的实例很多,那么回收队列就会很长,而只有一个线程
在执行这些方法,这些对象就会回收得很慢。

最近碰到的一个问题跟 finalize 方法有关。
厂商提供的代码把 javax.sql.Connection 做了层封装,覆写了 finalize 方法,在里面把 Connection 关闭了。

一个同事调用的方法定义是用封装类作为入参,他调用的时候是直接 new 了一个封装类,把 connection 传进去,后续数据库操作就不定位置出现连接关闭的异常。原因就是这个临时 new 出来的封装类被垃圾回收,把 connection 关闭了。由于垃圾回收的时间是不确定的,所以报异常的时间点也不确定。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

接口与Spring自动注入

接口与自动注入

有业务接口 IService 和两种业务逻辑的实现 Aservice, Bservice 如下:

public interface IService {}

@Component
public class Aservice implements IService {}

@Component
public class Bservice implements IService {}

采用下面的方式自动注入时,会报错:

public class ManageService {
    @Autowired
    private Aservice aservice;

    @Autowired
    private Bservice bservice;

    // ...
}

异常信息类似为:nested exception is java.lang.IllegalArgumentException: Can not set xxx.ManageService field xxx.ManageService.aservice to com.sun.proxy.$Proxy48

继续阅读

SonarQube 配置

1. 安装 SonarQube 和数据库

下载最新的 sonarqube-6.1、MySQL 5.7.X。

创建数据库:

CREATE DATABASE `SonarQube` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

CREATE USER SonarQube IDENTIFIED BY 'SonarQube#123';

GRANT ALL PRIVILEGES ON SonarQube.* TO SonarQube;
flush privileges;

配置 sonarqube 服务器, $sonarqube_home/conf/sonar.properties 文件:

sonar.jdbc.username=SonarQube
sonar.jdbc.password=SonarQube#123

sonar.jdbc.url=jdbc:mysql://localhost:3306/SonarQube?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance

sonar.web.host=0.0.0.0
sonar.web.port=9000

继续阅读

《程序员必读之软件架构》–笔记

架构是什么

架构作为名词解释时,概括起来都与结构有关:将产品分解为一序列组件、模块和交互。

架构作为动词来解释时,包括了理解你需要构建什么、设定愿景以便进行构建和做出恰当的设计决策。所有这些都以需求为基础,因为需求驱动架构。关键在于,架构是关于交流愿景以及引入技术领导力的,这样参与构建产品的每个人都能理解这个愿景,并为产品的成功做出积极贡献。

不论何种领域的架构,其实主要就是结构和愿景。

架构分类

应用程序架构的关注点是应用程序,通常包括将应用程序结构为类和组件,确保设计模式正确应用,构建或使用框架,等等。本质上,应用程序架构谈论的是软件设计的低级别切面,通常只考虑单一的技术栈。应用程序架构着重考虑软件和代码组织。

系统架构是更大规模的应用程序架构。大多数软件系统实际上是由横跨不同层次和技术的多个应用程序组成。系统架构还关注互操作性和与环境中其他系统的集成。系统架构描述为从组件和服务到子系统等高层次的抽象。系统架构的定义大多数还包括了软件和硬件。

软件架构就是应用程序和系统架构的结合。从代码结构和基础到将代码成功部署到生产环境,与一个软件系统重要元素相关的所有东西就是软件架构。

企业架构一般是指整个组织的中心工作,着眼于如何组织和利用人员、流程和技术来使企业有效和高效地工作。它是关于企业如何分成组或部门,业务流程如何在这上层运作,以及技术如何支撑这一切。

好的架构带来敏捷。

软件架构角色

架构 VS. 设计

作为名词,设计是指一个系统内命名的结构或行为,解决或有助于月解决该系统的一个或多个问题。因而,设计代表了潜在的决策空间中的一个点。

所有架构都是设计,但并非所有设计都是架构。

架构反映了使一个系统成型的重要设计决策,而重要性则通过改变的成本来衡量。

尽管“重要决策”没法彻底消失,但能通过架构分层等多种策略来改变。软件系统架构流程的一部分就是搞清楚哪些是重要的及为什么。

思考软件架构的好处

  • 让团队跟随一个清晰的愿景和路线图,无论这个愿景是一个人所有还是整个团队共有;
  • 技术领导力和更好的协调;
  • 与人交流的刺激因素,以便回答与重要决策、非功能需求、限制和其他横切关注点相关的问题;
  • 识别或减轻风险的框架;
  • 方法和标准的一致性,随之而来的结构良好的代码库;
  • 正在构建的产品的坚实基础;
  • 对不同的听众,以不同层次的抽象来交流解决方案的结构。

C4:语境、容器、组件和类

c4: 语境 容器 组件 类
软件的静态视图:

  • 语境:设定场景的高层次图,包括关键的系统依赖和参与者。

    语境图帮助回答下面的问题:

    1. 我们构建的(或已经构建的)软件系统是什么?
    2. 谁会用它?
    3. 如何融入已有的 IT 系统?
  • 容器:荣企图显示了高层次的技术选择,容器如何分担职责、如何通信。

    1. 软件系统的整体形态是什么样的?
    2. 高层次技术决策有哪些?
    3. 职责在系统中如何分布?
    4. 容器之间如何相互交流?
    5. 为了实现特性,作为一个开发者,我需要在哪里写代码?
  • 组件:组件图可以让你看到每个容器的关键逻辑组件及之间的关系。

    1. 系统由哪些组件/服务组成?
    2. 在高层次上,系统如何工作是否清晰?
    3. 所有组件/服务都有一个家吗(即驻留在一个容器中)?
  • 类:可选的细节层次。

原则

约束通常是强加于你的,而原则是你为了将一致性和清晰度引入最终代码库而想采用的原则(如编码规范、自动化测试等)或架构的原则(如分层策略,架构模式等)。

%e5%85%b3%e6%b3%a8%e7%82%b9


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。