SpringCloud 集成 Nacos 使用小結

Nacos 註冊中心
Nacos 是 Dubbo 生態系統中重要的註冊中心實現,其中 dubbo-registry-nacos 則是 Dubbo 融合 Nacos 註冊中心的實現。

Nacos服務安裝與啓動

安裝地址: https://github.com/alibaba/nacos/releases

  • 本文在Windows環境測試,下載zip版,解壓。
  • 配置conf目錄下的application.properties文件,配置nacos數據源,端口等。
#*************** Spring Boot Related Configurations ***************#
### Default web context path:
server.servlet.contextPath=/nacos
### Default web server port:
server.port=8848

#*************** Network Related Configurations ***************#
### If prefer hostname over ip for Nacos server addresses in cluster.conf:
# nacos.inetutils.prefer-hostname-over-ip=false

### Specify local server's IP:
# nacos.inetutils.ip-address=



#*************** Config Module Related Configurations ***************#
### If user MySQL as datasource:
spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?serverTimezone=UTC&characterEncoding=utf8
db.user=root
db.password=123456


#*************** Naming Module Related Configurations ***************#
### Data dispatch task execution period in milliseconds:
# nacos.naming.distro.taskDispatchPeriod=200

### Data count of batch sync task:
# nacos.naming.distro.batchSyncKeyCount=1000

### Retry delay in milliseconds if sync task failed:
# nacos.naming.distro.syncRetryDelay=5000
  • 初始化數據庫,腳本位於conf目錄下nacos-mysql.sql
  • 啓動nacos服務,在cmd中執行bin目錄下startup.cmd:
D:\>cd nacos/bin

D:\nacos\bin>startup.cmd

         ,--.
       ,--.'|
   ,--,:  : |                                           Nacos 1.2.1
,`--.'`|  ' :                       ,---.               Running in stand alone mode, All function modules
|   :  :  | |                      '   ,'\   .--.--.    Port: 8848
:   |   \ | :  ,--.--.     ,---.  /   /   | /  /    '   Pid: 20700
|   : '  '; | /       \   /     \.   ; ,. :|  :  /`./   Console: http://10.118.37.75:8848/nacos/index.html
'   ' ;.    ;.--.  .-. | /    / ''   | |: :|  :  ;_
|   | | \   | \__\/: . ..    ' / '   | .; : \  \    `.      https://nacos.io
'   : |  ; .' ," .--.; |'   ; :__|   :    |  `----.   \
|   | '`--'  /  /  ,.  |'   | '.'|\   \  /  /  /`--'  /
'   : |     ;  :   .'   \   :    : `----'  '--'.     /
;   |.'     |  ,     .-./\   \  /            `--'---'
'---'        `--`---'     `----'

2020-05-07 16:09:06,639 INFO Bean 'org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration' of type [org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration$$EnhancerBySpringCGLIB$$d2a494f3] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

