LittleQ

爱好:写代码

最近发现家里的网连我国外的服务器非常的慢,丢包已经基本上到了访问不了的地步了,所以搭的ss代理基本上只能在公司用.之前花几百快撸的腾讯云服务器放了好几个月都没管过了,正好拿来做中继代理.之前买了半年的阿里云服务器搞过,后来过期了没有续费了,这次撸了6年的应该够用了.

准备工作

其实就是运营商的国际出口比较堵,所以直接访问国外的服务器丢包严重,当然实测应该还有一个原因,应该是运营商主动干扰这些代理服务器.个人感觉后面的可能性要大一些,不过不管是哪种都不重要了,要实现低延迟链路,只能借助大厂的线路(阿里,腾讯),基本原理也很简单:

  • 直连方案:
    1
    local(A) --> sserver(C)
  • 中继方案:
    1
    local(A) --> aliyun/cloud tencent(B) --> sserver(C)

安装配置

主要是在中继机器B上操作,sserver(C)不用做任何变更,首先登录我们的中继机器(B):

1
sudo yum install haproxy -y

然后就是配置我们的机器了,编辑sudo vim /etc/haproxy/haproxy.cfg,直接改成下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
global
ulimit-n 51200

defaults
log global
mode tcp
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000

frontend ss-in
bind *: {B_local_listen_port}
default_backend ss-out

backend ss-out
server sserver_name {sserver_ip}:{sserver_port} maxconn 20480

这里需要设置的几个地方为:

1
2
{B_local_listen_port}:这个就是中继服务器监听外部请求的端口,为了你local改动小,这个端口可以设置和sserver(C)的端口一致
{sserver_ip}:这个就很好理解了,就是你部署sserver的机器的ip,后面那个端口也是ss服务器的端口

等这些都改完了,然后还需要做一件事,就是给你的中继服务器开安全组策略,阿里和腾讯的云主机基本上是一样的,开入站和出战规则即可,然后把设置好的安全组规则绑定到你的机器实例上.简单来说就是需要对{B_local_listen_port}开入站规则,对{sserver_port}开出战规则,如果你分不清觉得麻烦,就把这两个端口设置的一样,然后开一个端口的出站和入站规则即可.

服务启用

由于你改了安全组规则,所以需要重启你的服务器实例,重启好了之后,需要启动haproxy服务,不同的机器不一样,以我的腾讯云主机为例:

1
2
3
4
5
systemctl start haproxy.service
# 设置成开机启动(CentOS7)
systemctl enable haproxy.service
# 设置成开机启动(CentOS6,没测试过,网上查的)
chkconfig –level 3 haproxy.service on

启动服务之后可以检测一下服务是否开启了,直接用ps命令即可.

然后就是本地local机器,即sslocal的配置,因为我们上面配置的中继监听端口和sserver的端口一致,所以我们的sslocal只用改一个ip,端口不用改,ip由原来的sserver ip改为中继服务器的ip就行了.延迟由原来的300ms直接降为40ms,收工.

一直想整一下博客的评论系统,以前听说多说比较有名气.但是当我想搞的时候发现多说居然关闭了,找了一圈发现除了Gitment这个Github自家的东西比较靠谱,所以就折腾了一下,期间碰到不少问题.

安装Gitment

安装模块

在你的blog根目录安装

1
npm i --save gitment

申请应用

首先去New OAuth App为你的博客应用一个密钥:

1
2
3
4
Application name:随便写
Homepage URL:这个也可以随意写,就写你的博客地址就行
Application description:描述,也可以随意写
Authorization callback URL:这个必须写你的博客地址

申请好之后点注册,然后就可以看到两个东西ClientIDClient Secret,后面会用到.

配置

