微信小程序开发问题总结(1)

微信小程序开发问题总结(1)

项目外包研发小伙伴开发的小程序,遇到2个问题解决不了:

  1. 某同事Z的小米6手机,打开小程序,在选择法院界面,搜索输入任何内容都没有搜索结果。周围其他同事手机上没遇到问题。
  2. 某同事C的华为mate手机,进入法院选择页面,选择法院后跳转回首页,选择的结果未刷新。周围其他同事手机上能正常。

问题1

同样的程序,周围其他人手机上正常,说明可能跟环境有关。

远程调试,跟踪代码的时候发现,代码写法不严格,很多语句结尾没有加封号。相关的js文件里,把代码的结尾封号都加上,再远程调试,就好了。

原因推测:微信针对不同的Android手机平台,编译了不同版本的js引擎。

总结:虽然js语法宽松,但是为了避免出现莫名其妙的问题,代码写法需要保持严格。这是个低级错误。

问题2

首页(/homepage/homepage)相关代码:

1
2
3
4
5
onLoad: function(options) {
// ...
var courtName = wx.getStorageSync('courtName');
// 更新首页中已选择法院输入框的值
}

法院选择页(/court/court)相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bindcourt: function (e) {
var courtName = e.currentTarget.dataset.court; //选中的法院名称
var courtId = e.currentTarget.dataset.cid; //选中的法院ID
wx.setStorageSync('courtName', courtName);
wx.setStorageSync('courtId', courtId);
wx.switchTab({
url: '../homepage/homepage',
success: function (e) {
var page = getCurrentPages().pop();
if (page == undefined || page == null) return;
page.onLoad();
}
});
}

代码说明:在法院选择页,选中了法院之后,将选择结果保存到本地存储(小程序即使退出,下次进入也要默认使用最后一次的选择结果,所以存储)。然后调用switchTab返回首页,switchTab的success回调中,调用getCurrentPages()获取当前页,然后调用onLoad进行主动刷新。

上述代码有几个问题:

  1. 在switchTab的回调中去调用getCurrentPages(),可能会有同步问题,不能保证得到的页面就是目标页面。
  2. getCurrentPages().pop() 修改了页面栈,可能引发未知问题。
  3. 首页的onLoad是事件函数,一个页面只会调用一次。

解决方法:

  1. 首页在onShow事件中进行页面的刷新处理。
  2. 移出swtichTab中的回调处理代码。

感悟

编程习惯也是非常重要的技能,不好的编程习惯,误用的方法会引入莫名其妙的问题。

用Google的Jib构建springboot项目docker镜像

Jib

Jib 是google开源的用于对java项目进行容器化打包的工具。主要特点:

Whereas traditionally a Java application is built as a single image layer with the application JAR, Jib’s build strategy separates the Java application into multiple layers for more granular incremental builds. When you change your code, only your changes are rebuilt, not your entire application. These layers, by default, are layered on top of a distroless base image. For more information, check out the official blog post or watch this talk (slides).

传统的java项目打包方式,是将整个java应用相关的资源打包到一个层(layer)。Jib的构建策略是,将java应用的相关资源拆分成多个层(比如:依赖的jar和构建生成的class分层),这样rebuild的时候可以更快,因为可能只需要重新生成部分层的内容。

配置

Jib提供了maven和gradle插件,本文主要介绍 jib-maven-plugin 的使用方法。
将以下内容复制到maven项目的pom.xml中:

1
2
3
4
5
6
7
8
9
10
11
12
13
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>0.9.8</version>
<configuration>
<from>
<image>openjdk:8-jre-alpine</image>
</from>
<to>
<image>lf/${project.artifactId}:${project.version}</image>
</to>
</configuration>
</plugin>

说明:

  • from.image 指定使用的带java环境的基础镜像
    • 如上的配置是到registry.hub.docker.com下载,这里选择openjdk8的jre-alpine作为基础镜像
    • 未指定时默认值 gcr.io/distroless/java,这个是在google的服务器上,一般人连不上
    • 如果有内部的registry服务或mirror,可以这样指定:{registry-host}:5000/library/openjdk:8-jre-alpine
  • to.image 指定你要生成的镜像tag,镜像名称是 lf/${project.artifactId}
    • 可以去除”${project.version}”,这样版本默认就是latest

如果from.image使用的registry服务不支持https,需要修改配置如下(使用内部registry mirror服务):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>0.9.8</version>
<configuration>
<!-- 允许使用http与registry连接 -->
<allowInsecureRegistries>true</allowInsecureRegistries>
<from>
<image>172.16.14.73:5000/library/openjdk:8-jre-alpine</image>
</from>
<to>
<image>lf/${project.artifactId}</image>
</to>
</configuration>
</plugin>

