如何做自己的服务监控?spring boot 1.x服务监控揭秘

不问归期 提交于 2020-04-01 02:09:40

1.准备

  下载可运行程序:http://www.mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/

  2.添加服务监控依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <scope>provided</scope>
        </dependency>

3.启动spring boot项目

console 截图如下:

4.servlet和filter

  4.1 使用ServletRegistrationBean注册dispatcherServlet

/**
 * A {@link ServletContextInitializer} to register {@link Servlet}s in a Servlet 3.0+
 * container. Similar to the {@link ServletContext#addServlet(String, Servlet)
 * registration} features provided by {@link ServletContext} but with a Spring Bean
 * friendly design.
 * <p>
 * The {@link #setServlet(Servlet) servlet} must be specified before calling
 * {@link #onStartup}. URL mapping can be configured used {@link #setUrlMappings} or
 * omitted when mapping to '/*' (unless
 * {@link #ServletRegistrationBean(Servlet, boolean, String...) alwaysMapUrl} is set to
 * {@code false}). The servlet name will be deduced if not specified.
 *
 * @param <T> the type of the {@link Servlet} to register
 * @author Phillip Webb
 * @since 1.4.0
 * @see ServletContextInitializer
 * @see ServletContext#addServlet(String, Servlet)
 */

总结:类似于ServletContext#addServlet(String, Servlet)

查看所有注册的bean

http://127.0.0.1:8080/beans

并把返回的json 格式化 视图查看,在线工具(http://www.bejson.com/jsonviewernew/)

注册的流程:

spring-boot-autoconfigure模块spring.facotories的属性org.springframework.boot.autoconfigure.EnableAutoConfiguration=DispatcherServletAutoConfiguration

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServletRegistrationBean dispatcherServletRegistration(
                DispatcherServlet dispatcherServlet) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
                    dispatcherServlet, this.webMvcProperties.getServlet().getPath());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(
                    this.webMvcProperties.getServlet().getLoadOnStartup());
            if (this.multipartConfig != null) {
                registration.setMultipartConfig(this.multipartConfig);
            }
            return registration;
        }

4.2 使用FilterRegistrationBean注册各种filter

/**
 * A {@link ServletContextInitializer} to register {@link Filter}s in a Servlet 3.0+
 * container. Similar to the {@link ServletContext#addFilter(String, Filter) registration}
 * features provided by {@link ServletContext} but with a Spring Bean friendly design.
 * <p>
 * The {@link #setFilter(Filter) Filter} must be specified before calling
 * {@link #onStartup(ServletContext)}. Registrations can be associated with
 * {@link #setUrlPatterns URL patterns} and/or servlets (either by {@link #setServletNames
 * name} or via a {@link #setServletRegistrationBeans ServletRegistrationBean}s. When no
 * URL pattern or servlets are specified the filter will be associated to '/*'. The filter
 * name will be deduced if not specified.
 *
 * @param <T> the type of {@link Filter} to register
 * @author Phillip Webb
 * @since 1.4.0
 * @see ServletContextInitializer
 * @see ServletContext#addFilter(String, Filter)
 * @see DelegatingFilterProxyRegistrationBean
 */

总结:类似于ServletContext#addFilter(String, Filter)

 spring-boot-actuator-autoconfigure模块spring.facotories的属性org.springframework.boot.autoconfigure.EnableAutoConfiguration=WebMvcMetricsAutoConfiguration,......

5.RequestMappingHandlerAdapter查找controller注解

使用

2019-01-16 09:47:07.715  INFO 8468 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@66ac5762: startup date [Wed Jan 16 09:47:06 CST 2019]; root of context hierarchy

定义:

