欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

goweb-web服务

程序员文章站 2022-05-18 19:09:52
Web服务 Web服务可以让你在HTTP协议的基础上通过XML或者JSON来交换信息。如果你想知道上海的天气预报、中国石油的股价或者淘宝商家的一个商品信息,你可以编写一段简短的代码,通过抓取这些信息然后通过标准的接口开放出来,就如同你调用一个本地函数并返回一个值。 Web服务背后的关键在于平台的无关 ......

web服务

web服务可以让你在http协议的基础上通过xml或者json来交换信息。如果你想知道上海的天气预报、中国石油的股价或者淘宝商家的一个商品信息,你可以编写一段简短的代码,通过抓取这些信息然后通过标准的接口开放出来,就如同你调用一个本地函数并返回一个值。

web服务背后的关键在于平台的无关性,你可以运行你的服务在linux系统,可以与其他windows的asp.net程序交互,同样的,也可以通过同一个接口和运行在freebsd上面的jsp无障碍地通信。

目前主流的有如下几种web服务:rest、soap。

socket编程

在很多底层网络应用开发者的眼里一切编程都是socket,话虽然有点夸张,但却也几乎如此了,现在的网络编程几乎都是用socket来编程。你想过这些情景么?我们每天打开浏览器浏览网页时,浏览器进程怎么和web服务器进行通信的呢?当你用qq聊天时,qq进程怎么和服务器或者是你的好友所在的qq进程进行通信的呢?当你打开ppstream观看视频时,ppstream进程如何与视频服务器进行通信的呢? 如此种种,都是靠socket来进行通信的,以一斑窥全豹,可见socket编程在现代编程中占据了多么重要的地位,这一节我们将介绍go语言中如何进行socket编程。

什么是socket?

socket起源于unix,而unix基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。socket就是该模式的一个实现,网络的socket数据传输是一种特殊的i/o,socket也是一种文件描述符。socket也具有一个类似于打开文件的函数调用:socket(),该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过该socket实现的。

常用的socket类型有两种:流式socket(sock_stream)和数据报式socket(sock_dgram)。流式是一种面向连接的socket,针对于面向连接的tcp服务应用;数据报式socket是一种无连接的socket,对应于无连接的udp服务应用。

socket如何通信

网络中的进程之间如何通过socket通信呢?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程pid来唯一标识一个进程,但是在网络中这是行不通的。其实tcp/ip协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中需要互相通信的进程,就可以利用这个标志在他们之间进行交互