2020-05-07 16:09:06,713 INFO Bean 'objectPostProcessor' of type [org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

2020-05-07 16:09:06,715 INFO Bean 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler@2de56eb2' of type [org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

2020-05-07 16:09:06,717 INFO Bean 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration' of type [org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration$$EnhancerBySpringCGLIB$$f77937a5] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

2020-05-07 16:09:06,721 INFO Bean 'methodSecurityMetadataSource' of type [org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

2020-05-07 16:09:07,176 INFO Tomcat initialized with port(s): 8848 (http)

2020-05-07 16:09:07,280 INFO Root WebApplicationContext: initialization completed in 2308 ms

2020-05-07 16:09:16,998 INFO Initializing ExecutorService 'applicationTaskExecutor'

2020-05-07 16:09:17,138 INFO Adding welcome page: class path resource [static/index.html]

2020-05-07 16:09:17,390 INFO Creating filter chain: Ant [pattern='/**'], []

2020-05-07 16:09:17,418 INFO Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@163d04ff, org.springframework.security.web.context.SecurityContextPersistenceFilter@1698fc68, org.springframework.security.web.header.HeaderWriterFilter@56a4479a, org.springframework.security.web.csrf.CsrfFilter@565b064f, org.springframework.security.web.authentication.logout.LogoutFilter@53d1b9b3, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@207b8649, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@57eda880, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@7c209437, org.springframework.security.web.session.SessionManagementFilter@20a8a64e, org.springframework.security.web.access.ExceptionTranslationFilter@73163d48]

2020-05-07 16:09:17,502 INFO Exposing 2 endpoint(s) beneath base path '/actuator'

2020-05-07 16:09:17,528 INFO Initializing ExecutorService 'taskScheduler'

2020-05-07 16:09:17,639 INFO Tomcat started on port(s): 8848 (http) with context path '/nacos'

2020-05-07 16:09:17,645 INFO Nacos logs files: D:\nacos\logs\

2020-05-07 16:09:17,646 INFO Nacos conf files: D:\nacos\conf\

2020-05-07 16:09:17,646 INFO Nacos data files: D:\nacos\data\

2020-05-07 16:09:17,646 INFO Nacos started successfully in stand alone mode.

2020-05-07 16:09:17,851 INFO Initializing Servlet 'dispatcherServlet'

2020-05-07 16:09:17,858 INFO Completed initialization in 6 ms

Nacos平臺

  • 訪問路徑:http://localhost:8848/nacos
    在這裏插入圖片描述
  • 可以新建多個環境,項目中通過命名空間映射相應的環境配置
spring:
  profiles:
    active: dev
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://106.13.181.6:3306/demo
    username: root
    password: 123456
  • 新增配置後發佈
    在這裏插入圖片描述

SpringCloud項目配置

  • 依賴配置
 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
     <version>2.1.1.RELEASE</version>
 </dependency>
 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
     <version>2.1.1.RELEASE</version>
 </dependency>
  • 通過bootstrap.yml配置啓動,bootstrap-dev.yml配置文件如下:
spring:
  application:
    name: demo
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      config:
        namespace: 9c6b8156-d045-463d-8fe6-4658ce78d0cc
        file-extension: yml

  • 啓動類配置
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

  • 數據源配置
package com.example.demo.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;

import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;

@Configuration
public class SqlSessionConfig {

    private Logger logger = LoggerFactory.getLogger(SqlSessionConfig.class);

    @Bean("datasource")
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean("mySqlSessionFactoryBean")
    public MybatisSqlSessionFactoryBean createSqlSessionFactory(@Qualifier("datasource") DataSource dataSource,
                                                                @Qualifier("paginationInterceptor") PaginationInterceptor paginationInterceptor) {
        // MybatisSqlSessionFactory
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = null;
        try {
            // 實例SessionFactory
            sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
            // 配置數據源
            sqlSessionFactoryBean.setDataSource(dataSource);
            // 設置 MyBatis-Plus 分頁插件
            Interceptor [] plugins = {paginationInterceptor};
            sqlSessionFactoryBean.setPlugins(plugins);
            // 加載MyBatis配置文件
            PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
            sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath*:mapper/*.xml"));
        } catch (Exception e) {
            logger.error("創建SqlSession連接工廠錯誤:{}", e.getMessage());
        }
        return sqlSessionFactoryBean;
    }

    @Bean
    public MapperScannerConfigurer myGetMapperScannerConfigurer() {
        MapperScannerConfigurer myMapperScannerConfigurer = new MapperScannerConfigurer();
        myMapperScannerConfigurer.setBasePackage("com.example.demo.mapper");
        myMapperScannerConfigurer.setSqlSessionFactoryBeanName("mySqlSessionFactoryBean");
        return myMapperScannerConfigurer;
    }

}
  • controller
package com.example.demo.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.bean.UserDTO;
import com.example.demo.constants.SexEnum;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.List;

@RestController
@RequestMapping(value = "/demo")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public String create(@RequestBody UserDTO userDTO){
        User user = new User();
        user.setName(userDTO.getName());
        user.setPhone(userDTO.getPhone());
        user.setCreateTime(LocalDateTime.now());
        user.setEnable(true);
        user.setVersion(1L);
        user.setSex(SexEnum.getSexEnum(userDTO.getSex()));
        userService.insert(user);
        return "ok";
    }
}

  • 啓動項目
2020-05-08 15:37:26.384  INFO 12544 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$9ea5255e] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-05-08 15:37:26.601  INFO 12544 --- [           main] c.a.c.n.c.NacosPropertySourceBuilder     : Loading nacos data, dataId: 'demo', group: 'DEFAULT_GROUP', data: spring:
  profiles:
    active: dev
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://106.13.181.6:3306/demo
    username: root
    password: 123456
2020-05-08 15:37:26.608  WARN 12544 --- [           main] c.a.c.n.c.NacosPropertySourceBuilder     : Ignore the empty nacos configuration and get it based on dataId[demo.yml] & group[DEFAULT_GROUP]
2020-05-08 15:37:26.612  WARN 12544 --- [           main] c.a.c.n.c.NacosPropertySourceBuilder     : Ignore the empty nacos configuration and get it based on dataId[demo-dev.yml] & group[DEFAULT_GROUP]
2020-05-08 15:37:26.612  INFO 12544 --- [           main] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='demo-dev.yml'}, NacosPropertySource {name='demo.yml'}, NacosPropertySource {name='demo'}]}
2020-05-08 15:37:26.616  INFO 12544 --- [           main] com.example.demo.DemoApplication         : The following profiles are active: dev
2020-05-08 15:37:26.984  WARN 12544 --- [           main] o.m.s.mapper.ClassPathMapperScanner      : Skipping MapperFactoryBean with name 'userMapper' and 'com.example.demo.mapper.UserMapper' mapperInterface. Bean already defined with the same name!
2020-05-08 15:37:26.984  WARN 12544 --- [           main] o.m.s.mapper.ClassPathMapperScanner      : No MyBatis mapper was found in '[com.example.demo.mapper]' package. Please check your configuration.
2020-05-08 15:37:26.985  INFO 12544 --- [           main] o.s.c.a.ConfigurationClassPostProcessor  : Cannot enhance @Configuration bean definition 'sqlSessionConfig' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
2020-05-08 15:37:27.004  INFO 12544 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=118dc0e1-e30f-3c3e-b857-cf20ba8b4123
2020-05-08 15:37:27.131  INFO 12544 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$9ea5255e] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-05-08 15:37:27.302  INFO 12544 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-05-08 15:37:27.309  INFO 12544 --- [           main] o.a.coyote.http11.Http11NioProtocol      : Initializing ProtocolHandler ["http-nio-8080"]
2020-05-08 15:37:27.309  INFO 12544 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-05-08 15:37:27.309  INFO 12544 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-05-08 15:37:27.434  INFO 12544 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-05-08 15:37:27.435  INFO 12544 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 810 ms
 _ _   |_  _ _|_. ___ _ |    _ 
| | |\/|_)(_| | |_\  |_)||_|_\ 
     /               |         
                        3.3.1 
2020-05-08 15:37:27.822  WARN 12544 --- [           main] c.n.c.sources.URLConfigurationSource     : No URLs will be polled as dynamic configuration sources.
2020-05-08 15:37:27.823  INFO 12544 --- [           main] c.n.c.sources.URLConfigurationSource     : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2020-05-08 15:37:27.825  WARN 12544 --- [           main] c.n.c.sources.URLConfigurationSource     : No URLs will be polled as dynamic configuration sources.
2020-05-08 15:37:27.826  INFO 12544 --- [           main] c.n.c.sources.URLConfigurationSource     : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2020-05-08 15:37:27.951  INFO 12544 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-05-08 15:37:28.151  INFO 12544 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService
2020-05-08 15:37:28.391  INFO 12544 --- [           main] o.a.coyote.http11.Http11NioProtocol      : Starting ProtocolHandler ["http-nio-8080"]
2020-05-08 15:37:28.409  INFO 12544 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-05-08 15:37:28.549  INFO 12544 --- [           main] c.a.c.n.registry.NacosServiceRegistry    : nacos registry, DEFAULT_GROUP demo 10.118.37.75:8080 register finished
2020-05-08 15:37:28.618  INFO 12544 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 2.941 seconds (JVM running for 3.791)
2020-05-08 15:37:33.589  INFO 12544 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-05-08 15:37:33.590  INFO 12544 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-05-08 15:37:33.597  INFO 12544 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 5 ms
2020-05-08 15:37:33.663  INFO 12544 --- [nio-8080-exec-2] com.example.demo.config.LogAspect        : 
  • 測試
2020-05-08 16:05:13.994  INFO 12544 --- [nio-8080-exec-4] com.example.demo.config.LogAspect        : 
 請求信息:
【請求地址】:/demo/create
【請求頭】:content-type = application/json, user-agent = PostmanRuntime/7.24.0, accept = */*, postman-token = fc010815-8e7b-4e11-9fed-3d061f388faf, host = localhost:8080, accept-encoding = gzip, deflate, br, connection = keep-alive, content-length = 49
【請求方法】:String com.example.demo.controller.UserController.create(UserDTO)
【請求參數】:[UserDTO(id=null, name=zhangsan, phone=17751033130, sex=1)]
2020-05-08 16:05:14.004  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@2bacd020 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.008  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@5283f4ad (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.009  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@1fe3130d (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.010  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@11977567 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.011  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@641256c2 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.013  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@29f9f071 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.015  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@2e05f538 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.016  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@41391d2 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.017  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@72c163d4 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.019  WARN 12544 --- [nio-8080-exec-4] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@145d7f85 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2020-05-08 16:05:14.545 DEBUG 12544 --- [nio-8080-exec-4] c.example.demo.mapper.UserMapper.insert  : ==>  Preparing: INSERT INTO user ( name, sex, phone, create_time, enable, version ) VALUES ( ?, ?, ?, ?, ?, ? ) 
2020-05-08 16:05:14.548 DEBUG 12544 --- [nio-8080-exec-4] c.example.demo.mapper.UserMapper.insert  : ==> Parameters: zhangsan(String), MAN(String), 17751033130(String), 2020-05-08T16:05:13.995(LocalDateTime), true(Boolean), 1(Long)
2020-05-08 16:05:14.686 DEBUG 12544 --- [nio-8080-exec-4] c.example.demo.mapper.UserMapper.insert  : <==    Updates: 1
2020-05-08 16:05:14.697  INFO 12544 --- [nio-8080-exec-4] com.example.demo.config.LogAspect        : 
 執行結果:
【響應結果】:"ok"
【執行耗時】:701毫秒

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章