运行sole下面的soul-examples-springcloud服务

308 阅读3分钟

本篇文章介绍soul对spring cloud的插件支持,以及spring cloud服务接入soul网关。对应官方文档可以参考:dromara.org/zh-cn/docs/…

dubbo服务接入springcloud网关步骤

1、启动sole-admin。登陆localhost:9095, 进入System Manage -> Plugin, 把插件springcloud开启,其他插件有开启的全部关闭。

2、启动eureka服务

运行soul-examples-eureka下的EurekaServerApplication, 运行成功后,访问localhost:8761, 能正常访问代表eureka服务启动成功。

2、启动soul-bootstrap

  1. pom.xml引入对应jar包,添加对dubbo插件的支持
<!--soul springCloud plugin start-->
  <dependency>
       <groupId>org.dromara</groupId>
       <artifactId>soul-spring-boot-starter-plugin-springcloud</artifactId>
        <version>${last.version}</version>
  </dependency>

  <dependency>
       <groupId>org.dromara</groupId>
       <artifactId>soul-spring-boot-starter-plugin-httpclient</artifactId>
       <version>${last.version}</version>
   </dependency>
   <!--soul springCloud plugin end-->

   <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-commons</artifactId>
        <version>2.2.0.RELEASE</version>
   </dependency>
   <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        <version>2.2.0.RELEASE</version>
   </dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>
  1. 启动SoulBootstrapApplication,执行成功后发现服务注册到eureka上

3、启动sole-examples-springcloud

  1. Soul-examples-springcloud 的application.yml 添加如下配置
soul:
  springcloud:
    admin-url: http://localhost:9095
    context-path: /springcloud
    full: true  # full为true,执行ContextRegisterListener进行元数据、选择器及其配置规则的配置,full为false,执行SpringCloudClientBeanPostProcessor进行元数据、选择器及其配置规则的配置
  1. 运行SoulTestSpringCloudApplication. 发现sole-admin后台 System-Manage -> Metadata 里面注册上的springcloud的相关数据, PluginList -> springCloud 也有了对应的加了注解@SoulSpringCloudClient的的SelectorList 和 RuleList.

  2. 查看eureka注册中心,发现服务注册上去

4、我们通过sole-bootstrap访问springcloud请求

​ 访问localhost:9195/springcloud/order/path/111/dabing, 返回成功

{

​ "id": "111",

​ "name": "hello world spring cloud restful: dabing"

}

代表sole-bootstrap服务和springcloud服务都注册到了eureka 上了,可以正常用网关进行访问。


源码解读

通过前面几篇文章的学习,我们发现服务启动时会往soul-admin里面注册元数据和对应插件的选择器、配置规则,springcloud同样如此,并且有两种实现方式,实现了ApplicationListener的onApplicationEvent方法的ContextRegisterListener 和 实现了BeanPostProcessor 的postProcessAfterInitialization方法的SpringCloudClientBeanPostProcessor。两种实现方式由soul.springcloud.full控制选择哪种方式注册元数据。full为true,执行ContextRegisterListener的onApplicationEvent 方法进行元数据、选择器及其配置规则的注册,full为false,执行SpringCloudClientBeanPostProcessor的postProcessAfterInitialization 方法进行元数据、选择器及其配置规则的注册。

@Slf4j
public class ContextRegisterListener implements ApplicationListener<ContextRefreshedEvent> {

    private final AtomicBoolean registered = new AtomicBoolean(false);

    private final String url;

    private final SoulSpringCloudConfig config;

    private final Environment env;

    /**
     * Instantiates a new Context register listener.
     *
     * @param config the soul spring cloud config
     * @param env    the env
     */
    public ContextRegisterListener(final SoulSpringCloudConfig config, final Environment env) {
        ValidateUtils.validate(config, env);
        this.config = config;
        this.env = env;
        this.url = config.getAdminUrl() + "/soul-client/springcloud-register";
    }

