golang 微服务

一、 认识微服务

  1.      行业背景

             不同行业 IT 系统更新频率

  

      IT 系统存在的问题

  

          微服务架构在企业中应用情况

   

    docker 在企业中的使用情况(容器)  

        

 2.      什么是微服务

    使用一套小服务来开发单个应用的方式,每个服务运行在独立的进程里,一般采用

    轻量级的通讯机制互联,并且它们可以通过自动化的方式部署

    什么叫微?

    代码量少?开发时间少?微是设计思想

 3.      微服务的特点

    单一职责:独立的业务单独放在一个项目里,比如订单服务作为一个项目轻量级的通信:http,rpc,非轻量级(例如 java 的 RMI)

    隔离性:每个服务相互隔离,不干扰有自己的数据

    技术多样性

4.      微服务诞生背景

    互联网行业的快速发展,需求变化快,用户数量变化快

    敏捷开发深入人心,用最小的代价,做最快的迭代,频繁修改、测试、上线容器技术的成熟,是微服务的技术基础

 5.      互联网架构演进之路

     

  

    单体架构

    所有功能集成在一个项目中

        项目整个打包,可以部署到服务器运行应用与数据库可以分开部署,提高性能

        优点:

              小项目的首选,开发成本低,架构简单

        缺点:

              项目复杂之后,很难扩展和维护扩展成本高,有瓶颈

              技术栈受限 

                 

    垂直架构

         对于单体架构的拆分,大项目拆成单个的项目结构存在数据冗余

         项目之间要处理数据同步,通过数据库同步

         优点:

                小项目的首选,架构简单避免单体架构的无限扩大技术不再受限了

         缺点:

                很多功能放在一个工程中,有一定瓶颈

     系统性能扩展要通过集群结点扩展,成本较高

              

     SOA 架构(面向服务的架构)

         将重复性的功能进行抽取,抽取成对应的服务通过 ESB 服务总线去访问

         优点:

                  提高系统可重用性

                  ESB 减少系统接口耦合问题

         缺点:

                  系统与服务界限模糊,不利于开发

                  ESB 服务接口协议不固定,不利于系统维护抽取粒度较大,有一些耦合性

          

     微服务架构

         将服务层一个一个抽取为微服务遵循单一原则

         微服务之间采用一些轻量协议传输数据

         优点:

                服务拆分粒度非常细,利于开发提高系统可维护性

                比 ESB 更轻量

                适用于互联网更新换代快的情况

         缺点:

                服务过多,服务治理成本高开发技术要求更高

             

 6.      微服务架构图

      假设做一个商城网站

      用户可以登录和注册,发短信验证

      管理员可以查看商品,对商品增删改查传统访问方式如下:

            

         微服务架构访问方式:

           

           添加 ApiGeteway,对客户端暴露一套 API,方便调用 

                

7.      微服务架构的优势

     独立性

            使用者容易理解

            技术栈灵活

     高效团队

8.      微服务架构的不足 

     额外的工作,

     服务的拆分保证数据一致性

     增加了沟通成本

二、 微服务需要考虑的问题

1.  微服务如何通信

                              从通信模式考虑

一对一

一对多

同步

最常见

异步

通知-请求异步响应

发布订阅-发布异步响应

                            从通信协议考虑

                             RPC

2. 微服务 如何 发现 彼此

     传统服务如下:

           一般是 IP:端口号访问

          

       微服务服务发现有 2 种方式——客户端发现和服务端发现

            客户端发现:

                 微服务启动后,将自己 IP 和端口进行注册,客户端查询注册,得到提供服务的 IP 和端口,通过负载均衡,访问微服务

         

        服务端发现:

             客户端访问时,不去注册中心了,通过服务发现代理去直接访问

                

 3.      微服务如何部署、更新和扩容

            

         微服务部署到 docker 容器

         涉及服务编排:K8S,swarm

  