构建

生成镜像tar

配置好之后,打开shell窗口,执行如下命令即可自动构建生成镜像的tar文件

1
mvn compile jib:buildTar

镜像tar默认生成路径:target\jib-image.tar

自动构建镜像到本地docker

如果你本地装有docker环境,且daemon已启动,那么执行如下命令,可自动将镜像构建到本地的docker daemon

1
mvn compile jib:dockerBuild

注意:docker命令路径需要配置到系统的path目录,否则Jib会找不到

构建镜像并上传registry

如果想构建镜像后自动上传到registry,可以执行如下命令:

1
mvn compile jib:build

由于to.image未指定registry服务器地址,默认将上传到registry.hub.docker.com。

为了上传到内部的registry,需要在to.image上指定registry服务地址,如果服务开启了权限验证,还需要配置上有push权限的账号密码。以下以使用docker官方的registry镜像搭建的服务为例,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<allowInsecureRegistries>true</allowInsecureRegistries>
<from>
<image>172.16.14.73:5000/library/openjdk:8-jre-alpine</image>
</from>
<!-- 修改的配置 -->
<to>
<image>{registry.mydomain.com}:5000/lf/${project.artifactId}</image>
<auth>
<username>{my_username}</username>
<password>{my_password}</password>
</auth>
</to>
</configuration>
</plugin>

在maven package时自动构建镜像

增加配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<!-- 增加的配置 -->
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>dockerBuild</goal>
</goals>
</execution>
</executions>
<!-- 之前的配置 -->
<configuration>
......
</configuration>
</plugin>

配置好之后,执行如下命令就会自动构建镜像到本地docker

1
mvn package

配置容器参数

jvm参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<container>
<jvmFlags>
<jvmFlag>-Xms512m</jvmFlag>
<jvmFlag>-Xmx1024m</jvmFlag>
<jvmFlag>-XX:+UseParNewGC</jvmFlag>
<jvmFlag>-XX:+UseConcMarkSweepGC</jvmFlag>
<jvmFlag>-XX:+DisableExplicitGC</jvmFlag>
<jvmFlag>-Xdebug</jvmFlag>
</jvmFlags>
</container>
<!-- 之前的配置 -->
......
</configuration>
</plugin>

java程序入口mainClass

Jib默认会自动扫描寻找main class,如果你的项目中有多个main class,就需要显示声明:

1
2
3
4
5
6
7
8
9
10
11
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<container>
<mainClass>mypackage.MyApp</mainClass>
</container>
<!-- 之前的配置 -->
......
</configuration>
</plugin>

容器端口 EXPOSE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<container>
<ports>
<port>6565</port>
<port>8080</port>
</ports>
</container>
<!-- 之前的配置 -->
......
</configuration>
</plugin>

镜像创建时间

Jib默认不设置镜像创建时间,这样就是1970-01-01。如果要镜像创建时间,则增加配置如下

1
2
3
4
5
6
7
8
9
10
11
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<container>
<useCurrentTimestamp>true</useCurrentTimestamp>
</container>
<!-- 之前的配置 -->
......
</configuration>
</plugin>

镜像格式

构建的镜像默认是Docker格式,还可以指定为OCI格式

1
2
3
4
5
6
7
8
9
10
11
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<configuration>
<container>
<format>OCI</format>
</container>
<!-- 之前的配置 -->
......
</configuration>
</plugin>

如何用ab对webservice进行性能测试

模拟发送ws(webservice)请求

ws协议是http上的xml,所以发送ws请求,http请求体内必须是ws格式的xml。

使用工具 wizdl - Web Service GUI Test Tool,可以比较方便的生成你的ws应用的请求xml。

红框标识出来的部分就是发送的ws请求xml,把这段xml保存到一个xml文件中(wstest.xml)。

用curl验证ws的请求xml

为了验证我们生成的ws请求xml是否有效正确,可以使用curl来测试。

curl --data @wstest.xml http://172.16.24.215:81/example/ws/demoService?wsdl | xmllint --format -

说明:

  • 把ws的xml作为HTTP POST的请求数据:–data DATA HTTP POST data (H)
  • xmllint 命令对curl返回的xml进行格式化

使用ab进行ws性能测试

ab的帮助


相关的ab参数已经用红框标出,我们需要把wstest.xml直接作为HTTP的数据:

