HiSEN

Personal Technology Blog


  • 归档

  • 分类

  • 标签

  • 书单

  • 关于

  • 搜索
close
HiSEN

mac docker compose timezone 问题解决

发表于 2018-12-21 | 分类于 docker

一、背景介绍

想用docker搭建一个lnmp环境

使用这个脚本:

1
https://github.com/buxiaomo/docker-compose/tree/master/lnmp

执行命令之后报错

1
2
3
4
5
6
7
8
9
10
11
12
13
docker-compose -f lnmp.yml up -d
WARNING: Some services (mysql, nginx, php, redis) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
WARNING: Some services (mysql, nginx, php) use the 'configs' key, which will be ignored. Compose does not support 'configs' configuration - use `docker stack deploy` to deploy to a swarm.
lnmp_mysql_1 is up-to-date
Starting lnmp_redis_1 ...
lnmp_nginx_1 is up-to-date
Starting lnmp_redis_1 ... error

ERROR: for lnmp_redis_1 Cannot start service redis: b'Mounts denied: \r\nThe path /usr/share/zoneinfo/Asia/Shanghai\r\nis not shared from OS X and is not known to Docker.\r\nYou can configure shared paths from Docker -> Preferences... -> File Sharing.\r\nSee https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info.\r\n.'

memory: 100M
ERROR: for redis Cannot start service redis: b'Mounts denied: \r\nThe path /usr/share/zoneinfo/Asia/Shanghai\r\nis not shared from OS X and is not known to Docker.\r\nYou can configure shared paths from Docker -> Preferences... -> File Sharing.\r\nSee https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info.\r\n.'
ERROR: Encountered errors while bringing up the project.

二、报错原因

  1. mac机器的时间路径与linux不一样
  2. lnmp.yml redis用到了这个时间

三、解决办法

  1. 查看当前机器时间文件真实位置(/etc/localtime 这个路径是不让共享给docker的)

    1
    2
    ls -la /etc/localtime
    lrwxr-xr-x 1 root wheel 39 10 19 11:13 /etc/localtime -> /var/db/timezone/zoneinfo/Asia/Shanghai
  2. 设置位置 :Docker -> Preferences… -> File Sharing

  3. 新增共享目录:/var/db/timezone
  4. apply & restart
  5. 修改lnmp.yml配置文件 全局替换:/usr/share/zoneinfo/Asia/Shanghai 为:/var/db/timezone/zoneinfo/Asia/Shanghai

四、成功信息

1
2
3
4
5
6
7
8
docker-compose -f lnmp.yml up -d
WARNING: Some services (mysql, nginx, php, redis) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
WARNING: Some services (mysql, nginx, php) use the 'configs' key, which will be ignored. Compose does not support 'configs' configuration - use `docker stack deploy` to deploy to a swarm.
Removing lnmp_redis_1
Starting lnmp_nginx_1 ... done
Starting lnmp_mysql_1 ... done
Recreating f5b9db566588_lnmp_redis_1 ... done
Starting lnmp_php_1 ... done
HiSEN

定时任务的一点思考 - Java

发表于 2018-10-27 | 分类于 java

一、背景交代

  1. 部署方式:多点
  2. 业务概要:处理一批数据,可以失败重试

二、实现方式

  1. 主表入一条控制数据,两个控制字段:next,process(0:空闲,1:处理中),子表具体处理业务
  2. 定时任务只扫描主表,通过其他业务字段,加上主表两个控制字段(now>netx,process=0),捞出需要处理的数据
  3. 当开始处理数据时候,先更新process=1,如果更新成功,说明拿到锁(假装是一个分布式锁,因为两台机器)
  4. 如果拿到3的锁,则开始处理子表的业务数据,一次查100条(limit 0,100 desc update_time),直到子表全部处理完成
  5. 如果遇到错误,把错误数据的id存起来,更新子表重试次数,和更新时间,如果达到最大失败次数,更改状态(让4查不出这条数据)
  6. 如果遇到当前处理数据的id存在List中,那么说明当前所有的数据都处理过一次了,退出(如果不判断,失败后会无限循环,直到全部成功)

三、伪代码

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
public void process(MainTable mainTable) {

// 1. 锁控制表
boolean lock = service.lock(mainTable);
LOGGER.info("lockFlag:,", lock, ",mainTableId:", mainTable.getId());
if (!lockFlag) {
return;
}
List<Long> repeatIds = new ArrayList<>();
// 2. 根据主表id,下次扫描时间,每次查询100条
List<SubTable> subTables;
do {
//扫描子表中的数据
subTables = service.querySub(mainTable.getId(), 100);
for (SubTable sub : subTables) {
if (repeatIds.contains(batch.getId())) {
subTables = null;
break;
}
// 处理业务
handle(mainTable, subTables, repeatIds);
}
} while (!CollectionUtils.isEmpty(subTables));

// 5. 检查是否全部终态
checkHandleDone(mainTable);
// 7. 释放锁
boolean unLock = service.unLock(mainTable);
LOGGER.info("lockFlag:,", unLock, ",mainTableId:", mainTable.getId());
}
HiSEN

