python中调用a.xx,内部就是a.__getattr__(xx)或者getattr(a, xx),而a.xx(),其中xx实现了__call__()方法,即调用了getattr(a, xx)()。
但python的灵活之处在于可以重写__getattr__方法,通过这个方式可以包装一个类,使其中一个类看起来具有另一个类的方法,非常像继承获取的方法(其实应该是通过组合方法获取,但调用时更方便)。看一下代码:
class Test: def __init__(self): self.id = 5 def get(self): print("getting ...") def update(self): print("updating ...") def delete(self): print("deleting ...") class Wapper: def __init__(self, backend = None): self.backend = backend def __getattr__(self, key): return getattr(self.backend, key) if __name__ == "__main__": test = Test() wapper = Wapper(backend = test) wapper.get() print(wapper.id)
Wapper类并没有id属性,也没有get、update、delete方法,但Wapper的实例可以直接获取id属性和调用get、delete、update方法,就像是自己的属性一样。这在openstack中db中应用,nova.db.api部分代码:
from nova.openstack.common.db import api as db_api _BACKEND_MAPPING = {'sqlalchemy': 'nova.db.sqlalchemy.api'} IMPL = db_api.DBAPI(backend_mapping=_BACKEND_MAPPING) def compute_node_get_all(context, no_date_fields=False): """Get all computeNodes. :param context: The security context :param no_date_fields: If set to True, excludes 'created_at', 'updated_at', 'deteled_at' and 'deleted' fields from the output, thus significantly reducing its size. Set to False by default :returns: List of dictionaries each containing compute node properties, including corresponding service and stats """ return IMPL.compute_node_get_all(context, no_date_fields)
可以看出这里的实现都是通过调用IMPL中的方法直接返回的,而IMPL是nova.openstack.common.db.DBAPI实例,我们查看其代码:
class DBAPI(object): def __init__(self, backend_mapping=None): if backend_mapping is None: backend_mapping = {} self.__backend = None self.__backend_mapping = backend_mapping @lockutils.synchronized('dbapi_backend', 'nova-') def __get_backend(self): """Get the actual backend. May be a module or an instance of a class. Doesn't matter to us. We do this synchronized as it's possible multiple greenthreads started very quickly trying to do DB calls and eventlet can switch threads before self.__backend gets assigned. """ if self.__backend: # Another thread assigned it return self.__backend backend_name = CONF.database.backend self.__use_tpool = CONF.database.use_tpool if self.__use_tpool: from eventlet import tpool self.__tpool = tpool # Import the untranslated name if we don't have a # mapping. backend_path = self.__backend_mapping.get(backend_name, backend_name) backend_mod = importutils.import_module(backend_path) self.__backend = backend_mod.get_backend() return self.__backend def __getattr__(self, key): backend = self.__backend or self.__get_backend() attr = getattr(backend, key) if not self.__use_tpool or not hasattr(attr, '__call__'): return attr def tpool_wrapper(*args, **kwargs): return self.__tpool.execute(attr, *args, **kwargs) functools.update_wrapper(tpool_wrapper, attr) return tpool_wrapper
我们期望的是DBAPI一堆实现方法,可我们咋一看只有一个核心方法__getattr__,而且它直接继承object,那它怎么会有那么多方法呢?其中的奥秘就在于__getattr_方法,返回的实质是backend中的方法,回到nova.db.api代码,我们发现backend就是nova.db.sqlalchemy.api,因此调用DBAPI方法,实质就是调用的nova.db.sqlchemy.api方法。下面是nova.db.sqlalchemy.api部分代码:
@require_admin_context def compute_node_get_all(context, no_date_fields): # NOTE(msdubov): Using lower-level 'select' queries and joining the tables # manually here allows to gain 3x speed-up and to have 5x # less network load / memory usage compared to the sqla ORM. engine = get_engine() # Retrieve ComputeNode, Service, Stat. compute_node = models.ComputeNode.__table__ service = models.Service.__table__ stat = models.ComputeNodeStat.__table__ with engine.begin() as conn: redundant_columns = set(['deleted_at', 'created_at', 'updated_at', 'deleted']) if no_date_fields else set([]) def filter_columns(table): return [c for c in table.c if c.name not in redundant_columns] compute_node_query = select(filter_columns(compute_node)).\ where(compute_node.c.deleted == 0).\ order_by(compute_node.c.service_id) compute_node_rows = conn.execute(compute_node_query).fetchall() service_query = select(filter_columns(service)).\ where((service.c.deleted == 0) & (service.c.binary == 'nova-compute')).\ order_by(service.c.id) service_rows = conn.execute(service_query).fetchall() stat_query = select(filter_columns(stat)).\ where(stat.c.deleted == 0).\ order_by(stat.c.compute_node_id) stat_rows = conn.execute(stat_query).fetchall() # NOTE(msdubov): Transferring sqla.RowProxy objects to dicts. stats = [dict(proxy.items()) for proxy in stat_rows] # Join ComputeNode & Service manually. services = {} for proxy in service_rows: services[proxy['id']] = dict(proxy.items()) compute_nodes = [] for proxy in compute_node_rows: node = dict(proxy.items()) node['service'] = services.get(proxy['service_id']) compute_nodes.append(node) # Join ComputeNode & ComputeNodeStat manually. # NOTE(msdubov): ComputeNode and ComputeNodeStat map 1-to-Many. # Running time is (asymptotically) optimal due to the use # of iterators (itertools.groupby() for ComputeNodeStat and # iter() for ComputeNode) - we handle each record only once. compute_nodes.sort(key=lambda node: node['id']) compute_nodes_iter = iter(compute_nodes) for nid, nsts in itertools.groupby(stats, lambda s: s['compute_node_id']): for node in compute_nodes_iter: if node['id'] == nid: node['stats'] = list(nsts) break else: node['stats'] = [] return compute_nodes
真正的实现在nova.db.sqlchemy.api中。
要想知道nova的工作过程,首先就要掌握它的入口,即novaclient!命令nova和horizon都调用了novaclient。
github地址:https://github.com/openstack/python-novaclient
novaclient的功能很简单,即解析参数,构造url并发送请求,处理结果。比如运行nova --debug list,首先需要解析出选项参数--debug,另外还要获取环境变量参数和默认参数,然后解析子命令list,通过子命令获取相对应的回调函数,list对应为novaclient.v1_1.shell.do_list。
下面详细看看它的工作原理,首先看看命令nova到底是什么?
which nova | xargs -I{} file {} # 返回/usr/bin/nova: a /usr/bin/python script, ASCII text executable
可见命令nova只是一个python程序,让我们打开它
#!/usr/bin/python # PBR Generated from 'console_scripts' import sys from novaclient.shell import main if __name__ == "__main__": sys.exit(main())
命令nova调用了novaclient.shell的main函数,从这里开始进入了novaclient,现在让我们开始novaclient吧!
首先看看novaclient.shell的main函数:
def main(): """入口函数""" try: OpenStackComputeShell().main(map(strutils.safe_decode, sys.argv[1:])) except Exception as e: logger.debug(e, exc_info=1) print("ERROR: %s" % strutils.safe_encode(six.text_type(e)), file=sys.stderr) sys.exit(1)
发现它又调用了OpenstackComputeShell()的main函数。这个main函数才是真正的入口函数,以下是前半部分代码:
def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser() # 添加选项,比如--user, --password等 (options, args) = parser.parse_known_args(argv) self.setup_debugging(options.debug) # 如果options中有--debug,则设置logger的level为DEBUG,并输出到标准输出流 # Discover available auth plugins novaclient.auth_plugin.discover_auth_systems() # build available subcommands based on version self.extensions = self._discover_extensions( options.os_compute_api_version) self._run_extension_hooks('__pre_parse_args__') # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. if '--endpoint_type' in argv: spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' # 根据版本解析子命令 subcommand_parser = self.get_subcommand_parser( options.os_compute_api_version) self.parser = subcommand_parser # 如果--help,则打印help信息,并退出 if options.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) #解析命令行参数 argv=['list'] #print("args = %s" % args) self._run_extension_hooks('__post_parse_args__', args) # Short-circuit and deal with help right away. # nova help xxxx 命令 if args.func == self.do_help: self.do_help(args) return 0 # nova bash-completion elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0
parser是 NovaClientArgumentParser类型,该类型继承自argparse.ArgumentParser,argparse是python中的参数解析库。
get_base_parser方法即添加选项参数,诸如--debug, --timing,--os-username 等等,并会读取环境变量和设置默认值,下面是部分代码:
# Global arguments parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS, ) parser.add_argument('--version', action='version', version=novaclient.__version__) parser.add_argument('--debug', default=False, action='store_true', help="Print debugging output") parser.add_argument('--no-cache', default=not utils.bool_from_str( utils.env('OS_NO_CACHE', default='true')), action='store_false', dest='os_cache', help=argparse.SUPPRESS) parser.add_argument('--no_cache', action='store_false', dest='os_cache', help=argparse.SUPPRESS) parser.add_argument('--os-cache', default=utils.env('OS_CACHE', default=False), action='store_true', help="Use the auth token cache.") parser.add_argument('--timings', default=False, action='store_true', help="Print call timing info") parser.add_argument('--timeout', default=600, metavar='<seconds>', type=positive_non_zero_float, help="Set HTTP call timeout (in seconds)")
用过argparse库的一定不会陌生了。
回到main函数,接下来会设置debug,即如果有--debug选项,则设置logger的level为DEBUG并传入到标准输出流。
(options, args) = parser.parse_known_args(argv)返回解析结果,即options保存所有的选项参数,args保存位置参数,比如nova --debug list, options.debug等于True,args为['list']。
下一个函数get_subcommand_parser是一个核心方法,用于处理子命令比如list, flavor-list, boot等,以下是代码:
def get_subcommand_parser(self, version): parser = self.get_base_parser() self.subcommands = {} subparsers = parser.add_subparsers(metavar='<subcommand>') try: actions_module = { '1.1': shell_v1_1, '2': shell_v1_1, '3': shell_v3, }[version] except KeyError: actions_module = shell_v1_1 #默认是1.1版本 self._find_actions(subparsers, actions_module) self._find_actions(subparsers, self) for extension in self.extensions: self._find_actions(subparsers, extension.module) self._add_bash_completion_subparser(subparsers) return parser
这个方法是根据版本(默认是1.1)寻找可用的方法,我们假设使用shell_v1_1模块,它导入自from novaclient.v1_1 import shell as shell_v1_1,然后调用_find_actions方法。注意:这个方法传入的是一个模块,python中所有东西都是对象,模块也不例外,不过这里我们姑且认为它传入了一个类,类似与java的XXXClass.class类型,以下是代码:
def _find_actions(self, subparsers, actions_module): # actions_module = shell_v1.1 for attr in (a for a in dir(actions_module) if a.startswith('do_')): # attr = do_flavor_list # I prefer to be hypen-separated instead of underscores. command = attr[3:].replace('_', '-') # do_flavor_list -> flavor-list callback = getattr(actions_module, attr) desc = callback.__doc__ or '' action_help = desc.strip() arguments = getattr(callback, 'arguments', []) subparser = subparsers.add_parser(command, help=action_help, description=desc, add_help=False, formatter_class=OpenStackHelpFormatter ) subparser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS, ) self.subcommands[command] = subparser for (args, kwargs) in arguments: subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback)
可见这个方法是利用反射机制获取所有以do_开头的方法,这个do_XXX_XXX,XXX-XXX就是命令名,而do_XXX_XXX就是回调函数,把函数作为变量赋值给callback,是函数式编程的经典用法。最后把callback传入set_defaults方法。
至此我们知道nova list其实调用了novaclient.v1_1.shell.do_list()方法,而nova flavor-list调用了novaclient.v1_1.shell.do_flavor_list()方法,下面以nova --debug flavor-list为例继续深入。
我们看novaclient.v1_1.shell源码,发现好多do_XXX方法,但它本身并不做什么工作,而是调用cs去做,cs是什么现在不管。下面是do_flavor_list方法:
def do_flavor_list(cs, args): """Print a list of available 'flavors' (sizes of servers).""" if args.all: flavors = cs.flavors.list(is_public=None) else: flavors = cs.flavors.list() _print_flavor_list(flavors, args.extra_specs)
现在我们不知道cs是什么东西,那我们继续回到main函数,main函数中间其余代码均是在各种参数检查,我们忽略不管,直接跳到main函数结尾
def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser() # 添加选项,比如--user, --password等 (options, args) = parser.parse_known_args(argv) self.setup_debugging(options.debug) # 如果options中有--debug,则设置logger的level为DEBUG,并输出到标准输出流 # Discover available auth plugins novaclient.auth_plugin.discover_auth_systems() # build available subcommands based on version self.extensions = self._discover_extensions( options.os_compute_api_version) self._run_extension_hooks('__pre_parse_args__') # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. if '--endpoint_type' in argv: spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' # 根据版本解析子命令 subcommand_parser = self.get_subcommand_parser( options.os_compute_api_version) self.parser = subcommand_parser # 如果--help,则打印help信息,并退出 if options.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) #解析命令行参数 argv=['list'] #print("args = %s" % args) self._run_extension_hooks('__post_parse_args__', args) # Short-circuit and deal with help right away. # nova help xxxx 命令 if args.func == self.do_help: self.do_help(args) return 0 # nova bash-completion elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 # 这里省略大量代码 self.cs = client.Client(options.os_compute_api_version, os_username, os_password, os_tenant_name, tenant_id=os_tenant_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, auth_system=os_auth_system, auth_plugin=auth_plugin, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=options.debug, cacert=cacert, timeout=timeout) # 这里省略大量代码 args.func(self.cs, args) # 此时func等于do_flavor_list if args.timings: #如果有--timing选项,则打印请求时间 self._dump_timings(self.cs.get_timings())
可见cs是调用client.Client方法返回的,我们查看其代码client.py:
def get_client_class(version): version_map = { '1.1': 'novaclient.v1_1.client.Client', '2': 'novaclient.v1_1.client.Client', '3': 'novaclient.v3.client.Client', } try: client_path = version_map[str(version)] except (KeyError, ValueError): msg = "Invalid client version '%s'. must be one of: %s" % ( (version, ', '.join(version_map.keys()))) raise exceptions.UnsupportedVersion(msg) return utils.import_class(client_path) def Client(version, *args, **kwargs): client_class = get_client_class(version) return client_class(*args, **kwargs)
不难看出cs即根据版本选择的Client类型,这里我们用的是novaclient.v1_1.client.Client。这个模块可以认为是功能模块的注册类,比如flavors操作模块为flavors.py,为了让他生效,必须注册,即在Client中设置self.flavors=flavors.FlavorManager(self):
self.projectid = project_id self.tenant_id = tenant_id self.flavors = flavors.FlavorManager(self) self.flavor_access = flavor_access.FlavorAccessManager(self) self.images = images.ImageManager(self) self.limits = limits.LimitsManager(self) self.servers = servers.ServerManager(self)
从do_flavor_list方法中cs.flavor.list()即调用了flavors.FlavorManager().list方法。从这里我们可以看出openstack的设计原则,即支持自由灵活的可扩展性,如果需要添加新功能,几乎不需要修改太多代码,只要修改Client注册即可。
我们查看flavors.py中的list方法:
def list(self, detailed=True, is_public=True): """ Get a list of all flavors. :rtype: list of :class:`Flavor`. """ qparams = {} # is_public is ternary - None means give all flavors. # By default Nova assumes True and gives admins public flavors # and flavors from their own projects only. if not is_public: qparams['is_public'] = is_public query_string = "?%s" % urlutils.urlencode(qparams) if qparams else "" detail = "" if detailed: detail = "/detail" return self._list("/flavors%s%s" % (detail, query_string), "flavors")
很明显这里是把list命令组装url请求,然后调用_list方法,由于FlavorManager继承自base.ManagerWithFind,而base.ManagerWithFind继承自Manager,_list方法在Manager中定义。
def _list(self, url, response_key, obj_class=None, body=None): if body: _resp, body = self.api.client.post(url, body=body) else: _resp, body = self.api.client.get(url) if obj_class is None: obj_class = self.resource_class data = body[response_key] # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... if isinstance(data, dict): try: data = data['values'] except KeyError: pass with self.completion_cache('human_id', obj_class, mode="w"): with self.completion_cache('uuid', obj_class, mode="w"): return [obj_class(self, res, loaded=True) for res in data if res]
有源码中看出主要发送url请求即self.api.client.post(url, body=dody)或者self.api.client.get(url),具体根据是否有body,即是否数据选择GET或者POST请求。然后处理返回的数据。self.api在这里其实就是novaclient.v1_1.client.Client,只是前面用cs,这里用api。
我们回到novaclient.v1_1.client.Client的方法中,我们发现除了注册一系列功能外,还有一个比较特殊的,
self.client = client.HTTPClient(username, password, projectid=project_id, tenant_id=tenant_id, auth_url=auth_url, insecure=insecure, timeout=timeout, auth_system=auth_system, auth_plugin=auth_plugin, proxy_token=proxy_token, proxy_tenant_id=proxy_tenant_id, region_name=region_name, endpoint_type=endpoint_type, service_type=service_type, service_name=service_name, volume_service_name=volume_service_name, timings=timings, bypass_url=bypass_url, os_cache=self.os_cache, http_log_debug=http_log_debug, cacert=cacert)
这个self.client是client.HTTPClient类型,真正负责发送url请求的类,部分代码为:
def _cs_request(self, url, method, **kwargs): if not self.management_url: self.authenticate() # Perform the request once. If we get a 401 back then it # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: kwargs.setdefault('headers', {})['X-Auth-Token'] = self.auth_token if self.projectid: kwargs['headers']['X-Auth-Project-Id'] = self.projectid resp, body = self._time_request(self.management_url + url, method, **kwargs) return resp, body except exceptions.Unauthorized as e: try: # frist discard auth token, to avoid the possibly expired # token being re-used in the re-authentication attempt self.unauthenticate() self.authenticate() kwargs['headers']['X-Auth-Token'] = self.auth_token resp, body = self._time_request(self.management_url + url, method, **kwargs) return resp, body except exceptions.Unauthorized: raise e def get(self, url, **kwargs): return self._cs_request(url, 'GET', **kwargs)
最后会调用http.request方法发送请求,这里使用了python库Requests: HTTP for Humans,这个库比httplib2更好,查看地址:http://docs.python-requests.org/en/latest/。接收请求的工作就由nova-api负责了,这里不再深入。
接下来我们简单增加一个没用的功能test,首先在novaclient/v1_1下touch test.py,使用vim增加以下代码:
""" Test interface. """ from novaclient import base class Test(base.Resource): def test(self): print("This is a test") class TestManager(base.Manager): def test(self): print("This is a test")
然后我们需要在client中注册,编辑novaclient/v1_1/client.py文件,增加self.test = test.TestManager(self)
然后在shell下增加入口函数,注册新功能,
def do_test(cs, _args): """ do test. """ cs.test.test()
运行nova test, nova help test查看效果。
mount是linux很常用的命令,用于挂载各种设备(包括本地block设备,NFS,虚拟设备等),umount用于卸载设备。如果挂载一个设备到一个目录中,则通过该目录可以访问设备的文件(必须有权限访问),而原来目录的内容会暂时性隐藏(不会覆盖,卸载后恢复可见)。
mount最常用的使用方式为:
mount device dir
比如 mount /dev/sda1 /mnt
这是mount会自动检测设备的文件系统(如果可能的话,比如fstab,mtab中配置有),如果检测失败,需要指定文件系统类型,使用-t选项指定,比如
mount -t btrfs /dev/sda1 /mnt
/etc/fstab是系统默认挂载的配置,在系统启动时会自动挂载该文件下的设备(设置选项noauto除外。
如果你修改了该文件,你也可以使用 mount -a使系统重新读取该文件进行挂载。
在/etc/mtab会记录当前的挂载状况(在/proc/mounts也记录),当使用mount命令不加任何参数时,默认打印该文件的内容。
你也可以使用-l选项打印当前的挂载状况,使用-t选项过滤打印的文件系统类型。
以前一个设备只能挂载在一个地方,从内核2.4以后,支持把一个目录挂载到另一个目录,这时相当于相同文件内容可以同时有多个访问点(这在chroot中很有用,想想我在新的root中怎么访问/proc /dev目录),使用--bind 或者-B选项挂载目录
比如
mount --bind -t tmpfs /dev/ newDev
mount --bind -t proc /proc newProc
注意使用bind选项只能挂载指定目录下的内容,如果该目录下又有子挂载点,不会自动挂载,使用rbind选项,可以递归挂载,甚至可以挂载/ 到另一个目录中。
比如
mount --rbind -t ext4 / newRoot
不过挂载了就不能卸载了,因为该目录正使用(busy),可以使用lsof命令查看(可以使用--move选项,见以下)。
有时我们不想有人bind,可以使用mount --make-unbindable或者--make-runbindable(递归式)选项设置。比如
mount --make-runbindable /
不能再bind根目录了。
--move选项可以把一个挂载点移动到另一个目录,如
mount --move -t ext4 /mnt /media
-r(或者-o ro)选项指定只读挂载,访问挂载点只能读取内容,不能写,-w(或者-o rw)指定挂载为读写方式,这也是默认方式。
-o 指定挂载选项(多个选项使用逗号分隔),以上已经介绍了ro和rw选项,下面简单介绍下挂载选项:
auto ,再/etc/fstab下指定,使用mount -a时或者系统启动时自动挂载,使用noauto相反。
defaults使用默认挂载选项,相当于rw,suid,dev,exec,auto,nouser,async
dev 说明这是字符设备或者块设备,而不是虚拟设备,相反nodev说明可能是虚拟设备
exec,在挂载点可以执行文件,noexec说明在挂载点上不能执行文件,有一次我在家目录下不小心指定了noexec,写了个c程序,运行./a.out出错,后面发现原来是指定了noexec选项
group 一般文件系统挂载只能root身份才有权限,在fstab下指定group,则属于设备组的用户可以挂载。
owner指定设备的所有者可以挂载,比如某设备属于Mary,则root和Mary都有权限挂载。
remount,重新挂载已经挂载的设备,用于覆盖原来的选项,比如原来的文件系统/dev/sda1是只读的,挂载在/mnt下,则可以使用
mount -o remount,rw /dev/sda1 /mnt 重新挂载,并且可读写。
user 用于fstab文件,任何普通用户都可以挂载该设备。
nouser用于fstab,表明只有root可以挂载,这是默认行为。
还有一些选项,专门针对指定的文件系统,比如ext2的errors。
mount还可以用于挂载loop设备,比如iso镜像,img文件等。此时需要指定文件系统类型(-t)和loop选项,比如
mount -t ext4 -o loop ubuntu.iso /mnt
mount -t iso9660 -o loop ubuntu.iso /mnt
卸载loop设备使用命令losetup -d 或者umount -d
试一下
dd if=/dev/zero of=tmp.img bs=500m count=2
mkfs.ext4 tmp.img
mount -t ext4 tmp.img /mnt
使用mount可以挂载iso文件,但磁盘格式为qcow2则不能直接挂载了,可以查看以前的文章挂载http://krystism.is-programmer.com/posts/47074.html。
在java6中有一个新类Console,能够实现无回显输入,用于安全输入!以下是代码样例:
import java.io.*; import java.util.*; public class ConsoleDemo { public static void main(String[] args) { Console console = System.console(); if (console == null) { System.err.println("Failed to get console!"); return; } String name = console.readLine("Please type your name:"); char[] password1 = null; char[] password2 = null; console.printf("Your name is %s\n", name); password1 = console.readPassword("Please type your password:"); password2 = console.readPassword("Please type your password again:"); if (!Arrays.equals(password1, password2)) { System.err.println("Password doesn't match the confirmation."); return; } console.printf("Hi, %s, Your password is '%s'\n", name, new String(password1)); Arrays.fill(password1, '\0'); /* 安全清理 */ Arrays.fill(password2, '\0'); } }
这个问题很简单,就是在一个无序数组中判断某个元素是否存在,当然如果自己定义数据结构,可以使用trie,hash,如果不要求完全准确,允许误判,还能使用布隆!显然不能使用二分搜索,因为数组是无序的!当我们要经常搜索时,排序是必要的,或者使用其他数据结构。
这篇文章讨论了下http://www.programcreek.com/2014/04/check-if-array-contains-a-value-java/#more-12168,于是自己测试了下,下面是我的代码(必须使用java8版本以上):
import java.util.*; interface StringPolicy { boolean contains(String[] arr, String value); } interface LongPolicy { boolean contains(long[] arr, long value); } class Policies { public static boolean useList(String[] arr, String value) { return Arrays.asList(arr).contains(value); } public static boolean useList(long[] arr, long value) { return Arrays.asList(arr).contains(value); } public static boolean useSet(String[] arr, String value) { return new HashSet<>(Arrays.asList(arr)).contains(value); } public static boolean useSet(long[] arr, long value) { return new HashSet<>(Arrays.asList(arr)).contains(value); } public static boolean useLoop(String[] arr, String value) { for (String s : arr) { if (s.equals(value)) return true; } return false; } public static boolean useLoop(long[] arr, long value) { for (long l : arr) { if (l == value) return true; } return false; } public static boolean useStream(String[] arr, String value) { return Arrays.stream(arr).anyMatch(value::equals); } public static boolean useStream(long[] arr, long value) { return Arrays.stream(arr).anyMatch(l -> l == value); } public static boolean useParallelStream(String[] arr, String value) { return Arrays.stream(arr).parallel().anyMatch(value::equals); } public static boolean useParallelStream(long[] arr, long value) { return Arrays.stream(arr).parallel().anyMatch(l -> l == value); } } public class SearchDemo { public static void fill(String[] arr) { for(int i = 0; i < arr.length; ++i) { arr[i] = String.valueOf(i + 1); } } public static void fill(long[] arr) { for (int i = 0; i < arr.length; ++i) { arr[i] = i + 1; } } public static void fill(String[] arr, String value) { Arrays.fill(arr, value); } public static void fill(long[] arr, long value) { Arrays.fill(arr, value); } public static void test(String name, StringPolicy p, String[] arr, String value, int times) { long start = System.nanoTime(); for (int i = 0; i < times; ++i) { p.contains(arr, value); } long end = System.nanoTime(); long d = (end - start) / 1_000_000; System.out.printf("%s: %d\n", name, d); } public static void test(String name, LongPolicy p, long[] arr, long value, int times) { long start = System.nanoTime(); for (int i = 0; i < times; ++i) { p.contains(arr, value); } long end = System.nanoTime(); long d = (end - start) / 1_000_000; System.out.printf("%s: %d\n", name, d); } public static void main(String[] args) { String[] arr1 = new String[10_000]; long[] arr2 = new long[10_000]; fill(arr1); fill(arr2); System.out.println("******************* String **************************"); test("useLoop", Policies::useLoop, arr1, "A", 10_000); test("useList", Policies::useList, arr1, "A", 10_000); test("useSet", Policies::useSet, arr1, "A", 10_000); test("useStream", Policies::useStream, arr1, "A", 10_000); test("useParallelStream", Policies::useParallelStream, arr1, "A", 10_000); System.out.println("******************* Long **************************"); test("useLoop", Policies::useLoop, arr2, -1, 10_000); test("useList", Policies::useList, arr2, -1, 10_000); test("useSet", Policies::useSet, arr2, -1, 10_000); test("useStream", Policies::useStream, arr2, -1, 10_000); test("useParallelStream", Policies::useParallelStream, arr2, -1, 10_000); }
结果输出是:
众所周知,一般使用java实现单例模式有两种方法,分别为急切式(饥饿式)和双重加锁式,急切式就是在声明时即创建,这样在类加载时就已经创建好了,即时我们可能并不不需要它,它的生命周期是永久的,造成内存泄漏的可能!第二种方式是lazy的,只有在使用时创建,实现了延迟加载。代码为
1.急切式
class Singleton { private final static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance() { return instance; } }
2. 双重加锁式
class Singleton { private volatile static Singleton instance = null; private Singleton(){ } public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
还有一种方法是使用内部类的方法,这主要为克服方法1中的问题,既能实现延迟加载,又能保证线程安全,而且性能不错。代码为:
class Singleton { private Singleton() { } private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }
以上方法都是通过使用private构造方法来阻止外部直接创建对象,但如果使用反射机制,则不能保证实例的唯一性了!!!
public class SingletonDemo { //@SuppressWarnings("unchecked") //public static <T> T newInstance(Class<?> cl) { // T t = null; // try { // t = (T)cl.newInstance(); /* 只能调用public构造方法 */ // } catch(Exception e) { // e.printStackTrace(); // } // return t; //} @SuppressWarnings("unchecked") public static <T> T newInstance(Class<?> cl) { T t = null; try { Constructor<?> constructor = cl.getDeclaredConstructor(); constructor.setAccessible(true); t = (T)constructor.newInstance(); } catch(Exception e) { e.printStackTrace(); } return t; } @SuppressWarnings("unchecked") public static <T> T newInstance(Class<?> cl, Class<?>...args) { T t = null; try { Constructor<?> constructor = cl.getDeclaredConstructor(args); constructor.setAccessible(true); t = (T)constructor.newInstance("name", 1); } catch(Exception e) { e.printStackTrace(); } return t; } public static void main(String[] args) { Singleton instance = Single.INSTANCE; Singleton s1 = newInstance(Singleton.class, String.class, int.class); Singleton s2 = newInstance(Singleton.class, String.class, int.class); //Tmp t1 = newInstance(Tmp.class); //Tmp t2 = newInstance(Tmp.class); //System.out.println(s1 == s2); //System.out.println(t1 == t2); } }
Joshua Bloch (Effect java 作者)提出一种新的方法实现单例模式,就是使用Enum(其实也是类,一种特殊的类,构造方法必须是private ,final的)!如下:
enum Singleton { INSTANCE; public void sayHi() { System.out.println("Hi"); } }
这样当试图通过反射机制创建对象时,会抛出异常!
从java7开始在concurrent包中加入了Phaser类,它几乎可以取代CountDownLatch和CyclicBarrier, 其功能更灵活,更强大,支持动态调整需要控制的线程数。下面以一个具体实例说明这个Phaser类的用处,相信理解这个例子后,其功能不言而喻。
例子:有若干考生参加考试,考试科目是统一的,考试顺序为语文、数学、英语共三门,若其中一门挂科,则不能参加后面的考试。现在假设考试时间不受限制,开考时间以最后一名考完上一科时间为准,即先考完的考生并且成绩合格,需要参加下一门考试,则必须等待所有考生考完当科。
import java.util.*; import java.util.concurrent.*; class Examinee implements Runnable { public static Random random = new Random(new Date().getTime()); private String name; private Phaser phaser; private int english; private int chinese; private int math; public Examinee(String name, Phaser phaser) { this.name = name; this.phaser = phaser; } public boolean english() { int time = random.nextInt(10); try { TimeUnit.SECONDS.sleep(time); /* 考试时间是一个随机数 */ } catch (Exception e) { e.printStackTrace(System.err); } english = random.nextInt(101) ; /* 假定考试成绩是随机的 */ if (english < 60) { System.out.printf("%s Failed to pass English!\n", name); phaser.arriveAndDeregister(); /* 考试不合格,取消考试资格 */ return false; } else { phaser.arriveAndAwaitAdvance(); /* 通过考试,可以进行下一门考试 */ return true; } } public boolean chinese() { int time = random.nextInt(10); try { TimeUnit.SECONDS.sleep(time); } catch (Exception e) { e.printStackTrace(System.err); } chinese = random.nextInt(101) ; if (chinese < 60) { System.out.printf("%s Failed to pass Chinese!\n", name); phaser.arriveAndDeregister(); return false; } else { phaser.arriveAndAwaitAdvance(); return true; } } public boolean math() { int time = random.nextInt(10); try { TimeUnit.SECONDS.sleep(time); } catch (Exception e) { e.printStackTrace(System.err); } math = random.nextInt(101) ; if (math < 60) { System.out.printf("%s Failed to pass Math!\n", name); phaser.arriveAndDeregister(); return false; } else { phaser.arriveAndAwaitAdvance(); return true; } } public void showInfo() { System.out.printf("%s\tChinese:%d\tEnglish:%d\tMath:%d\n", name, chinese, english, math); phaser.arriveAndDeregister(); } @Override public void run() { phaser.arriveAndAwaitAdvance(); /* 等待考生做好准备,即在同一个起跑线上 */ System.out.printf("%s is ready!\n", name); if (!chinese() || !english() || !math()) return; showInfo(); /* 只显示所有科目均合格的考生分数 */ } } public class PhaserDemo2 { public static void main(String[] args) { final String[] names = {"Jim", "Hary", "Mary", "Jhon", "Ali", "Lucy", "Wang", "Zhang", "Li", "Uhm", "Hey", "Gu", "Asu", "Who"}; Phaser phaser = new Phaser(names.length); List<Thread> threads = new ArrayList<>(names.length); for (String name : names) { threads.add(new Thread(new Examinee(name, phaser), name)); /* 创建各个考生线程 */ } for (Thread t : threads) { t.start(); /* 开始考试 */ } for (Thread t : threads) { try { t.join(); /* 等待考试结束 */ } catch (InterruptedException e) { e.printStackTrace(System.err); } } System.out.printf("Terminal ? %s\n", phaser.isTerminated()); } }
由上面的例子,可以看出Phaser的功能。其中arrive方法很像CountDownLatch的countDown方法和CyclicBarrier的await方法,请自行比较之!Phaser类还有很多其他的功能,可以查看其API文档。
java.util.concurrent包中有许多涉及多线程并发的工具,使用这些现成工具可以大大简化编码,并且安全高效。
Exchanger提供了一个同步点,即调用exchange方法,当两个线程都到达这个同步点时,两个线程交换数据。
比如,有两个人,一个人需要买一本书,另一个人当然卖一本书,这两个人约定在某地交易,但这两个人距离约定地不同,到达目的地的时间也不同,先到的必须等待直到另一个人到达完成交易。
使用Exchanger类可以很容易实现!
import java.util.*; import java.util.concurrent.*; interface Exchangable { } class Money implements Exchangable { private int value; public Money(int value) { this.value = value; } public String toString() { return "$" + value; } } class Book implements Exchangable { private int value; private String name; public Book(String name, int value) { this.name = name; this.value = value; } public String toString() { String format = "Book: %s($%d)"; return String.format(format, name, value); } } class Seller implements Runnable { Exchanger<Exchangable> exchanger; Exchangable book; public Seller(Exchanger<Exchangable> exchanger) { this.exchanger = exchanger; this.book = new Book("Love Java", 88); } public void run() { try { Random random = new Random(System.currentTimeMillis()); int seconds = random.nextInt(10) + 1; System.out.println("Hi, I am Seller, I am going to sell a book,waiting " + seconds + "s"); TimeUnit.SECONDS.sleep(seconds); System.out.println("Hi, I am Seller, I get " + exchanger.exchange(book)); } catch (Exception e) { } } } class Buyer implements Runnable { Exchanger<Exchangable> exchanger; Exchangable money; public Buyer(Exchanger<Exchangable> exchanger) { this.exchanger = exchanger; this.money = new Money(88); } public void run() { try { Random random = new Random(System.currentTimeMillis() + 99); int seconds = random.nextInt(10) + 1; System.out.println("Hi, I am Buyer, I am going to buy a book,waiting " + seconds + "s"); TimeUnit.SECONDS.sleep(seconds); System.out.println("Hi, I am Buyer, I get " + exchanger.exchange(money)); } catch (Exception e) { } } } public class ExchangerDemo { public static void main(String[] args) { Exchanger<Exchangable> exchanger = new Exchanger<>(); Seller seller = new Seller(exchanger); Buyer buyer = new Buyer(exchanger); new Thread(seller).start(); new Thread(buyer).start(); } }
Exchanger类使用很简单,只有两个public方法,一个是exchange(V x)方法,死等待直到完成交易,另一个是exchange(V x, long time, TimeUnit timeunit),其中x是要交换的数据,time是时间,timeunit指定时间单位,合起来表示最长等待时间,比如exchange(x, 5, TimeUnit.SECONDS)表示最长等待5秒!
ubuntu13.10或以下版本也可以编译,只是源中maven和protobuf都版本太低,需要自己下源码重新编译安装,很繁琐。
hadoop2.5有编译好的发布包,但是默认是64位的,老是出现以下错误
OpenJDK Server VM warning: You have loaded library /hadoop/lib/native/libhadoop.so.1.0.0 which might have disabled stack guard. The VM will try to fix the stack guard now. It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'. 14/08/19 21:07:42 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
看起来好像只是警告无所谓,但在启动脚本中NAMENODES=$(hdfs getconf -namenodes),这时会把所有以上输出以空格作为分隔符作为namenode,显然出错,而且没法加载本地库,所以必须进行重新build。
网上很多说修改HADOOP_OPTS可以解决以上问题,但我试过很多次,根本不行,其实想想也是不可能的,64位的库怎么可以运行在32位呢!
从官方http://www.apache.org/dyn/closer.cgi/hadoop/core/下载源码。
源码是使用maven进行管理的,要求版本必须是maven3以上,由于maven调用了ant,因此ant也是必要的。
另外本地库使用c写的,还要求有cmake,make,auto工具,否则无法编译。总结以下是必要的包:
openstack热迁移有多种方式,下面配置block-migration方式,即以拷贝的方式实现热迁移,这种方式配置简单,但需要从源计算节点拷贝文件到目标计算节点,会比较耗时。而使用共享存储的方式,把所有的/var/lib/nova/instances下的虚拟机信息放到一块,比如放到控制节点,所有的计算使用nfs节点挂载该目录。这种方式迁移时不需要拷贝虚拟机文件,但所有的计算机节点的文件集中一起,管理不方便,配置方法见官方文档。
下面是block-migration方式配置:
首先在所有的计算节点中的nova.conf中增加live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_UNSAFE
开启热迁移功能。
然后需要配置versh免密码连接,修改/etc/libvirt/libvirtd.conf