some基础知识

来源:互联网 发布:那些事那些人 知乎 编辑:程序博客网 时间:2024/04/29 01:20

协程

Lua的协程通过扩展库coroutine来实现,其中的所有函数如下(具体可以参考Lua的官方manual)
1
2
3
4
5
6
coroutine.create
coroutine.resume
coroutine.running
coroutine.status
coroutine.wrap
coroutine.yield
如果将程序分为IO密集型应用和CPU密集型应用,二者的server的发展如下:
IO密集型应用: 多进程->多线程->事件驱动->协程
CPU密集型应用:多进程-->多线程


如果说多进程对于多CPU,多线程对应多核CPU,那么事件驱动和协程则是在充分挖掘不断提高性能的单核CPU的潜力。

无论是线程还是进程,使用的都是同步进程每次阻塞,切换都需要陷入系统调用(system call),先让CPU跑操作系统的调度程序,然后再有调度程序决定跑哪一个进程(线程)。多个线程之间在一些访问互斥的代码时还需要加上锁,这也是导致所线程编程难的原因之一。

现下流行的异步server都是基于事件驱动的(nginx)。事件驱动简化了编程模型,很好的解决了多线程难于编程,难于调试的问题。异步事件驱动模型中,会把导致阻塞的操作转化为一个异步操作,主线程负责起这个异步操作,并处理这个异步操作的结果。由于所有阻塞的操作都转化为异步操作,理论上主线程的大部分时间都是在处理实际的计算任务,少了多线程的调度时间,所以这种模型的性能通常会比较好

以nginx为代表的事件驱动的异步server正在横扫天下

许多GUI框架(如windows的MFC,android的GUI框架),zookeeper的watcher等都使用了事件驱动机制。

协程的好处:

  • 跨平台
  • 跨体系架构
  • 无需线程上下文切换的开销
  • 无需原子操作锁定及同步的开销
  • 方便切换控制流,简化编程模型
  • 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

缺点:

  • 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
  • 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序:这一点和事件驱动一样,可以使用异步IO操作来解决
========================================================================================

========================================================================================

========================================================================================

协程通过yield实现,或者通过其他三方的版本,如greenlet,gevent

from greenlet import greenlet


def test1():
    print (12)
    gr2.switch()  #使用switch进行切换
    print (34)
    gr2.switch()


def test2():
    print (56)
    gr1.switch()
    print (78)


gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

2222222222222222222222222222222222222222222222222222222222222222222222222222

2222222222222222222222222222222222222222222222222222222222222222222222222222

import gevent


def foo():
    print('\033[32;1mRunning in foo\033[0m')
    gevent.sleep(0)  #遇到阻塞自动切换
    print('\033[32;1mExplicit context switch to foo again\033[0m')


def bar():
    print('Explicit context to bar')
    gevent.sleep(0)
    print('Implicit context switch back to bar')


def ex():
    print('\033[31;1mExplicit context to ex\033[0m')
    gevent.sleep(0)
    print('\033[31;1mImplicit context switch back to ex\033[0m')


gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
    gevent.spawn(ex),
])

33333333333333333333333333333333333333333333333333333333333333333

33333333333333333333333333333333333333333333333333333333333333333

33333333333333333333333333333333333333333333333333333333333333333

协程和线程差异
那么这个过程看起来比线程差不多哇。其实不然 线程切换从系统层面远不止 保存和恢复 CPU上下文这么简单。操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
4444444444444444444444444444444444444444444444444444444444444444444444444444444
4444444444444444444444444444444444444444444444444444444444444444444444444444444
4444444444444444444444444444444444444444444444444444444444444444444444444444444
RPC(remote procedure call protocol)-------------远程过程调用协议
RPC是系统间的一种通信方式,系统间常用的通信方式还有http,webservice,rpc等,一般来讲rpc比http和webservice性能高一些,常见的RPC框架有:thrift,Finagle,dubbo,grpc,json-rpc等。
5555555555555555555555555555555555555555555555555555555555555555555555555555555