go程序设计语言练习 - 同统计重复 - gop1.io/ch1/dup2/dup2.go

发表于 2018-10-27 | 分类于 go

一直在写java,看语法逻辑什么的没有问题

但是刚刚看书上写出来的这个程序,居然不知道怎么在goland里面运行…

于是曲线救国,了解到打印输入的参数。

这断代码就是为了找出输入数据中的重复行

  1. 直接启动,不带参数,启动之后输入参数

    1
    2
    3
    4
    5
    参数0: /private/var/folders/lk/p8gbq87n6rvfndk7wlk23y8r0000gn/T/___go_build_dup2_go
    1 # 一行输入一个
    1 # 一行输入一个
    ^D #这是command + D
    2 1 # 输出的结果:个数 输入值
  2. 在命令行启动,带

    1
    2
    3
    4
    5
    6
    $ go run dup2.go 'hisen.txt'
    参数0: /var/folders/lk/p8gbq87n6rvfndk7wlk23y8r0000gn/T/go-build525680776/b001/exe/dup2
    参数1: hisen.txt #代码同级目录的文件名
    2 hisen
    2 hisenyuan
    2 123

代码如下:

阅读全文 »

HiSEN

MySQL批量删除重复数据,保留id最小的一条

发表于 2018-10-16 | 分类于 sql

stu数据如下:

idname
1hisen
2hisen
3hisen
4hisenyuan
5hisenyuan

删除重复的name,保留id最小的。

思路就是先找出重复数据,然后再找出需要保留的数据(重复中id最小的)

然后删除id不在需要保留的id中的所有数据

1
2
3
4
delete
from stu
where id not in (select t.minId
from (select min(id) minId from stu group by name having count(name) > 1) t)
HiSEN

Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder

发表于 2018-10-15 | 分类于 java

使用jsonwebtoken出现如下错误

1
2
3
Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder
java.lang.NoClassDefFoundError: Could not initialize class io.jsonwebtoken.impl.DefaultJwtBuilder
at io.jsonwebtoken.Jwts.builder(Jwts.java:116) ~[jjwt-0.7.0.jar:0.7.0]

原因,因为jackson-databindb版本冲突,直接去掉了依赖

jwt必须依赖Jackson所以报错了

出错时候的配置

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
<exclusions>
<exclusion>
<artifactId>jackson-databind</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
</exclusions>
</dependency>

解决办法

1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>

HiSEN

《穿布鞋的马云》 - 部分文字摘录

发表于 2018-09-08 | 分类于 read

《穿布鞋的马云》 2018.10.01 ~ 2018.10.02

国庆假期没有什么安排,看了几部电影之后感觉蛮愧疚,又浪费了大把的时间

于是乎在书架上找了本决定看起来压力不那么大的书来看

这本书感觉整体上写的一般,可能定位就是通俗易懂吧

但是对于了解一些细节,还是很有帮助

由于目前待在创业公司,看到很多文字的时候还是很有感触的

如果你想成功,积极乐观地看待任何问题

以下为摘抄:

阅读全文 »

HiSEN

《Go语言程序设计》 - ch1/lissajous - GIF动画

发表于 2018-09-08 | 分类于 go

一、程序说明

  1. 可以以web的方式查看,也可以生成一个图片
  2. 直接go run输出的是一丢乱码(图片)
  3. 需要go build 然后运行程序(具体看代码里面注释)

具体的内容可以看下面的程序代码(虽然是抄书)

二、程序代码

阅读全文 »
HiSEN

mac安装Go环境 & IDE(GoLand) & 基本运行方法(标准输入)

发表于 2018-09-08 | 分类于 go

一、安装GO

1.1 使用Homebrew安装go环境(如果很慢,可以换个源)

1
brew install go

1.2 查看安装信息

1
go env

主要关注如下输出

1
GOROOT="/usr/local/Cellar/go/1.10.3/libexec" # 安装目录

1.3 配置环境变量

1
vi ~/.bash_profile # 没有的话会新建一个文件

输入如下内容,第一行是安装的目录,第三行是工作目录(可以改成自己喜欢的路径)

1
2
3
4
5
GOROOT=/usr/local/Cellar/go/1.10.3/libexec
export GOROOT
export GOPATH=/Users/hisenyuan/golang
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN:$GOROOT/bin

1.4 让配置文件生效并且查看环境变量

1
2
source ~/.bash_profile
go env # 这时你会发现环境变量已经有改变

二、安装GoLand

我是习惯了用jetbrains的idea

发现它家也有go语言的IDE GoLand

于是就去官网下载,安装,找个注册码,修改一下host防止注册码失效

这里就不再累赘了

三、运行需要输入的程序

买了一本《Go语言程序设计》

阅读全文 »

HiSEN

线上事故记录 - 死锁

发表于 2018-08-09 | 分类于 java

一、背景描述

在支付业务当中,每一笔交易都得进行记账。

两种情况:

  1. 第一步先冻结,交易成功,解冻并扣款(A账户->B账户->C账户);
  2. 第一步先冻结,交易失败,解冻并归还(A账户->B账户->A账户);

