编程


jar命令是用来将java编译产生的.class文件打包成jar包的工具。
Jar包可以方便地对外发布,甚至就像exe一样易于使用。

这里以一个工具类JarParser为例,这个示例程序是用来遍历Jar包中的类的。

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
/*JarParser.java*/
/*源码转载自:https://blog.csdn.net/lululove19870526/article/details/78837119*/

import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class JarParser {

public static void main(String args[]) {
if(args.length<1) System.exit(0);
String jar = args[0];
try {
JarFile jarFile = new JarFile(jar);
Enumeration enu = jarFile.entries();
while (enu.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) enu.nextElement();
String name = jarEntry.getName();
if (name.endsWith(".class"))
System.out.println("Class: "+name.replace('/','.'));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

使用javac命令编译这个Java文件:javac JarParser.java

接着创建一个文本文件,这个文件用来作为jar包中的MANIFEST.MF描述文件。

1
echo Main-Class: JarParser > test.mf

这句Main-Class的作用是指明Jar包的入口类。

最后使用jar命令打包:

1
jar cvfm JarParser.jar test.mf JarParser.class

生成的jar包使用java -jar命令运行。



系统环境:

  • Ruby: ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
  • Rails: Rails 5.2.2
  • Gem: 2.7.7
  • SQLite: sqlite3 (1.4.0, 1.3.13)

今天在基于上面的环境使用Rails新建项目的时候,报了一个ActiveRecord::ConnectionNotEstablished错误。
根据排查,是由于SQLite3 gem版本问题导致的。
具体的报错内容如下:

1
Puma caught this error: Error loading the 'sqlite3' Active Record adapter. Missing a gem it depends on? can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0. Make sure all dependencies are added to Gemfile. (LoadError)

页面报错如下:

1
2
ActiveRecord::ConnectionNotEstablished
No connection pool with 'primary' found.

后来找到了解决方法,修改Gemfile,将sqlite3的版本降级到1.4.0以下即可。
修改Gemfile:

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
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.5.1'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.2'
# Use sqlite3 as the database for Active Record
gem 'sqlite3', '< 1.4.0' # <<修改这里,将sqlite3版本设定为小于1.4.0
# Use Puma as the app server
gem 'puma', '~> 3.11'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'mini_racer', platforms: :ruby

# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use ActiveStorage variant
# gem 'mini_magick', '~> 4.8'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
gem 'listen', '>= 3.0.5', '< 3.2'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
# Easy installation and use of chromedriver to run system tests with Chrome
gem 'chromedriver-helper'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

修改完后执行bundle即可。



你在MongoDB启动时也遇到以下警告吗?

1
2
3
WARNING: You are running on a NUMA machine.
We suggest launching mongod like this to avoid performance problems:
numactl –interleave=all mongod [other options]

这是当MongoDB运行在非统一内存访问架构 NUMA(Non Uniform Memory Access Architecture)下产生的性能告警。
关于NUMA是什么,可以参考这篇文章:Non-uniform memory access

这里主要提一下在CentOS 7系统、NUMA架构服务器中上述告警该如何消除。

首先需要使用numactl这个命令,而这条命令服务器中默认是没有的,在支持NUMA架构的CentOS7服务器中,可以运行yum install numactl来安装它。

接着关闭MongoDB服务:
systemctl stop mongod

disable掉MongoDB服务的自动启动:
systemctl disable mongod

使用numactl来启动MongoDB:
numactl --interleave=all mongod --config /etc/mongod.conf

如果想要开机自动使用numactl启动MongoDB,将上述命令写到开机启动的脚本文件中即可。

笔者尝试修改mongod.service服务描述中的ExecStart选项,尝试将numctl作为mongod服务的执行体时报了很多错,如果你有相关的解决方法,欢迎来信或在评论里探讨。



笔者有一台CentOS 7系统的Linux服务器,在通过ssh远程连接的时候,一直存在两个问题。
一是连通速度缓慢,ping服务器速度很快,但是SSH连接的响应非常慢。
再有就是连接一段时间,客户端这边如果没有操作和输入,服务器就会自动断开,每次都要重新连,很麻烦。

昨天晚上仔细检查了一下CentOS 7 sshd的配置,终于解决了这两个问题。其实很easy,修改CentOS默认的sshd配置就行了。

针对第一个ssh连接响应缓慢的问题,在/etc/passwd文件里这样配置就行了:

大概是在第129行的位置,找到UseDNS选项,取消前面的注释,将yes改为no即可:

1
2
3
4
5
6
7
127 #ClientAliveCountMax 3
128 #ShowPatchLevel no
129 UseDNS no
130 #PidFile /var/run/sshd.pid
131 #MaxStartups 10:30:100
132 #PermitTunnel no
133 #ChrootDirectory none

针对第二个连接超时timeout的问题,同样是修改sshd配置,大概在第126行的位置,找到选项ClientAliveInterval,将其值修改为30:

1
2
3
4
5
6
123 UsePrivilegeSeparation sandbox          # Default for new installations.
124 #PermitUserEnvironment no
125 #Compression delayed
126 ClientAliveInterval 30
127 #ClientAliveCountMax 3
128 #ShowPatchLevel no

这样ssh服务器就会每隔30秒判断一次客户端是否超时,由于30秒一般是不会超时的,所以连接就能持续。
而第127行的选项也可以关注一下,它代表的是最大的超时次数。

完美修复文章开头提到的两个问题。



Python 3 通过SMTP库发送普通邮件(Through SSL)

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
#!/usr/bin/python3

import smtplib
from email.header import Header
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

mail_host = "smtp.chorder.net"
mail_port = 994
mail_user = "example@chorder.net"
mail_pass = "password"

sender_name = "Example"
sender_account = 'example@chorder.net'
receivers = ['someboby@chorder.net']
receiver_name = "Somebody"

subject = 'Mail Subject'
mail_msg = '''
<h1>This is a test main</h1>
<p>Some text</p>
'''


msgRoot = MIMEMultipart('related')
msgRoot['From'] = Header(sender_name, 'utf-8')
msgRoot['To'] = Header(receiver_name, 'utf-8')
msgRoot['Subject'] = Header(subject, 'utf-8')

msgAlternative = MIMEMultipart('alternative')
msgAlternative.attach(MIMEText(mail_msg, 'html', 'utf-8'))

msgRoot.attach(msgAlternative)

try:
smtpObj = smtplib.SMTP_SSL( mail_host, mail_port )
#smtpObj.ehlo()
#smtpObj.set_debuglevel(1)
smtpObj.login( mail_user, mail_pass )
smtpObj.sendmail(sender_account, receivers, msgRoot.as_string())
print ("邮件发送成功")
except smtplib.SMTPException:
print ("Error: 无法发送邮件")

Python 3 通过SMTP库发送带图片的邮件(Through SSL)

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
#!/usr/bin/python3

import smtplib
from email.header import Header
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

mail_host = "smtp.chorder.net"
mail_port = 994
mail_user = "example@chorder.net"
mail_pass = "password"

sender_name = "Example"
sender_account = 'example@chorder.net'
receivers = ['someboby@chorder.net']
receiver_name = "Somebody"


subject = 'Mail Subject'
mail_msg = '''
<h1>This is a test main</h1>
<p>Some text</p>
<h2>Image</h2>
<p><img src="cid:image1"></p>
'''

# add image attachment
fp = open('test.png', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()

msgImage.add_header('Content-ID', '<image1>')

msgRoot = MIMEMultipart('related')

msgRoot['From'] = Header(sender_name, 'utf-8')
msgRoot['To'] = Header(receiver_name, 'utf-8')
msgRoot['Subject'] = Header(subject, 'utf-8')

msgAlternative = MIMEMultipart('alternative')
msgAlternative.attach(MIMEText(mail_msg, 'html', 'utf-8'))

msgRoot.attach(msgAlternative)
msgRoot.attach(msgImage)

try:
smtpObj = smtplib.SMTP_SSL( mail_host, mail_port )
#smtpObj.ehlo()
#smtpObj.set_debuglevel(1)
smtpObj.login( mail_user, mail_pass )
smtpObj.sendmail(sender_account, receivers, msgRoot.as_string())
print ("邮件发送成功")
except smtplib.SMTPException:
print ("Error: 无法发送邮件")


Docker镜像仓库在国内访问速度巨慢无比,感谢DaoCloud公司提供的稳定加速
镜像,具体的配置方法如下:

1. 修改 /etc/docker 目录下的daemon.json

如果没有/etc/docker这个目录或者/etc/docker目录下没有daemon.json文件,手动创建即可。
将daemon.json的内容修改如下:

1
2
3
{
"registry-mirrors": ["http://f1361db2.m.daocloud.io"]
}

2. 重启Docker服务

运行这两个命令:

1
2
systemctl daemon-reload
systemctl restart docker


Ruby有着便捷强大的Gem包管理机制,作为五十年后人类使用最多的编程语言,有必要提前普及一下Ruby Gem的编写制作教程,帮助大家更好的使用Ruby,使用Gem,最好也能让Ruby的Gem库更加丰富,社区能够更加活跃。那么如何从零开始制作和发布自己的Gem呢?一起来动手操作一下。

制作和发布Ruby Gem包一共分如下几个步骤:

  1. 在RubyGems上注册帐号(如果你只想在本地或团队内部使用你的Gem,可以跳过这一步)
  2. 编写和打包Gem(gem build xxx.gemspec)
  3. 测试Gem(mang自mu信者可以跳过这一步)
  4. 发布Gem(gem push xxx)

1. 注册RubyGems帐号

RubyGems.ORG 是一个大型的Gems仓库,你可以现在就加入它,贡献你的Gem。这一步想必无需多说,注册完成之后,你就拥有了属于自己的RubyGems帐号,别人就可以看到你的主页和你发布的Gems。

注册完成之后,你还需要将你的crendentials保存到本地,方便后续直接在命令行使用gem命令。运行

1
curl -u 你的用户名 https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials

2. 编写和打包Gem

编写Gem

我们这次要发布的Gem叫做template_chorder,你也可以改名成任意你想要的名字。首先我们创建一个工程目录,我的目录名叫做GemTemplate,其中包含如下内容:

其中,template.gemspec是项目的描述文件,gem build命令会基于这个文件进行打包。

template.gemspec内容如下:

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

Gem::Specification.new do |s|
s.name = 'template_chorder'
s.version = '0.0.0'
s.date = '2019-01-11'
s.summary = "Template!"
s.description = "A simple template gem"
s.authors = ["Chorder"]
s.email = 'chorder@chorder.net'
s.files = ["lib/template.rb"]
s.homepage = 'https://chorder.net/2019/01/11/rubygemtemplate/'
s.license = 'MIT'
end

其中s.name就是将来这个包发布出去的名字。默认的,我们需要将工程的主代码放在lib目录里面,这是ruby的约定。我们的Gem只包含一个很简单的功能,只需要放置全部代码在一个文件当中即可。

lib/template.rb的代码如下:

1
2
3
4
5
class Template
def self.test
puts "Hello, Ruby!"
end
end

就是这么简单。

打包Gem

在工程的根目录,运行

1
gem build template.gemspec

工程根目录将会产生一个 template_chorder-0.0.0.gem文件。
就是这么简单。

3. 测试Gem

对于刚刚生成的template_chorder-0.0.0.gem,运行

1
gem install template_chorder-0.0.0.gem

来安装它。

安装好以后,就可以在irb解释器中引入和运行它了。完整的过程如下:

疑问
这里其实是有点疑问的,就是为什么gem的名字叫做template_chorder,而在irb中引入的却是template。确实,如果gem包中的文件取名不严谨的话,可能会与其他包产生冲突。至于如何消除冲突,请君自己先思考下。

卸载Gem包,运行 gem uninstall template_chorder

2. 发布Gem

制作好这样一个不负责任的Gem包以后,就可以厚颜无耻地在RubyGems上发布它。但是为了社区的环境卫生清洁,尽量还是不要发布那些粗制滥造的包,就以此为戒吧。
发布Gem包,运行 gem push template_chorder-0.0.0.gem,如果第1步当中的帐号注册和保存
crendentials文件都没有问题,那么包就可以发布出去了。

本文的包发布在:https://rubygems.org/gems/template_chorder

希望能够对你有所帮助。



关于多线程与多进程的区别此处就不再赘述了。
Python3中已经具备了非常完善的多线程与多进程的相关库,可以非常容易的实现程序多进程与多线程的功能。

示例代码如下:

多线程示例

multithread.py
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

import os
import time
import threading


def test_proc(name):
print( "Run child process %s(%s)" % (name,os.getpid()) )
time.sleep(10)


class MultiThread( threading.Thread ):

def __init__(self,thread_name):
threading.Thread.__init__(self)
self.thread_name = thread_name

def run(self):
test_proc("test")


threads = []
for i in range(1,10):
thread = MultiThread( "Thread-%s" % i )
thread.start()
threads.append( thread )

for t in threads:
t.join()

运行结果:

多进程示例

multiprocess.py
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

import os
import time
import multiprocessing


def test_proc(name):
print( "Run child process %s(%s)" % (name,os.getpid()) )
time.sleep(10)

if __name__=="__main__":
print("Process start %s" % os.getpid())

processes = []

for i in range( 0, 10):
p=multiprocessing.Process( target=test_proc, args=("Process-%s" % i,) )
p.start()
processes.append(p)


for p in processes:
p.join()

print( "Main process end.")

运行结果:



使用aapt工具获取apk的信息,可以使用

1
aapt dump badging xxx.apk

如果没有aapt,使用apt安装:

1
apt-get install aapt

写个脚本批量获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash 

target=$1
if [ -d $target ]
then
for apk in `ls $target/*.apk`
do
app_name=`aapt dump badging $apk | grep 'application: label' | awk -F "'" '{print $2}'`
echo $apk $app_name
done
elif [ ${target#*.} == "apk" ]
then
app_name=`aapt dump badging $target | grep 'application: label' | awk -F "'" '{print $2}'`
echo $target $app_name
else
echo $target "nOt aN aPk fiLe"
fi

脚本下载链接:

get_apk_name


前言

随着技术的发展,近年很多的网站、移动应用程序、微信小程序等等,采用前后端分离、使用JSON API数据传递这种方式来设计和开发。如此,前端程序可以保持独立、并且相对来说比较完整,后端程序逻辑复杂、性能强劲。前端可以有丰富的对数据的展现方式(图表、动画等等),后端也只需要完善相应的对模型的控制,同时输出一份前端所需要的数据,采用Ajax+JSON方式传递,从而让前后端的开发相对而言都更为轻松,在结构合理、技术完备的情况下,工程层面也能实现较大程度的规模上的突破。

Rails作为一种流行的MVC框架,本身在API的开发上已经有了很成熟的配套设施了。Rails的模板支持.json.jbuilder形式的JSON数据渲染,配合jbuilder从模型对象数组直接创建JSON数据,在开发上有着无与伦比的便利。在此之前,如果使用php开发程序,从数组创建JSON,需要先整理好数组,再json_encode(Array)进行输出,很是麻烦。当然身为PHP程序员的你可能要说,PHP也有很强大的API构建框架,可以很方便的构建出健壮的API,是的,我承认有这些框架的存在,只是限于本人的水平和喜好,在PHP开发方面并没有走的那么深远,也就不熟悉其中的一些高级操作了。如果有兴趣,也可以发邮件就这方面进行一些探讨。

近期由于一个项目的需要,需要从已有的Rails应用中开辟出一个新的独立接口,供客户端程序调用,因此学习了一下Rails API的构建方法,也顺便留下此文,作为记录。关于Rails API的构建,我参考了这篇文章 https://blog.csdn.net/li_001/article/details/65937664

以下就是如何使用Rails构建JSON API

我当前采用的环境是 Ruby 2.4.1 + Rails 5.2.0

配置控制器

首先假设我们的应用已经创建好了,并且建立一个Article模型,包含两个字段,title和content,运行迁移并创建种子数据。

此时进入项目根目录,新建一个控制器:

1
rails g controller api/v1/base --no-assets

这个控制器的代码就会位于app/controllers/api/v1/base_controller.rb,我们需要在其中完善一些权限有关的配置。

由于Rails默认的安全属性,以及我们如果之前自定义了一些控制器中的过滤方法,我们需要在该控制器中跳过它,例如我们首先禁用rails自带的CSRF 保护和CSRF token的输出,以及去除掉返回包中的set-cookies头,由于我的应用还使用了devise作为用户认证的模块,所以还要在此禁用掉authenticate_user方法。
完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Api::V1::BaseController < ApplicationController
# disable the CSRF token
protect_from_forgery with: :null_session

# disable cookies (no set-cookies header in response)
before_action :destroy_session

# disable the CSRF token
skip_before_action :verify_authenticity_token

# skip devise user authentication
skip_before_action :authenticate_user!

def destroy_session
request.session_options[:skip] = true
end
end

创建好base控制器以后,我们需要在这个控制器的基础上派生出一些新的控制器,这里我们新建一个articles控制器

1
rails g controller api/v1/articles --no-assets

此时除了会生成控制器代码以外,还会在app/views/api/v1目录下创建articles目录,作为模板存放的目录。

articles控制器的代码位于app/controllers/api/v1/users_controller.rb,内容如下

先在其中定义一个show方法:

1
2
3
4
5
class Api::V1::ArticlesController < Api::V1::BaseController
def show
@article = Article.find(params[:id])
end
end

配置路由

接下来我们对路由config/routes.rb进行配置

1
2
3
4
5
namespace :api do
namespace :v1 do
resources :articles
end
end

配置视图

articles控制器的视图代码位于app/views/api/v1/articles/,我们在其中建立show.json.jbuilder文件,内容如下:

1
2
3
json.article do
json.(@article, :id, :title, :content)
end

上面这段代码,是jbuilder建立json的语句代码,至于jbuilder的用法,可以参考这篇文章http://ohcoder.com/blog/2015/04/19/jbuilder/

访问接口

启动Rails程序,访问/api/v1/articles/1.json,即可看到接口输出的JSON数据。

接口通信

除了从接口获取JSON数据以外,我们还需要向接口POST JSON数据,并希望接口实现数据的解析和保存。
首先像往常一样,需要在控制器中创建create方法,用于新建数据:

1
2
3
def create
@host = Host.create(article_params)
end

呵呵,一行代码,是的,就这么简单。
而article_params,则是个私有方法,大概需要这样实现:

1
2
3
def article_params
params.require(:article).permit(:title, :content)
end

在客户端(或网页中)向该接口发送数据,地址是/api/v1/articles

发送的数据类似下面这样:

1
2
3
4
5
6
{
"article":{
"title":"test",
"content":"a test article from client"
}
}

直接发送这条数据可能会出错,因为在请求的时候需要带上json格式描述

1
2
Content-Type: application/json
Accept: application/json

完整的POST请求,参考下:

1
2
3
4
5
6
7
8
9
10
11
POST /api/v1/articles HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Accept: application/json

{
"article":{
"title":"test",
"content":"a test article from client"
}
}


Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×