激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

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

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

服務器之家 - 編程語言 - Java教程 - Spring Cloud學習教程之Zuul統一異常處理與回退

Spring Cloud學習教程之Zuul統一異常處理與回退

2021-04-23 11:41洛陽融科聶晨 Java教程

Spring Cloud Zuul對異常的處理整體來說還是比較方便的,流程也比較清晰,下面這篇文章主要給大家介紹了關于Spring Cloud學習教程之Zuul統一異常處理與回退的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,

前言

zuul 是netflix 提供的一個開源組件,致力于在云平臺上提供動態路由,監控,彈性,安全等邊緣服務的框架。也有很多公司使用它來作為網關的重要組成部分,碰巧今年公司的架構組決定自研一個網關產品,集動態路由,動態權限,限流配額等功能為一體,為其他部門的項目提供統一的外網調用管理,最終形成產品(這方面阿里其實已經有成熟的網關產品了,但是不太適用于個性化的配置,也沒有集成權限和限流降級)。

本文主要給大家介紹了關于spring cloud zuul統一異常處理與回退的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。

一、filter中統一異常處理

  其實在springcloud的edgware sr2版本中對于zuulfilter中的錯誤有統一的處理,但是在實際開發當中對于錯誤的響應方式,我想每個團隊都有自己的處理規范。那么如何做到自定義的異常處理呢?

我們可以先參考一下springcloud提供的senderrorfilter:

?
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
/*
 * copyright 2013-2015 the original author or authors.
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *  http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */
 
package org.springframework.cloud.netflix.zuul.filters.post;
 
import javax.servlet.requestdispatcher;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
 
import org.apache.commons.logging.log;
import org.apache.commons.logging.logfactory;
import org.springframework.beans.factory.annotation.value;
import org.springframework.cloud.netflix.zuul.util.zuulruntimeexception;
import org.springframework.util.reflectionutils;
import org.springframework.util.stringutils;
 
import com.netflix.zuul.zuulfilter;
import com.netflix.zuul.context.requestcontext;
import com.netflix.zuul.exception.zuulexception;
 
import static org.springframework.cloud.netflix.zuul.filters.support.filterconstants.error_type;
import static org.springframework.cloud.netflix.zuul.filters.support.filterconstants.send_error_filter_order;
 
/**
 * error {@link zuulfilter} that forwards to /error (by default) if {@link requestcontext#getthrowable()} is not null.
 *
 * @author spencer gibb
 */
//todo: move to error package in edgware
public class senderrorfilter extends zuulfilter {
 
 private static final log log = logfactory.getlog(senderrorfilter.class);
 protected static final string send_error_filter_ran = "senderrorfilter.ran";
 
 @value("${error.path:/error}")
 private string errorpath;
 
 @override
 public string filtertype() {
  return error_type;
 }
 
 @override
 public int filterorder() {
  return send_error_filter_order;
 }
 
 @override
 public boolean shouldfilter() {
  requestcontext ctx = requestcontext.getcurrentcontext();
  // only forward to errorpath if it hasn't been forwarded to already
  return ctx.getthrowable() != null
    && !ctx.getboolean(send_error_filter_ran, false);
 }
 
 @override
 public object run() {
  try {
   requestcontext ctx = requestcontext.getcurrentcontext();
   zuulexception exception = findzuulexception(ctx.getthrowable());
   httpservletrequest request = ctx.getrequest();
 
   request.setattribute("javax.servlet.error.status_code", exception.nstatuscode);
 
   log.warn("error during filtering", exception);
   request.setattribute("javax.servlet.error.exception", exception);
 
   if (stringutils.hastext(exception.errorcause)) {
    request.setattribute("javax.servlet.error.message", exception.errorcause);
   }
 
   requestdispatcher dispatcher = request.getrequestdispatcher(
     this.errorpath);
   if (dispatcher != null) {
    ctx.set(send_error_filter_ran, true);
    if (!ctx.getresponse().iscommitted()) {
     ctx.setresponsestatuscode(exception.nstatuscode);
     dispatcher.forward(request, ctx.getresponse());
    }
   }
  }
  catch (exception ex) {
   reflectionutils.rethrowruntimeexception(ex);
  }
  return null;
 }
 
