最近遇到一个问题,公司的 java 服务端应用,启动后,通过 jstack pid |grep nid -c,可以看到大概创建了2044个线程,然后此时应用就会报错,提示无法创建更多线程, jvm 开始抛错.
查看 mac 的内存,发现还是够的.因为一般认为可创建的线程数=(总内存-其他占用的内存)/线程大小,所以内存够的情况下,应该是能创建的.

google 一圈,发现mac 对单线程创建的线程是有限制的.理由应该是为了保持系统稳定性.主要有两个参数

sysctl kern.num_threads 这个可以看一下,说明了系统能够创建的总共的线程,单个应用能够创建的线程是sysctl kern.num_taskthreads,第二个参数就是导致我们创建不出来更多线程的原因,
因为2044+一些 gc 的线程,基本上刚刚达到这个极限.

那么要么改程序,要么改参数.改程序这是不可能的..因为只有 mac 会有这个问题..该参数尝试通过sudo sysctl -w kern.num_taskthreads=4096,修改,会发现提示是只读属性.google 了一圈,无解.

最终意外解决..

参考这里开启性能模式

  1. nvram boot-args
  2. sudo nvram boot-args="serverperfmode=1 $(nvram boot-args 2>/dev/null | cut -f 2-)"
  3. 重启

如果想要恢复的话: sudo nvram boot-args="$(nvram boot-args 2>/dev/null | sed -e $'s/boot-args\t//;s/serverperfmode=1//')"

当时各种搜索,加打apple 支持电话.无解.搜索意外看到这个说明,说开启之后,可以支持更多服务应用之类的.猜测应该会改这个值..果然..改完之后,直接重启,这个限制会变成5000..完美解决..理论上,应该通过继续修改这个参数
是可以自定义这个值的.不过还没尝试.

Comment and share

mac 下使用 homebrew 作为包管理工具是非常好的. brew 用来安装非 gui 界面的程序. cask 用来安装 gui 界面的程序.但是这两个是使用的源在国外.所以你懂得..

1.替换 homebrew 默认源

1
2
cd /usr/local
git remote set-url origin git://mirrors.ustc.edu.cn/brew.git

这里注意记一下以前的默认源.防止以后想换回来..

默认源是
https://github.com/Homebrew/brew

2.替换homebrew bottles默认源

1
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.bashrc

这里的.bashrc根据自己的情况替换.我是 zsh,就写到.zshrc 文件.

Comment and share

自从入了 hexo 的坑.这玩意折腾的我不要不要的.各种诡异的问题,不过也顺便搞了搞其他的东西.看了很多相关的代理设置方案.最终沿用 windows 下的策略.最简单高效.

mac 下的 ss 代理是只能设置浏览器代理的.对于一些不走 http 代理的.比如终端.或者其他软件.那么就需要将 ss 代理指定给其他软件或者终端使用.

1.有一个 ss 代理
2.安装proxifier,直接 brew cask install proxifier
3.安装好之后,添加Proxies 里面,把 ssh 的信息添加进入
4.添加 Rules, 我为了简单..直接将default 设置成走代理.这样,就啥也不用管了.等 hexo deploy 结束.再关闭proxifier 就行了.

实际使用中.可以先开全局代理.然后知道哪个程序走了代理.需要走代理.然后单独设置即可.软件很好使用.

不得不说, wall 越来越令人难受与不安.

Comment and share

mac 下面不想安装多个 GUI 的 svn 客户端.所以使用 sourceTree 来做.

sourceTree 对于 svn 只能从远程 url 拷贝.不能从本地来.所以在 gui 页面进行添加

但是会发现报错

1
Can't locate SVN/Core.pm in @INC (you may need to install the SVN::Core module) (@INC contains:

网上搜了一下.原因是 Perl 升级后 版本路径不对.于是执行