/**
 * An {@link AbstractHandlerMethodAdapter} that supports {@link HandlerMethod}s
 * with their method argument and return type signature, as defined via
 * {@code @RequestMapping}.
 *
 * <p>Support for custom argument and return value types can be added via
 * {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.
 * Or alternatively, to re-configure all argument and return value types,
 * use {@link #setArgumentResolvers} and {@link #setReturnValueHandlers}.
 *
 * @author Rossen Stoyanchev
 * @author Juergen Hoeller
 * @since 3.1
 * @see HandlerMethodArgumentResolver
 * @see HandlerMethodReturnValueHandler
 */

内部实现源码

private void initControllerAdviceCache() {
        if (getApplicationContext() == null) {
            return;
        }
        if (logger.isInfoEnabled()) {
            logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
        }

        List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
        AnnotationAwareOrderComparator.sort(beans);

        List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();

        for (ControllerAdviceBean bean : beans) {
            Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
            if (!attrMethods.isEmpty()) {
                this.modelAttributeAdviceCache.put(bean, attrMethods);
                if (logger.isInfoEnabled()) {
                    logger.info("Detected @ModelAttribute methods in " + bean);
                }
            }
            Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
            if (!binderMethods.isEmpty()) {
                this.initBinderAdviceCache.put(bean, binderMethods);
                if (logger.isInfoEnabled()) {
                    logger.info("Detected @InitBinder methods in " + bean);
                }
            }
            if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
                requestResponseBodyAdviceBeans.add(bean);
                if (logger.isInfoEnabled()) {
                    logger.info("Detected RequestBodyAdvice bean in " + bean);
                }
            }
            if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
                requestResponseBodyAdviceBeans.add(bean);
                if (logger.isInfoEnabled()) {
                    logger.info("Detected ResponseBodyAdvice bean in " + bean);
                }
            }
        }

        if (!requestResponseBodyAdviceBeans.isEmpty()) {
            this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
        }
    }

6.使用RequestMappingHandlerMapping查找controller映射路径

2019-01-16 09:47:07.758  INFO 8468 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.mkyong.WelcomeController.welcome(java.util.Map<java.lang.String, java.lang.Object>)

定义:

/**
 * Creates {@link RequestMappingInfo} instances from type and method-level
 * {@link RequestMapping @RequestMapping} annotations in
 * {@link Controller @Controller} classes.
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @author Sam Brannen
 * @since 3.1
 */

作用:使用注解@RequestMapping在controller类内创建一个类型或者方法级别的RequestMappingInfo实例

7.EndpointHandlerMapping映射的监控项

