拾壹博客Spring Boot转换成Spring Cloud Alibaba架构

改的地方实在太多了,所以过程记录不下去了,但是业务模块跟Boot是一毛一样的没有变化,直接发项目代码,包括了nacos中的配置文件在对应的模块,运行大概是没有问题。
成果:

链接:https://pan.baidu.com/s/10ccshvY6sjYtrbRS8AhZpA
提取码:4pdr
PS:其实过年前就已经改造结束了,但是因为带娃没空整理(借口)。前阵子看到了篇文章《Spring Boot 单体应用一键升级成 Spring Cloud Alibaba》,感觉再不发就没机会了!

引入模块:

  • 架构:Spring Cloud Alibaba-2020.0.5
  • 服务发现、治理中心:Nacos-2.0.1
  • 网关:Spring Cloud Gateway
  • 服务调用:Feign-3.0.6
  • 流量控制、服务降级:Sentinel

正文结束!下面都是一些杂乱的笔记,不保证正确。

👻记录

一、git提交记录

经历了三个阶段:pom整理 => cloud模块引入 => 业务模块调试
image.png

二、Pom整理记录

day1
原项目拆解出来的模块。
image.png
day2
迁移后引用报错解决,pom拆分。
引入spring cloud alibaba依赖

<dependencyManagement>
    <dependencies>
        <!-- SpringCloud 微服务 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!-- SpringCloud Alibaba 微服务 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

三、Nacos

pom引入nacos

<!-- 引入nacos -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

docker安装nacos

#拉取镜像
docker pull nacos/nacos-server

#创建映射文件
mkdir -p /root/nacos/init.d /root/nacos/logs
touch /root/nacos/init.d/custom.properties

#在文件中写入
management.endpoints.web.exposure.include=*

#创建容器并启动提供a、b两种方案
#a.创建容器:使用standalone模式并开放8848端口,并映射配置文件和日志目录,数据库默认使用 Derby
docker run -d -p 8848:8848 -e MODE=standalone -e PREFER_HOST_MODE=hostname -v /root/nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties -v /root/nacos/logs:/home/nacos/logs --restart always --name nacos nacos/nacos-server

访问http://nacos.dinganwang.top/nacos可以打开nacos管理页面,默认账号密码nacos/nacos。
新建命名空间dev、pro区分一下开发环境还是正式环境。如果项目够大,项目粒度够多,可以根据模块创建命名空间。
Data ID需要与应用名称保持一致。
image.png
新建配置文件bootstrap.yml,增加配置。

spring:
  application:
    #应用名称
    name: dingx-web
  cloud:
    nacos:
      discovery:
        server-addr: nacos.dinganwang.top
        # 命名空间
        namespace: 命名空间ID

在启动类上使用**@EnableDiscoveryClient** 开启服务注册发现功能。

四、Gateway

引入gateway

<!-- SpringCloud Gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

报错:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found.


Action:

Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration.

原因:gateWay 工程中同时引入了 Web依赖 和 webflux依赖,或者是父工程中有Web依赖,二当前微服务中有webflux依赖,这样会出现冲突。
查找了下是sa-token-spring-boot-starter中存在spring-boot-starter-web的依赖,排除掉即可。

<!-- Common Redis-->
<dependency>
    <groupId>com.dingx</groupId>
    <artifactId>common-redis</artifactId>
    <version>1.0-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </exclusion>
    </exclusions>
</dependency>

根据nacos的配置方式加入配置后启动,服务列表出现一例服务实例,表示成功。
image.png
最后的目录结构:
image.png
从上到下依次介绍:

api:统一管理feign远程调用,对应modules中的业务模块,比如api-admin中存放的是对/modules/admin模块下接口的调用。
auth:统一权限管理,包括用户登录注销,生成token等。
common:所有可以拆解的工具模块。
┗━common-code:代码生成器模块。
┗━common-core:核心配置类,包括业务模块依赖包引入pom、工具类、全局变量、全局枚举、全局异常处理等等。业务模块必须引用。
┗━common-datasource:数据库连接模块,包括拦截器等。
┗━common-elasticsearch:es工具模块。
┗━common-log:日志切面处理。
┗━common-redis:redis工具模块。
┗━common-security:鉴权相关,主要给auth模块提供支持。
┗━common-swagger:swagger模块。
gateway:网关。
modules:业务模块统一管理。
┗━modules-admin:后台管理。
┗━modules-file:文件管理。
┗━modules-job:定时任务模块。
┗━modules-web:前台页面。
monitor:监控模块。