下面就是配置Gitment,主要编辑在themes/next/_config.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Gitment
# Introduction: https://imsun.net/posts/gitment-introduction/
gitment:
enable: true
mint: true # RECOMMEND, A mint on Gitment, to support count, language and proxy_gateway
count: true # Show comments count in post meta area
lazy: false # Comments lazy loading with a button
cleanly: false # Hide 'Powered by ...' on footer, and more
language: # Force language, or auto switch by theme
github_user: {you github user id}
github_repo: 随便写一个你的公开的git仓库就行,到时候评论会作为那个项目的issue
client_id: {刚才申请的ClientID}
client_secret: {刚才申请的Client Secret}
proxy_gateway: # Address of api proxy, See: https://github.com/aimingoo/intersect
redirect_protocol: # Protocol of redirect_uri with force_redirect_protocol when mint enabled

开通评论

注意到这里基本上已经OK了,再看你的博客应该可以显示评论了.不过每篇博客都需要你手动初始化评论功能(如果你的历史博客很多那就一篇一篇去点吧,不过貌似有人写了批量处理脚本,没试过哈).

问题

  • Error: Validation Failed

issue的Label有长度限制,对于中文博客来说,中文标题很容易就超过长度限制,所以需要做一下特殊处理,修改themes/next/layout/_third-party/comments/gitment.swig:

