微服务是一种软件架构风格,它将一个大型应用程序拆分成一组更小、独立的服务,每个服务都可以独立开发、部署和扩展。每个服务都有自己的业务逻辑和数据库,并且通过轻量级通信机制(如RESTful
API)来相互通信。
尽管存在这些挑战,微服务架构仍然是越来越受欢迎的软件架构风格,因为它可以帮助企业更快地开发和部署应用程序,并提供更好的可扩展性和可维护性。
Spring Cloud Alibaba致力于提供微服务开发的一站式解决方案。 此项目包含开发分布式应用微服务的必需组件,方便开发者通过
Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务
由于SpringCloud有些组件不在提供维护,所以现在基本都是在用SpringCloudAlibaba
Spring Cloud Alibaba 是基于 Spring Cloud 的一套开发框架,提供了一系列的解决方案,可以帮助开发者快速构建分布式应用程序。它具有以下优点:
集成了众多阿里巴巴开源产品,如 Nacos、Sentinel、Dubbo 等,可以轻松实现服务注册和发现、服务治理、流量控制等功能。
提供了丰富的组件和工具,如 Spring Cloud Alibaba Stream、Spring Cloud Alibaba Bus 等,可以简化开发者的编码工作。
它具有高可用性、高可扩展性、高性能等特点,可以满足企业级应用的需求。
它是开源的,可以免费使用,并且社区活跃,可以获得及时的技术支持和更新。
综上所述,使用 Spring Cloud Alibaba 可以使开发者更加轻松地构建分布式应用程序,提高开发效率和应用性能,因此得到了越来越多开发者的青睐。
Nacos是一个开源的动态服务发现、配置管理和服务管理平台,由阿里巴巴集团开源。它提供了一种简单易用的方式来管理云原生应用的动态配置和服务发现,支持多种数据格式(如JSON、XML、YAML等),支持多种语言(如Java、Go、Python等),并且具有高可用、可扩展、易于部署等特点。
Nacos的主要功能包括:
服务发现和注册:Nacos提供了一个服务注册中心,可以让服务提供者将自己的服务注册到中心,让服务消费者可以通过中心来发现和调用服务。
配置管理:Nacos提供了一个统一的配置中心,可以让应用程序动态地获取配置信息,支持配置的动态发布和变更。
服务管理:Nacos提供了一个服务管理平台,可以让用户管理服务的生命周期,包括服务的上线、下线、健康状态等。
Nacos的优点包括:
功能全面:Nacos提供了服务发现、配置管理和服务管理等多种功能,可以满足云原生应用的各种需求。
易于使用:Nacos提供了简单易用的API和UI界面,可以让用户快速上手。
高可用性:Nacos支持集群部署,可以保证高可用性和可扩展性。
生态丰富:Nacos与Spring Cloud、Dubbo等主流微服务框架集成良好,可以方便地与其他微服务组件进行集成。
总之,Nacos是一个功能全面、易于使用、高可用性、生态丰富的动态服务发现、配置管理和服务管理平台,是云原生应用开发的重要组件之一。
https://github.com/alibaba/nacos/releases
这里我使用的是2.0.3
因为我们在本地写项目,也一般用不到集群,所以nacos启动我们设置为单机模式(standalone)
首先是这四个文件
startup 是用于启动nacos服务的,可以看到有两个文件,一个是cmd用于windows系统启动,一个是sh,shell脚本,用于linux上启动
shutdown是用于停止nacos服务的,两个文件同上
进入bin目录,打开cmd执行
startup.cmd -m standalone
更改nacos启动文件
将cluster更改为standalone设置为单机模式,然后保存退出,点击startup.cmd即可启动nacos服务
可以看到这样就启动服务成功了,并且是单机模式
http://localhost:8848/nacos/#/login
因为我是在本地配置的nacos,所以地址就是本地的地址localhost(http://127.0.0.1/),如果你们是在虚拟机上配置的,这个需要改成你虚拟机的ip地址
在服务管理中的服务列表,就可以看到你注册到nacos的服务
springboot版本可自行选择,反正我感觉不要太低,也不要太高
下面是选择依赖,这里不选择,直接点击Finish,依赖我们进去自己配置
创建完成后就是一个普通的springboot项目,但是可能有时候会出现依赖报错的问题
依赖报错问题解决方案
查看这里是不是你的本地Maven仓库
配置阿里云镜像或者华为镜像
删除依赖,重新进行下载,因为可能有网络原因,他下载没有下载完整
可以看到上图,他的项目结构,因为我们不在主项目也就是父项目里写代码,所以要删除没用的包
留下这四个就行
右键项目 - New - Module
我们选择Maven项目,next
Finish
创建完成后看你的Maven
可以看到鼠标选择的地方后边出现了一个root,他也就是主项目,service就是他的子项目
然后重复上部操作,创建common子项目,gateway子项目
然后介绍一下这三个子项目的作用
service
首先service就是用于放子子项目的,也就是我们的服务,举个例子(存放用户服务,订单服务),一个服务也是一个项目,
common
这个可以理解为,公共服务,我们用到的一些util类,包括实体类等,还用于一些公共依赖,例如sql,
mybtis,lombok等,其他服务通过依赖的方式去导入它
gateway
首先说一下gateway是什么
Spring Cloud Alibaba Gateway是一种基于Spring Cloud Gateway实现的API网关,它提供了一些额外的功能,如动态路由、限流、熔断、安全认证等。它可以作为微服务架构中的入口,将所有的请求统一转发到后端的微服务中,并提供了一些通用的功能,如请求转发、请求过滤、请求重试等。同时,它还支持多种协议,如HTTP、WebSocket、TCP等。Spring Cloud Alibaba Gateway是一个非常强大的工具,可以帮助开发者快速构建高可用、高性能的微服务架构。
简单理解可以理解为,它就是一个在消费者和提供者中间的一个城门,消费者都去访问它,它通过配置的规则去转发请求到对应的服务里
具体配置一会再配置
最后就是这样的一个样子
也可以看到主项目的pom文件
现在往主项目里导入依赖
将这三个依赖删除
将依赖替换成
org.springframework.boot spring-boot-starter-parent 2.3.12.RELEASE pom import org.springframework.cloud spring-cloud-dependencies Hoxton.SR12 pom import com.alibaba.cloud spring-cloud-alibaba-dependencies 2.2.7.RELEASE pom import
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-logging org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true mysql mysql-connector-java 8.0.22 org.springframework.boot spring-boot-starter-mail org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-amqp com.aliyun.oss aliyun-sdk-oss 3.10.2 org.projectlombok lombok com.baomidou mybatis-plus-boot-starter 3.5.1 com.baomidou mybatis-plus-generator 3.5.2 org.apache.velocity velocity-engine-core 2.3 org.springframework.boot spring-boot-starter-log4j2 com.alibaba fastjson 1.2.73 com.auth0 java-jwt 3.18.2 com.belerweb pinyin4j 2.5.0 org.apache.httpcomponents httpclient 4.5.1 commons-io commons-io 2.6 com.google.code.gson gson 2.8.2 io.jsonwebtoken jjwt 0.7.0 cn.hutool hutool-all 5.8.3 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign io.github.openfeign feign-httpclient
org.springframework.boot spring-boot-devtools runtime true com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.cloud spring-cloud-alibaba-sentinel-gateway
直接使用common的公共依赖,如果单独需要,那么就在有需求的服务里导入依赖
这个groupId记得要改,是你自己起的包名
com.fyj common 0.0.1-SNAPSHOT
因为service子项目也不写代码,所以src文件夹可以删掉
方法同刚才一样,只不过是有些需要改的,到下图这个页面,改成service
创建用户(user)服务和order(订单)服务,子项目会继承父项目的所有依赖,自己看一下Maven就知道了
其实上面也讲了,就是放一些公共的东西,可以去我的项目里复制,或者你自己配置也行。
在生成前,先创建表 user表和order表
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用户名', `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用户密码', `age` int NULL DEFAULT NULL COMMENT '年龄', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, '小明', '123213123', 18); SET FOREIGN_KEY_CHECKS = 1;
DROP TABLE IF EXISTS `order`; CREATE TABLE `order` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `user_id` int NULL DEFAULT NULL COMMENT '用户ID', `order_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '订单编号', `order_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '订单名称', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of order -- ---------------------------- INSERT INTO `order` VALUES (1, 1, 'xaw-2004-0926', '订单001'); INSERT INTO `order` VALUES (2, 1, 'xaw-2001-0926', '订单002'); SET FOREIGN_KEY_CHECKS = 1;
然后连接数据库,生成代码
package utils; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.OutputFile; import java.util.Collections; /** * 代码生成器 * @Author: beisheng, * Date: 2022/6/18 15:01, * Version: IntelliJ IDEA 2021.2.1 * */ public class Generator { public static void main(String[] args) { generate(); } private static void generate(){ FastAutoGenerator.create("jdbc:mysql://8.143.205.127/sa_salary?serverTimezone=Asia/Shanghai&useUnicode=true&" + "characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true", "root", "Zqw123456") .globalConfig(builder -> { builder.author("beisheng") // 设置作者 // .enableSwagger() // 开启 swagger 模式 .fileOverride() // 覆盖已生成文件 .commentDate("yyyy-MM-dd") .outputDir("D:\\项目文件\\凌云\\薪想事成\\Salary\\service\\salary\\src\\main\\java\\com\\buba\\"); // 指定输出目录 * }) .packageConfig(builder -> { builder.parent("") // 设置父包名 !!! .moduleName("") // 设置父包模块名 .mapper("mapper") .xml("mapper") .pathInfo(Collections.singletonMap(OutputFile.xml, "D:\\项目文件\\凌云\\薪想事成\\Salary\\service\\salary\\src\\main\\resources\\mapper\\")); // 设置mapperXml生成路径 !!! }) .strategyConfig(builder -> { builder.addInclude("payrolls") // 设置需要生成的表名 !!! .addTablePrefix("", "_table")// 设置过滤表前缀 !!! .serviceBuilder() //service生成策略 .formatServiceFileName("%sService") //service类名,%s适配,根据表明替换 .formatServiceImplFileName("%sServiceImpl") // service实现类 .entityBuilder() //实体类生成策略 .enableLombok() //开启lombok // .logicDeleteColumnName("deleted) //说明逻辑删除是哪个字段 .enableTableFieldAnnotation() // 属性加上说明注解 .controllerBuilder() //controller生成策略 .enableHyphenStyle() //开启驼峰转连字符 .formatFileName("%sController") .enableRestStyle() //开启RestController .mapperBuilder() .superClass(BaseMapper.class) //dao层继承父类 .formatMapperFileName("%sMapper") // .enableMapperAnnotation() //@Mapper注解开启 .formatXmlFileName("%sMapper"); }) // .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板 .execute(); } }
模板可以去项目里复制
执行main方法,生成代码
生成完后把实体类放到common里去,因为各个服务可能会出现调用
application.yml
server: port: 8003 servlet: context-path: /user spring: #配置服务名及nacos application: name: user cloud: nacos: discovery: server-addr: 127.0.0.1:8848 alibaba: seata: tx-service-group: my_test_tx_group # 数据源 config datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/txt?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true username: root password: root jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 #Redis配置 redis: host: 127.0.0.1 port: 6379 jedis: pool: max-active: 8 max-wait: -1ms max-idle: 500 min-idle: 0 lettuce: shutdown-timeout: 0ms # password: root servlet: multipart: max-file-size: 500MB #单个数据大小 max-request-size: 1024MB #总数据大小 mybatis-plus: #mapper配置文件 扫描所有* xml文件 mapper-locations: classpath*:mapper/**/*.xml configuration: auto-mapping-behavior: full log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印sql语句 #指定包的别名 type-aliases-package: com.fyj.* logging: level: #需要开启日志打印,为防止过多冗余日志。可以指定我们feign的接口。 com.bgs.feign: debug feign: client: config: #指定服务名 nacos-producer: loggerLevel: FULL httpclient: # 为feign启用 apache httpclient 做请求,而不使用默认的urlconection enabled: true # feign 最大连接数 max-connections: 200 # feign 单个路径请求的最大连接数 max-connections-per-route: 50
启动类:
package com.fyj; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class userApplication { public static void main(String[] args) { SpringApplication.run(userApplication.class,args); } }
启动user服务
yml
server: port: 8002 servlet: context-path: /order spring: #配置服务名及nacos application: name: order cloud: nacos: discovery: server-addr: 127.0.0.1:8848 # 数据源 config datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/txt?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true username: root password: root jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 #Redis配置 redis: host: 127.0.0.1 port: 6379 jedis: pool: max-active: 8 max-wait: -1ms max-idle: 500 min-idle: 0 lettuce: shutdown-timeout: 0ms # password: root servlet: multipart: max-file-size: 500MB #单个数据大小 max-request-size: 1024MB #总数据大小 mybatis-plus: #mapper配置文件 扫描所有* xml文件 mapper-locations: classpath*:mapper/**/*.xml configuration: auto-mapping-behavior: full log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印sql语句 #指定包的别名 type-aliases-package: com.fyj.* logging: level: #需要开启日志打印,为防止过多冗余日志。可以指定我们feign的接口。 com.bgs.feign: debug feign: client: config: #指定服务名 nacos-producer: loggerLevel: FULL httpclient: # 为feign启用 apache httpclient 做请求,而不使用默认的urlconection enabled: true # feign 最大连接数 max-connections: 200 # feign 单个路径请求的最大连接数 max-connections-per-route: 50
启动类
package com.fyj; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient//服务注册发现 @SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class,args); } }
在nacos的服务列表里就可以看到两个启动的服务
接下来写一个单服务的全查,进行访问
postman测试
这里出了一个问题
因为order好像是关键字,他会报错,改一下名字,实体类也要改
改完后就测试成功了
但是通常前端都是把请求封装起来成一个js,端口号是不变的,所以我们配置gateway进行请求的转发
# 项目入口,可以使用80,也可以随意 server: port: 8090 spring: application: name: gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 sentinel: transport: # 添加sentinel的控制台地址 dashboard: 127.0.0.1:8861 gateway: globalcors: # 跨域配置 cors-configurations: '[/**]': allow-credentials: true #允许携带认证信息Cookie # 我们可以通 过域名的方式 也可以通过指定ip 还可以 allowed-origins: "*" #放行所有跨域请求 allowed-origins: "*" allowed-headers: "*" #允许所有请求头 allowed-methods: "*" #允许所有请求方式 max-age: 86400 # 86400 秒,也就是 24 小时 在有效时间内,浏览器无须为同一请求再次发起预检请求,可以减少发送请求的次数,减少系统部分压力。 routes: # 路由id,可以任意写,但是要保证唯一 # “-”:代表为list,可以配置多个 - id: user # 代理的服务地址(将匹配到的url路由到代理地址上) # uri: http://localhost:8050 # 代理的服务地址(将匹配到的url路由到代理地址上) feign-consummer nacos服务名 uri: lb://user # 谓词: url要匹配的规则,如果匹配成功则路由到上面的uri上。 predicates: - Path=/user/** # 路由id,可以任意写,但是要保证唯一 # “-”:代表为list,可以配置多个 - id: order # 代理的服务地址(将匹配到的url路由到代理地址上) # uri: http://localhost:8050 # 代理的服务地址(将匹配到的url路由到代理地址上) feign-consummer nacos服务名 uri: lb://order # 谓词: url要匹配的规则,如果匹配成功则路由到上面的uri上。 predicates: - Path=/order/** logging: level: org.springframework.cloud.gateway: debug
配置启动类以及解决跨域配置
package com.buba.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; /** * @Author: beisheng * @Date: 2022/9/29 11:40 */ @Configuration public class CorsConfig { @Bean public CorsWebFilter corsWebFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration configuration = new CorsConfiguration(); configuration.addAllowedHeader("*"); configuration.addAllowedMethod("*"); configuration.addAllowedOrigin("*"); // 是否允许携带cookie跨域 configuration.setAllowCredentials(true); source.registerCorsConfiguration("/**", configuration); return new CorsWebFilter(source); } }
将gateway也注册到nacos中
package com.fyj; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class GateWayApplication { public static void main(String[] args) { SpringApplication.run(GateWayApplication.class, args); } }
测试把端口号改成gateway的端口号,可以发现也能访问
OpenFeign是Spring Cloud Alibaba中的一个组件,它是一个声明式的Web服务客户端,可以让开发者更加方便地调用HTTP API。
OpenFeign的使用方式类似于Spring MVC中的注解方式,开发者只需要定义一个接口,然后使用注解来描述这个接口需要调用的HTTP API,OpenFeign就会自动根据这些注解生成一个实现了这个接口的代理对象。这个代理对象可以像调用本地方法一样调用远程的HTTP API。
OpenFeign还提供了一些其他的功能,比如负载均衡、熔断器、请求重试等。这些功能可以让开发者更加方便地构建高可用、高可靠的分布式系统。
总之,Spring Cloud Alibaba OpenFeign是一个非常方便的工具,可以让开发者更加轻松地调用HTTP API,同时也提供了一些其他的功能,可以帮助开发者构建高可用、高可靠的分布式系统。
现在有个需求
用户服务需要根据用户ID去查询当前用户的订单,所以需要用户服务去调用订单服务,使用springcloudalibaba的openFeign去调用其他服务
/* * * @Author: beisheng * @Descriprion: 根据用户ID查询订单 * @Date: 2023/5/12 17:06 * @Return: common.R * @Param: [id] */ @GetMapping("/selOrderByUserId") public ListselOrderByUserId(@RequestBody Integer id){ List users = orderService.list(new QueryWrapper ().eq("user_id", id)); return users; }
代码
package com.fyj.feign; import entity.order.Orders; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import java.util.List; @FeignClient(name = "order",path = "/order") public interface orderFeign { /* * * @Author: beisheng * @Descriprion: 根据用户ID查询订单 * @Date: 2023/5/12 17:06 * @Return: common.R * @Param: [id] */ @GetMapping("/selOrderByUserId") ListselOrderByUserId(@RequestBody Integer id); }
在user服务上调用
package com.fyj.controller; import com.fyj.feign.orderFeign; import common.R; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** ** 前端控制器 *
* * @author yixin * @since 2023-05-12 */ @RestController @RequestMapping("/user") public class UserController { @Autowired private orderFeign orderFeign; @GetMapping("zzz") public R zzz(){ return R.success(orderFeign.selOrderByUserId(1)); } }
重启两个服务
测试
结果已经出来了
中间我也遇到了一些问题,可能在上面没有优化好,有什么问题可以问
现在就是用到了springCloudAlibaba的nacos,gateway,以及openFeign
后面可能还会去继续完善,例如nacos配置中心,服务熔断,消息中间件等