2019-01-15 14:19:30.985  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.986  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/health || /health.json],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(java.security.Principal)
2019-01-15 14:19:30.987  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
2019-01-15 14:19:30.987  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.988  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.988  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/heapdump || /heapdump.json],methods=[GET],produces=[application/octet-stream]}" onto public void org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint.invoke(boolean,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException,javax.servlet.ServletException
2019-01-15 14:19:30.989  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.990  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.992  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
2019-01-15 14:19:30.992  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/env || /env.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.995  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/info || /info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.996  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2019-01-15 14:19:30.996  INFO 9440 --- [           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()

其中,

/trace 通过EndpointMvcAdapter.invoke()触发TraceEndpoint的invoke方法

/health通过HealthMvcEndpoint.invoke()触发

/metrics/{name:.*}通过MetricsMvcEndpoint.value()触发

/metrics通过EndpointMvcAdapter.invoke()触发MetricsEndpoint的invoke方法

/dump通过EndpointMvcAdapter.invoke()触发DumpEndpoint的invoke方法

/heapdump通过HeapdumpMvcEndpoint.invoke()触发

/beans通过EndpointMvcAdapter.invoke()触发BeansEndpoint的invoke方法

/autoconfig通过EndpointMvcAdapter.invoke()触发AutoconfigEndpoint的invoke方法

/env/{name:.*}通过EnvironmentMvcEndpoint.value()方法触发

/info通过EndpointMvcAdapter.invoke()触发InfoEndpoint的invoke方法

/mappings通过通过EndpointMvcAdapter.invoke()触发RequestMappingEndpoint的invoke方法

小结:

restful请求实现分两种,一种通过EndpointMvcAdapter.invoke()触发

而EndpointMvcAdapter.invoke()通过注解@GetMapping实现了restful服务

/**
 * Adapter class to expose {@link Endpoint}s as {@link MvcEndpoint}s.
 *
 * @author Dave Syer
 * @author Andy Wilkinson
 */
public class EndpointMvcAdapter extends AbstractEndpointMvcAdapter<Endpoint<?>> {

    /**
     * Create a new {@link EndpointMvcAdapter}.
     * @param delegate the underlying {@link Endpoint} to adapt.
     */
    public EndpointMvcAdapter(Endpoint<?> delegate) {
        super(delegate);
    }

    @Override
    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Object invoke() {
        return super.invoke();
    }

}

另一种,通过继承MvcEndpoint的invoke方法来触发 

例如HealthMvcEndpoint

    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Object invoke(Principal principal) {
        if (!getDelegate().isEnabled()) {
            // Shouldn't happen because the request mapping should not be registered
            return getDisabledResponse();
        }
        Health health = getHealth(principal);
        HttpStatus status = getStatus(health);
        if (status != null) {
            return new ResponseEntity<Health>(health, status);
        }
        return health;
    }

7.1 EndpointHandlerMapping的定义

/**
 * {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getId()}.
 * The semantics of {@code @RequestMapping} should be identical to a normal
 * {@code @Controller}, but the endpoints should not be annotated as {@code @Controller}
 * (otherwise they will be mapped by the normal MVC mechanisms).
 * <p>
 * One of the aims of the mapping is to support endpoints that work as HTTP endpoints but
 * can still provide useful service interfaces when there is no HTTP server (and no Spring
 * MVC on the classpath). Note that any endpoints having method signatures will break in a
 * non-servlet environment.
 *
 * @author Phillip Webb
 * @author Christian Dupuis
 * @author Dave Syer
 */

7.2 层次结构

7.3 查看对应的bean的生成

      {
        "bean": "endpointHandlerMapping",
        "aliases": [
          
        ],
        "scope": "singleton",
        "type": "org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping",
        "resource": "class path resource [org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.class]",
        "dependencies": [
          
        ]
      }

 7.4 获取流程

    @Bean
    @ConditionalOnMissingBean
    public EndpointHandlerMapping endpointHandlerMapping() {
        Set<? extends MvcEndpoint> endpoints = mvcEndpoints().getEndpoints(); //1
        CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties);
        EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
                corsConfiguration);                                          //2
        boolean disabled = this.managementServerProperties.getPort() != null  
                && this.managementServerProperties.getPort() == -1;           
        mapping.setDisabled(disabled);
        if (!disabled) {
            mapping.setPrefix(this.managementServerProperties.getContextPath()); //3
        }
        if (this.mappingCustomizers != null) {
            for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
                customizer.customize(mapping);   //4
            }
        }
        return mapping;
    }

7.4.1 MvcEndpoints获取endpoint定义

    @Override
    public void afterPropertiesSet() throws Exception {
        Collection<MvcEndpoint> existing = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class)
                .values();
        this.endpoints.addAll(existing);
        this.customTypes = findEndpointClasses(existing);
        @SuppressWarnings("rawtypes")
        Collection<Endpoint> delegates = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class)
                .values();
        for (Endpoint<?> endpoint : delegates) {
            if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {
                EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);
                String path = determinePath(endpoint,
                        this.applicationContext.getEnvironment());
                if (path != null) {
                    adapter.setPath(path);
                }
                this.endpoints.add(adapter);
            }
        }
    }

7.4.2 定义映射关系

其内部实现源码:

    private String getPath(Object handler) {
        if (handler instanceof String) {
            handler = getApplicationContext().getBean((String) handler);
        }
        if (handler instanceof MvcEndpoint) {
            return ((MvcEndpoint) handler).getPath();
        }
        return "";
    }