image.png

五、Feign

引入feign

<!-- feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>${feign.version}</version>
</dependency>

新建模块统一管理。
模块下创建service。

@FeignClient(contextId = "remoteAdminLogService" ,value = ServiceNameConstants.ADMIN_SERVICE)
public interface RemoteAdminLogService {

    @PostMapping(value = "/system/adminLog/insert")
    ResponseResult insert(@RequestBody AdminLog log);

}

具体使用的地方与其他service一样引入即可
image.png

六、Auth

鉴权中心模块,由业务登录校验抽离。
报错
Caused by: java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
由于SpringCloud Feign在Hoxton.M2 RELEASED版本之后不再使用Ribbon而是使用spring-cloud-loadbalancer,所以不引入spring-cloud-loadbalancer会报错
引入pom

<!-- SpringCloud Loadbalancer -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

image.png

七、Modules

报错:Parameter 0 of constructor in com.dingx.modules.admin.service.article.impl.ArticleServiceImpl required a bean of type ‘*.mapper’ that could not be found.
原因:Mapper未扫描到。
解决方案:在MybatisPlusConfig上加@MapperScan("com.dingx.modules.**.mapper.**")注解

报错:Parameter 2 of constructor in com.dingx.modules.admin.service.article.impl.ArticleServiceImpl required a bean of type ‘org.springframework.web.client.RestTemplate’ that could not be found.
原因:错误提示说RestTemplate没找到,这是因为在 Spring Boot 1.3版本中,会默认提供一个RestTemplate的实例Bean,而在 Spring Boot 1.4以及以后的版本中,这个默认的bean不再提供了,我们需要在Application启动时,手动创建一个RestTemplate的配置。
解决方案:写个通用BeanConfig,再遇到同类问题统一管理

@Configuration
public class BeanConfig {

    @Bean
    RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

报错:Caused by: com.baomidou.dynamic.datasource.exception.CannotFindDataSourceException: dynamic-datasource can not find primary datasource
原因:引入了多数据源配置。
解决方案:暂时没用,先注释。

报错:[RemoteUserService#updateLoginInfo(LoginInfoVO)]: [{“timestamp”:“2022-12-07T10:28:10.782+00:00”,“status”:405,“error”:“Method Not Allowed”,“message”:“”,“path”:“/system/user/updateLoginInfo”}]
解决方案:加入依赖

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

异常:feign调用参数未传递
image.png
解决方案:指定调用方式consumes

@PostMapping(value = "/system/user/selectNameAndPassword", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)

八、sa-token

1、Gateway网关引入依赖

<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:https://sa-token.cc -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-reactor-spring-boot-starter</artifactId>
    <version>1.33.0</version>
</dependency>

<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-dao-redis-jackson</artifactId>
    <version>1.33.0</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

2、Auth实现鉴权接口

/**
* 自定义权限验证接口扩展 
*/
@Component   
    public class StpInterfaceImpl implements StpInterface {

        @Override
        public List<String> getPermissionList(Object loginId, String loginType) {
            // 返回此 loginId 拥有的权限列表 
            return ...;
        }

        @Override
        public List<String> getRoleList(Object loginId, String loginType) {
            // 返回此 loginId 拥有的角色列表
            return ...;
        }

    }

3、Gateway注册全局过滤器

/**
 * [Sa-Token 权限认证] 配置类 
 * @author kong
 */
@Configuration
public class SaTokenConfigure {
    // 注册 Sa-Token全局过滤器 
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
            // 拦截地址 
            .addInclude("/**")    /* 拦截全部path */
            // 开放地址 
            .addExclude("/favicon.ico")
            // 鉴权方法:每次访问进入 
            .setAuth(obj -> {
                // 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录 
                SaRouter.match("/**", "/user/doLogin", r -> StpUtil.checkLogin());

                // 权限认证 -- 不同模块, 校验不同权限 
                SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));

                // 更多匹配 ...  */
            })
            // 异常处理方法:每次setAuth函数出现异常时进入 
            .setError(e -> {
                return SaResult.error(e.getMessage());
            })
            ;
    }
}

4、子模块引用sa-token-spring-boot-starter

注意:sa-token-spring-boot-startersa-token-reactor-spring-boot-starter存在冲突,不可同时引用。
直接访问需要鉴权
image.png

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>