 zuulexception findzuulexception(throwable throwable) {
  if (throwable.getcause() instanceof zuulruntimeexception) {
   // this was a failure initiated by one of the local filters
   return (zuulexception) throwable.getcause().getcause();
  }
 
  if (throwable.getcause() instanceof zuulexception) {
   // wrapped zuul exception
   return (zuulexception) throwable.getcause();
  }
 
  if (throwable instanceof zuulexception) {
   // exception thrown by zuul lifecycle
   return (zuulexception) throwable;
  }
 
  // fallback, should never get here
  return new zuulexception(throwable, httpservletresponse.sc_internal_server_error, null);
 }
 
 public void seterrorpath(string errorpath) {
  this.errorpath = errorpath;
 }
}

在這里我們可以找到幾個關鍵點:

  1)在上述代碼中,我們可以發現filter已經將相關的錯誤信息放到request當中了:

    request.setattribute("javax.servlet.error.status_code", exception.nstatuscode);

    request.setattribute("javax.servlet.error.exception", exception);

    request.setattribute("javax.servlet.error.message", exception.errorcause);

  2)錯誤處理完畢后,會轉發到 xxx/error的地址來處理 

  那么我們可以來做個試驗,我們在gateway-service項目模塊里,創建一個會拋出異常的filter:

?
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
package com.hzgj.lyrk.springcloud.gateway.server.filter;
import com.netflix.zuul.zuulfilter;
import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.component;
@component
@slf4j
public class myzuulfilter extends zuulfilter {
 @override
 public string filtertype() {
  return "post";
 }
 
 @override
 public int filterorder() {
  return 9;
 }
 
 @override
 public boolean shouldfilter() {
  return true;
 }
 
 @override
 public object run() {
  log.info("run error test ...");
  throw new runtimeexception();
  // return null;
 }
}

  緊接著我們定義一個控制器,來做錯誤處理:

?
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
package com.hzgj.lyrk.springcloud.gateway.server.filter;
import org.springframework.http.httpstatus;
import org.springframework.http.responseentity;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.restcontroller;
 
import javax.servlet.http.httpservletrequest;
 
@restcontroller
public class errorhandler {
 
 @getmapping(value = "/error")
 public responseentity<errorbean> error(httpservletrequest request) {
  string message = request.getattribute("javax.servlet.error.message").tostring();
  errorbean errorbean = new errorbean();
  errorbean.setmessage(message);
  errorbean.setreason("程序出錯");
  return new responseentity<>(errorbean, httpstatus.bad_gateway);
 }
 
 private static class errorbean {
  private string message;
 
  private string reason;
 
  public string getmessage() {
   return message;
  }
 
  public void setmessage(string message) {
   this.message = message;
  }
 
  public string getreason() {
   return reason;
  }
 
  public void setreason(string reason) {
   this.reason = reason;
  }
 }
}

  啟動項目后,我們通過網關訪問一下試試:

Spring Cloud學習教程之Zuul統一異常處理與回退

二、關于zuul回退的問題

1、關于zuul的超時問題:

  這個問題網上有很多解決方案,但是我還要貼一下源代碼,請關注這個類 abstractribboncommand,在這個類里集成了hystrix與ribbon。

?
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
 * copyright 2013-2016 the original author or authors.
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *  http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 *
 */
 
package org.springframework.cloud.netflix.zuul.filters.route.support;
 
import org.apache.commons.logging.log;
import org.apache.commons.logging.logfactory;
import org.springframework.cloud.netflix.ribbon.ribbonclientconfiguration;
import org.springframework.cloud.netflix.ribbon.ribbonhttpresponse;
import org.springframework.cloud.netflix.ribbon.support.abstractloadbalancingclient;
import org.springframework.cloud.netflix.ribbon.support.contextawarerequest;
import org.springframework.cloud.netflix.zuul.filters.zuulproperties;
import org.springframework.cloud.netflix.zuul.filters.route.ribboncommand;
import org.springframework.cloud.netflix.zuul.filters.route.ribboncommandcontext;
import org.springframework.cloud.netflix.zuul.filters.route.zuulfallbackprovider;
import org.springframework.cloud.netflix.zuul.filters.route.fallbackprovider;
import org.springframework.http.client.clienthttpresponse;
import com.netflix.client.abstractloadbalancerawareclient;
import com.netflix.client.clientrequest;
import com.netflix.client.config.defaultclientconfigimpl;
import com.netflix.client.config.iclientconfig;
import com.netflix.client.config.iclientconfigkey;
import com.netflix.client.http.httpresponse;
import com.netflix.config.dynamicintproperty;
import com.netflix.config.dynamicpropertyfactory;
import com.netflix.hystrix.hystrixcommand;
import com.netflix.hystrix.hystrixcommandgroupkey;
import com.netflix.hystrix.hystrixcommandkey;
import com.netflix.hystrix.hystrixcommandproperties;
import com.netflix.hystrix.hystrixcommandproperties.executionisolationstrategy;
import com.netflix.hystrix.hystrixthreadpoolkey;
import com.netflix.zuul.constants.zuulconstants;
import com.netflix.zuul.context.requestcontext;
 