1
2
sudo ln -s /Applications/Xcode.app/Contents/Developer/Library/Perl/5.18/darwin-thread-multi-2level/SVN /System/Library/Perl/Extras/5.18/SVN
sudo ln -s /Applications/Xcode.app/Contents/Developer/Library/Perl/5.18/darwin-thread-multi-2level/auto/SVN/ /System/Library/Perl/Extras/5.18/auto/SVN

这里就是创建两个软连接.以便 sourceTree 识别.这里注意.如果你装了 Xcode 的 CommandLineTools, 而不是完整的 Xcode.
那么你的目录是没有这个原始文件的所以需要执行的

1
2
sudo ln -s /Library/Developer/CommandLineTools/Library/Perl/5.18/darwin-thread-multi-2level/SVN /System/Library/Perl/Extras/5.18/SVN
sudo ln -s /Library/Developer/CommandLineTools/Library/Perl/5.18/darwin-thread-multi-2level/auto/SVN/ /System/Library/Perl/Extras/5.18/auto/SVN

但是执行的时候还是会报错.因为新版本的 mac系统.已经不允许在 System 目录写文件了.除非关闭安全选项.这就得不偿失了.

但是从stackexchange说法看.

1
2
3
4
5
mkdir /Library/Perl/5.18/auto
sudo ln -s /Library/Developer/CommandLineTools/Library/Perl/5.18/darwin-thread-multi-2level/SVN /Library/Perl/5.18/SVN
sudo ln -s /Library/Developer/CommandLineTools/Library/Perl/5.18/darwin-thread-multi-2level/auto/SVN /Library/Perl/5.18/auto/SVN

这个方法也是可以的.就是使用另一个目录作为软连接的目录.测试通过.同理,上面的真实目录根据你装的是 Xcode 还是 CommandLineTools 来替换.记录备用.

参考:http://apple.stackexchange.com/questions/208300/issue-with-creating-a-symbolic-link-inside-system-folder

Comment and share

一旦服务器启动,服务开始提供,并且在配置中心注册了(配置中心可以是本地的地址,也可以是zk,也可以是其他的实现),那么客户端就要开始调用了

点击看大图

服务引用

服务引用 RefererConfig.getRef()

先是获取集群支持(先忽略,主要是配置中心相关的)

configHandler.refer(interfaceClass, clusters, proxy)
开始获取接口代理

1.一旦知道接口名,Class.forName加载接口类,就开始通过proxy工厂来为服务端接口创建代理了

2.jdk的Proxy类,直接来创建代理.同时代理要传入RefererInvocationHandler
这个类可以看错是真正的stub,封装了rpc调用请求.当在客户端获取到服务接口的bean的时候,实际上调用过程被这个类拦截,进行封装发送rpc

1.当接口被调用的时候,这个拦截器险根据拦截到的请求构造一个rpc请求

2.这里就会存在一个策略.该调用哪个,以FailoverHaStrategy为例

1.选择一个服务提供方

1.如果是jvm服务,那么直接从本地的服务map中取出一个调用就行

2.如果是真正的远程服务,这时候就进入nettyClient部分了

把请求向netty的Channel中写就行了.服务端会从Channel中取进行处理,然后放回来.这样客户端就拿到结果了

Comment and share

这一篇继续从这个demo开始,分析一下这个服务是怎么发布出去的.关键的代码从motanDemoService.export();开始.

一图胜千言.

点击看大图

服务发布

服务发布ServiceConfig.export()

1.加载有的配置中心url列表/新建

2.doExport(ProtocolConfig,port,registryURLs) //配置中心地址列表

2.1导出的时候,会先判断是否存在.其实就是根据协议名,ip,接口,参数来生成一个唯一key.

2.2ConfigHandler.export(Class interfaceClass, T ref, List registryUrls) //接口.实现.配置中心url列表

2.2.1.根据协议名创建协议,这里ProtocolFilterDecorator

2.2.2.根据接口,实现类,serviceUrl,构造一个Provider,用来提供服务

2.2.3.使用协议进行导出Provider, export(Provider provider, URL url)

2.2.3.1创建一个Exporter