    @Override
    public void onApplicationEvent(final ContextRefreshedEvent contextRefreshedEvent) {
        if (!registered.compareAndSet(false, true)) {
            return;
        }
        if (config.isFull()) {
            RegisterUtils.doRegister(buildJsonParams(), url, RpcTypeEnum.SPRING_CLOUD);
        }
    }

    private String buildJsonParams() {
        String contextPath = config.getContextPath();
        String appName = env.getProperty("spring.application.name");
        String path = contextPath + "/**";
        SpringCloudRegisterDTO registerDTO = SpringCloudRegisterDTO.builder()
                .context(contextPath)
                .appName(appName)
                .path(path)
                .rpcType(RpcTypeEnum.SPRING_CLOUD.getName())
                .enabled(true)
                .ruleName(path)
                .build();
        return OkHttpTools.getInstance().getGson().toJson(registerDTO);
    }
}
@Slf4j
public class SpringCloudClientBeanPostProcessor implements BeanPostProcessor {

    private final ThreadPoolExecutor executorService;

    private final String url;

    private final SoulSpringCloudConfig config;

    private final Environment env;

    /**
     * Instantiates a new Soul client bean post processor.
     *
     * @param config the soul spring cloud config
     * @param env    the env
     */
    public SpringCloudClientBeanPostProcessor(final SoulSpringCloudConfig config, final Environment env) {
        ValidateUtils.validate(config, env);
        this.config = config;
        this.env = env;
        this.url = config.getAdminUrl() + "/soul-client/springcloud-register";
        executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
    }

    @Override
    public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
        if (config.isFull()) {
            return bean;
        }
        Controller controller = AnnotationUtils.findAnnotation(bean.getClass(), Controller.class);
        RestController restController = AnnotationUtils.findAnnotation(bean.getClass(), RestController.class);
        RequestMapping requestMapping = AnnotationUtils.findAnnotation(bean.getClass(), RequestMapping.class);
        if (controller != null || restController != null || requestMapping != null) {
            String prePath = "";
            SoulSpringCloudClient clazzAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), SoulSpringCloudClient.class);
            if (Objects.nonNull(clazzAnnotation)) {
                if (clazzAnnotation.path().indexOf("*") > 1) {
                    String finalPrePath = prePath;
                    executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(clazzAnnotation, finalPrePath), url,
                            RpcTypeEnum.SPRING_CLOUD));
                    return bean;
                }
                prePath = clazzAnnotation.path();
            }
            final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(bean.getClass());
            for (Method method : methods) {
                SoulSpringCloudClient soulSpringCloudClient = AnnotationUtils.findAnnotation(method, SoulSpringCloudClient.class);
                if (Objects.nonNull(soulSpringCloudClient)) {
                    String finalPrePath = prePath;
                    executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(soulSpringCloudClient, finalPrePath), url,
                            RpcTypeEnum.SPRING_CLOUD));
                }
            }
        }
        return bean;
    }

    private String buildJsonParams(final SoulSpringCloudClient soulSpringCloudClient, final String prePath) {
        String contextPath = config.getContextPath();
        String appName = env.getProperty("spring.application.name");
        String path = contextPath + prePath + soulSpringCloudClient.path();
        String desc = soulSpringCloudClient.desc();
        String configRuleName = soulSpringCloudClient.ruleName();
        String ruleName = ("".equals(configRuleName)) ? path : configRuleName;
        SpringCloudRegisterDTO registerDTO = SpringCloudRegisterDTO.builder()
                .context(contextPath)
                .appName(appName)
                .path(path)
                .pathDesc(desc)
                .rpcType(soulSpringCloudClient.rpcType())
                .enabled(soulSpringCloudClient.enabled())
                .ruleName(ruleName)
                .build();
        return OkHttpTools.getInstance().getGson().toJson(registerDTO);
    }
}

小插曲:

第一次启动bootstrap时忘记往pom.xml里面添加eureka的jar包,导致启动后服务调用失败。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>