一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringBoot 配置 okhttp3的操作

SpringBoot 配置 okhttp3的操作

2021-08-13 11:53Jaemon Java教程

這篇文章主要介紹了SpringBoot 配置 okhttp3的操作方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

1. Maven 添加依賴

?
1
2
3
4
5
<dependency>
 <groupId>com.squareup.okhttp3</groupId>
 <artifactId>okhttp</artifactId>
 <version>3.10.0</version>
</dependency>

2. application.properties 配置文件

?
1
2
3
4
5
6
7
ok.http.connect-timeout=30
ok.http.read-timeout=30
ok.http.write-timeout=30
# 連接池中整體的空閑連接的最大數量
ok.http.max-idle-connections=200
# 連接空閑時間最多為 300
ok.http.keep-alive-duration=300

3. OkHttpConfiguration 配置類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
/**
 * @author Answer.AI.L
 * @date 2019-04-09
 */
@Configuration
public class OkHttpConfiguration {
 @Value("${ok.http.connect-timeout}")
 private Integer connectTimeout;
 @Value("${ok.http.read-timeout}")
 private Integer readTimeout;
 @Value("${ok.http.write-timeout}")
 private Integer writeTimeout;
 @Value("${ok.http.max-idle-connections}")
 private Integer maxIdleConnections;
 @Value("${ok.http.keep-alive-duration}")
 private Long keepAliveDuration;
 @Bean
 public OkHttpClient okHttpClient() {
  return new OkHttpClient.Builder()
    .sslSocketFactory(sslSocketFactory(), x509TrustManager())
    // 是否開啟緩存
    .retryOnConnectionFailure(false)
    .connectionPool(pool())
    .connectTimeout(connectTimeout, TimeUnit.SECONDS)
    .readTimeout(readTimeout, TimeUnit.SECONDS)
    .writeTimeout(writeTimeout,TimeUnit.SECONDS)
    .hostnameVerifier((hostname, session) -> true)
    // 設置代理
//      .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888)))
    // 攔截器
//    .addInterceptor()
    .build();
 }
 @Bean
 public X509TrustManager x509TrustManager() {
  return new X509TrustManager() {
   @Override
   public void checkClientTrusted(X509Certificate[] chain, String authType)
     throws CertificateException {
   }
   @Override
   public void checkServerTrusted(X509Certificate[] chain, String authType)
     throws CertificateException {
   }
   @Override
   public X509Certificate[] getAcceptedIssuers() {
    return new X509Certificate[0];
   }
  };
 }
 @Bean
 public SSLSocketFactory sslSocketFactory() {
  try {
   // 信任任何鏈接
   SSLContext sslContext = SSLContext.getInstance("TLS");
   sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
   return sslContext.getSocketFactory();
  } catch (NoSuchAlgorithmException | KeyManagementException e) {
   e.printStackTrace();
  }
  return null;
 }
 @Bean
 public ConnectionPool pool() {
  return new ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.SECONDS);
 }
}

4. OkHttp 類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
 * @author Answer.AI.L
 * @date 2019-04-09
 */