使用tcp/ip协议的应用程序通常采用应用编程接口:unix bsd的套接字(socket)和unix system v的tli(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是为什么说“一切皆socket”。

socket基础知识

通过上面的介绍我们知道socket有两种:tcp socket和udp socket,tcp和udp是协议,而要确定一个进程的需要三元组,需要ip地址和端口。

ipv4地址

目前的全球因特网所采用的协议族是tcp/ip协议。ip是tcp/ip协议中网络层的协议,是tcp/ip协议族的核心协议。目前主要采用的ip协议的版本号是4(简称为ipv4),发展至今已经使用了30多年。

ipv4的地址位数为32位,也就是最多有2的32次方的网络设备可以联到internet上。近十年来由于互联网的蓬勃发展,ip位址的需求量愈来愈大,使得ip位址的发放愈趋紧张,前一段时间,据报道ipv4的地址已经发放完毕,我们公司目前很多服务器的ip都是一个宝贵的资源。

地址格式类似这样:127.0.0.1 172.122.121.111

ipv6地址

ipv6是下一版本的互联网协议,也可以说是下一代互联网的协议,它是为了解决ipv4在实施过程中遇到的各种问题而被提出的,ipv6采用128位地址长度,几乎可以不受限制地提供地址。按保守方法估算ipv6实际可分配的地址,整个地球的每平方米面积上仍可分配1000多个地址。在ipv6的设计过程中除了一劳永逸地解决了地址短缺问题以外,还考虑了在ipv4中解决不好的其它问题,主要有端到端ip连接、服务质量(qos)、安全性、多播、移动性、即插即用等。

地址格式类似这样:2002:c0e8:82e7:0:0:0:c0e8:82e7

这一节讲了tcp编程,幸好我之前学习过一点tcp编程的知识,不然又是啥都不知道~~~~

websocket

websocket是html5的重要特性,它实现了基于浏览器的远程socket,它使浏览器和服务器可以进行全双工通信,许多浏览器(firefox、google chrome和safari)都已对此做了支持。

在websocket出现之前,为了实现即时通信,采用的技术都是“轮询”,即在特定的时间间隔内,由浏览器对服务器发出http request,服务器在收到请求后,返回最新的数据给浏览器刷新,“轮询”使得浏览器需要对服务器不断发出请求,这样会占用大量带宽。

websocket采用了一些特殊的报头,使得浏览器和服务器只需要做一个握手的动作,就可以在浏览器和服务器之间建立一条连接通道。且此连接会保持在活动状态,你可以使用javascript来向连接写入或从中接收数据,就像在使用一个常规的tcp socket一样。它解决了web实时化的问题,相比传统http有如下好处:

  • 一个web客户端只建立一个tcp连接
  • websocket服务端可以推送(push)数据到web客户端.
  • 有更加轻量级的头,减少数据传送量

websocket url的起始输入是ws://或是wss://(在ssl上

go实现websocket

websocket分为客户端和服务端,接下来我们将实现一个简单的例子:用户输入信息,客户端通过websocket将信息发送给服务器端,服务器端收到信息之后主动push信息到客户端,然后客户端将输出其收到的信息,客户端的代码如下:

<html>
<head></head>
<body>
    <script type="text/javascript">
        var sock = null;
        var wsuri = "ws://127.0.0.1:1234";

        window.onload = function() {

            console.log("onload");

            sock = new websocket(wsuri);

            sock.onopen = function() {
                console.log("connected to " + wsuri);
            }

            sock.onclose = function(e) {
                console.log("connection closed (" + e.code + ")");
            }

            sock.onmessage = function(e) {
                console.log("message received: " + e.data);
            }
        };

        function send() {
            var msg = document.getelementbyid('message').value;
            sock.send(msg);
        };
    </script>
    <h1>websocket echo test</h1>
    <form>
        <p>
            message: <input id="message" type="text" value="hello, world!">
        </p>
    </form>
    <button onclick="send();">send message</button>
</body>
</html>

可以看到客户端js,很容易的就通过websocket函数建立了一个与服务器的连接sock,当握手成功后,会触发websocket对象的onopen事件,告诉客户端连接已经成功建立。客户端一共绑定了四个事件。

1)onopen 建立连接后触发
2)onmessage 收到消息后触发
3)onerror 发生错误时触发
4)onclose 关闭连接时触发
我们服务器端的实现如下:

package main

import (
    "golang.org/x/net/websocket"
    "fmt"
    "log"
    "net/http"
)

func echo(ws *websocket.conn) {
    var err error

    for {
        var reply string

        if err = websocket.message.receive(ws, &reply); err != nil {
            fmt.println("can't receive")
            break
        }

        fmt.println("received back from client: " + reply)

        msg := "received:  " + reply
        fmt.println("sending to client: " + msg)

        if err = websocket.message.send(ws, msg); err != nil {
            fmt.println("can't send")
            break
        }
    }
}

func main() {
    http.handle("/", websocket.handler(echo))

    if err := http.listenandserve(":1234", nil); err != nil {
        log.fatal("listenandserve:", err)
    }
}

当客户端将用户输入的信息send之后,服务器端通过receive接收到了相应信息,然后通过send发送了应答信息

目前随着html5的发展,我想未来websocket会是web开发的一个重点,我们需要储备这方面的知识

rest

restful,是目前最为流行的一种互联网软件架构。因为它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。本小节我们将来学习它到底是一种什么样的架构?以及在go里面如何来实现它。

什么是rest

rest(representational state transfer)这个概念,首次出现是在 2000年roy thomas fielding(他是http规范的主要编写者之一)的博士论文中,它指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是restful的。

要理解什么是rest,我们需要理解下面几个概念:

  • 资源(resources) rest是"表现层状态转化",其实它省略了主语。"表现层"其实指的是"资源"的"表现层"。
    那么什么是资源呢?就是我们平常上网访问的一张图片、一个文档、一个视频等。这些资源我们通过uri来定位,也就是一个uri表示一个资源。

  • 表现层(representation)

资源是做一个具体的实体信息,他可以有多种的展现方式。而把实体展现出来就是表现层,例如一个txt文本信息,他可以输出成html、json、xml等格式,一个图片他可以jpg、png等方式展现,这个就是表现层的意思。

