Alex

有的故事值得一直说下去.
Home » Latest Posts

网络::HTTP

Net::HTTP 提供了一个丰富的库,可用于构建 HTTP 用户代理。有关 HTTP 的更多详细信息,请参阅RFC2616
Net::HTTP 旨在与 URI 密切合作。,并设计为与 Net::HTTP 一起使用。URI::HTTP#hostURI::HTTP#portURI::HTTP#request_uri
如果您只执行几个 GET 请求,则应尝试 OpenURI。

简单示例

所有示例都假定您已使用以下命令加载了 Net::HTTP:

require 'net/http'

这也需要“uri”,因此您不需要单独要求它。
下一节中的 Net::HTTP 方法不保留连接。如果要执行许多 HTTP 请求,则不建议使用它们。

获取

Net::HTTP.get('example.com', '/index.html') # => String

按 URI 获取

uri = URI('http://example.com/index.html?count=10')
Net::HTTP.get(uri) # => String

使用动态参数获取

uri = URI('http://example.com/index.html')
params = { :limit => 10, :page => 3 }
uri.query = URI.encode_www_form(params)
res = Net::HTTP.get_response(uri)
puts res.body if res.is_a?(Net::HTTPSuccess)

发布

uri = URI('http://www.example.com/search.cgi')
res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50')
puts res.body

具有多个值的 POST

uri = URI('http://www.example.com/search.cgi')
res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50')
puts res.body

如何使用 Net::HTTP

以下示例代码可用作 HTTP 用户代理的基础,该用户代理可以使用持久连接执行各种请求类型。

uri = URI('http://example.com/some_path?query=string')
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Get.new uri
response = http.request request # Net::HTTPResponse object
end

Net::HTTP::start 会立即创建与 HTTP 服务器的连接,该服务器在块期间保持打开状态。如果服务器指示它支持持久连接,则该连接将对块中的多个请求保持打开状态。
如果您希望在多个 HTTP 请求中重用连接而不自动关闭它,则可以使用 ::new,然后调用和手动。#start#finish
Net::HTTP 支持的请求类型在下面的“HTTP 请求类”一节中列出。
对于所有 Net::HTTP 请求对象和快捷方式请求方法,您可以为请求路径提供 String 或 Net::HTTP 将从中提取请求路径的 URI。

响应数据

uri = URI('http://example.com/index.html')
res = Net::HTTP.get_response(uri)
# Headers
res['Set-Cookie']            # => String
res.get_fields('set-cookie') # => Array
res.to_hash['set-cookie']    # => Array
puts "Headers:#{res.to_hash.inspect}"
# Status
puts res.code       # => '200'
puts res.message    # => 'OK'
puts res.class.name # => 'HTTPOK'
# Body
puts res.body if res.response_body_permitted?

重定向后

每个 Net::HTTPResponse 对象都属于其响应代码的类。
例如,所有 2XX 响应都是 Net::HTTPSuccess 子类的实例,3XX 响应是 Net::HTTPRedirection 子类的实例,200 响应是 Net::HTTPOK 类的实例。有关响应类的详细信息,请参阅下面的“HTTP 响应类”部分。
使用 case 语句,您可以正确处理各种类型的响应:

def fetch(uri_str, limit = 10)
# You should choose a better exception.
raise ArgumentError, 'too many HTTP redirects' if limit == 0
response = Net::HTTP.get_response(URI(uri_str))
case response
when Net::HTTPSuccess then
response
when Net::HTTPRedirection then
location = response['location']
warn "redirected to#{location}"
fetch(location, limit - 1)
else
response.value
end
end
print fetch('http://www.ruby-lang.org')

发布

可以使用 Net::HTTP::P ost 请求类进行 POST。此示例创建一个 URL 编码的 POST 正文:

uri = URI('http://www.example.com/todo.cgi')
req = Net::HTTP::Post.new(uri)
req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31')
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end
case res
when Net::HTTPSuccess, Net::HTTPRedirection
# OK
else
res.value
end

要发送 multipart/form-data,请使用:Net::HTTPHeader#set_form

req = Net::HTTP::Post.new(uri)
req.set_form([['upload', File.open('foo.bar')]], 'multipart/form-data')

可以使用相应的请求类 (Net::HTTP::P ut) 以相同的方式创建可以包含正文(如 PUT)的其他请求。

设置标题

下面的示例使用 If-Modified-Since 标头执行条件 GET。如果文件自标头中的时间以来未被修改,则将返回“未修改”响应。有关详细信息,请参阅 RFC 2616 第 9.3 节。