@Slf4j
@Component
public class OkHttpCli {
 private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
 private static final MediaType XML = MediaType.parse("application/xml; charset=utf-8");
 @Autowired
 private OkHttpClient okHttpClient;
 /**
  * get 請求
  * @param url  請求url地址
  * @return string
  * */
 public String doGet(String url) {
  return doGet(url, null, null);
 }
 /**
  * get 請求
  * @param url  請求url地址
  * @param params 請求參數 map
  * @return string
  * */
 public String doGet(String url, Map<String, String> params) {
  return doGet(url, params, null);
 }
 /**
  * get 請求
  * @param url  請求url地址
  * @param headers 請求頭字段 {k1, v1 k2, v2, ...}
  * @return string
  * */
 public String doGet(String url, String[] headers) {
  return doGet(url, null, headers);
 }
 /**
  * get 請求
  * @param url  請求url地址
  * @param params 請求參數 map
  * @param headers 請求頭字段 {k1, v1 k2, v2, ...}
  * @return string
  * */
 public String doGet(String url, Map<String, String> params, String[] headers) {
  StringBuilder sb = new StringBuilder(url);
  if (params != null && params.keySet().size() > 0) {
   boolean firstFlag = true;
   for (String key : params.keySet()) {
    if (firstFlag) {
     sb.append("?").append(key).append("=").append(params.get(key));
     firstFlag = false;
    } else {
     sb.append("&").append(key).append("=").append(params.get(key));
    }
   }
  }
  Request.Builder builder = new Request.Builder();
  if (headers != null && headers.length > 0) {
   if (headers.length % 2 == 0) {
    for (int i = 0; i < headers.length; i = i + 2) {
     builder.addHeader(headers[i], headers[i + 1]);
    }
   } else {
    log.warn("headers's length[{}] is error.", headers.length);
   }
  }
  Request request = builder.url(sb.toString()).build();
  log.info("do get request and url[{}]", sb.toString());
  return execute(request);
 }
 /**
  * post 請求
  * @param url  請求url地址
  * @param params 請求參數 map
  * @return string
  */
 public String doPost(String url, Map<String, String> params) {
  FormBody.Builder builder = new FormBody.Builder();
  if (params != null && params.keySet().size() > 0) {
   for (String key : params.keySet()) {
    builder.add(key, params.get(key));
   }
  }
  Request request = new Request.Builder().url(url).post(builder.build()).build();
  log.info("do post request and url[{}]", url);
  return execute(request);
 }
 /**
  * post 請求, 請求數據為 json 的字符串
  * @param url  請求url地址
  * @param json  請求數據, json 字符串
  * @return string
  */
 public String doPostJson(String url, String json) {
  log.info("do post request and url[{}]", url);
  return exectePost(url, json, JSON);
 }
 /**
  * post 請求, 請求數據為 xml 的字符串
  * @param url  請求url地址
  * @param xml  請求數據, xml 字符串
  * @return string
  */
 public String doPostXml(String url, String xml) {
  log.info("do post request and url[{}]", url);
  return exectePost(url, xml, XML);
 }
 private String exectePost(String url, String data, MediaType contentType) {
  RequestBody requestBody = RequestBody.create(contentType, data);
  Request request = new Request.Builder().url(url).post(requestBody).build();
  return execute(request);
 }
 private String execute(Request request) {
  Response response = null;
  try {
   response = okHttpClient.newCall(request).execute();
   if (response.isSuccessful()) {
    return response.body().string();
   }
  } catch (Exception e) {
   log.error(ExceptionUtils.getStackTrace(e));
  } finally {
   if (response != null) {
    response.close();
   }
  }
  return "";
 }
}

5. 使用驗證

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class AnswerController {
 @Autowired
 private OkHttpCli okHttpCli;
 
 
 @RequestMapping(value = "show", method = RequestMethod.GET)
 public String show() {
  String url = "https://www.baidu.com/";
 String message = okHttpCli.doGet(url);
  return message;
 }
 
}

6. 雙向認證(待證)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Bean
public SSLSocketFactory sslSocketFactory() {
 String certPath = "";
 String caPath = "";
 String certPwd = "";
 String caPwd = "";
 try {
  ClassPathResource selfcertPath = new ClassPathResource(certPath);
  ClassPathResource trustcaPath = new ClassPathResource(caPath);
  KeyStore selfCert = KeyStore.getInstance("pkcs12");
  selfCert.load(selfcertPath.getInputStream(), certPwd.toCharArray());
  KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
  kmf.init(selfCert, certPwd.toCharArray());
  KeyStore caCert = KeyStore.getInstance("jks");
  caCert.load(trustcaPath.getInputStream(), caPwd.toCharArray());
  TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
  tmf.init(caCert);
  SSLContext sslContext = SSLContext.getInstance("TLS");
  sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
  return sslContext.getSocketFactory();
 } catch (Exception e) {
  e.printStackTrace();
 }
 return null;
}

補充:Spring Cloud Feign 總結問題,注意點,性能調優,切換okhttp3

Feign常見問題總結

FeignClient接口如使用@PathVariable ,必須指定value屬性

?
1
2
3
4
5
6
7
//在一些早期版本中, @PathVariable("id") 中的 "id" ,也就是value屬性,必須指定,不能省略。
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
 @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
 public User findById(@PathVariable("id") Long id);
 ...
}