/**
 * @author spencer gibb
 */
public abstract class abstractribboncommand<lbc extends abstractloadbalancerawareclient<rq, rs>, rq extends clientrequest, rs extends httpresponse>
  extends hystrixcommand<clienthttpresponse> implements ribboncommand {
 
 private static final log logger = logfactory.getlog(abstractribboncommand.class);
 protected final lbc client;
 protected ribboncommandcontext context;
 protected zuulfallbackprovider zuulfallbackprovider;
 protected iclientconfig config;
 
 public abstractribboncommand(lbc client, ribboncommandcontext context,
   zuulproperties zuulproperties) {
  this("default", client, context, zuulproperties);
 }
 
 public abstractribboncommand(string commandkey, lbc client,
   ribboncommandcontext context, zuulproperties zuulproperties) {
  this(commandkey, client, context, zuulproperties, null);
 }
 
 public abstractribboncommand(string commandkey, lbc client,
         ribboncommandcontext context, zuulproperties zuulproperties,
         zuulfallbackprovider fallbackprovider) {
  this(commandkey, client, context, zuulproperties, fallbackprovider, null);
 }
 
 public abstractribboncommand(string commandkey, lbc client,
         ribboncommandcontext context, zuulproperties zuulproperties,
         zuulfallbackprovider fallbackprovider, iclientconfig config) {
  this(getsetter(commandkey, zuulproperties, config), client, context, fallbackprovider, config);
 }
 
 protected abstractribboncommand(setter setter, lbc client,
         ribboncommandcontext context,
         zuulfallbackprovider fallbackprovider, iclientconfig config) {
  super(setter);
  this.client = client;
  this.context = context;
  this.zuulfallbackprovider = fallbackprovider;
  this.config = config;
 }
 
 protected static hystrixcommandproperties.setter createsetter(iclientconfig config, string commandkey, zuulproperties zuulproperties) {
  int hystrixtimeout = gethystrixtimeout(config, commandkey);
  return hystrixcommandproperties.setter().withexecutionisolationstrategy(
    zuulproperties.getribbonisolationstrategy()).withexecutiontimeoutinmilliseconds(hystrixtimeout);
 }
 
 protected static int gethystrixtimeout(iclientconfig config, string commandkey) {
  int ribbontimeout = getribbontimeout(config, commandkey);
  dynamicpropertyfactory dynamicpropertyfactory = dynamicpropertyfactory.getinstance();
  int defaulthystrixtimeout = dynamicpropertyfactory.getintproperty("hystrix.command.default.execution.isolation.thread.timeoutinmilliseconds",
   0).get();
  int commandhystrixtimeout = dynamicpropertyfactory.getintproperty("hystrix.command." + commandkey + ".execution.isolation.thread.timeoutinmilliseconds",
   0).get();
  int hystrixtimeout;
  if(commandhystrixtimeout > 0) {
   hystrixtimeout = commandhystrixtimeout;
  }
  else if(defaulthystrixtimeout > 0) {
   hystrixtimeout = defaulthystrixtimeout;
  } else {
   hystrixtimeout = ribbontimeout;
  }
  if(hystrixtimeout < ribbontimeout) {
   logger.warn("the hystrix timeout of " + hystrixtimeout + "ms for the command " + commandkey +
    " is set lower than the combination of the ribbon read and connect timeout, " + ribbontimeout + "ms.");
  }
  return hystrixtimeout;
 }
 
 protected static int getribbontimeout(iclientconfig config, string commandkey) {
  int ribbontimeout;
  if (config == null) {
   ribbontimeout = ribbonclientconfiguration.default_read_timeout + ribbonclientconfiguration.default_connect_timeout;
  } else {
   int ribbonreadtimeout = gettimeout(config, commandkey, "readtimeout",
    iclientconfigkey.keys.readtimeout, ribbonclientconfiguration.default_read_timeout);
   int ribbonconnecttimeout = gettimeout(config, commandkey, "connecttimeout",
    iclientconfigkey.keys.connecttimeout, ribbonclientconfiguration.default_connect_timeout);
   int maxautoretries = gettimeout(config, commandkey, "maxautoretries",
    iclientconfigkey.keys.maxautoretries, defaultclientconfigimpl.default_max_auto_retries);
   int maxautoretriesnextserver = gettimeout(config, commandkey, "maxautoretriesnextserver",
    iclientconfigkey.keys.maxautoretriesnextserver, defaultclientconfigimpl.default_max_auto_retries_next_server);
   ribbontimeout = (ribbonreadtimeout + ribbonconnecttimeout) * (maxautoretries + 1) * (maxautoretriesnextserver + 1);
  }
  return ribbontimeout;
 }
 
 private static int gettimeout(iclientconfig config, string commandkey, string property, iclientconfigkey<integer> configkey, int defaultvalue) {
  dynamicpropertyfactory dynamicpropertyfactory = dynamicpropertyfactory.getinstance();
  return dynamicpropertyfactory.getintproperty(commandkey + "." + config.getnamespace() + "." + property, config.get(configkey, defaultvalue)).get();
 }
 
 @deprecated
 //todo remove in 2.0.x
 protected static setter getsetter(final string commandkey, zuulproperties zuulproperties) {
  return getsetter(commandkey, zuulproperties, null);
 }
 
 protected static setter getsetter(final string commandkey,
   zuulproperties zuulproperties, iclientconfig config) {
 
  // @formatter:off
  setter commandsetter = setter.withgroupkey(hystrixcommandgroupkey.factory.askey("ribboncommand"))
        .andcommandkey(hystrixcommandkey.factory.askey(commandkey));
  final hystrixcommandproperties.setter setter = createsetter(config, commandkey, zuulproperties);
  if (zuulproperties.getribbonisolationstrategy() == executionisolationstrategy.semaphore){
   final string name = zuulconstants.zuul_eureka + commandkey + ".semaphore.maxsemaphores";
   // we want to default to semaphore-isolation since this wraps
   // 2 others commands that are already thread isolated
   final dynamicintproperty value = dynamicpropertyfactory.getinstance()
     .getintproperty(name, zuulproperties.getsemaphore().getmaxsemaphores());
   setter.withexecutionisolationsemaphoremaxconcurrentrequests(value.get());
  } else if (zuulproperties.getthreadpool().isuseseparatethreadpools()) {
   final string threadpoolkey = zuulproperties.getthreadpool().getthreadpoolkeyprefix() + commandkey;
   commandsetter.andthreadpoolkey(hystrixthreadpoolkey.factory.askey(threadpoolkey));
  }
  
  return commandsetter.andcommandpropertiesdefaults(setter);
  // @formatter:on
 }
 
 @override
 protected clienthttpresponse run() throws exception {
  final requestcontext context = requestcontext.getcurrentcontext();
 
  rq request = createrequest();
  rs response;
  
  boolean retryableclient = this.client instanceof abstractloadbalancingclient
    && ((abstractloadbalancingclient)this.client).isclientretryable((contextawarerequest)request);
  
  if (retryableclient) {
   response = this.client.execute(request, config);
  } else {
   response = this.client.executewithloadbalancer(request, config);
  }
  context.set("ribbonresponse", response);
 
  // explicitly close the httpresponse if the hystrix command timed out to
  // release the underlying http connection held by the response.
  //
  if (this.isresponsetimedout()) {
   if (response != null) {
    response.close();
   }
  }
 
  return new ribbonhttpresponse(response);
 }
 
 @override
 protected clienthttpresponse getfallback() {
  if(zuulfallbackprovider != null) {
   return getfallbackresponse();
  }
  return super.getfallback();
 }
 
 protected clienthttpresponse getfallbackresponse() {
  if (zuulfallbackprovider instanceof fallbackprovider) {
   throwable cause = getfailedexecutionexception();
   cause = cause == null ? getexecutionexception() : cause;
   if (cause == null) {
    zuulfallbackprovider.fallbackresponse();
   } else {
    return ((fallbackprovider) zuulfallbackprovider).fallbackresponse(cause);
   }
  }
  return zuulfallbackprovider.fallbackresponse();
 }
 
 public lbc getclient() {
  return client;
 }
 
 public ribboncommandcontext getcontext() {
  return context;
 }
 
 protected abstract rq createrequest() throws exception;
}

  請注意:getribbontimeout方法與gethystrixtimeout方法,其中這兩個方法 commandkey的值為路由的名稱,比如說我們訪問:http://localhost:8088/order-server/xxx來訪問order-server服務, 那么commandkey 就為order-server

  根據源代碼,我們先設置gateway-server的超時參數:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#全局的ribbon設置
