整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Windows環境下jenkins實現自動化部署

          Windows環境下jenkins實現自動化部署

          、前言

          Jenkins是一個開源軟件項目,是基于Java開發的一種持續集成工具,用于監控持續重復的工作,旨在提供一個開放易用的軟件平臺,使軟件的持續集成變成可能。本文使用Jenkins構建github上面的springboot項目,原理為jenkins從github上拉取源碼,然后使用maven進行打包,最后使用java運行springboot項目打包后的jar包。

          二、環境準備

          2.1 本文工具版本

          JDK:1.8.0_152

          Maven:3.6.0

          Git:2.14.1

          Jenkins:2.172

          2.2 安裝JDK

          網上已有數不清的教程,此處不再造輪子。

          2.3 安裝Maven

          同上。

          2.4 安裝Git

          同上+1。

          三、部署Jenkins

          3.1 下載Jenkins

          Jenkins官網下載地址:https://jenkins.io/index.html,點擊下載

          在頁面下方,選擇你想下載的版本號和相應的平臺,這里下載的版本號為2.164.2 for Windows,如下圖:


          下載完成后,將壓縮包放到你想要的位置,然后解壓。

          3.2 啟動Jenkins

          找到解壓后的文件夾,進入到jenkins.war所在的目錄,按住shift鍵,點擊鼠標左鍵,在彈出的菜單中找到“在此處打開Powershell窗口”。

          執行命令:

          java -jar jenkins.war --httpPort=8090

          其中jenkins.war是你要啟動的war包的名稱,httpPort是啟動的端口號,可以不指定,默認為8080。

          不指定端口號的話,執行以下命令就行:

          java -jar jenkins.war

          當看到以下信息時,代表jenkins已經啟動成功。啟動后,不要關閉命令窗口。關閉命令窗口,服務也會停止。

          3.3 訪問Jenkins

          打開瀏覽器,在地址欄輸入:http://localhost:8090 即可訪問jenkins。

          第一次訪問需要按頁面提示輸入密碼。如果在頁面上沒有看到,可以去啟動日志中查找。

          四、安裝插件

          • 在左邊菜單欄點擊“系統管理”。

          • 然后點擊“插件管理”。

          • 點擊“可選插件”

          • 在右上角過濾欄中,輸入你想要安裝的插件,常用的插件有以下幾個:

             Maven Integration plugin

             SSH plugin

             Deploy to container Plugin

             GitLab

             Gitlab Hook

          • 勾選好自己要安裝的插件之后,點擊下面的“直接安裝”按鈕。

          • 然后等待安裝,安裝完成之后,最好手動重啟一下jenkins。

          五、Jenkins的配置

          5.1 系統設置

          回到系統管理界面,點擊“系統設置”。(ps:這里系統設置只是簡單地設置了一下系統管理員的郵件地址,也可以不設置,沒有這個需求的可以直接跳轉到 5.2 全局工具配置。)

          找到Jenkins Location,配置系統管理員郵件地址

          配置通知郵件,勾選“通過發送測試郵件測試配置”,在圖中2所指之處,填入接受測試郵件的郵箱地址,然后點擊“Test configuration”。

          如果配置成功,將會收到如下的一條測試郵件:

          5.2 全局工具配置

          回到系統管理頁面,點擊“全局工具配置”,進入到全局工具配置頁面,開始Maven、JDK、Git的配置。

          首先配置Maven的settings.xml文件,可以使用maven默認的配置文件,也可以指定特定路徑下的settings.xml文件。

          然后在頁面下方,找到Maven一欄,點擊Maven安裝。

          接下來進行Maven的配置,如下圖,Name可以任取,MAVEN_HOME就是上面2.2 步驟Maven的安裝路徑。

          5.2.2 JDK的配置

          找到JDK一欄,點擊JDK安裝。

          找到Git一欄,進行Git的配置。Name同樣隨便起,Path to Git executable處填寫git.exe文件的路徑,如c:\git\git.exe。

          至此,全局的Maven、JDK和Git已配置完成。

          六、新建任務

          回到主界面,左邊菜單欄點擊“新建任務”。

          在新建任務頁面,輸入任務名稱,然后點擊“構建一個自由風格的軟件項目”或者“構建一個Maven項目”,最后點擊左下角的確定按鈕,自動跳轉到任務的配置頁面。

          6.1 任務的配置

          下面開始任務的配置,總共有下面幾個部分,我們分步講解。

          6.1.1 General

          配置如下圖:

          6.1.2 源碼管理

          配置如下圖:

          這個目前我還沒有用到過,等以后用到了再更新。

          6.1.4 構建環境

          同上。

          6.1.5 構建

          這里構建分以下幾步進行:

          • 構建前的準備(查看端口是否被占用,若被占用,則殺死進程)
          • 打包(包括從github拉取指定分支的最新代碼,執行maven的clean和package命令)
          • 啟動Jar包(這里使用8090端口)

          下面分步講解:

          ① 點擊增加構建步驟,選擇執行Windows批處理命令:

          附上源碼:

          @echo off
          setlocal enabledelayedexpansion
          set port=8090
          for /f "tokens=1-5" %%a in ('netstat -ano ^| find ":%port%"') do (
              if "%%e%"=="" (
                  set pid=%%d
              ) else (
                   set pid=%%e
              )
              echo !pid!
          )
          if NOT "!pid!"=="" (
             taskkill /f /pid !pid!
          )

          ② 點擊增加構建步驟,選擇調用頂層Maven目標:

          輸入以下配置:

          ③ 點擊增加構建步驟,選擇執行Windows批處理命令:

          附上源碼:

          @echo off
          set BUILD_ID=dontKillMe
          start java -jar .\target\test-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
          exit

          6.1.6 構建后操作

          可以配置一些構建完成后要做的工作。

          至此,任務配置完畢,點擊左下角的保存,跳轉到任務主界面。

          6.2 啟動任務

          左邊菜單欄,點擊立即構建。

          點擊控制臺輸出,可以查看構建過程中的日志輸出。

          當看到以下信息時,表明已經構建成功。

          同時彈出了一個cmd窗口,記錄了SpringBoot的啟動日志。


          至此,使用jenkins自動化部署github項目已經大功告成,盡情享受jenkins帶來的便利吧!

          、jar包和war包的區別

          1.1 war包

          1. war包是Java Web應用程序的一種打包方式符合Servlet標準,它是Web Archive的縮寫,主要用于存儲Web應用程序相關的文件,包括Java類文件、JSP、HTML、CSS、JavaScript、圖片等資源文件。
          2. war包需要部署到web服務器中(Tomcat、Apache、IIS)

          1.2 jar包

          1. jar包是類的歸檔文件,主要用于存儲Java類文件和相關資源文件。它通常被用于封裝Java應用程序或Java類庫,方便程序的部署和發布
          2. jar包可以被JVM直接加載和運行。

          1.3 主要區別:

          1. jar包主要用于存儲Java類文件和相關資源文件,而war包主要用于存儲Web應用程序相關的文件。
          2. jar包可以被JVM直接加載和運行,而war包需要被Web服務器加載和運行。
          3. jar包通常用于封裝Java應用程序或Java類庫,而war包用于封裝Java Web應用程序。

          二、SpringBoot使用war包啟動

          war包啟動:需要先啟動外部的Web服務器,實現Servlet3.0規范中引導應用啟動類,然后將war包放入Web服務器下,Web服務器通過回調引導應用啟動類方法啟動應用。

          2.1 Servlet3.0規范中引導應用啟動的說明

          • 在Servlet容器(Tomcat、Jetty等)啟動應用時,會掃描應用jar包中 ServletContainerInitializer 的實現類。
          • 框架必須在jar包的 META-INF/services 的文件夾中提供一個名為 javax.servlet.ServletContainerInitializer 的文件,文件內容要寫明 ServletContainerInitializer 的實現類的全限定名。
          • 這個 ServletContainerInitializer 是一個接口,實現它的類必須實現一個方法:onStartUp
          • 可以在這個 ServletContainerInitializer 的實現類上標注 @HandlesTypes 注解,在應用啟動的時候自行加載一些附加的類,這些類會以字節碼的集合形式傳入 onStartup 方法的第一個參數中。
          public interface ServletContainerInitializer {
              void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
          }

          2.2 SpringBootServletInitializer的作用和原理

          Spirng中SpringServletContainerInitializer實現了Servlet的規范

          @HandlesTypes(WebApplicationInitializer.class)
          public class SpringServletContainerInitializer implements ServletContainerInitializer {
              @Override
              public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
                      throws ServletException {
                  // SpringServletContainerInitializer會加載所有的WebApplicationInitializer類型的普通實現類
          
                  List<WebApplicationInitializer> initializers=new LinkedList<WebApplicationInitializer>();
          
                  if (webAppInitializerClasses !=null) {
                      for (Class<?> waiClass : webAppInitializerClasses) {
                          // 如果不是接口,不是抽象類
                          if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                                  WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                              try {
                                  // 創建該類的實例
                                  initializers.add((WebApplicationInitializer) waiClass.newInstance());
                              }
                              catch (Throwable ex) {
                                  throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                              }
                          }
                      }
                  }
          
                  if (initializers.isEmpty()) {
                      servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
                      return;
                  }
          
                  servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
                  AnnotationAwareOrderComparator.sort(initializers);
                  // 啟動Web應用onStartup方法
                  for (WebApplicationInitializer initializer : initializers) {
                      initializer.onStartup(servletContext);
                  }
              }
          }

          @HandlesTypes使用BCEL的ClassParser在字節碼層面讀取了/WEB-INF/classes和jar中class文件的超類名和實現的接口名,判斷是否與記錄的注解類名相同,若相同再通過org.apache.catalina.util.Introspection類加載為Class對象保存起來,最后傳入onStartup方法參數中

          SpringServletContainerInitializer類上標注了@HandlesTypes(WebApplicationInitializer.class),所以會導入WebApplicationInitializer實現類

          SpringBoot中SpringBootServletInitializer是WebApplicationInitializer的抽象類,實現了onStartup方法

          @Override
          public void onStartup(ServletContext servletContext) throws ServletException {
              // Logger initialization is deferred in case an ordered
              // LogServletContextInitializer is being used
              this.logger=LogFactory.getLog(getClass());
              // 創建 父IOC容器
              WebApplicationContext rootAppContext=createRootApplicationContext(servletContext);
              if (rootAppContext !=null) {
                  servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                      @Override
                      public void contextInitialized(ServletContextEvent event) {
                          // no-op because the application context is already initialized
                      }
                  });
              }
              else {
                  this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not "
                          + "return an application context");
              }
          }

          創建父容器

          protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
              // 使用Builder機制,前面也介紹過
              SpringApplicationBuilder builder=createSpringApplicationBuilder();
              builder.main(getClass());
              ApplicationContext parent=getExistingRootWebApplicationContext(servletContext);
              if (parent !=null) {
                  this.logger.info("Root context already created (using as parent).");
                  servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
                  builder.initializers(new ParentContextApplicationContextInitializer(parent));
              }
              // 設置Initializer
              builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
              // 在這里設置了容器啟動類:AnnotationConfigServletWebServerApplicationContext
              builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
              // 【引導】多態進入子類(自己定義)的方法中
              builder=configure(builder);
              builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
              // builder.build(),創建SpringApplication
              SpringApplication application=builder.build();
              if (application.getAllSources().isEmpty()
                      && AnnotationUtils.findAnnotation(getClass(), Configuration.class) !=null) {
                  application.addPrimarySources(Collections.singleton(getClass()));
              }
              Assert.state(!application.getAllSources().isEmpty(),
                      "No SpringApplication sources have been defined. Either override the "
                              + "configure method or add an @Configuration annotation");
              // Ensure error pages are registered
              if (this.registerErrorPageFilter) {
                  application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
              }
              // 啟動SpringBoot應用
              return run(application);
          }

          所以我們只需要自定義類繼承SpringBootServletInitializer并實現configure方法告訴啟動類所在的位置就可以實現SpringBoot自啟動了

          例如:

          public class MyInitializer extends SpringBootServletInitializer {
           
             @Override
             protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
                //MySpringBootApplication為SpingBoot啟動類
                return application.sources(MySpringBootApplication.class);
             }
          }

          三、SpringBoot使用jar包啟動

          按照java官方文檔規定,java -jar命令引導的具體啟動類必須配置在MANIFEST.MF中的Main-class屬性中,該值代表應用程序執行入口類也就是包含main方法的類。

          從MANIFEST.MF文件內容可以看到,Main-Class這個屬性定義了org.springframework.boot.loader.JarLauncher,JarLauncher就是對應Jar文件的啟動器。而我們項目的啟動類SpringBootDemoApplication定義在Start-Class屬性中,

          JarLauncher會將BOOT-INF/classes下的類文件和BOOT-INF/lib下依賴的jar加入到classpath下,然后調用META-INF/MANIFEST.MF文件Start-Class屬性完成應用程序的啟動。

          關于 jar 官方標準說明請移步

          JAR File Specification JAR (file format)

          SpringBoot的jar包,會有3個文件夾:

          • BOOT-INF:存放自己編寫并編譯好的 .class 文件和靜態資源文件、配置文件等
          • META-INF:有一個 MANIFEST.MF 的文件
          • org:spring-boot-loader 的一些 .class 文件

          META-INF 下面的 MANIFEST.MF 文件,里面的內容如下:

          Manifest-Version: 1.0
          Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
          Implementation-Title: my-small-test
          Implementation-Version: 1.0-SNAPSHOT
          Spring-Boot-Layers-Index: BOOT-INF/layers.idx
          Start-Class: com.small.test.SpringBootDemoApplication
          Spring-Boot-Classes: BOOT-INF/classes/
          Spring-Boot-Lib: BOOT-INF/lib/
          Build-Jdk-Spec: 1.8
          Spring-Boot-Version: 2.4.0
          Created-By: Maven Jar Plugin 3.2.0
          Main-Class: org.springframework.boot.loader.JarLauncher
          • 在 Start-Class 中注明了 SpringBoot 的主啟動類
          • 在 Main-Class 中注明了一個類: JarLauncher
          package org.springframework.boot.loader;
          
          import java.io.IOException;
          import java.util.jar.Attributes;
          import java.util.jar.Manifest;
          import org.springframework.boot.loader.archive.Archive;
          
          public class JarLauncher extends ExecutableArchiveLauncher {
            private static final String DEFAULT_CLASSPATH_INDEX_LOCATION="BOOT-INF/classpath.idx";
            
            static final Archive.EntryFilter NESTED_ARCHIVE_ENTRY_FILTER;
            
            static {
              NESTED_ARCHIVE_ENTRY_FILTER=(entry -> entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/"));
            }
            
            public JarLauncher() {}
            
            protected JarLauncher(Archive archive) {
              super(archive);
            }
            
            protected ClassPathIndexFile getClassPathIndex(Archive archive) throws IOException {
              if (archive instanceof org.springframework.boot.loader.archive.ExplodedArchive) {
                String location=getClassPathIndexFileLocation(archive);
                return ClassPathIndexFile.loadIfPossible(archive.getUrl(), location);
              } 
              return super.getClassPathIndex(archive);
            }
            
            private String getClassPathIndexFileLocation(Archive archive) throws IOException {
              Manifest manifest=archive.getManifest();
              Attributes attributes=(manifest !=null) ? manifest.getMainAttributes() : null;
              String location=(attributes !=null) ? attributes.getValue("Spring-Boot-Classpath-Index") : null;
              return (location !=null) ? location : "BOOT-INF/classpath.idx";
            }
            
            protected boolean isPostProcessingClassPathArchives() {
              return false;
            }
            
            protected boolean isSearchCandidate(Archive.Entry entry) {
              return entry.getName().startsWith("BOOT-INF/");
            }
            
            protected boolean isNestedArchive(Archive.Entry entry) {
              return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
            }
           
            public static void main(String[] args) throws Exception {
              (new JarLauncher()).launch(args);
            }
          }

          父類Launcher#launch

            protected void launch(String[] args) throws Exception {
              if (!isExploded())
              //3.1 注冊URL協議并清除應用緩存
              JarFile.registerUrlProtocolHandler(); 
              //3.2 設置類加載路徑
              ClassLoader classLoader=createClassLoader(getClassPathArchivesIterator());
              String jarMode=System.getProperty("jarmode");
              String launchClass=(jarMode !=null && !jarMode.isEmpty()) ? "org.springframework.boot.loader.jarmode.JarModeLauncher" : getMainClass();
              //3.3 執行main方法
              launch(args, launchClass, classLoader);
            }

          3.1 registerUrlProtocolHandler:注冊URL協議并清除應用緩存

          先設置當前系統的一個變量 java.protocol.handler.pkgs,而這個變量的作用,是設置 URLStreamHandler 實現類的包路徑。

          之后要重置緩存,目的是清除之前啟動的殘留。

          private static final String MANIFEST_NAME="META-INF/MANIFEST.MF";
          
              private static final String PROTOCOL_HANDLER="java.protocol.handler.pkgs";
          
              private static final String HANDLERS_PACKAGE="org.springframework.boot.loader";
          
              public static void registerUrlProtocolHandler() {
                  String handlers=System.getProperty(PROTOCOL_HANDLER, "");
                  System.setProperty(PROTOCOL_HANDLER,
                          ("".equals(handlers) ? HANDLERS_PACKAGE : handlers + "|" + HANDLERS_PACKAGE));
                  resetCachedUrlHandlers();
              }
          
              // 重置任何緩存的處理程序,以防萬一已經使用了jar協議。
          // 我們通過嘗試設置null URLStreamHandlerFactory來重置處理程序,除了清除處理程序緩存之外,它應該沒有任何效果。
              private static void resetCachedUrlHandlers() {
                  try {
                      URL.setURLStreamHandlerFactory(null);
                  }
                  catch (Error ex) {
                      // Ignore
                  }
              }

          3.2createClassLoader:設置類加載路徑

           protected ClassLoader createClassLoader(Iterator<Archive> archives) throws Exception {
              List<URL> urls=new ArrayList<>(50);
              while (archives.hasNext())
                urls.add(((Archive)archives.next()).getUrl()); 
              return createClassLoader(urls.<URL>toArray(new URL[0]));
            }
            protected ClassLoader createClassLoader(URL[] urls) throws Exception {
              return new LaunchedURLClassLoader(isExploded(), getArchive(), urls, getClass().getClassLoader());
            }

          3.3 執行main方法

            protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
              Thread.currentThread().setContextClassLoader(classLoader);
              createMainMethodRunner(launchClass, args, classLoader).run();
            }
            
            protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
              return new MainMethodRunner(mainClass, args);
            }
          package org.springframework.boot.loader;
          
          import java.lang.reflect.Method;
          
          public class MainMethodRunner {
            private final String mainClassName;
            
            private final String[] args;
            
            public MainMethodRunner(String mainClass, String[] args) {
              this.mainClassName=mainClass;
              this.args=(args !=null) ? (String[])args.clone() : null;
            }
            
            public void run() throws Exception {
              Class<?> mainClass=Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
              //獲取主啟動類的main方法
              Method mainMethod=mainClass.getDeclaredMethod("main", new Class[] { String[].class });
              mainMethod.setAccessible(true);
              //執行main方法
              mainMethod.invoke((Object)null, new Object[] { this.args });
            }
          }

          所以 SpringBoot 應用在開發期間只需要寫 main 方法,引導啟動即可。

          下內容純干貨,需要理論知識的請自行查詢。示例均在centos7環境下測試通過。

          Nginx開機自啟動

          準備工作

          1、 已根據自己的需求正確安裝并配置Nginx服務(源碼方式安裝)

          2、 可以正常啟動關閉Nginx

          3、 本文假定nginx安裝目錄為默認的/usr/local/nginx

          配置

          1、 編輯配置文件

          2、 配置文件如下

          3、 保存配置文件并退出,可以使用service命令或systemctl命令測試服務效果

          Redis開機自啟動

          Redis本身沒有nginx那種參數式的操作方式,所以自啟動的服務配置方式也不同。

          準備工作

          1、 源碼編譯方式安裝好redis軟件并可以正常使用(如果是在線安裝的方式會自帶服務,不需要通過此方式配置)

          2、 Redis需要配置以后臺守護方式運行

          3、 本文假定常規方式安裝redis,安裝目錄/usr/local/redis,端口6379

          配置

          1、 編輯配置文件

          2、 文件內容如下

          3、 修改腳本執行權限

          4、 將腳本添加到啟動項中

          5、 查看啟動項

          6、 開啟自啟動

          7、 更多操作

          啟動:/etc/init.d/redisd start

          關閉:/etc/init.d/redisd stop

          重啟:/etc/init.d/redisd restart

          注:本配置中主要使用chkconfig命令操作,更多關于此命令的介紹可參考網絡資料:

          https://www.cnblogs.com/tiandi/p/7170905.html

          重啟計算機查看redis是否已啟動

          Jar包開機自啟動

          Jar包的啟動,一種是簡單的我們自己寫腳本啟動,另外一種功能更強大的的使用第三方工具Java Service Wrapper。今天主要介紹第一種,能夠滿足日常需要即可。

          準備工作

          1、 有一個可以運行的jar包(java -jar命令可執行),運行環境已配置好

          配置

          1、 編寫jar包啟動腳本,建議通過腳本啟動jar包

          示例:startJar.sh內容

          記得修改此腳本的權限 chmod +x start.sh

          2、 將執行腳本的命令添加到系統啟動文件

          保存文件并退出編輯器

          這里有一個很重要的操作,修改rc.local文件的執行權限:

          3、 重啟計算機查看java進程即可

          注:以上jar包的啟動方式比較簡單,只能達到開機啟動的目的,并不能通過命令控制其啟動、關閉及重啟等,如果要達到更多控制功能,參考redis開機自啟動的配置即可,原理差不多。


          主站蜘蛛池模板: 国产福利一区视频| 日韩精品一区二区三区中文字幕 | 在线观看免费视频一区| 在线日产精品一区| 一区二区三区中文字幕| 亚洲丰满熟女一区二区哦| 一区二区三区四区精品视频| 亚洲AV成人精品日韩一区18p| 日本一区免费电影| 国产一区二区好的精华液| 精品无人区一区二区三区在线| 中文字幕精品无码一区二区三区| 日本精品一区二区久久久| 亚洲欧美国产国产综合一区| 无码视频一区二区三区在线观看 | 国产精品视频免费一区二区| 亚洲国产一区国产亚洲 | 男人的天堂亚洲一区二区三区| 自拍日韩亚洲一区在线| 国产精品久久一区二区三区| 亚洲AV无码一区二区三区人 | 日韩精品中文字幕视频一区| 老熟妇仑乱一区二区视頻| 色久综合网精品一区二区| 色一乱一伦一图一区二区精品| 国产一区二区电影在线观看| 国产成人AV一区二区三区无码| 高清一区二区三区日本久| 久久精品动漫一区二区三区| 久久久久人妻精品一区蜜桃| 精品视频一区二区三区在线观看| 久久久精品日本一区二区三区| 精品人妻少妇一区二区| 99精品高清视频一区二区| 红桃AV一区二区三区在线无码AV| 三上悠亚一区二区观看| 久久精品一区二区三区日韩| 麻豆一区二区免费播放网站| 国产精品福利一区| 中文字幕一区二区三区在线不卡 | 亚洲一区二区免费视频|