uri = URI('http://example.com/cached_response')
file = File.stat 'cached_response'
req = Net::HTTP::Get.new(uri)
req['If-Modified-Since'] = file.mtime.rfc2822
res = Net::HTTP.start(uri.hostname, uri.port) {|http|
http.request(req)
}
open 'cached_response', 'w' do |io|
io.write res.body
end if res.is_a?(Net::HTTPSuccess)

基本身份验证

基本身份验证根据RFC2617执行。

uri = URI('http://example.com/index.html?key=value')
req = Net::HTTP::Get.new(uri)
req.basic_auth 'user', 'pass'
res = Net::HTTP.start(uri.hostname, uri.port) {|http|
http.request(req)
}
puts res.body

流式响应正文

默认情况下,Net::HTTP 将整个响应读入内存。如果您正在处理大型文件或希望实现进度条,则可以改为将正文直接流式传输到 IO。

uri = URI('http://example.com/large_file')
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Get.new uri
http.request request do |response|
open 'large_file', 'w' do |io|
response.read_body do |chunk|
io.write chunk
end
end
end
end

HTTPS的

通过以下方式为 HTTP 连接启用 HTTPS。Net::HTTP#use_ssl=

uri = URI('https://secure.example.com/some_path?query=string')
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
request = Net::HTTP::Get.new uri
response = http.request request # Net::HTTPResponse object
end

或者,如果您只想发出 GET 请求,则可以传入具有 HTTPS URL 的 URI 对象。如果 URI 对象具有“https”URI 方案,则 Net::HTTP 会自动启用 TLS 验证。

uri = URI('https://example.com/')
Net::HTTP.get(uri) # => String

在以前的 Ruby 版本中,您需要需要 'net/https' 才能使用 HTTPS。这不再是真的。

代理

如果存在,Net::HTTP 将自动从环境变量创建代理。要禁用 ,请传递代理地址。http_proxyhttp_proxynil
您还可以创建自定义代理:

proxy_addr = 'your.proxy.host'
proxy_port = 8080
Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http|
# always proxy via your.proxy.addr:8080
}

有关更多详细信息和示例,请参阅 Net::HTTP.new,例如需要用户名和密码的代理。

压缩

Net::HTTP 会自动添加 Accept-Encoding 来压缩响应正文,并自动解压缩 gzip 和 deflate 响应,除非发送了 Range 标头。
可以通过 Accept-Encoding: 标识标头禁用压缩。

什么是protobuf

Protobuf(Protocol Buffer)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。(参考链接

什么是protoc

protoc是protobuf文件(.proto)的编译器(参考链接),可以借助这个工具把 .proto 文件转译成各种编程语言对应的源码,包含数据类型定义、调用接口等。

采集失败,请手动处理

https://static.studygolang.com/180325/41775c725a2745e2e785f22bbd96a08c.png
 
通过查看protoc的源码(参见github库)可以知道,protoc在设计上把protobuf和不同的语言解耦了,底层用c++来实现protobuf结构的存储,然后通过插件的形式来生成不同语言的源码。可以把protoc的编译过程分成简单的两个步骤(如上图所示):1)解析.proto文件,转译成protobuf的原生数据结构在内存中保存;2)把protobuf相关的数据结构传递给相应语言的编译插件,由插件负责根据接收到的protobuf原生结构渲染输出特定语言的模板。
源码中(参见github库)包含的插件有 csharp、java、js、objectivec、php、python、ruby等多种。

什么是protoc-gen-go

protoc-gen-go是protobuf编译插件系列中的Go版本。从上一小节知道原生的protoc并不包含Go版本的插件,不过可以在github上发现专门的代码库(参见github库)。
由于protoc-gen-go是Go写的,所以安装它变得很简单,只需要运行 go get -u github.com/golang/protobuf/protoc-gen-go,便可以在$GOPATH/bin目录下发现这个工具。至此,就可以通过下面的命令来使用protoc-gen-go了。

protoc --go_out=output_directory input_directory/file.proto

其中"--go_out="表示生成Go文件,protoc会自动寻找PATH(系统执行路径)中的protoc-gen-go执行文件。

protoc-gen-go的源码

采集失败,请手动处理

https://static.studygolang.com/180325/fcfa10f6aa545027a3904783f0fd6ab5.png
 