ribbon:
 connecttimeout: 3000
 readtimeout: 3000
hystrix:
 command:
 default:
  execution:
  isolation:
   thread:
   timeoutinmilliseconds: 3000
zuul:
 host:
 connecttimeoutmillis: 10000

  當然也可以單獨為order-server設置ribbon的超時參數:order-server.ribbon.xxxx=xxx , 為了演示zuul中的回退效果,我在這里把hystrix超時時間設置短一點。當然最好不要將hystrix默認的超時時間設置的比ribbon的超時時間短,源碼里遇到此情況已經給與我們警告了。

  那么我們在order-server下添加如下方法:

?
1
2
3
4
5
@getmapping("/sleep/{sleeptime}")
 public string sleep(@pathvariable long sleeptime) throws interruptedexception {
  timeunit.seconds.sleep(sleeptime);
  return "success";
 }

2、zuul的回退方法

我們可以實現zuulfallbackprovider接口,實現代碼:

?
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
package com.hzgj.lyrk.springcloud.gateway.server.filter;
import com.google.common.collect.immutablemap;
import com.google.gson.gsonbuilder;
import org.springframework.cloud.netflix.zuul.filters.route.zuulfallbackprovider;
import org.springframework.http.httpheaders;
import org.springframework.http.httpstatus;
import org.springframework.http.mediatype;
import org.springframework.http.client.clienthttpresponse;
import org.springframework.stereotype.component;
import java.io.bytearrayinputstream;
import java.io.ioexception;
import java.io.inputstream;
import java.time.localdatetime;
import java.time.localtime;
@component
public class fallbackhandler implements zuulfallbackprovider {
 