5555555555555555555555555555555555555555555555555555555555555555555555555555555

5555555555555555555555555555555555555555555555555555555555555555555555555555555

RPC简述

在某种意义上,WebService、REST均是RPC的实现,那么RPC的发展过程如何呢?本文参考了wikipedia,对RPC做一下简要摘记。

RPC(RemoteProcedureCall),是进程间通信(IPC,Inter-Process Communication)的一种技术,一般指不同机器上的进程间通信。在采用C等古老语言编程的时候,RPC被称作了对S端的“子程序”的调用,所以称“过程调用”。在OOP出现后,RPC也可以称为远程方法调用(RemoteMethodInvocation),或者远程调用(RemoteInvocation)。

RPC过程可以是同步的,也可以是异步的。同步方式:C端向S端发送请求,阻塞等待;S端执行一段子程序,发送响应;C端继续执行;异步方式,比如XHTTP调用。

 

RPC的调用过程(Stub这个术语应该是借鉴了JavaRMI):

  1. Client向ClientStub发送请求(Call)。
  2. ClientStub对请求参数进行封包(也叫Marshalling),发出系统调用,OS向S端发送消息。
  3. S端接收到消息后,把封包消息传递给ServerStub。ServerStub解包(UnMarshalling)。
  4. ServerStub调用S端的子程序。处理完毕后,以同样的方式向C端发送结果。

注:ServerStub又叫Skeleton。

 

什么是Stub?

Stub是一段代码,用来转换RPC过程中传递的参数。处理内容包括不同OS之间的大小端问题。另外,Client端一般叫Stub,Server端一般叫Skeleton。

生产方式:1)手动生成,比较麻烦;2)自动生成,使用IDL(InterfaceDescriptionLanguate),定义C/S的接口。

交互机制标准:一般采用IDL,生成IDL的工具 RPCGEN()。

 

RPC相关实现方式

  • JavaRMI
  • XML-RPC,XML+HTTP来进行机器之间的调用
  • JSON-RPC
  • SOAP,XML-RPC的升级版
  • Facebook Thrift
  • CORBA
  • AMF,AdobeFlex
  • Libevent,是一个用于构建RPC Server和Client的框架。
  • WCF,来自微软
  • .net Remoting,逐步被WCF取代


66666666666666666666666666666666666666666666666666666666666666666

66666666666666666666666666666666666666666666666666666666666666666

66666666666666666666666666666666666666666666666666666666666666666

Java RMI之HelloWorld篇
 
Java RMI 指的是远程方法调用 (Remote Method Invocation)。它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法。可以用此方法调用的任何对象必须实现该远程接口。
 
Java RMI不是什么新技术(在Java1.1的时代都有了),但却是是非常重要的底层技术。
大名鼎鼎的EJB都是建立在rmi基础之上的,现在还有一些开源的远程调用组件,其底层技术也是rmi。
 
在大力鼓吹Web Service、SOA的时代,是不是每个应用都应该选用笨拙的Web Service组件来实现,通过对比测试后,RMI是最简单的,在一些小的应用中是最合适的。
 
下面通过一个简单的例子来说明RMI的原理和应用,下面这个例子是一个简单HelloWorld,但已涵盖RMI的核心应用与开发模式。
 
/** 
* Created by IntelliJ IDEA. 
* User: leizhimin 
* Date: 2008-8-7 21:50:02 
* 定义一个远程接口,必须继承Remote接口,其中需要远程调用的方法必须抛出RemoteException异常 
*/
 
public interface IHello extends Remote { 

    /** 
     * 简单的返回“Hello World!"字样 
     * @return 返回“Hello World!"字样 
     * @throws java.rmi.RemoteException 
     */
 
    public String helloWorld() throws RemoteException; 

    /** 
     * 一个简单的业务方法,根据传入的人名返回相应的问候语 
     * @param someBodyName  人名 
     * @return 返回相应的问候语 
     * @throws java.rmi.RemoteException 
     */
 
    public String sayHelloToSomeBody(String someBodyName) throws RemoteException; 
}
 