ab -n 100000 -c 20 -k -p wstest.xml -T "application/x-www-form-urlencoded" -m POST http://172.16.24.215:81/example/ws/demoService?wsdl
参数说明:

  • ‘-n 100000’ 发送10万次请求
  • ‘-c 20’ 使用20个并发(20个用户,绝对并发)
  • ‘-k’ 启用keep-alive
  • ‘-p wstest.xml’ 把wstest.xml文件的内容作为HTTP POST的数据
  • ‘-T’ 设置POST数据的Content-type
  • ‘-m POST’ 使用POST方法

ab测试结果

PS:测试的demoService方法里边执行了sleep(5)。

git处理文件换行符

[TOC]

有时我们会遇到跨平台开发的情况,在windows上开发同时支持linux和windows部署的程序。
那么我们的程序中可能就会有平台相关的一些文件,比如shell脚本(bat、sh)。
如果没有正确处理好这些文件的换行符,将会导致脚本文件在对应的平台上不能正确执行。

对于使用Git管理源码的项目,git对text文件有自动转换换行符的机制,相关的配置有:

  • git config core.autocrlf
  • git config core.safecrlf
  • git config core eol
  • .gitattributes

windows和linux换行符的区别

  • 在linux系统中,行尾使用一个换行符(LF=line feed)表示
  • 在windows系统中,行尾使用回车(CR=carriage return)+换行(LF=line feed)2个字符表示

git config core.autocrlf

可选的配置值有:

  • true 提交文件时自动转换为LF,检出文件时自动转换为CRLF。在windows平台上,这个是git的默认设置。
  • input 提交的时候自动转换为LF,检出文件时不做转换。
  • false 提交和检出的时候都不对换行符进行自动转换。提交时文件时什么样,服务器上就是什么样。
    1
    2
    3
    4
    5
    6
    7
    core.autocrlf=true:    core.autocrlf=input:      core.autocrlf=false:

    repo repo repo
    ^ V ^ V ^ V
    / \ / \ / \
    crlf->lf lf->crlf crlf->lf \ / \
    / \ / \ / \

因为 core.autocrlf 在windows平台上默认是true,所以检出的时候sh文件将自动转换成CRLF换行符。
如果在windows平台上打包安装程序,那么安装后的程序sh文件将不能正常执行。

git config core.safecrlf

由于git有自动转换换行符的机制,如果设置了autocrlf,那么文件的换行符可能被统一处理。
例如: 对于混合了CRLF和LF的文件,都被统一成LF提交服务器了。

这种就是git处理转换后不可逆的情况:在同个机器上,提交后再检出,不能还原出一样的文件。
如果git错误的将binary文件判断为text文件,自动进行了CRLF转换,那么就会损坏文件数据。

通过开启 safecrlf 选项,可以防止出现上述错误:

  • true 如果git根据配置的CRLF转换自动处理文件,将导致文件不可逆,那么git将拒绝提交文件。
  • warn 如果git根据配置的CRLF转换自动处理文件,将导致文件不可逆,那么git将显示警告信息但继续处理文件。
  • false 关闭检查。

开启该选项不会改变git的CRLF转换处理,但通过该选项,可以保证提交的text文件里不会出现混合LF、CRLF的情况。

git config core.eol

当autocrlf=false时,通过.gitattributes配置了text属性的文件,其换行符将使用本配置值。
可选的值有:cr,crlf,native(使用本地操作系统匹配的换行符)

.gitattributes

在项目的git根路径下,添加.gitattributes文件并纳入git管理,然后打开编辑内容:

1
2
3
4
5
6
7
8
9
10
11
*.bat eol=crlf
*.sh eol=lf

*.jpg -text
*.png -text
*.pdf -text

*.exe -text
*.so -text

*.txt text=auto

说明:

  • bat 文件在检入检出时,使用CRLF换行符
  • sh 文件在检入检出时,使用LF换行符
  • ‘-text’ 的意思是,标记该类型的文件不是text。git对这类文件会跳“是否text文件”的检测,也不会自动转换换行符。
  • ‘text=auto’ 的意思是,标记该类型的文件是text,同时使用core.eol配置的换行符(如果有配置的话)。
  • 优先级: autocrlf > text=auto + core.eol

对于明确是二进制数据的文件,可以在本文件中显示的声明非text,防止git检测有误时错误的对文件进行处理而损坏数据。

总结

通过git config进行配置,只能在对所有的文件进行统一处理,不能分文件类型处理。
而且仅对各客户端有效,不能保证在不同开发人员那保持一致。
所以最好在项目根路径下添加.gitatttibutes文件,对关键的文件类型分别统一配置CRLF转换,也可避免不同客户端环境不同的影响。