Java代碼自定義Feign Client的注意點與坑

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@FeignClient(name = "microservice-provider-user", configuration = UserFeignConfig.class)
public interface UserFeignClient {
 @GetMapping("/users/{id}")
 User findById(@PathVariable("id") Long id);
}
/**
 * 該Feign Client的配置類,注意:
 * 1. 該類可以獨立出去;
 * 2. 該類上也可添加@Configuration聲明是一個配置類;
 * 配置類上也可添加@Configuration注解,聲明這是一個配置類;
 * 但此時千萬別將該放置在主應用程序上下文@ComponentScan所掃描的包中,
 * 否則,該配置將會被所有Feign Client共享,無法實現細粒度配置!
 * 個人建議:像我一樣,不加@Configuration注解
 *
 * @author zhouli
 */
class UserFeignConfig {
 @Bean
 public Logger.Level logger() {
 return Logger.Level.FULL;
 }
}

配置類上也可添加@Configuraiton 注解,聲明這是一個配置類;但此時千萬別將該放置在主應用程序上下文@ComponentScan 所掃描的包中,否則,該配置將會被所有Feign Client共享(相當于變成了通用配置,其實本質還是Spring父子上下文掃描包重疊導致的問題),無法實現細粒度配置!

個人建議:像我一樣,不加@Configuration注解,省得進坑。

最佳實踐:盡量用配置屬性自定義Feign的配置!!!

@FeignClient 注解屬性

?
1
2
3
4
//@FeignClient(name = "microservice-provider-user")
//在早期的Spring Cloud版本中,無需提供name屬性,從Brixton版開始,@FeignClient必須提供name屬性,否則應用將無法正常啟動!
//另外,name、url等屬性支持占位符。例如:
@FeignClient(name = "${feign.name}", url = "${feign.url}")

類級別的@RequestMapping會被Spring MVC加載

?
1
2
3
4
5
@RequestMapping("/users")
@FeignClient(name = "microservice-user")
public class TestFeignClient {
 // ...
}

類上的@RequestMapping 注解也會被Spring MVC加載。該問題現已經被解決,早期的版本有兩種解決方案:方案1:不在類上加@RequestMapping 注解;方案2:添加如下代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
@ConditionalOnClass({ Feign.class })
public class FeignMappingDefaultConfiguration {
 @Bean
 public WebMvcRegistrations feignWebRegistrations() {
  return new WebMvcRegistrationsAdapter() {
   @Override
   public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
    return new FeignFilterRequestMappingHandlerMapping();
   }
  };
 }
 private static class FeignFilterRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
  @Override
  protected boolean isHandler(Class<?> beanType) {
   return super.isHandler(beanType) && !beanType.isInterface();
  }
 }
}

首次請求失敗Ribbon的饑餓加載(eager-load)模式

如需產生Hystrix Stream監控信息,需要做一些額外操作Feign本身已經整合了Hystrix,可直接使用@FeignClient(value = "microservice-provider-user", fallback = XXX.class) 來指定fallback類,fallback類繼承@FeignClient所標注的接口即可。

但是假設如需使用Hystrix Stream進行監控,默認情況下,訪問http://IP:PORT/actuator/hystrix.stream 是會返回404,這是因為Feign雖然整合了Hystrix,但并沒有整合Hystrix的監控。如何添加監控支持呢?需要以下幾步:

第一步:添加依賴,示例:

?
1
2
3
4
5
<!-- 整合hystrix,其實feign中自帶了hystrix,引入該依賴主要是為了使用其中的hystrix-metrics-event-stream,用于dashboard -->
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

第二步:在啟動類上添加@EnableCircuitBreaker 注解,示例:

?
1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableCircuitBreaker
public class MovieFeignHystrixApplication {
 public static void main(String[] args) {
 SpringApplication.run(MovieFeignHystrixApplication.class, args);
 }
}

第三步:在application.yml中添加如下內容,暴露hystrix.stream端點:

?
1
2
3
4
5
management:
 endpoints:
 web:
  exposure:
  include: 'hystrix.stream'

這樣,訪問任意Feign Client接口的API后,再訪問http://IP:PORT/actuator/hystrix.stream ,就會展示一大堆Hystrix監控數據了。

Feign 上傳文件

加依賴

?
1
2
3
4
5
6
7
8
9
10
<dependency>
 <groupId>io.github.openfeign.form</groupId>
 <artifactId>feign-form</artifactId>
 <version>3.0.3</version>
</dependency>
<dependency>
 <groupId>io.github.openfeign.form</groupId>
 <artifactId>feign-form-spring</artifactId>
 <version>3.0.3</version>
</dependency>