按照Go的代码风格,protoc-gen-go源码主要包含六个包(package):

  • main包
    • doc.go 主要是说明。
    • link_grpc.go 显式引用protoc-gen-go/grpc包,触发grpc的init函数。
    • main.go 代码不到50行,初始化generator,并调用generator相应的方法输出protobuf的Go语言文件。
  • generator包
    • generator.go 包含了大部分由protobuf原生结构到Go语言文件的渲染方法,其中 func (g *Generator) P(str ...interface{}) 这个方法会把渲染输出到generator的output(generator匿名嵌套了bytes.Buffer,因此有Buffer的方法)。
    • name_test.go 测试,主要包含generator中名称相关方法的测试。
  • grpc包
    • grpc.go 与generator相似,但是包含了很多生成grpc相关方法的方法,比如渲染转译protobuf中定义的rpc方法(在generator中不包含,其默认不转译service的定义)
  • descriptor 包含protobuf的描述文件(.proto文件及其对应的Go编译文件),其中proto文件来自于proto库(参见这里
  • plugin 包含plugin的描述文件(.proto文件及其对应的Go编译文件),其中proto文件来自于proto库,参见这里

结语

从巴别塔的传说(参见这里)可以知道,欲要构建大系统,个体之间的沟通规范很重要。protobuf的出现,为不同系统之间的连接提供了一种语言规范,只要遵循了这个规范,各个系统之间就是解耦的,非常适合近年来流行的微服务架构。
如果吧protoc和protoc-gen-go看成两个微服务,可以发现这两个服务就是完全解耦的;两者完全负责不同的功能,可以分别编码、升级,串接这两个服务的就是proto规范。

您好,需要放行 80   443  5800等端口才可以
你可以试试下面命令安装nginx1.22版本(1.20 则是安装nginx1.20)

  1. bash /www/server/panel/install/install_soft.sh 0 install nginx 1.22
复制代码

在php中,执行异步任务是一种很常见的需求,如批量发邮箱,短信等等执行耗时任务时,需要程序异步执行,给用户带来好的体验。
在php异步执行代码就很尬尴,不像java 可以创建多线程,在php中也可以用pcntl_fork()实现创建子进程,来实现异步,但这样可能会产生很多僵尸进程。其他的方式借助框架实现,如优秀的swoole框架,本文介绍无需安装如何框架扩展,php自带的方式处理
下面直接上代码,我用的thinkphp6
在extend下创建一个util类,代码如下
<?php
namespace util;
class AsyncHook
{
private static $hook_list = array();
private static $hooked = false;
/**
* hook函数fastcgi_finish_request执行
*
* @param callback $callback
* @param array $params
*/
public static function hook($callback, $params = [])
{
self::$hook_list[] = array('callback' => $callback, 'params' => $params);
if (self::$hooked == false) {
self::$hooked = true;
register_shutdown_function(array(__CLASS__, '__run'));
}
}
/**
* 由系统调用
*
* @return void
*/
public static function __run()
{
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
if (empty(self::$hook_list)) {
return;
}
foreach (self::$hook_list as $hook) {
$callback = $hook['callback'];
$params = $hook['params'];
call_user_func_array($callback, $params);
}
}
}
调用方式
$namespaceClass = "app\\api\\task\\Test";
$classObj = app($namespaceClass);
\util\AsyncHook:hook([$classObj,"index"],['name'=>1]);
$this->success("");
执行结果,会先$this->success()返回数据,后执行hook中的Test index方法。很简单。

使用场景:
为了安全着想,存入数据库的密码是加密后存入的,但是现在需要从数据库中获取账号和密码来调用封装的第三方接口来获取关键参数,此时密码信息就需要是解密的,但是如果从每个调用的地方都来一次解密,又显得有点累赘。所以就重写Model的init方法来达到目的。
from django.db import models
class Company(models.Model):
name = models.CharField(u'公司名称', max_length=64, null=False)
account = models.CharField(u'登陆账号', max_length=64, null=False)
passwd = models.CharField(u'登陆密码', max_length=64, null=False)
address = models.CharField(u'住址', max_length=64, null=False)
phone = models.CharField(u'电话号码', max_length=11, null=False)
email = models.EmailField(u'邮箱', max_length=64, null=False)
class Meta:
verbose_name = "公司"
verbose_name = verbose_name
db_table = "business_company"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
改写方法:
from django.db import models
from utils import AESCipher # 自己定义的AES的加解密类
class Company(models.Model):
name = models.CharField(u'公司名称', max_length=64, null=False)
account = models.CharField(u'登陆账号', max_length=64, null=False)
passwd = models.CharField(u'登陆密码', max_length=64, null=False)
address = models.CharField(u'住址', max_length=64, null=False)
phone = models.CharField(u'电话号码', max_length=11, null=False)
email = models.EmailField(u'邮箱', max_length=64, null=False)
class Meta:
verbose_name = "公司"
verbose_name = verbose_name
db_table = "business_company"
# 重写此方法
def __init__(self, *args, **keargs):
fields_iter = iter(self._meta.concrete_fields)
new_args = list()
for val, field  in zip(args, fields_iter):
if field.attname == "passwd":
new_args.append(AESCipher().decrypt(val))
else:
new_args.append(val)
super(Company, self).__init__(*tuple(new_args), **kwargs)

Life is fantastic
🥕 More