三、 RPC

      1.      RPC 简介

             远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议

             该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程

             如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用

       2.      流行 RPC 框架的对比

            

        3.      golang 中如何实现 RPC

                golang 中实现 RPC 非常简单,官方提供了封装好的库,还有一些第三方的库

                golang 官方的 net/rpc 库使用 encoding/gob 进行编解码,支持 tcp 和 http 数据传输方式,由于其他语言不支持 gob 编解码方式,

                所以 golang 的 RPC 只支持 golang 开发的服务器与客户端之间的交互

                官方还提供了 net/rpc/jsonrpc 库实现 RPC 方法,jsonrpc 采用 JSON 进行数据编解码,因而支持跨语言调用,目前 jsonrpc 库是基于 tcp 协议实现的,暂不支持 http 传输方式

                golang 的 RPC 必须符合 4 个条件才可以

                      1. 结构体字段首字母要大写,要跨域访问,所以大写

                      2. 函数名必须首字母大写(可以序列号导出的)

                      3. 函数第一个参数是接收参数,第二个参数是返回给客户端参数,必须是指针类型

                      4. 函数必须有一个返回值 error

                另外,net/rpc/jsonrpc 库通过 json 格式编解码,支持跨语言调用

        4.      RPC 调用流程

                  微服务架构下数据交互一般是对内 RPC,对外 REST

               

                 将业务按功能模块拆分到各个微服务,具有提高项目协作效率、降低模块耦合度、提高系统可用性等优点,但是开发门槛比较高,

                 比如 RPC 框架的使用、后期的服务监控等工作

                一般情况下,我们会将功能代码在本地直接调用,微服务架构下,我们需要将这个函数作为单独的服务运行,客户端通过网络调用

      5.      网络传输数据格式

                

                成熟的 RPC 框架会有自定义传输协议,这里网络传输格式定义如下,前面是固定长度消息头,后面是变长消息体

                  

        6.      实现 RPC 服务端

                 服务端接收到的数据需要包括什么?

                 调用的函数名、参数列表 

                 一般会约定函数的第二个返回值是 error 类型通过反射实现

                 服务端需要解决的问题是什么?

                 Client 调用时只传过来函数名,需要维护函数名到函数之间的 map 映射服务端的核心功能有哪些?

                 维护函数名到函数反射值的 map

                 client 端传函数名、参数列表后,服务端要解析为反射值,调用执行函数的返回值打包,并通过网络返回给客户端

          7.      实现 RPC 客户端

                 客户端只有函数原型,使用 reflect.MakeFunc()  可以完成原型到函数的调用

                 reflect.MakeFunc()是 Client 从函数原型到网络调用的关键

           8.      实现 RPC 通信测试

                  给服务端注册一个查询用户的方法,客户端去 RPC 调用

四、 gRPC 入门

           1.      gRPC 简介

                   gRPC 由 google 开发,是一款语言中立、平台中立、开源的远程过程调用系统

                   gRPC 客户端和服务端可以在多种环境中运行和交互,例如用 java 写一个服务端,可以用 go 语言写客户端调用

          2.      gRPC Protobuf 介绍

                  微服务架构中,由于每个服务对应的代码库是独立运行的,无法直接调用,彼此间的通信就是个大问题

                  gRPC 可以实现微服务,将大的项目拆分为多个小且独立的业务模块,也就是服务,各服务间使用高效的 protobuf 协议进行 RPC 调用,gRPC 默认使用 protocol buffers,

                  这是 google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)

                  可以用 proto files 创建 gRPC 服务,用 message 类型来定义方法参数和返回类型

          3.      安装 gRPC Protobuf

                 

     go get -u -v github.com/golang/protobuf/proto 
go
get google.golang.org/grpc(无法使用,用如下命令代替) git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text go get -u github.com/golang/protobuf/{proto,protoc-gen-go} git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto
cd $GOPATH/src/ go install google.golang.org/grpc go get github.com/golang/protobuf/protoc-gen-go

                 上面安装好后,会在 GOPATH/bin 下生成 protoc-gen-go.exe

                 但还需要一个 protoc.exe,windows 平台编译受限,很难自己手动编译,直接去网站下载一个,

                 地址:https://github.com/protocolbuffers/protobuf/releases/tag/v3.9.0

                 同样放到 GOPATH/bin 下