2.2.3.1.1.创建的时候会将服务提供方Provider和url有个映射关系,这样当一个url请求过来的时候,就知道改调用谁了.ProviderMessageRouter,讲一个请求路由注册到server上,同时包装了一个心跳包

2.2.3.2进行导出 导出就是一个服务器打开的过程/server.open();

2.2.3.2.1进入nettyServer初始化,主要就是添加handler,编码解码.和一个rpc处理的
相当于一个请求过来的时候,先进行解码,然后调用业务处理handler进行处理,处理完成后,进行编码,然后返回给客户端

服务器启动后,相当于这个服务就发布了

2.2.4.注册register(registryUrls, serviceUrl) //这一步就是将serviceUrl,向对应的jvm/rpc服务中心注册url,本地注册就是LocalRegistryService类里一个map..zk的.就是向zk写node.等等

Comment and share

工作中一直在使用rpc,但是只是对简单的原理比较熟悉.最近看到有motan的一个介绍,代码拉下来看了看,除了测试用例比较少之外.其他还是不错的,和阿里的rpc框架比起来,还是弱了一些,好处就是方便用来学习.
motan 是weibo的一个rpc框架,据说已经在线上使用了.

在学习rpc框架之前,建议看一个hello world级别的文章RPC框架几行代码就够了,写的非常好,看完基本就知道rpc的核心了.

Remote Procedure Calls中最关键的那个图,就能说明了.

rpc的flow

本地client调用本地client stub,stub对消息进行封装,通过socket发送,服务端的server stub接收到,然后解包,将里面传递的方法名,方法参数.等等信息,识别出来,调用服务端对应的服务,然后得到结果后,又通过socket返回,本地client又进行解包.就行了.

这里面会涉及到,封装,封装就是吧对象序列化,这样才能在网络中传递.

而生产环境的rpc框架需要考虑的有:

stub怎么生成,序列化怎么最高效,如何统一不同机器之前的调用,(大小端的机器等),如何识别该调用哪个机器,负载均衡.socket通信.等等.

先跑个demo熟悉一下.

下载motan源码,导入ide,然后先启动服务端,MotanApiExportDemo,这个类,然后控制台会打出服务已经启动.然后运行MotanApiClientDemo,会发现一个控制台打出motan,服务端打出hello motan.就说明跑起来了.

如果控制台日志没有.修改对应resources下面的log4j.properties文件.首行添加log4j.rootLogger=debug,stdout ,会设置默认日志级别为debug,并且在控制台输出.
或者直接fork我这个

后面会逐步分析,希望坚持下来.

Comment and share

不说废话,直接上。本文主要包括tycho的使用,版本号的自动更新。

eclipse插件开发中,依赖的管理是个问题。如果采用常规的搞个lib目录,然后加到MF文件中。一旦依赖越来越多。或者要更换版本号就变得非常麻烦。所以要用到tycho

首先说明一下目录结构。一个parent的maven工程,一个plugin工程,。两个features。一个是deps的。一个是plugin的,这个依赖deps是独立的mvn项目。可以先不用管。一个deps依赖工程(这个依赖工程独立)。
在主pom下。

步骤如下。

关于依赖部分

  1. 新建一个普通的mvn工程,比如deps。打包类型写成<packaging>bundle</packaging>,同时在pom.xml中添加build部分
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
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.0.1</version>
<extensions>true</extensions>
<configuration>
<niceManifest>true</niceManifest>
<manifestLocation>META-INF</manifestLocation>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Embed-Dependency>*</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
<Embed-Directory>lib</Embed-Directory>
<Bundle-ClassPath>{maven-dependencies}</Bundle-ClassPath>
<_exportcontents>*</_exportcontents>
<_failok>true</_failok>
<_nouses>true</_nouses>
<Import-Package></Import-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>

然后正常添加一些依赖到这个工程中。然后执行一下mvn clean install ,你就会发现本地mvn仓库生成了一个jar包,这个jar里直接打包了所有的jar