参考

TProfiler使用总结

TProfiler是阿里开源的一个java性能分析工具,实现原理和部署配置可看wiki

关于TProfiler中的以下几个关键配置,官方wiki描述的不是很清楚:

  • startProfTime = 9:00:00 开始profile的时间点
  • endProfTime = 11:00:00 结束profile的时间点
  • eachProfUseTime = 5 profile时间长度(单位秒)
  • eachProfIntervalTime = 50 两次profile的时间间隔(单位秒)

TProfiler对函数调用情况的采样调度是在类 com.taobao.profile.thread.DataDumpThread 中实现的:

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 run() {
try {
while (true) {
if (Manager.instance().canDump()) {
Manager.instance().setProfileFlag(true); // 疯炎疯语#1: 打开采样开关
TimeUnit.SECONDS.sleep(eachProfUseTime); // 疯炎疯炎#2: 采样期间,记录函数调用情况
Manager.instance().setProfileFlag(false);
// 等待已开始的End方法执行完成
TimeUnit.MILLISECONDS.sleep(500L); // 疯炎疯语#3: 简单的等待已经Start的函数End返回

dumpProfileData();
}
TimeUnit.SECONDS.sleep(eachProfIntervalTime);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
Manager.instance().setProfileFlag(false);
if (fileWriter != null) {
fileWriter.closeFile();
}
// 等待已开始的End方法执行完成
try {
TimeUnit.MILLISECONDS.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
Profiler.clearData();
}
}

TProfiler从startProfTime开始第一次函数调用情况采样(打开采样开关),此时被关注的函数调用时就会被记录执行信息,
经过eachProfUseTime时间后,采样开关会被关闭(采样停止)。
在采样停止后,线程会等待500毫秒的时间,然后再将采样到的数据输出到文件中,并重置程序中的相关状态。

函数调用情况的采样由类 com.taobao.profile.Profiler 实现:

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
com.taobao.profile.Profiler

/**
* 方法开始时调用,采集开始时间
*
* @param methodId
*/
public static void Start(int methodId) {
if (!Manager.instance().canProfile()) {
return;
}
... // 记录采样数据
}

/**
* 方法退出时调用,采集结束时间
*
* @param methodId
*/
public static void End(int methodId) {
if (!Manager.instance().canProfile()) {
return;
}
... // 记录采样数据
}

只有函数执行完之后触发了上边的End调用后,采样的数据才有可能会被输出。
如果一个函数时间执行过长,以致于在采样停止后500毫秒内还没有返回,那么这个函数的调用将无法被记录。
例如:函数执行的时间超过 “eachProfUseTime+500”。

所以,如果你设置的eachProfUseTime时间过短,那么你将无法通过TProfiler来发现执行时间超过这个时间的慢函数,
也就是无法通过TProfiler来发现这样存在严重性能问题的函数。

因此,设置eachProfUseTime时需要估计你的程序中函数可能执行的最慢时间是多少,然后设置一个比这个数稍大的时间。
但是,因为每次eachProfUseTime期间记录的采样信息是在本次采样结束时才输出到文件中的,
如果设置的eachProUseTime过长的话,那么TProfiler对内存的使用会上升,这还取决于采样函数执行的频率高低。

虽然TProfiler存在这样的问题,不过在系统访问压力的不大的情况下(例如在功能测试而非性能测试的时候),
也可以用来分析程序中哪些函数调用的频率比较高,或者平均调用时间比较长,从而做出针对性的调优。

至于 eachProfIntervalTime 则控制了2次采样之间的时间间隔,把这个数调大可以降低对应用程序的性能影响,
但是过大的话则采样数据可能不准。

修改android手机的wifi主机名

本文亦适用于其他android设备。

当你的android打开wifi连上网络时,在无线路由器的DHCP客户端列表上,
你的android设备的网络名称是形如“androidxxxxxxx”的无意义字符串。
给你的android设备设置一个有意义的名字,可以便于识别你自己的设备,并通过网络名来连接和操作你的设备。

android系统中并没有提供设置wifi网络名的地方,本文提供的方法虽然过程比较简单,
但是一些前提条件可能比较麻烦:
你的android设备需要已经root过(关于如何root请自行Google/Baidu);
有一台电脑,上边安装了Android SDK(需要adb命令和你的设备驱动)。

  • 在手机设置中,开启【开发者选项】中的【USB调试】

android_dev_usb_debug

  • 将你的手机使用USB数据线连上电脑

  • 在电脑上打开命令行窗口,执行以下命令

1
2
3
4
5
6
7
8
9
E:\> adb devices
List of devices attached
HT13WTJ47928 device

E:\> adb connect 127.0.0.1:5037
connected to 127.0.0.1:5037

E:\> adb pull /system/build.prop
450 KB/s (5996 bytes in 0.013s)
  • 在当前路径(本示例中是 E:\)下找到build.prop,打开进行编辑,在文件的最末尾加上一行
1
2
#net.hostname=[你的机器名],例如
net.hostname=my-HTC_G12
  • 启动 adb shell 执行如下命令,将修改后的build.prop替换/system/build.prop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
E:\> adb shell
root@android:/ # su
su
root@android:/ # mount -o remount,rw /system
mount -o remount,rw /system
root@android:/ # exit
exit
root@android:/ # exit
exit

E:\> adb push build.prop /system
585 KB/s (5996 bytes in 0.010s)

E:\> adb shell
root@android:/ # su
su
root@android:/ # mount -o remount,ro /system
mount -o remount,ro /system
root@android:/ # exit
exit
root@android:/ # exit
exit
  • 重启你的android手机,大功告成

参考文章:[How to Change Wifi Host Name of Your Android Device][ref]
[ref]: http://nileshgr.com/2012/10/13/how-to-change-wifi-host-name-of-your-android-device

Thunderbird:使用Active Directory配置LDAP地址簿

公司人员较多时,同名情况偶有发生,而且因为入职离职总有变动,还有可能出现帐号名重用的情况,
使用静态地址簿不仅会有地址失效,有时还会发生一些令人发囧的状况。

如何才能有一个实时同步的邮件地址簿呢?
好消息是,Mozilla Thunderbird邮件客户端可以配置LDAP作为邮件地址簿。

在Windows操作系统占主流的中国,企业内部大多使用Active Directory作为目录服务,例如我们公司。
但因为各公司AD中的配置不同,Thunderbird一些默认的LDAP配置并不能正确工作,需要手工做些修改。

首先你需要了解贵公司的AD服务器地址,例如我们公司是 “xyz.xxx.com“。

其次需要了解贵公司AD的分类和用户属性,可以下载 Active Directory Explorer 进行查看。

下图是我公司的用户对象属性列表:

  • displayNam 一般是中文名
  • sAMAccountName 是用户的域帐户名
  • userPrincipalName 应该与贵公司的电子邮件格式一致,形如“zhangsan@corp.com

distinguishedName 能够知道AD中用户的BaseDN是多少,
形如 “CN=张三,CN=Users,DC=corp,DC=com”,
去除掉第一个逗号及其之前的内容(“CN=张三,”)即可是用户的BaseDN,后边配置Thunderbird时需要。

掌握这些之后,打开Thunderbird:Tools=>Options=>Composition=>Addressing=>Edit Directories=>Add,将显示如下界面:

  • “Hostname”配置贵公司AD服务器的地址,
  • “Base DN”配置为用户分类的DN,端口默认就是389,
  • “Bind DN”配置你自己的域帐户,用于登录AD。

再切换到“Advanced”页签,按下图配置:

“Search filter”配置串的意思是,当用户输入帐户名搜索时,与AD上用户的哪个属性进行匹配。

配置完这些后,还需要最关键的一步:配置AD查询结果与邮件地址簿数据结构间的映射关系。
打开Thunderbird:Tools=>Options=>Advanced=>General=>Config Editor,将显示如下界面:

需要修改2个最关键的映射:用户姓名、主地址邮件。

  • “ldap_2.servers.default.attrmap.NickName”,用于配置将AD上用户的哪个属性值映射为地址簿姓名,
    你需要根据上边使用ADExplorer工具查看到的结果来确定哪个属性才是存储的用户中文姓名,然后在这配上它。
  • “ldap_2.servers.default.attrmap.PrimaryEmail”,用于配置将AD上用户的哪个属性值映射为地址簿主邮件地址,
    一般配置为userPrincipalName即可,不过你最好根据ADExplorer工具查看的结果来选择正确的属性。

修改完就大功告成了,可以打开Address Book,切换到你配置的LDAP地址簿(本文中的 MyAdBook),
在搜索框中输入自己的邮件帐户名测试一下(例如:zhangsan)。

Enjoy it!

Stephen Breyer大法官在清华大学法学院演讲有感

观美国联邦最高法院大法官Stephen Breyer在清华大学法学院演讲有感

很早就看了大法官Stephen Breyer在清华法学院的演讲,深有感触一直想写篇文章,奈何提笔容易下笔难,码字码了半年多。
很喜欢这个老头,睿智坦诚,言谈幽默,一把老骨头演讲时还是充满激情。

布雷耶大法官首先谈到了美国的法律系统。美国是联邦制国家,各州有很大的自治权,各州拥有独立的法律体系(立法司法),
美国的大多数法律是由各州制定的(美国95%的法律都是州法,例如刑法、民法、金融法、商法等等),
同样的行为在一州是合法的在另一州却可能是非法。
而且美国是判例法国家(所谓判例法,就是基于法院的判决而形成的具有法律效力的判定,
这种判定对以后的判决具有法律规范效力,能够作为法院判案的法律依据 — 百科),
因此美国的法律非常复杂,百姓一般都搞不明白。美国的联邦最高法院一般只受理与联邦法律(包括宪法)相关的案件。
各州也可以审理与联邦法相关的案件,当这类案件的败诉方层层上诉之后就到了最高法院那里。
因为上诉到最高法院的案子非常多,所以不是所有的上诉案件都会被受理,最高法可以行使“司法选择权”决定受理哪些案子。

对于美国联邦最高法院的职责,Stephen Breyer大法官总结为2句非常精辟的话:
“第一个职责是决定我应该决定的事;第二个职责是决定什么事需要我去决定。”
通俗点说:职责一是审理应该最高法院审理的案子(管辖权与裁判权),
职责二是选择受理哪些案子由最高法进行审理(司法选择权)。

这2句话非常有趣,虽然有点绕口,但却很有逻辑。

法官的职责是根据查明的事实,选择适用的法律(或者判例)做出判决。
因为不同的法官对法律条文可能会有不同的理解,所以可能出现法条适用不一致的情况。
美国联邦最高法院的主要任务就是“确保联邦法律和宪法(在各州)的适用的统一”,
最高法院拥有对法律条文的最终解释权。为什么“确保法律适用的统一”这么重要呢?
这是因为法律的权威在于它的统一,统一了才有公正,有了公正才能得到人们的尊重,才有权威。

举个例子,情形相似的故意杀人案,在A省判死刑(立即执行),在B省判死缓,
A省的死刑犯家属就会以B省为例子质疑为何判死刑,
B省受害者家属就会以A省为例子质疑为何判死缓,当法律因为失去公正而被人们质疑时,法律就失去了权威。
古人言“不患寡而患不均,不患贫而患不公”说的也是这样的道理。

布雷耶大法官接着谈了谈美国的法院系统。美国的司法体制遵循“宪法至上”原则,而最高法院拥有对宪法的最终解释权。
“最终解释权”是个非常大的权力,这是判定是非的权力,只有手握真理的人才能拥有,在宗教里这样的人是先知。
将这样的权力交给谁是个难题,布雷耶大法官坦承也不能给出确切的答案,他只能谈谈美国人民是怎么处理这个问题的。
在美国,政府握有武力,国会握有财力,交给前者容易形成独裁,交给后者有可能造成法制不公,
因为民选的政客为了讨好选民可能制定出有利大多数选民但却损害少数选民的法律,这将有违法律公平公正的原则。
因此美国人民将这个权力交给了法官们,因为法官没有任何实权,他们无法利用这个权力去干坏事,当然前提是司法独立。
美国最高法院的法官均由总统直接提名经参议院同意后任命。

加纳首席大法官曾问布雷耶大法官:“为什么美国法官说什么人民都会照做?”。
对于这个问题,布雷耶大法官反复强调,“这是一个非常复杂的过程,构建法治的规则并不是一件容易的事情”,
不是有了法律就能有法治。美国建国200多年后能在法治上有如今的成就也是非常不易,
布雷耶大法官通过三个故事来阐述这个过程有多曲折艰难。

第一个故事讲的切诺基诉佐治亚州案。
佐治亚州北部有一个印第安部落切诺基人(the Cherokee)。在美国建国之初,政府和切诺基人签订了诸多条约,
承认切诺基人对土地的所有权,并许诺会保护切诺基人。1829年在诺基人的土地上发现了金矿,
佐治亚州为了占有金矿强占了切诺基人的土地,于是切诺基人聘请了律师上告到了联邦最高法院。
最高法院裁定印第安人胜诉,土地和金矿属于切诺基人。
但当时的总统安德鲁·杰克逊(Andrew Jackson)拒绝执行最高法院的判决,
杰克逊说:“既然是最高法院判的就让他们自己执行去”。
不仅如此,杰克逊还把联邦军队派到佐治亚州去驱逐印第安人,印第安人被迫踏上了迁徙的“血泪之路”。

第二个故事讲的是布朗诉托皮卡教育局案。
19世纪中期美国的内战解放了黑奴,但之后的80年,美国的种族隔离制度仍很严重。
美国南方大概有三分之一的地方,白人和黑人必须分别就读不同公立学校。
1954年美国最高法院判决种族隔离本质上就是一种不平等,隔离教育违反了美国宪法第十四修正案中所保障的同等保护权,
因此违宪。最高法院在判决中明确指出,禁止在南方搞种族隔离制度。
在最高法院1954年作出该判决之后,1955年、1956年南方各州都没有有效的执行该判决。
1957年在阿肯色州,州长奥尔弗·法柏斯(英语:Orval Faubus)命令了当地的国民兵阻挡黑人学童进入当地的
小石城中央中学(英语:Little Rock Central High School)就读。
艾森豪总统与州长谈判后发出指令,联邦政府接管当地所有国民兵,令黑人学童能够顺利进入学校就读。
后来,一群白人包围学校,制造种族暴力事件,企图阻止黑人学童到校上课,
总统随后派遣美国第101空降师中的 1,000 位伞兵来维持秩序。

第三个故事讲的是布什诉戈尔案。
2000年的美国总统大选上演了一场旷日持久的“世纪司法大战”。
大选的投票结果显示双方孰胜孰败的问题仅在毫厘间,戈尔与布什谁赢得佛罗里达州25张选举人票,谁就坐上总统宝座。
民主党人要求在棕榈滩县几个“自己的地盘”上重新进行人工计票。
与此同时,共和党人州务卿哈里斯宣布了计票的最后期限。
无法按时完成人工计票的民主党将官司从州法院打到州最高法院。
布什阵营则向联邦最高法院提出上诉,对佛州最高法院有利于民主党判决的合法性提出质疑。
最高法院受理此案,并于12月4日做出九票一致的判决:搁置州最高法院的判决,将案件发回。
12月8日,佛州最高法院以4∶3票给予戈尔最后的计票机会。
布什阵营再向联邦最高法院提起紧急上诉。最高法院以5∶4票发出立即停止计票的紧急命令。
12月12日,最高法院以同样的票数作出最后裁决:“推翻佛州最高法院继续人工计票的决定。”
布什最终以少数民选票当选总统。
虽然最高法院的这个影响了很多人的判决很不受欢迎,并且不一定对的,但美国人民服从了这个判决,没有暴力发生。

从这三个故事中,我们认识到美国的法治化过程并非一帆风顺,是一系列的历史机缘才塑造出了今天的美国,
因此美国是独一无二的,它的成功只有参考意义而无指导意义。

美国的大法官们也曾遇到判决得不到执行的无奈,在一件又一件的重大案件中,
通过法官们对法律的坚守、执行者与人民对法律的支持,才使美国实现了如今“法官说什么人民都会照做”的法治效果。
为了不破坏司法权威,作为执行者的zf可以选择拒绝执行法院的判决,但不应强迫法院改变判决,丧失了独立性的司法没有权威可言。
司法的权威是通过一系列公正的判决来确立的,斯伟江律师曾说:“正义虽然不在当下,但,我们等得到!”,
对于法院的判决,有时我们无法在当下判断出它的好坏,但是时间终会证明一切。

最后,耶鲁法学院葛维宝教授在评述中说到:”即便在法治较为成熟的国家,建立并维护他们的法律体系也依赖于多方的长久努力”。
法治不是一件一劳永逸的事情,法治的建立维护需要所有人持续不懈的努力。
如果法律得不到人民的支持,大家都不遵守,就将失去效用沦为空谈。
应该通过加强公民教育,让所有人尤其是下一代了解什么是法律,什么是宪法,法治的意义等,
让他们知道参与法治建设是公民的义务,只有这样法治才有希望。

AMD APU系列CPU crossfire支持

AMD的APU系列CPU支持crossfire技术,可以支持双显卡。
也就是说APU支持通过crossfire技术同时启用双显卡,提升系统整体的显示性能。

截至本文发表时,AMD共有三款APU:A4、A6、A8,在这三款APU中都集成了一个GPU(显卡),
当主板支持AMD的crossfire技术时,APU中的集成显卡可与主板上另外安装的的独立显卡协同工作,
此时在操作系统中看到的显卡型号既不是APU中集成显卡的型号,也不是主板上独立显卡的型号,而是一个新的显卡型号。

要启用crossfire支持双显卡,APU与独立显卡的搭配也是有要求的,在APU的包装盒侧面会有相关的说明。
APU与显卡配对的列表如下:

http://www.amd.com/us/products/technologies/dual-graphics/Pages/dual-graphics.aspx#3

例如我之前买的 AMD A-Serise X4 A6-3670 ,APU中的集成显卡为 AMD Radeon™ HD 6530D,
要想配对使用启用双显卡支持的话,则必须选择以下ATI芯片之一:
HD 6450/HD 6570/HD 6670
在APU的外包装盒侧面有相应的说明:

其配对使用后在系统中识别出来的显卡型号是新的型号,分别为:
HD 6550D2/HD 6610D2/HD 6690D2

关于双显卡的性能,在网上找到一个 “HD6550D + HD 6570 = HD 6630D2” 性能对比,各位可以参考:

http://www.tomshardware.com/reviews/amd-a8-3850-llano,2975-2.html

装机时没有整清楚APU与显卡的配对关系,买了迪兰恒进(Dataland)HD 6850 无法启用显卡交火技术,
事后终于搞清楚了,所以此文仅为后来者鉴。

T61升级记:8G DDR2内存64位系统

08年初买了一台港版的T61,当时配置是2G内存,160G硬盘(SATA-I),用了这许多年觉得有必要做些升级。
因为笔记本的内存插槽有限,平时开的应用多内存吃的厉害,所以考虑升级为8G内存。

一、可行性分析

升级8G内存需要考虑机器硬件上能否支持,操作系统能否识别。

1、主板是否支持单根4G,双根8G内存

DDR2出现早期,单根4G的内存还不流行,所以一些早起的笔记本其主板是否支持单根4G的内存需要查阅主板的相关技术文档。
网上转了一圈之后,发现 金士顿官网 上有个页面可以查询主板对内存的支持情况。
例如我的T61查询结果为:

主板支持8G内存也就是硬件上就可以支持8G的内存寻址了。

2、操作系统识别8G内存

32位的操作系统最大支持4G的寻址。因为操作系统的信号中断、其他各类硬件设备(如显卡的显存)都要占用一部分寻址地址,
扣除这部分地址之后剩余的地址大小就是你的机器所能识别的最大内存大小了。如果你的机器上硬件设备越多,
那么占用的寻址地址越多,操作系统能用于内存寻址的地址就越少。扣除之后一般在3G~3.5G上下。
这就是为什么你插了4G内存条,操作系统却只识别出3G左右的原因。

为了让操作系统能识别4G以上的内存,需要安装64位操作系统。

2、CPU支持运行64位操作系统

CPU要能够支持64位的指令集才可以运行64位的操作系统。家用PC一般都是X86架构,
那么你的CPU要能够支持X86-64指令集才可运行64位操作系统。
通过 EVEREST 可以查看你的CPU是否支持64位指令集。如图:

支持X86-64指令集的的CPU可以安装 win7_x64 系统,可以兼容运行x86-32位的程序。
如果你的CPU支持的是IA-64指令集,那么你需要安装 win7_IA64 系统。

二、安装64位操作系统注意事项

配置了8G内存,安装win7_x64操作系统时,操作系统分区(一般是C盘)的大小要设置大点。
如果是笔记本的话,建议设置在80G以上(我的就是设置为80G)。
微软官方建议,安装了64位操作系统后系统盘的可用空间应在20G以上。
原因如下:

1、windows的页面文件

windows需要设置虚拟内存,否则有些程序可能无法正常运行。
系统默认配置是从系统盘分配内存大小的 1~1.5倍 空间作为虚拟内存。
所以,当你的内存为8G时,页面文件大小将是8G~14G左右。
页面文件是系统文件,默认是隐藏的,文件名叫pagefile.sys,显示隐藏文件后可见。
如图:

2、windows的休眠功能

当你启用休眠功能时,windows将把内存的快照数据保存到硬盘中,然后关闭系统。
所以快照文件最大可达8G(同内存大小),快照文件的实际大小与当前已使用的内存大小有关。
windows默认将内存快照文件存放在系统盘,该文件是系统文件默认隐藏,文件名叫hiberfil.sys。
见上图。

3、程序与临时文件

有些程序,即使你将其安装目录指向非系统盘,它也需要往系统盘下的程序文件夹或者windows文件夹放入一些文件。
如果你安装的程序很多的话,就将占用较多的磁盘空间。
此外系统的临时目录也在系统盘下,如果你不经常清理的话,那么这些垃圾文件也会占用大量的磁盘空间。

三、效果

机器升级完之后效果如下:

因为显卡可以按比例使用内存作为共享显存,所以显存的大小已经达到3710M!