1
2
3
4
var gitment = new {{CommentsClass}}({
id: '{{ page.date }}',
owner: '{{ theme.gitment.github_user }}',
repo: '{{ theme.gitment.github_repo }}',

主要是那个id改一下,一般而言你写博客不可能同一时间创建两份博客,所以这个一般而言是不会重的.
**NOTE:**这一需要特别强调一下缓存问题,必须清除浏览器缓存,否则会一直报Not Found,具体表现就获取issue的地址里面有undefined,这个折腾了差不多一天才搞定.

  • object ProgressEvent

出现这个问题是由于作者在gitment.browser.js中硬编码了自己的服务器人证域名:https://gh-oauth.imsun.net,现在域名过期了,要么自己搭一个认证服务器,要么可以用其他人的,在gitment的issues里面找到了答案:#175,需要把Hexo你所用的主题中的引用文件给改一下,我用的是next主题,改动方法如下:
修改themes/next/layout/_third-party/comments/gitment.swig

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- LOCAL: You can save these files to your site and update links -->
{% if theme.gitment.mint %}
{% set CommentsClass = "Gitmint" %}
<link rel="stylesheet" href="https://aimingoo.github.io/gitmint/style/default.css">
<script src="https://aimingoo.github.io/gitmint/dist/gitmint.browser.js"></script>
{% else %}
{% set CommentsClass = "Gitment" %}
<!-- <link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css"> -->
<!-- <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/theme-next/theme-next-gitment@1/default.css">
<script src="https://cdn.jsdelivr.net/gh/theme-next/theme-next-gitment@1/gitment.browser.js"></script>
{% endif %}
<!-- END LOCAL -->

注释的部分是原来的地址,后面的是引用的网友的解决方案地址,需要注意的是确保themes/next/_config.ymltheme.gitment.mint设置为false,才会走到我们改动的分支.

之前Hexo博客一直是使用的Jacman主题,用久的有点儿审美疲劳,最近看上比较简洁的主题Next,视觉上确实要好看很多,配色简洁看着比较舒服.

安装

首先进入到你的博客的根目录

1
2
➜  Blog git:(master) ✗ 
➜ Blog git:(master) ✗ git clone https://github.com/theme-next/hexo-theme-next themes/next

配置

接下来就是把主题配置成Next,修改博客根目录下的配置文件_config.yml:

1
2
3
4
# Extensions
## Plugins: http://hexo.io/plugins/
## Themes: http://hexo.io/themes/
theme: next

然后启动博客就可以看到效果了.

修改next样式

不过默认的博客样式不是很好,一些标签页展示的不是很好,所以还需要改下样式.进入themes/next文件夹,修改_config.yml

修改导航菜单

1
2
3
4
5
6
7
8
9
menu:
home: / || home
archives: /archives/ || archive
categories: /categories/ || th
tags: /tags/ || tags
about: /about/ || user
#schedule: /schedule/ || calendar
#sitemap: /sitemap.xml || sitemap
#commonweal: /404/ || heartbeat

修改了这几个之后还不够,需要创建这几个的导航页面(home导航页为根目录不需要创建):

创建归档页面

创建页面

1
hexo new page categories

修改内容:

1
2
3
4
title: 分类
date: 2018-05-14 23:34:12
type: "categories"
---

创建标签页

创建标签页

1
hexo new page tags 

修改内容:

1
2
3
4
title: 标签
date: 2018-05-14 23:36:18
type: "tags"
---

个人主页

创建个人主页

1
hexo new page about

修改内容:

1
2
3
title: 个人简介
date: 2018-05-14 23:38:55
---

切换主题布局

可以看到_config.yaml文件中默认是使用的Muse主题,这个主题是把标签之类的放到顶部,我更喜欢双栏布局,所以把对应部分改成下面这样:

1
2
3
4
5
# Schemes
#scheme: Muse
#scheme: Mist
scheme: Pisces
#scheme: Gemini

设置语言

默认语言在~/Blog/next/languages下面:

1
2
3
4
➜  next git:(master) ✗ ls -l languages/zh-*
-rw-rw-r-- 1 anonymous anonymous 2100 5月 15 10:47 languages/zh-CN.yml
-rw-rw-r-- 1 anonymous anonymous 2094 5月 15 10:47 languages/zh-HK.yml
-rw-rw-r-- 1 anonymous anonymous 2094 5月 15 10:47 languages/zh-TW.yml

默认zh-CN.yml就已经给我们映射好了,只需要把博客设置成对应的语言,修改~/Blog/_config.yml:

1
2
3
# Site
language: zh-CN
timezone: Asia/Chongqing

GitComment

如果你配了用GitComment来作为你的博客评论,那你还需要改下对应得配置,不然评论都不会显示了,详情可以参见:Hexo 使用Gitment评论功能

做数据的写代码多了,会经常碰到传输文件的需求,之前还好,一般是下载文件,直接用python内置的server起个服务就搞定了.但是对于跨机房有防火墙存在的情况,一般数据是单向的,就是假设(A–>B)A作为HTTPServer,B可以下载文件.但是反过来就不好使了,因为防火墙策略没有开,(B–>A)用B作为HTTPServer,A无法访问到服务.所以这个时候,A仍然得作为服务端,主要有两种不同的方式.

  • nc传输文件

1.Data Transfer模式:A(sender/client)->B(receiver/server)
数据Transfer模式简单来说就是在家等着收数据,可以理解为被动模式.所以receiver监听的是本机的端口,然后等着sender会把数据发送到这个地方.

1
2
A(sender): tar -zcvf - file/directory | nc {B_IP} 12345
B(receiver): nc -l 12345 | sudo tar -zxvf -

PS:receiver端先启动,然后启动sender发送数据.如果是想B->A传输,对调一下就行,注意服务的开启顺序。不管是哪种情况,要确保sendreceiver的网络是通的,即数据可以发送到接收端。验证方式也比较简单,可以在接收端运行python -m SimpleHTTPServer 12345,然后在发送端执行telnet {B_IP} 12345

2.Data Take模式:A(sender/server)->B(receiver/client)
数据的Take模式和Transfer有一点不大一样,可以理解为主动模式.就是你得自己去指定机器上主动取数据.所以sender会把数据发送到本机指定端口,receiver从指定机器以及端口获取数据

1
2
A(sender): nc -l 12345 < file/directory
B(receiver): nc {A_IP} 12345 > file/directory

PS:sender端先启动,然后启动receiver接收数据.注意和第一种方式区分

上面两种传输方式虽然有一点不大一样,不过有一个共同点就是:server一定要先启动,然后才是client端才启动.但是不管是哪种方式,只要记住一点,就是clientserver提供服务端port一定是通的,比如说本机可以访问服务器指定端口的服务,但是服务器就无法访问本机指定端口服务,所以不管是想从服务器拷贝数据还是发送数据到服务器,服务器只能是server.

  • 配合pv使用

pv我就不介绍是啥了,这个小工具也非常的好用,因为一般传输大文件的时候,我们希望看到传输进度啊,速度啊之类的,还有一个很重要的功能就是限速,尤其是专线跨机房问题.
A:pv -p -r -L 10m heap.bin | nc -l 9099
B:nc {A_IP} 9099 > heap.bin
常用参数:

1
2
3
-p, --progress           show progress bar
-r, --rate show data transfer rate counter
-L, --rate-limit RATE limit transfer to RATE bytes per second

  • 单引号双引号输出

1.双引号:

1
awk '{print "\""}'

使用“”双引号把一个双引号括起来,然后用转义字符\对双引号进行转义,输出双引号.

2.单引号:

1
awk '{print "'\''"}'

使用一个双引号“”然后在双引号里面加入两个单引号‘’,接着在两个单引号里面加入一个转义的单引号\',输出单引号.

  • 字符串操作

1.取子串

1
substr(String, M, [N])

2.分割域访问
NF代表域个数,$NF最后一个,$(NF-1)倒数第二个
字符串,数字比较直接<=;>=;==这样

1
-F'xxx'用xxx分割

3.分割结果保存到数组
($()),例如想将ls -l的文件列表分割出来:

1
file_list=($(hadoop fs -ls /user/hive/warehouse/xxx.db | grep user | awk -F' ' '{print $NF}'))

这个就是获取hdfs上指定库目录的所有表名,然后就可以for遍历

1
2
3
for table in ${file_list[@]};do
echo $table
done

首先不要参考官网的例子,pyhs2的作者已经不再更新了,所以你如果用官网的例子,多半会由于版本问题而搞出各种莫名奇妙的问题,这里推荐一下使用dropbox推出的PyHive,PyHive Github地址

官网配置貌似不大对,所以可以直接参考我的这个,起码我是本地测试通过的,环境:

1
2
3
4
5
OS: Ubuntu 16.04 64bit
JDK: 1.7.40
Hadoop: 2.7.3
Hive: 2.1.0
Python: 2.7.12

开启hiveserver2

这个地方官方说的不是很好,导致后面按官网配置可能依然导致无法连接hiveserver2,下面说下我的步骤,首先hive-site.xml里面的默认配置都不用改,除非你的端口被占用或者你想指定端口.
重点是需要改Hadoop的配置文件${HADOOP_HOME}/etc/hadoop/core-site.xml文件,我们假设你需要用dev这个用户来访问hiveServer2服务,那么需要在core-site.xml`文件里面加上下面的配置:

1
2
3
4
5
6
7
8
9
<property>
<name>hadoop.proxyuser.dev.groups</name>
<value>*</value>
</property>

<property>
<name>hadoop.proxyuser.dev.hosts</name>
<value>*</value>
</property>

如果你这里不写这个,那么很可能也连接的时候会报错,提示你没有权限,然后其他的默认就行,不过确保你的hive-site.xml文件里面的配置是下面这样:

1
2
3
4
<property>
<name>hive.server2.enable.doAs</name>
<value>true</value>
</property>

然后可以开启hiveServer2服务

1
bash /workspace/dev/apache-hive-2.1.0-bin/bin/hiveserver2

常见错误

当配置好hiveserver之后,执行简单的select可能没有啥问题,但是像有一些稍微复杂的sql可能会失败.

  • hiveserver 执行简单的count报错
1
2
3
4
Exception in thread "LeaseRenewer:anonymous@localhost:9000"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "LeaseRenewer:anonymous@localhost:9000"
Exception in thread "HiveServer2-Handler-Pool: Thread-49"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "HiveServer2-Handler-Pool: Thread-49"

解决方案:修改hadoop配置文件core-site.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
<property>
<name>mapreduce.map.memory.mb</name>
<value>1024</value>
</property>

<property>
<name>mapred.child.map.java.opts</name>
<value>-Xmx900M</value>
</property>

<property>
<name>mapreduce.map.java.opts</name>
<value>-Xmx900M</value>
</property>

<property>
<name>mapreduce.reduce.memory.mb</name>
<value>2048</value>
</property>

<property>
<name>mapred.child.reduce.java.opts</name>
<value>-Xmx1900M</value>
</property>

<property>
<name>mapreduce.reduce.java.opts</name>
<value>-Xmx1900M</value>
</property>

简单来说就是内存溢出,所以更改下默认的map,reduce阶段的一些内存设置.

搞一下爬虫,装了一下scrapy报错:

1
2
3
4
5
6
7
8
build/temp.linux-x86_64-2.7/_openssl.c:434:30: fatal error: openssl/opensslv.h: No such file or di
rectory
compilation terminated.
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

----------------------------------------
Failed building wheel for cryptography
Running setup.py clean for cryptography

具体报错就是上面那个:

fatal error: openssl/opensslv.h

解决方法很简单:

1
sudo apt-get install libssl-dev

最近在研究怎么通过Flux来解析日志,前前后后踩了很多坑,毕竟貌似网上也没人这么干过,写一个通用化的代码,只通过配置文件来定义流计算任务.基本上写好了代码,以后再有新的任务都不用再写java代码,然后打包上传提交任务了.
开始集群上的jstorm是2.1.1的,并不支持flux方式的提交任务,所有升级了一下到2.2.1版本,最后跑了一下官方的word-countdemo成功了.
然后开发好了通用的jar包来解析日志,简单来说就是:

  • CommonKafkaSpout
    给构造函数传入指定topic来从kafka读取日志数据

  • CommonBolt
    传入解析规则,基本上就是正则表达式,细节不多说了,解析出<key,value>

  • Write2ESBolt
    把上游的内容写入到es中,构造函数也需要传入es的配置,基本上就是index,type这些

所有的基本走通了之后,开始测试的时候Topology是用java代码写的,在集群上测试之后没啥问题,然后改用yaml文件来定义jstorm任务,结果提交到集群就有问题了,十分的奇怪,照理说word-countdemo跑通了应该没啥问题了,但是涉及到真正的实战需要连接kafka,es就出问题了,报错:

1
2
3
java.lang.NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor
类似的还有
NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.directExecutor conflits on Elastic Search jar

然后google了很久,发现这个并不是es的问题,是guava的问题,原文在

http://stackoverflow.com/questions/20791351/java-lang-nosuchmethoderror-com-google-common-util-concurrent-moreexecutors-sam

Check your classpath and see what version of guava is being used by your WAR. The error suggests that the version of guava jar being found at runtime does not match the version that was used at compile time.

简单一句话总结就是,jstorm集群上的guava版本和我们写的公共jar包里面的guava版本不一致.然后我去集群上的lib包上看了一下,发现jstorm 2.1.1用的guava是16.01版本的,于是我把集群上所有的机器里面的guava换成了20.0的,然后重启了集群,然后重新提交任务,果然就好使了,从kafka读取日志以及写入es也没问题了,任务终于不报错了.

前后端交互数据的时候,经常需要前端把数据提交给后端,前端一般就用ajax提交数据,就像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
$.ajax({
type: "post",
url: "pos_url.do",
contentType: "application/json",
data: JSON.stringify(params),
success: function (resp) {
xxxx;
},
error: function (resp) {
xxxx;
}
});

然后后端采用spring的代码如下:

1
2
3
4
5
@RequestMapping(value = "post_url", method = RequestMethod.POST)
@ResponseBody
public String postURL(@RequestBody JsonParam[] params) {

}

然后就是JsonParam这个类的定义:

1
2
3
4
5
public class JsonParam implements Serializable {

private String name;

private String value;

这样的话是可以从后端接受参数的,但是后来觉得这个JsonParam定义的不是很好,所以就把name改成了key,结果就报错了400 bad request,不知道为啥,连请求都报错了,更别说参数的接收和解析了,网上查资料,找到一个答案提示了我:

Verify the following things:
Whether the name of JSON matches the User class’s field name.
Also check whether JSON value is supported by the corresponding field name datatype in User class.
Do try it, I have faced the same issue numerous times.

然后我才明白,之前我自定义的Json类的两个属性就是name,value,然后改成key,value就报错了,所以错误的根源应该就是前端传过来的数据是name=xxx,value=xxx的数据格式,然后后端想按key=xxx,value=xxx这样来接收解析,然后就报400 bad request这个错误了,如果直接使用request来接收应该就没有这个问题了,但是参数和参数的值就得自己解析了.

具体的详细策略可以看spring源码,大概就是需要实现一些转化器来实现http-->bean之间的互转

Spring中需要用到spring的事务回滚,注解方式是最简单易用的,一开始发现spring的事务没有起作用,最后查了很多资料,最后发现是配置文件中没有添加事务注解的配置,所以在spring的配置文件里面加了下面这行:

1
<tx:annotation-driven  transaction-manager="transactionManager" />

结果发现之前可以运行的项目突然不能运行了,涉及到事务注解的service全部无法注入

一般出现这个问题都会去网上查查是不是自己用的注入方式不对,有一些人还建议使用@Resource注解替换@Autowried,我试过时候发现还是不行.后来网上查了一下,其实很多人这么说都是人云亦云,很多人都不知道为什么,就知道@Resource是java自带的注解,所以比@Autowried靠谱,真的是这样吗?其实这个通过查资料就很容易知道并不是这样的:

  • @Autowire

默认按照类型装配,默认情况下它要求依赖对象必须存在如果允许为null,可以设置它required属性为false,如果我们想使用按照名称装配,可 以结合@Qualifier注解一起使用;

  • @Resource

默认按照名称装配,当找不到与名称匹配的bean才会按照类型装配,可以通过name属性指定,如果没有指定name属 性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找 依赖对象.

所以说,正常情况下你可以理解为没啥区别,如果非要说后者比前者的优势,那就是减少外部依赖,因为@Autowried是spring提供的.

但是我还是照着网上说的,既然注入失败,那肯定是bean的名字还有类型找不到,所以我尝试强制指定Service的名字:

1
@Service("serviceName")

然后在Controller里面注入的时候使用@Resource注解指定名字:

1
@Resource(name = "serviceName")

结果奇迹出现了,还是报错,不过这个错误和之前不一样了,关键信息如下:

1
but was actually of type [com.sun.proxy.$Proxy17

这句话什么意思呢?意思是bean的名字是找到了,但是这个类型不符合,所以注入依然失败,总而言之就是我们要注入的Service的类型变成了com.sun.proxy.$Proxy17这个类型了,这是个啥类型?然后我网上查了很久,最后终于在stackoverflow上找到了答案,原来很多人都碰到了这个问题,原帖地址

http://stackoverflow.com/questions/841231/fixing-beannotofrequiredtypeexception-on-spring-proxy-cast-on-a-non-singleton-be

摘一段Spring官方的文档:

Applies to proxy mode only. Controls what type of transactional proxies are created for classes annotated with the @Transactional annotation. If “proxy-target-class” attribute is set to “true”, then class-based proxies will be created. If “proxy-target-class” is “false” or if the attribute is omitted, then standard JDK interface-based proxies will be created. (See the section entitled Section 6.6, “Proxying mechanisms” for a detailed examination of the different proxy types.)

大概翻译一下就是:是否只采用代理模式来做事务管理,并且有个关键字class-based proxies,这样就不会有类型不匹配的了.
而且这个作者的这个问题也是由于他使用的事务注解而导致的,和我的这个问题非常相像,核心意思是这个注解默认是使用系统的代理模式,即com.sun.proxy这个里面的类,但是一般spring的项目都是使用的cglib,aspectj这类的库来做代理的默认实现,所以导致了以上的这种问题,那么解决方法就是将最开始的配置改为:

1
<tx:annotation-driven  transaction-manager="transactionManager" proxy-target-class="true"/>
0%