/** 
* Created by IntelliJ IDEA. 
* User: leizhimin 
* Date: 2008-8-7 21:56:47 
* 远程的接口的实现 
*/
 
public class HelloImpl extends UnicastRemoteObject implements IHello { 
    /** 
     * 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须声明抛出RemoteException异常 
     * 
     * @throws RemoteException 
     */
 
    public HelloImpl() throws RemoteException { 
    } 

    /** 
     * 简单的返回“Hello World!"字样 
     * 
     * @return 返回“Hello World!"字样 
     * @throws java.rmi.RemoteException 
     */
 
    public String helloWorld() throws RemoteException { 
        return "Hello World!"
    } 

    /** 
     * 一个简单的业务方法,根据传入的人名返回相应的问候语 
     * 
     * @param someBodyName 人名 
     * @return 返回相应的问候语 
     * @throws java.rmi.RemoteException 
     */
 
    public String sayHelloToSomeBody(String someBodyName) throws RemoteException { 
        return "你好," + someBodyName + "!"
    } 
}
 
/** 
* Created by IntelliJ IDEA. 
* User: leizhimin 
* Date: 2008-8-7 22:03:35 
* 创建RMI注册表,启动RMI服务,并将远程对象注册到RMI注册表中。 
*/
 
public class HelloServer { 
    public static void main(String args[]) { 

        try { 
            //创建一个远程对象 
            IHello rhello = new HelloImpl(); 
            //本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少(Java默认端口是1099),必不可缺的一步,缺少注册表创建,则无法绑定对象到远程注册表上 
            LocateRegistry.createRegistry(8888); 

            //把远程对象注册到RMI注册服务器上,并命名为RHello 
            //绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的) 
            Naming.bind("rmi://localhost:8888/RHello",rhello); 
//            Naming.bind("//localhost:8888/RHello",rhello); 

            System.out.println(">>>>>INFO:远程IHello对象绑定成功!"); 
        } catch (RemoteException e) { 
            System.out.println("创建远程对象发生异常!"); 
            e.printStackTrace(); 
        } catch (AlreadyBoundException e) { 
            System.out.println("发生重复绑定对象异常!"); 
            e.printStackTrace(); 
        } catch (MalformedURLException e) { 
            System.out.println("发生URL畸形异常!"); 
            e.printStackTrace(); 
        } 
    } 
}
 
/** 
* Created by IntelliJ IDEA. 
* User: leizhimin 
* Date: 2008-8-7 22:21:07 
* 客户端测试,在客户端调用远程对象上的远程方法,并返回结果。 
*/
 
public class HelloClient { 
    public static void main(String args[]){ 
        try { 
            //在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法 
            IHello rhello =(IHello) Naming.lookup("rmi://localhost:8888/RHello"); 
            System.out.println(rhello.helloWorld()); 
            System.out.println(rhello.sayHelloToSomeBody("熔岩")); 
        } catch (NotBoundException e) { 
            e.printStackTrace(); 
        } catch (MalformedURLException e) { 
            e.printStackTrace(); 
        } catch (RemoteException e) { 
            e.printStackTrace();   
        } 
    } 
}
 
运行RMI服务端程序:
 
运行RMI客户端程序:
 
总结:
从上面的过程来看,RMI对服务器的IP地址和端口依赖很紧密,但是在开发的时候不知道将来的服务器IP和端口如何,但是客户端程序依赖这个IP和端口。
这也是RMI的局限性之一。这个问题有两种解决途径:一是通过DNS来解决,二是通过封装将IP暴露到程序代码之外。
RMI的局限性之二是RMI是Java语言的远程调用,两端的程序语言必须是Java实现,对于不同语言间的通讯可以考虑用Web Service或者公用对象请求代理体系(CORBA)来实现。
 
http://java.sun.com/javase/6/docs/technotes/guides/rmi/index.html
 
http://java.sun.com/javase/6/docs/technotes/guides
 
http://docs.huihoo.com/java/rmi/whitepage/index.html
 


0 0