五、 Protobuf 语法

        1.      基本规范

                 文件以.proto 做为文件后缀,除结构定义外的语句以分号结尾

                 结构定义可以包含:message、service、enum

                 rpc 方法定义结尾的分号可有可无

                 Message 命名采用驼峰命名方式,字段命名采用小写字母加下划线分隔方式

message SongServerRequest {
    required string song_name = 1;
}

                 Enums 类型名采用驼峰命名方式,字段命名采用大写字母加下划线分隔方式

enum Foo {
    FIRST_VALUE = 1; SECOND_VALUE = 2;
}

                  Service 与 rpc 方法名统一采用驼峰式命名

           2.      字段规则

                    字段格式:限定修饰符 | 数据类型 | 字段名称 | = | 字段编码值 | [字段默认值]

                    限定修饰符包含 required\optional\repeated

                        Required: 

                            表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。

                            发送之前没有设置 required 字段或者无法识别 required 字段都会引发编解码异常,导致消息被丢弃

                       Optional:

                            表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。

                            对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。

                           ---因为 optional 字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为 optional 字段,

                           这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,

                           因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡

 

                      Repeated:

                           表示该字段可以包含 0~N 个元素。其特性和 optional 一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值

 

                    数据类型

                            Protobuf 定义了一套基本数据类型。几乎都可以映射到 C++\Java 等语言的基础数据类型

            N  表示打包的字节并不是固定。而是根据数据的大小或者长度 

            关于 fixed32 和 int32 的区别。fixed32 的打包效率比 int32 的效率高,但是使用的空间一般比 int32 多。因此一个属于时间效率高,一个属于空间效率高

            字段名称

                 字段名称的命名与 C、C++、Java 等语言的变量命名方式几乎是相同的

                 protobuf 建议字段的命名采用以下划线分割的驼峰式。例如 first_name 而不是 firstName

           字段编码值

                 有了该值,通信双方才能互相识别对方的字段,相同的编码值,其限定修饰符和数据类型必须相同,编码值的取值范围为 1~2^32(4294967296)

                 其中 1~15 的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低,所以建议把经常要传递的值把其字段编码设置为 1-15 之间的值

                1900~2000 编码值为 Google protobuf 系统内部保留值,建议不要在自己的项目中使用

          字段默认值

                 当在传递数据时,对于 required 数据类型,如果用户没有设置值,则使用默认值传递到对端

         3.      service 如何定义

               如果想要将消息类型用在 RPC 系统中,可以在.proto 文件中定义一个 RPC 服务接口,protocol buffer 编译器会根据所选择的不同语言生成服务接口代码

               例如,想要定义一个 RPC 服务并具有一个方法,该方法接收 SearchRequest 并返回一个 SearchResponse,此时可以在.proto 文件中进行如下定义:

                

service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse) {}
}

            生成的接口代码作为客户端与服务端的约定,服务端必须实现定义的所有接口方法,客户端直接调用同名方法向服务端发起请求,

            比较麻烦的是,即便业务上不需要参数也必须指定一个请求消息,一般会定义一个空 message

        4.      Message 如何定义

              一个 message 类型定义描述了一个请求或响应的消息格式,可以包含多种类型字段例如定义一个搜索请求的消息格式,

              每个请求包含查询字符串、页码、每页数目字段名用小写,转为 go 文件后自动变为大写,message 就相当于结构体

syntax = "proto3";

message SearchRequest {
    string  query = 1;            //  查询字符串 
    int32  page_number = 2;       //  页码
    int32  result_per_page = 3;   //  每页条数
}

             首行声明使用的 protobuf 版本为 proto3

             SearchRequest 定义了三个字段,每个字段声明以分号结尾,.proto 文件支持双斜线   // 添加单行注释

        5.      添加更多 Message 类型

              一个.proto 文件中可以定义多个消息类型,一般用于同时定义多个相关的消息,例如在同一个.proto 文件中同时定义搜索请求和响应消息

syntax = "proto3";