編寫Feign Client

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@FeignClient(name = "ms-content-sample", configuration = UploadFeignClient.MultipartSupportConfig.class)
public interface UploadFeignClient {
 @RequestMapping(value = "/upload", method = RequestMethod.POST,
   produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},
   consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
 @ResponseBody
 String handleFileUpload(@RequestPart(value = "file") MultipartFile file);
 class MultipartSupportConfig {
  @Bean
  public Encoder feignFormEncoder() {
   return new SpringFormEncoder();
  }
 }
}

如代碼所示,在這個Feign Client中,我們引用了配置類MultipartSupportConfig ,在MultipartSupportConfig 中,我們實例化了SpringFormEncoder 。這樣這個Feign Client就能夠上傳啦。

注意點

?
1
2
3
4
//RequestMapping注解中的produeces 、consumes 不能少;
@RequestMapping(value = "/upload", method = RequestMethod.POST,
   produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},
   consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

接口定義中的注解@RequestPart(value = "file") 不能寫成@RequestParam(value = "file") 。

最好將Hystrix的超時時間設長一點,例如5秒,否則可能文件還沒上傳完,Hystrix就超時了,從而導致客戶端側的報錯。

Feign實現Form表單提交

添加依賴:

?
1
2
3
4
5
6
7
8
9
10
<dependency>
 <groupId>io.github.openfeign.form</groupId>
 <artifactId>feign-form</artifactId>
 <version>3.2.2</version>
</dependency>
<dependency>
 <groupId>io.github.openfeign.form</groupId>
 <artifactId>feign-form-spring</artifactId>
 <version>3.2.2</version>
</dependency>

Feign Client示例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@FeignClient(name = "xxx", url = "http://www.itmuch.com/", configuration = TestFeignClient.FormSupportConfig.class)
public interface TestFeignClient {
 @PostMapping(value = "/test",
   consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
   produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
   )
 void post(Map<String, ?> queryParam);
 class FormSupportConfig {
  @Autowired
  private ObjectFactory<HttpMessageConverters> messageConverters;
  // new一個form編碼器,實現支持form表單提交
  @Bean
  public Encoder feignFormEncoder() {
   return new SpringFormEncoder(new SpringEncoder(messageConverters));
  }
  // 開啟Feign的日志
  @Bean
  public Logger.Level logger() {
   return Logger.Level.FULL;
  }
 }
}

調用示例:

?
1
2
3
4
5
6
7
8
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
 HashMap<String, String> param = Maps.newHashMap();
 param.put("username","zhangsan");
 param.put("password","pwd");
 this.testFeignClient.post(param);
 return new User();
}

日志:

?
1
2
3
4
5
6
7
...[TestFeignClient#post] ---> POST http://www.baidu.com/test HTTP/1.1
...[TestFeignClient#post] Accept: application/json;charset=UTF-8
...[TestFeignClient#post] Content-Type: application/x-www-form-urlencoded; charset=UTF-8
...[TestFeignClient#post] Content-Length: 30
...[TestFeignClient#post]
...[TestFeignClient#post] password=pwd&username=zhangsan
...[TestFeignClient#post] ---> END HTTP (30-byte body)

由日志可知,此時Feign已能使用Form表單方式提交數據。

Feign GET請求如何構造多參數

假設需請求的URL包含多個參數,例如http://microservice-provider-user/get?id=1&username=張三 ,該如何使用Feign構造呢?我們知道,Spring Cloud為Feign添加了Spring MVC的注解支持,那么我們不妨按照Spring MVC的寫法嘗試一下:

?
1
2
3
4
5
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
 @RequestMapping(value = "/get", method = RequestMethod.GET)
 public User get0(User user);
}

然而,這種寫法并不正確,控制臺會輸出類似如下的異常。

?
1
2
feign.FeignException: status 405 reading UserFeignClient#get0(User); content:
{"timestamp":1482676142940,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/get"}

由異常可知,盡管我們指定了GET方法,Feign依然會使用POST方法發送請求。于是導致了異常。正確寫法如下

方法一[推薦]注意:使用該方法無法使用Fegin的繼承模式

?
1
2
3
4
5
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
 @GetMapping("/get")
 public User get0(@SpringQueryMap User user);
}

方法二[推薦]

?
1
2
3
4
5
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
 @RequestMapping(value = "/get", method = RequestMethod.GET)
 public User get1(@RequestParam("id") Long id, @RequestParam("username") String username);
}

這是最為直觀的方式,URL有幾個參數,Feign接口中的方法就有幾個參數。使用@RequestParam注解指定請求的參數是什么。

方法三[不推薦]多參數的URL也可使用Map來構建。當目標URL參數非常多的時候,可使用這種方式簡化Feign接口的編寫。

?
1
2
3
4
5
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
 @RequestMapping(value = "/get", method = RequestMethod.GET)
 public User get2(@RequestParam Map<String, Object> map);
}

在調用時,可使用類似以下的代碼。

?
1
2
3
4
5
6
public User get(String username, String password) {
 HashMap<String, Object> map = Maps.newHashMap();
 map.put("id", "1");
 map.put("username", "張三");
 return this.userFeignClient.get2(map);
}

注意:這種方式不建議使用。主要是因為可讀性不好,而且如果參數為空的時候會有一些問題,例如map.put("username", null); 會導致服務調用方(消費者服務)接收到的username是"" ,而不是null。

切換為 Okhttp3 提升 QPS 性能優化

加依賴引入okhttp3

?
1
2
3
4
5
<dependency>
 <groupId>io.github.openfeign</groupId>
 <artifactId>feign-okhttp</artifactId>
 <version>${version}</version>
</dependency>

寫配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
feign:
 # feign啟用hystrix,才能熔斷、降級
 # hystrix:
 # enabled: true
 # 啟用 okhttp 關閉默認 httpclient
 httpclient:
 enabled: false #關閉httpclient
 # 配置連接池
 max-connections: 200 #feign的最大連接數
 max-connections-per-route: 50 #fegin單個路徑的最大連接數
 okhttp:
 enabled: true
 # 請求與響應的壓縮以提高通信效率
 compression:
 request:
  enabled: true
  min-request-size: 2048
  mime-types: text/xml,application/xml,application/json
 response:
  enabled: true

參數配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * 配置 okhttp 與連接池
 * ConnectionPool 默認創建5個線程,保持5分鐘長連接
 */
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class) //SpringBoot自動配置
public class OkHttpConfig {
 // 默認老外留給你彩蛋中文亂碼,加上它就 OK
 @Bean
 public Encoder encoder() {
  return new FormEncoder();
 }
 @Bean
 public okhttp3.OkHttpClient okHttpClient() {
  return new okhttp3.OkHttpClient.Builder()
    //設置連接超時
    .connectTimeout(10, TimeUnit.SECONDS)
    //設置讀超時
    .readTimeout(10, TimeUnit.SECONDS)
    //設置寫超時
    .writeTimeout(10, TimeUnit.SECONDS)
    //是否自動重連
    .retryOnConnectionFailure(true)
    .connectionPool(new ConnectionPool(10, 5L, TimeUnit.MINUTES))
    .build();
 }
}

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。如有錯誤或未考慮完全的地方,望不吝賜教。

原文鏈接:https://jaemon.blog.csdn.net/article/details/89103162

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 午夜福利体检 | 青丝视频免费版在线看 | 美女的隐私脱裤子无遮挡 | 热99这里只有精品 | 五月天精品视频在线观看 | 视频大全在线观看网址 | 操爽| 亚洲香蕉网久久综合影院3p | 日韩视频在线观看中字 | 日本高清二三四本2021 | 大胸孕妇孕交pregnantsex 大象视频污 | 亚洲精品九色在线网站 | 亚洲精品九色在线网站 | 7777色鬼xxxx欧美色夫 | 吃瓜视频在线观看 | 亚洲咪咪| 动漫肉在线观看 | 特级毛片全部免费播放器 | 国产精品日本一区二区三区在线看 | 欧美日韩在线观看区一二 | 激情涩涩| 日韩精品首页 | 艾秋麻豆果冻剧传媒在线播放 | 欧美视频在线播放观看免费福利资源 | 免费xxxx日本大片在线观看 | 欧美日韩亚洲国内综合网香蕉 | 亚洲精品91香蕉综合区 | 国产亚洲高清国产拍精品 | chinese国产打屁股 | 国产伦精品一区二区 | 国产亚洲精品美女2020久久 | 亚洲香蕉网久久综合影院3p | 欧美性野久久久久久久久 | 国产大片视频免费观看 | 色哟哟观看| 99免费在线视频 | 日韩亚洲国产激情在线观看 | 青青久久久国产线免观 | 国产资源中文字幕 | 午夜影院免费看 | 欧美草逼视频 |