 @override
 public string getroute() {
  //代表所有的路由都適配該設置
  return "*";
 }
 
 @override
 public clienthttpresponse fallbackresponse() {
  return new clienthttpresponse() {
   @override
   public httpstatus getstatuscode() throws ioexception {
    return httpstatus.ok;
   }
 
   @override
   public int getrawstatuscode() throws ioexception {
    return 200;
   }
 
   @override
   public string getstatustext() throws ioexception {
    return "ok";
   }
 
   @override
   public void close() {
 
   }
 
   @override
   public inputstream getbody() throws ioexception {
    string result = new gsonbuilder().create().tojson(immutablemap.of("errorcode", 500, "content", "請求失敗", "time", localdatetime.now()));
    return new bytearrayinputstream(result.getbytes());
   }
 
   @override
   public httpheaders getheaders() {
    httpheaders headers = new httpheaders();
    headers.setcontenttype(mediatype.application_json);
    return headers;
   }
  };
 }
}

此時我們訪問:http://localhost:8088/order-server/sleep/6 得到如下結果:

Spring Cloud學習教程之Zuul統一異常處理與回退

當我們訪問:http://localhost:8088/order-server/sleep/1 就得到如下結果:

Spring Cloud學習教程之Zuul統一異常處理與回退

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:http://www.cnblogs.com/niechen/p/8856551.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 午夜丰满少妇高清毛片1000部 | 亚洲国产高清一区 | 日本黄色免费播放 | 日韩午夜一区二区三区 | 爱射av| 国产成人精品日本亚洲语音 | 999精品久久久 | 亚洲精品一区二区三区免 | 好吊色欧美一区二区三区四区 | 久久久久久久久浪潮精品 | 成人在线观看一区二区 | 天天夜天天操 | 九九黄色| av影院在线| 国产91久久久久久 | 高清av免费 | 欧美另类在线视频 | 久久综合精品视频 | 老师你怎么会在这第2季出现 | 国产精品观看在线亚洲人成网 | 成人做爽爽爽爽免费国产软件 | 欧美a∨一区二区三区久久黄 | av电影在线免费 | 久久国产精品一区 | 视屏一区 | 91成人免费电影 | 全黄毛片 | 日韩在线播放一区二区 | 久久精品男人 | 国产1区2区3区中文字幕 | 特级黄aaaaaaaaa毛片 | 久久成人黄色 | 91成人在线免费 | 久久综合九色 | 日韩欧美动作影片 | 亚洲精品免费播放 | 国产91九色在线播放 | 日韩在线视频二区 | 激情黄页| 久久精品之 | 史上最强炼体老祖动漫在线观看 |