关于插件部分

  1. 新建一个类型为pom的parent工程。用来包含下面的子工程,通过
    1
    2
    3
    4
    <modules>
    <module>xxx.plugin.1</module>
    <module>xxx.plugin.2</module>
    </modules>

来管理。同时 添加如下的插件

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
<properties>
<tycho.version>0.24.0</tycho.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<phase>none</phase>
</execution>
</executions>
<version>2.4</version>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-maven-plugin</artifactId>
<version>${tycho.version}</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho.version}</version>
<configuration>
<pomDependencies>consider</pomDependencies>
</configuration>
</plugin>
</plugins>
</build>
  1. 新建一个专门用来管理依赖的features,这样用户就可以安装这个features,来把所有的依赖安装到eclipse插件目录了。packing是eclipse-feature

  2. 新建一个plugin,新建一个这个插件对应的feature。然后通过右键转换成mvn工程。如图

mvn转换

  1. 新建一个repository项目,这个项目主要就是为了发布,生成发布相关的文件。新建Update set project。然后添加两个features。转换成mvn。其中packing改成eclipse-repository。
    注意,这里的版本号必须对应。比如plugin.xml的版本是1.5.0.qualifier,那么对应pom中必须是1.5.0-SNAPSHOT

然后去根目录下执行一下mvn clean install,就会在repository目录生成需要部署的zip文件或者web site文件

遗留问题是以后要是升级版本号怎么办。。不用担心。。主pom中添加

1
2
3
4
5
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-versions-plugin</artifactId>
<version>${tycho.version}</version>
</plugin>

然后 在根目录执行 mvn -Dtycho.mode=maven org.eclipse.tycho:tycho-versions-plugin:set-version -DnewVersion=1.6.0 即可全部替换pom和plugin的版本号

另外如果项目导入后在eclipse中报错,一般是因为缺少了上面说的那个依赖。可以直接复制到eclipse的plugin依赖目录。或者在mvn clean install之后,通过安装 依赖feature的方式解决。

附上一些文档

EclipseTycho
Building OSGi project with Maven and tycho

Comment and share

由于线下机器太多.有没有日志平台,所以查询日志比较麻烦.发现了ansible,按照官方文档(ubuntu)

1
2
3
4
$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible

一步步执行,第二步执行的时候,可能会报错

1
sudo: add-apt-repository: command not found

这时候.先执行

1
2
$ sudo add-apt-repository ppa:git-core/ppa
$ sudo apt-get update

然后接着执行上面的第二步就行了..

安装完成后,配置集群

1
cat /etc/ansible/hosts

1
2
3
4
[servergroup1]
192.168.1.1 ansible_ssh_user=root ansible_ssh_pass=root
[servergroup2]
192.168.2.1 ansible_ssh_user=root ansible_ssh_pass=root

后面的账号和密码,如果你使用ssh key登陆的话就不需要了.但是如果有很多机器,需要加到known_hosts就太多了.
这时候可以参考Batch_key
这个脚本.稍微修改一下,就能批量生成了

然后就是执行命令了

ansible常见用法为ansible host-pattern -m 模块 -a 命令,host-pattern类似于简化的正则表达式,而模块可以通过ansible-doc -l命令来查询。下面是一些常用模块的使用方法:

安装软件:ansible servergroup1 -m apt -a 'name=gcc state=present' 或者ansible local -m yum -a "name=nmap state=installed"

执行命令:ansible servergroup1 -m shell -a 'uptime'

拷贝文件:ansible servergroup1 -m copy -a 'src=/tmp/server dest=/tmp/server'

文件属性:ansible servergroup1 -m file -a 'dest=/tmp/server mode=755 owner=root group=root'

还有一个playbook的,看上去就是一个任务定义.我也暂时用不上..

参考文档:

  1. http://docs.ansible.com/ansible/intro_installation.html

  2. http://lifeonubuntu.com/ubuntu-missing-add-apt-repository-command/

  3. http://www.cnblogs.com/feisky/p/4102613.html

Comment and share

bystander

author.bio


author.job


China