7.4.3 增加contextpath

7.4.4 自定义EndpointHandlerMappingCustomizer

8.其它通过

    @Bean
    @ConditionalOnBean(EnvironmentEndpoint.class)
    @ConditionalOnEnabledEndpoint("env")
    public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
        return new EnvironmentMvcEndpoint(delegate);
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnEnabledEndpoint("heapdump")
    public HeapdumpMvcEndpoint heapdumpMvcEndpoint() {
        return new HeapdumpMvcEndpoint();
    }

    @Bean
    @ConditionalOnBean(HealthEndpoint.class)
    @ConditionalOnEnabledEndpoint("health")
    public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate,
            ManagementServerProperties managementServerProperties) {
        HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
                isHealthSecure(), managementServerProperties.getSecurity().getRoles());
        if (this.healthMvcEndpointProperties.getMapping() != null) {
            healthMvcEndpoint
                    .addStatusMapping(this.healthMvcEndpointProperties.getMapping());
        }
        return healthMvcEndpoint;
    }

    @Bean
    @ConditionalOnBean(MetricsEndpoint.class)
    @ConditionalOnEnabledEndpoint("metrics")
    public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
        return new MetricsMvcEndpoint(delegate);
    }

    @Bean
    @ConditionalOnEnabledEndpoint("logfile")
    @Conditional(LogFileCondition.class)
    public LogFileMvcEndpoint logfileMvcEndpoint() {
        return new LogFileMvcEndpoint();
    }

    @Bean
    @ConditionalOnBean(ShutdownEndpoint.class)
    @ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
    public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
        return new ShutdownMvcEndpoint(delegate);
    }

 以health为例

    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Object invoke(Principal principal) {
        if (!getDelegate().isEnabled()) {
            // Shouldn't happen because the request mapping should not be registered
            return getDisabledResponse();
        }
        Health health = getHealth(principal);
        HttpStatus status = getStatus(health);
        if (status != null) {
            return new ResponseEntity<Health>(health, status);
        }
        return health;
    }

调用HealthEndpoint

    /**
     * Invoke all {@link HealthIndicator} delegates and collect their health information.
     */
    @Override
    public Health invoke() {
        return this.healthIndicator.health();
    }

 总结:

spring boot提供http请求的方式可以分两种:

1.通过查找@Controller注解中的@RequestMapping来形成HandlerMapping

2.直接通过@RequestMapping来形成HandlerMapping如actuator模块,这里面又分成两种:

   2.1  一种集中式的通过继承@RequestMapping来实现如通过EndpointMvcAdapter.invoke()触发

   2.2  另一种通过直接的@RequestMapping注解实现

3.spring boot1.x监控的实现

  /trace 通过EndpointMvcAdapter.invoke()触发TraceEndpoint的invoke方法

  /health通过HealthMvcEndpoint.invoke()触发

  /metrics/{name:.*}通过MetricsMvcEndpoint.value()触发

  /metrics通过EndpointMvcAdapter.invoke()触发MetricsEndpoint的invoke方法

  /dump通过EndpointMvcAdapter.invoke()触发DumpEndpoint的invoke方法

  /heapdump通过HeapdumpMvcEndpoint.invoke()触发

  /beans通过EndpointMvcAdapter.invoke()触发BeansEndpoint的invoke方法

  /autoconfig通过EndpointMvcAdapter.invoke()触发AutoconfigEndpoint的invoke方法

  /env/{name:.*}通过EnvironmentMvcEndpoint.value()方法触发

  /info通过EndpointMvcAdapter.invoke()触发InfoEndpoint的invoke方法

  /mappings通过通过EndpointMvcAdapter.invoke()触发RequestMappingEndpoint的invoke方法。

 4. 这些实现都定义在spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.InfoContributorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricsDropwizardAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricsChannelAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration

org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!