//SearchRequest 搜索请求
message SearchRequest {
    string   query = 1;            //  查询字符串
    int32    page_number = 2;      //  页码
    int32    result_per_page = 3;  //  每页条数
}
//  SearchResponse 搜索响应
message SearchResponse {
    ...
}

          6.      如何使用其他 Message

                 message 支持嵌套使用,作为另一 message 中的字段类型

message SearchResponse {
    repeated Result results = 1;
}


message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
}

          7.      Message 嵌套的使用

                 支持嵌套消息,消息可以包含另一个消息作为其字段。也可以在消息内定义一个新的消息

                 内部声明的 message 类型名称只可在内部直接使用

message SearchResponse {
 
    message Result {
        string url = 1;
        string title = 2;
        repeated string snippets = 3;
    }

    repeated Result results = 1;

}

另外,还可以多层嵌套

message Outer { // Lev el 0
    message MiddleAA { // Level 1
        message Inner { // Level 2
            int64 ival = 1;
            bool booly = 2;
}
} message Midd leBB { // Level
1 message Inn er { // Le vel 2 int32 ival = 1; bool booly = 2
}
}
}

        8. proto3 的 Map 类型

               proto3 支持 map 类型声明

map<key_type, value_type> map_field = N;

message Project {...}

map<string, Project> projects = 1;

              键、值类型可以是内置的类型,也可以是自定义 message 类型

              字段不支持 repeated 属性

          9.  .proto 文件编译

               

                  通过定义好的.proto 文件生成 Java, Python, C++, Go, Ruby, JavaNano, Objective-C, or C# 代码,需要安装编译器 protoc

                  当使用 protocol buffer 编译器运行.proto 文件时,编译器将生成所选语言的代码,用于使用在.proto 文件中定义的消息类型、服务接口约定等。

                  不同语言生成的代码格式不同:

                      C++: 每个.proto 文件生成一个.h 文件和一个.cc 文件,每个消息类型对应一个类

                      Java:  生成一个.java  文件,同样每个消息对应一个类,同时还有一个特殊的Builder 类用于创建消息接口

                      Python: 姿势不太一样,每个.proto 文件中的消息类型生成一个含有静态描述符的模块,该模块与一个元类 metaclass 在运行时创建需要的 Python 数据访问类

                      Go: 生成一个.pb.go 文件,每个消息类型对应一个结构体

                      Ruby: 生成一个.rb 文件的 Ruby 模块,包含所有消息类型

                     JavaNano: 类似 Java,但不包含 Builder 类

                     Objective-C: 每个.proto 文件生成一个 pbobjc.h 和一个 pbobjc.m 文件

                     C#: 生成.cs 文件包含,每个消息类型对应一个类

         10. import 导入定义

                  可以使用 import 语句导入使用其它描述文件中声明的类型

                  protobuf 接口文件可以像 C 语言的 h 文件一个,分离为多个,在需要的时候通过 import 导入需要对文件。

                  其行为和 C 语言的#include 或者 java 的 import 的行为大致相同,例如 import "others.proto";

                  protocol buffer 编译器会在 -I / --proto_path 参数指定的目录中查找导入的文件,如果没有指定该参数,默认在当前目录中查找

         11.   包的使用 

                  在.proto 文件中使用 package 声明包名,避免命名冲突

syntax = "proto3";

package foo.bar; message Open {...}

                  在其他的消息格式定义中可以使用包名+消息名的方式来使用类型,如

message Foo {
    ...
    foo.bar.Open open = 1;
    ...
}

                  在不同的语言中,包名定义对编译后生成的代码的影响不同

                       C++  中:对应 C++命名空间,例如 Open 会在命名空间 foo::bar 中

                       Java  中:package 会作为 Java 包名,除非指定了 option jave_package 选项

                       Python 中:package 被忽略

                       Go 中:默认使用 package 名作为包名,除非指定了 option go_package 选项 JavaNano 中:同 Java

                       C# 中:package 会转换为驼峰式命名空间,如 Foo.Bar,除非指定了 option csharp_namespace 选项