上面的两种情况各自都是在一个事物当中。

二、问题描述

在同一个商户进行并发操作的时候,交易有成功有失败;

  1. 成功的时候钱是:A账户->B账户;
  2. 失败的时候钱是:B账户->A账户;

因为在各自的事物当中更新两条记录的信息,并且使用了for update(innodb引擎)
ß
在某一瞬间:成功的先锁A账户,失败的先锁了B记录

接下来就两个事物各自持有对方想要的资源,并且不释放已经占有的资源,就造成了死锁

三、解决方法

在程序里面,更新两个账户的钱的时候,始终先更新ID更小的那条记录,那样不管多少个事务同时进来

都会按照固定的顺序去持有资源,比如先A再B,这样就不会出现各自持有对方想要的资源

1
2
3
ID ACCOUNT
10 A
11 B

每个事务都是先锁定A再锁定B,拿不到锁就一直等待

HiSEN

WebSocket - Java & html & JavaScript - 单发 & 群发

发表于 2018-07-28 | 分类于 java

一、背景说明

最近在做app后台相关接口

自建通知中心目前不能很好的支持给APP推送消息

长连接可以保持推送速度,目前app中内嵌了H5,所以考虑使用websocket

之前没有接触过websocket,百度了一堆之后,页面上可以正常使用

但是没有发现可用使用Java后台进行消息的发送,于是乎就琢磨了一上午,解决了这个问题

现在把这个小工程分享给大家,少走点弯路==

ps:很多不能在后台发送消息,是因为缺少java的客户端

二、准备工作

建立一个maven web 工程

添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>com.neovisionaries</groupId>
<artifactId>nv-websocket-client</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>

三、主要代码

websocket服务端主逻辑

为了实现简单的非群发操作,在连接websocket的时候,加上了一些get参数

例如:ws://localhost:8080/websocket?sendTo=hisen&method=methodSingle&user=hisenyuan

然后在后端判断,根据参数做出不同的动作

demo完整工程:https://github.com/hisenyuan/IDEAPractice/tree/master/websocket-demo

配置完Tomcat,即可使用,在java后台运行测试类(com.hisen.ws.client.ClientApp4Java)可发送消息到页面

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
package com.hisen.ws.server;

import com.hisen.ws.util.Constants;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/websocket")
public class WebSocketServer {
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;

// 实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
// 当前用户
private String user;

/**
* 客户端可以是web页面,也可以是Java后台
* <p>
* 通过连接或者message可以控制发送给谁
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
// 获取url传过来的参数
Map<String, List<String>> parameterMap = session.getRequestParameterMap();
// 发送方式
String method = null;
// 发送给哪些人
List<String> receivers = new ArrayList<>();
// 发送者
String sernder = null;
if (parameterMap.containsKey(Constants.METHOD)) {
method = parameterMap.get(Constants.METHOD).get(0);
}
if (parameterMap.containsKey(Constants.SEND_TO)) {
receivers = parameterMap.get(Constants.SEND_TO);
}
if (parameterMap.containsKey(Constants.USER)) {
sernder = parameterMap.get(Constants.USER).get(0);
}

System.out.println("sender:" + sernder + ",receivers:" + receivers.toString() + ",method:" + method);
if (method == null || method.equals(Constants.METHOD_ALL)) {
//发送所有
send2All(message);
} else {
//单发
send2Users(receivers, message);
}

}

/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
this.user = session.getRequestParameterMap().get(Constants.USER).get(0);
// 放入map
webSocketMap.put(user, this);
//在线数加1
addOnlineCount();
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount() + ",session:" + session.getId() + ",user:" + this.user);
}

/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
// 移除
webSocketMap.remove(this.user);
//在线数减1
subOnlineCount();
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount() + ",user:" + this.user);
}


private void send2Users(List<String> receivers, String message) {
receivers.forEach(e -> {
System.out.println("single receiver:" + e);
Optional.ofNullable(webSocketMap.get(e))
.filter(webSocketServer -> webSocketServer.session.isOpen())
.ifPresent(webSocketServer -> sendOnce(message, e, webSocketServer));
});
}

private void send2All(String message) {
webSocketMap.forEach((key, value) -> {
sendOnce(message, key, value);
});
}

private void sendOnce(String message, String e, WebSocketServer webSocketServer) {
try {
webSocketServer.sendMessage(message);
} catch (IOException exp) {
System.out.println("发送出错,receiver:" + e);
}
}

/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误,user:" + this.user);
error.printStackTrace();
}

/**
* 自定义的方法
*
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}

public static synchronized int getOnlineCount() {
return onlineCount;
}

public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}

public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
1…91011…27
hisenyuan

hisenyuan

Java R & D

266 日志
33 分类
112 标签
GitHub Weibo
Links
  • 科技爱好者周刊
  • 美团技术团队
  • duanple(老师木)
  • 当然我在扯淡(王垠)
  • 段永平的博客
  • 梦殇国际
© 2016 - 2024 hisenyuan
由 Hexo 强力驱动
您是第  个访问者    |   
主题 - NexT.Mist