uri确定一个资源,但是如何确定它的具体表现形式呢?应该在http请求的头信息中用accept和content-type字段指定,这两个字段才是对"表现层"的描述。

  • 状态转化(state transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,肯定涉及到数据和状态的变化。而http协议是无状态的,那么这些状态肯定保存在服务器端,所以如果客户端想要通知服务器端改变数据和状态的变化,肯定要通过某种方式来通知它。

客户端能通知服务器端的手段,只能是http协议。具体来说,就是http协议里面,四个表示操作方式的动词:get、post、put、delete。它们分别对应四种基本操作:get用来获取资源,post用来新建资源(也可以用于更新资源),put用来更新资源,delete用来删除资源。

综合上面的解释,我们总结一下什么是restful架构:

(1)每一个uri代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个http动词,对服务器端资源进行操作,实现"表现层状态转化"。
web应用要满足rest最重要的原则是:客户端和服务器之间的交互在请求之间是无状态的,即从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外此请求可以由任何可用服务器回答,这十分适合云计算之类的环境。因为是无状态的,所以客户端可以缓存数据以改进性能。

另一个重要的rest原则是系统分层,这表示组件无法了解除了与它直接交互的层次以外的组件。通过将系统知识限制在单个层,可以限制整个系统的复杂性,从而促进了底层的独立性。

当rest架构的约束条件作为一个整体应用时,将生成一个可以扩展到大量客户端的应用程序。它还降低了客户端和服务器之间的交互延迟。统一界面简化了整个系统架构,改进了子系统之间交互的可见性。rest简化了客户端和服务器的实现,而且对于使用rest开发的应用程序更加容易扩展

restful的实现

go没有为rest提供直接支持,但是因为restful是基于http协议实现的,所以我们可以利用net/http包来自己实现,当然需要针对rest做一些改造,rest是根据不同的method来处理相应的资源,目前已经存在的很多自称是rest的应用,其实并没有真正的实现rest

rest就是根据不同的method访问同一个资源的时候实现不同的逻辑处理。

rest是一种架构风格,汲取了www的成功经验:无状态,以资源为中心,充分利用http协议和uri协议,提供统一的接口定义,使得它作为一种设计web服务的方法而变得流行。在某种意义上,通过强调uri和http等早期internet标准,rest是对大型应用程序服务器时代之前的web方式的回归。目前go对于rest的支持还是很简单的,通过实现自定义的路由规则,我们就可以为不同的method实现不同的handle,这样就实现了rest的架构。

rpc

前面几个小节我们介绍了如何基于socket和http来编写网络应用,通过学习我们了解了socket和http采用的是类似"信息交换"模式,即客户端发送一条信息到服务端,然后(一般来说)服务器端都会返回一定的信息以表示响应。客户端和服务端之间约定了交互信息的格式,以便双方都能够解析交互所产生的信息。但是很多独立的应用并没有采用这种模式,而是采用类似常规的函数调用的方式来完成想要的功能。

rpc就是想实现函数调用模式的网络化。客户端就像调用本地函数一样,然后客户端把这些参数打包之后通过网络传递到服务端,服务端解包到处理过程中执行,然后执行的结果反馈给客户端。

rpc(remote procedure call protocol)——远程过程调用协议,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。它假定某些传输协议的存在,如tcp或udp,以便为通信程序之间携带信息数据。通过它可以使函数调用模式网络化。在osi网络通信模型中,rpc跨越了传输层和应用层。rpc使得开发包括网络分布式多程序在内的应用程序更加容易。

go rpc

go标准包中已经提供了对rpc的支持,而且支持三个级别的rpc:tcp、http、jsonrpc。但go的rpc包是独一无二的rpc,它和传统的rpc系统不同,它只支持go开发的服务器与客户端之间的交互,因为在内部,它们采用了gob来编码。

go已经提供了对rpc的良好支持,通过上面http、tcp、json rpc的实现,我们就可以很方便的开发很多分布式的web应用,我想作为读者的你已经领会到这一点。但遗憾的是目前go尚未提供对soap rpc的支持,欣慰的是现在已经有第三方的开源实现了。

可惜,rpc这我根本看不懂┭┮﹏┭┮,只是大概知道它是怎嘛做的

这一章我们介绍了目前流行的几种主要的网络应用开发方式,第一小节介绍了网络编程中的基础:socket编程,因为现在网络正在朝云的方向快速进化,作为这一技术演进的基石的的socket知识,作为开发者的你,是必须要掌握的。第二小节介绍了正愈发流行的html5中一个重要的特性websocket,通过它,服务器可以实现主动的push消息,以简化以前ajax轮询的模式。第三小节介绍了rest编写模式,这种模式特别适合来开发网络应用api,目前移动应用的快速发展,我觉得将来会是一个潮流。第四小节介绍了go实现的rpc相关知识,对于上面四种开发方式,go都已经提供了良好的支持,net包及其子包,是所有涉及到网络编程的工具的所在地

(〝▼皿▼)这一章一节比一节难,尤其是最后一节