六、   Go Micro 入门

          1.      go-micro 简介

                   Go Micro 是一个插件化的基础框架,基于此可以构建微服务,Micro 的设计哲学

                   是可插拔的插件化架构

                   在架构之外,它默认实现了 consul 作为服务发现(2019 年源码修改了默认使用mdns),通过 http 进行通信,通过 protobuf 和 json 进行编解码

          2.      go-micro 的主要功能

                    服务发现:

                           自动服务注册和名称解析。服务发现是微服务开发的核心。

                           当服务 A 需要与服务 B 通话时,它需要该服务的位置。默认发现机制是多播 DNS(mdns),一种零配置系统。

                           您可以选择使用 SWIM 协议为 p2p 网络设置八卦,或者为弹性云原生设置设置 consul

                    负载均衡:

                           基于服务发现构建的客户端负载均衡。一旦我们获得了服务的任意数量实例的地址,我们现在需要一种方法来决定要路由到哪个节点。

                           我们使用随机散列负载均衡来提供跨服务的均匀分布,并在出现问题时重试不同的节点

                   消息编码:

                           基于内容类型的动态消息编码。客户端和服务器将使用编解码器和内容类型为您无缝编码和解码 Go 类型。

                           可以编码任何种类的消息并从不同的客户端发送。客户端和服务器默认处理此问题。这包括默认的 protobuf 和 json

                  请求/响应:

                           基于 RPC 的请求/响应,支持双向流。我们提供了同步通信的抽象。

                           对服务的请求将自动解决,负载平衡,拨号和流式传输。启用 tls 时,默认传输为 http / 1.1 或 http2

                  Async Messaging:

                          PubSub 是异步通信和事件驱动架构的一流公民。事件通知是微服务开发的核心模式。

                          启用 tls 时,默认消息传递是点对点 http / 1.1 或 http2

                 可插拔接口:

                          Go Micro 为每个分布式系统抽象使用 Go 接口,因此,这些接口是可插拔的,并允许 Go Micro 与运行时无关,可以插入任何基础技术

                          插件地址:https://github.com/micro/go-plugins

          3.   go-micro 通信流程

                 Server 监听客户端的调用,和 Brocker 推送过来的信息进行处理。

                 并且 Server 端需要向 Register 注册自己的存在或消亡,这样 Client 才能知道自己的状态

                 Register 服务的注册的发现,Client 端从 Register 中得到 Server 的信息,然后每次调用都根据算法选择一个的 Server 进行通信,

                 当然通信是要经过编码/解码,选择传输协议等一系列过程的如果有需要通知所有的 Server 端可以使用 Brocker 进行信息的推送,Brocker 信息队列进行信息的接收和发布

          4.      go-micro 核心接口

                   go-micro 之所以可以高度订制和他的框架结构是分不开的,go-micro 由 8 个关键的 interface 组成,

                   每一个 interface 都可以根据自己的需求重新实现,这 8 个主要的 inteface 也构成了 go-micro 的框架结构

                      

八、 Go Micro 接口详解

       1.  Transort 通信接口

                    服务之间通信的接口,也就是服务发送和接收的最终实现方式,是由这些接口定制的

type Socket interface {
    Recv(*Message) error
    Send(*Message) error
    Close() error
}

type Client interface {
    Socket
}

type Listener interface {
    Addr()  string
    Close() error
    Accept( func( Socket ) ) error
}

type Transport interface {
    Dial(addr string, opts ...DialOption)  (Client, error)
    Listen(addr string, opts ...ListenOption)  (Listener, error)
    String()  string
}

      2.  Codec 编码接口

              go-micro 有很多种编码解码方式,默认的实现方式是 protobuf,当然也有其他的实现方式,json、jsonrpc、mercury 等等

type Codec interface {
    ReadHeader(*Message,  MessageType)  error
    ReadBody(interface{})  error
    Write(*Message,  interface{})  error
    Close()   error
    String()  string
}

     3.  Registry 注册接口

             服务的注册和发现,目前实现的有 consul、mdns、etcd、zookeeper、kubernetes 等

type Registry interface {
    Register(*Service, ...RegisterOption)  error
    Deregister(*Service)  error
    GetService(string)   ([]*Service, error)
    ListServices()   ([]*Service, error)
    Watch(...WatchOption)   (Watcher, error)
    String()   string
    Options()   Options
}

      4.    Selector 负载均衡

                以 Registry 为基础,Selector 是客户端级别的负载均衡,当有客户端向服务发送请求时,

                selector 根据不同的算法从 Registery 中的主机列表,得到可用的 Service 节点,进行通信,目前实现的有循环算法和随机算法,默认的是随机算法

type Selector interface {

    Init(opts ...Option)  error

    Options()  Options

    // Select returns a function which should return the next node 
    Select(service string, opts ...SelectOption) (Next, error)

    // Mark sets the success/error against a node 
    Mark(service string, node *registry.Node, err error) 

// Reset returns state back to zero for a service Reset(service string) // Close renders the selector unusable Close() error // Name of the selector String() string }

     5.  Broker 发布订阅接口

          Broker 是消息发布和订阅的接口。

          例如,因为服务的节点是不固定的,如果需要修改所有服务行为,可以使服务订阅某个主题,当有信息发布时,所有的监听服务都会收到信息,根据你的需要做相应的行为即可

type Broker interface {

    Options()  Options
    Address()  string
    Connect()  error
    Disconnect()  error
    Init(...Option)  error
    Publish(string, *Message, ...PublishOption)  error
    Subscribe(string, Handler, ...SubscribeOption)  (Subscriber, error)
    String()  string

}

     6.  Client 客户端接口

              Client 是请求服务的接口,他封装 Transport 和 Codec 进行 rpc 调用,也封装了 Brocker进行信息的发布

type Client interface {

    Init(...Option) error
    Options() Options
    NewMessage(topic string, msg interface{}, opts ...MessageOption) Message 
NewRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) Publish(ctx context.Context, msg Message, opts ...PublishOption) error String() string }

     7.  Server 服务端接口

            Server 监听等待 rpc 请求,监听 broker 的订阅信息,等待信息队列的推送等

type Server interface {
    Options()  Options
    Init(...Option)  error
    Handle(Handler)  error
    NewHandler(interface{}, ...HandlerOption)  Handler
    NewSubscriber(string, interface{}, ...SubscriberOption)  Subscriber
    Subscribe(Subscriber)  error
    Register()  error
    Deregister()  error
    Start()  error
    Stop()  error
    String()  string
}

      8.  Serveice 接口

              Service 是 Client 和 Server 的封装,他包含了一系列的方法使用初始值去初始化

              Service 和 Client,使我们可以很简单的创建一个 rpc 服务

type Service interface {
    Init(...Option)
    Options() Options
    Client() client.Client
    Server() server.Server
    Run() error
    String() string
}

go-micro 安装

查看的网址:https://github.com/micro/

cmd 中输入下面 3 条命令下载,会自动下载相关的很多包

    go get -u -v github.com/micro/micro

    go get -u -v github.com/micro/go-micro

    go get -u -v github.com/micro/protoc-gen-micro

go-micro  跨域问题

    //  RPC请求, 请求跨域
    // 设置允许的跨域地址
    if origin := r.Header.Get("Origin");true{
        w.Header().Set("Access-Control-Allow-Origin",origin)
    }
    //  设置跨越请求允许的请求方式
    w.Header().Set("Access-Control-Allow-Methods","POST,GET")
    //  设置跨越请求允许的数据格式
    w.Header().Set("Access-Control-Allow-Headers-Content-Type"," Content Length, Accept Encoding,X Token, X Client")
    //  设置跨越请求是否可携带证书
    w.Header().Set("Access-Control-Allow-Credentials","true")




设置允许的跨域地址

"Access-Control-Allow-Origin"

设置跨越请求允许的请求方式

"Access-Control-Allow-Methods",  "POST,GET"

设置跨越请求允许的数据格式

"Access-Control-Allow-Headers-Content-Type",   " Content Length, Accept Encoding,X Token, X Client"

设置跨越请求是否可携带证书

"Access-Control-Allow-Credentials"  "true"

猜你喜欢

转载自www.cnblogs.com/huidou/p/12072947.html