SpringCloud Alibaba简介
测试member和coupon的远程调用
想要获取当前会员领取到的所有优惠券。先去注册中心找优惠券服务,注册中心调一台优惠券服务器给会员,会员服务器发送请求给这台优惠券服务器,然后对方响应。
Feign与注册中心spring cloud feign,声明式远程调用
Feign是一个声明式的HTTP客户端,他的目的就是让远程调用更加简单。给远程服务发的是HTTP请求。他还整合了Ribbon(负载均衡)和Hystrix(熔断服务)
1、会员服务想要远程调用优惠券服务,只需要给会员服务(member)里引入openfeign依赖,他就有了远程调用其他服务的能力。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、在coupon中修改如下的内容
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;
@RequestMapping("/member/list")
public R membercoupons(){ //全系统的所有返回都返回R
// 应该去数据库查用户对于的优惠券,但这个我们简化了,不去数据库查了,构造了一个优惠券给他返回
CouponEntity couponEntity = new CouponEntity();
couponEntity.setCouponName("满100减10");//优惠券的名字
return R.ok().put("coupons",Arrays.asList(couponEntity));
}
}
3、这样我们准备好了优惠券的调用内容,创建feign包并在member的配置类上加注解@EnableFeignClients(basePackages="com.liu.gulimall.member.feign")
,告诉spring这里面是一个远程调用客户端,member要调用的所有接口
@SpringBootApplication
@MapperScan("com.liu.gulimall.member.dao")
@EnableDiscoveryClient
@EnableFeignClients(basePackages="com.liu.gulimall.member.feign")
public class GulimallMemberApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallMemberApplication.class, args);
}
}
4、那么要调用什么东西呢?就是我们刚才写的优惠券的功能,复制函数部分,在member的com.liu.gulimall.member.feign包下新建类:
@FeignClient("gulimall-coupon") // 告诉spring cloud这个接口是一个远程客户端,要先调用gulimall-coupon这个服务,再去调用coupon服务中/coupon/coupon/member/list对应的方法
public interface CouponFeignService {
@RequestMapping("/coupon/coupon/member/list") // 这一块路径得写全
public R membercoupons();// 得到一个R对象
}
5、然后我们在member的Controller中写一个测试请求
@RestController
@RequestMapping("member/member")
public class MemberController {
@Autowired
private MemberService memberService;
@Autowired
CouponFeignService couponFeignService;
/**
* 测试我们会员服务与优惠券服务的调用
* @return
*/
@RequestMapping("/coupons")
public R test(){
MemberEntity memberEntity = new MemberEntity();
memberEntity.setNickname("张三");
R membercoupons = couponFeignService.membercoupons(); // 假设张三去数据库查了后返回了张三的优惠券信息
// 打印会员和优惠券信息,这里的coupons是远程返回结果中的
return R.ok().put("member",memberEntity).put("coupons",membercoupons.get("coupons"));
}
}
6、重新启动服务,访问:http://localhost:8000/menber/member/coupons
http://localhost:8000/member/member/coupons
{"msg":"success","code":0,"coupons":[{"id":null,"couponType":null,"couponImg":null,"couponName":"满100减10","num":null,"amount":null,"perLimit":null,"minPoint":null,"startTime":null,"endTime":null,"useType":null,"note":null,"publishCount":null,"useCount":null,"receiveCount":null,"enableStartTime":null,"enableEndTime":null,"code":null,"memberLevel":null,"publish":null}],"member":{"id":null,"levelId":null,"username":null,"password":null,"nickname":"张三","mobile":null,"email":null,"header":null,"gender":null,"birth":null,"city":null,"job":null,"sign":null,"sourceType":null,"integration":null,"growth":null,"status":null,"createTime":null}}
7、这里可能会少依赖出错
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>3.0.4</version>
</dependency>
Receiver class org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient does not define or inherit an implementation of the resolved method
<!--服务注册/发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
配置中心
我们还可以用nacos作为配置中心。配置中心的意思是不在application.properties等文件中配置了,而是放到nacos配置中心公用,这样无需服务上线后要修改配置时重启服务。
1、引入配置中心依赖,放到common中
<!--配置中心来做配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2、在coupons项目中创建/src/main/resources/bootstrap.properties
,这个文件是springboot里规定的,他优先级别比application.properties高,并且在common里面导入bootstrap依赖
# 改名字,对应nacos里的配置文件名
spring.application.name=gulimall-coupon
# 配置中心的地址,这里配置中心还是nacos
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-bootstrap -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.0.4</version>
</dependency>
3、测试在我们的application.properties
中设置coupon.user.name = zhangsan、coupon.user.age =18
@RestController
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;
@Value("${coupon.user.name}")// 从application.properties中获取 // 不要写user.name,他是环境里的变量
private String name;
@Value("${coupon.user.age}")
private Integer age;
@RequestMapping("/test")
public R test(){
return R.ok().put("name",name).put("age",age);
}
}
启动访问coupon服务:http://localhost:7000/coupon/coupon/test:{“msg”:“success”,“code”:0,“name”:“zhangsan”,“age”:18}
4、浏览器去nacos里的配置列表,点击+号,data ID:gulimall-coupon.properties,配置,这里是服务名,规定的
# gulimall-coupon.properties
coupon.user.name="张三"
coupon.user.age=12
5、但是怎么实时修改呢?实际生产中不能重启应用。在coupon的控制层上加@RefreshScope
,表示实时刷新每次配置都从配置中心去获取配置,重启nacos、重启coupon,访问:http://localhost:7000/coupon/coupon/test:{"msg":"success","code":0,"name":"\"张三\"","age":12}
6、最终代码如下
@RefreshScope
@RestController
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;
@Value("${coupon.user.name}")// 从application.properties中获取 //不要写user.name,他是环境里的变量
private String name;
@Value("${coupon.user.age}")
private Integer age;
@RequestMapping("/test")
public R test(){
return R.ok().put("name",name).put("age",age);
}
}
7、重启后,在nacos浏览器里修改配置,修改就可以观察到能动态修改了nacos的配置内容优先于项目本地的配置内容。
小结
- 引入依赖
spring-cloud-starter-alibaba-nacos-config
,starter表示启动就回去找配置,所以下一步编写配置 - 编写
bootstrap.properties
- 在nacos配置中心添加一个数据集(Data Id),默认规则:应用名.properties:gulimall-coupon.properties,在这里面进行配置编写
- 动态获取配置,添加注解:
@RefreshScope、@Value("${配置项的名}")
获取配置,配置中心优先与服务配置的相同项
配置中心进阶
在nacos浏览器中还可以配置:
- 命名空间:用作配置隔离。(一般每个微服务一个命名空间),默认public。默认新增的配置都在public空间下
开发、测试、开发可以用命名空间分割(dev、test、prop)。properties每个空间有一份,在bootstrap.properties里配置采用那个命名空间的配置文件。默认是采用public中的
1、我们使用prop中的环境,利用命名空间做环境隔离
spring.cloud.nacos.config.namespace=7fbf2f71-d0b3-4e96-9317-5e548e344268
# 可以选择对应的命名空间 ,即写上对应环境的命名空间ID
# 启动访问,发现用的prop中的配置
2、为每个微服务配置一个命名空间,微服务互相隔离
,不然每个会使每一个空间下(public、dev等)有大量的配置文件
在微服务gulimall-coupon中就采用自己的命名空间
spring.cloud.nacos.config.namespace=7818bdad-b382-4eab-ac92-974e486af35e
# 可以选择对应的命名空间 ,即写上对应环境的命名空间ID
-
配置集:所有的配置的集合。一组相关或不相关配置项的集合。
-
配置集ID:类似于配置文件名,即Data ID
-
配置分组:默认所有的配置集都属于
DEFAULT_GROUP
。自己可以创建分组,比如双十一,618,双十二
配置bootstrap.properties
spring.cloud.nacos.config.group=1111 # 采用双十一分组,默认是DEFAULT_GROUP
- 最终方案:每个微服务创建自己的命名空间,然后使用配置分组区分环境(dev/test/prod)
spring.cloud.nacos.config.group=dev # prod、、、等
# bootstrap.properties的总配置
spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=7818bdad-b382-4eab-ac92-974e486af35e
spring.cloud.nacos.config.group=dev
由于配置文件中的内容会越来越多,我们采用多个配置文件来拆分文件内容
- datasource.yml
- other.yml
- 然后在bootstrap.properties中修改配置,然后把application.yml中配置全部注释掉,启动测试:
- http://localhost:7000/coupon/coupon/list、http://localhost:7000/coupon/coupon/test都没问题
# spring.cloud.nacos.config.group=dev
# 相当于加载了一个配置文件,refresh动态刷新
spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml
spring.cloud.nacos.config.ext-config[0].group=dev
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.ext-config[1].data-id=mybatis.yml
spring.cloud.nacos.config.ext-config[1].group=dev
spring.cloud.nacos.config.ext-config[1].refresh=true
spring.cloud.nacos.config.ext-config[2].data-id=other.yml
spring.cloud.nacos.config.ext-config[2].group=dev
spring.cloud.nacos.config.ext-config[2].refresh=true
- 上图说明会去读取我们的四个配置文件,但是由于在nacos中我们把gulimall-coupon.properties的默认分组删除了,所以用的服务中的配置
小结
- 微服务的任何配置信息,配置文件都可以放在配置中心中
- 只需要在bootsrap.properties中说明加载 配置中心的哪些配置文件即可
- 以前springboot中的任何方法从配置文件中获取的值,都能使用。@Value、@ConfigurationProperties等等
- 配置中心有的优先使用配置中心的
网关
spring cloud的gateway组件做网关功能。网关是请求浏览的入口,常用功能包括路由转发,权限校验,限流控制等。springcloud gateway取代了zuul网关。
网关作用?
-
发送请求需要知道商品服务的地址,如果商品服务器有100服务器,1号掉线后,又得重新修改请求地址,所以需要网关动态地管理,他能从注册中心中实时地感知某个服务上线还是下线。
-
请求也要加上询问权限,看用户有没有权限访问这个请求,也需要网关。
spring-cloud-gateway官网:https://spring.io/projects/spring-cloud-gateway
spring-cloud-gateway文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
spring-cloud 中文网:https://www.springcloud.cc/
三大核心概念:
Route路由: The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates断言, and a collection of filters. A route is matched if the aggregate predicate is true. 发一个请求给网关,网关要将请求路由到指定的服务。路由有id,目的地uri,断言的集合,匹配了断言就能到达指定位置,
Predicate断言: This is a Java 8 Function Predicate. The input type is a Spring Framework ServerWebExchange. This lets you match on anything from the HTTP request, such as headers or parameters. 就是java里的断言函数,匹配请求里的任何信息,包括请求头等
Filter过滤: These are instances of Spring Framework GatewayFilter that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request.
过滤器请求和响应都可以被修改。客户端发请求给服务端。中间有网关。先交给映射器,如果能处理就交给handler处理,然后交给一系列filer,然后给指定的服务,再返回回来给客户端。
创建模块gulimall-gateway
- spring模块,导入gateway依赖项目即可
1、在pom.xml引入gulimall-common
<dependency>
<groupId>com.liu.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--版本环境需保持一致-->
<spring-boot.version>2.6.1</spring-boot.version>
<spring-cloud.version>2021.0.0</spring-cloud.version>
2、开启注册服务发现@EnableDiscoveryClient
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
// 这里是因为我们引入common的依赖,他里面有mybatis的数据源依赖,但是我们的gateway中没有使用,会导致一些错误,所以我们要排除数据源依赖可以在pom中exclusions排除依赖,或者直接在启动的时候加上exclude
@EnableDiscoveryClient
public class GulimallGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallGatewayApplication.class, args);
}
}
3、在applicaion.properties中配置nacos注册中心地址
spring.application.name=gulimall-gateway
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# 因为我们是通过网关来请求跳转所以不采用8080,自己配置
server.port=88
4、创建bootstrap.properties填写配置中心地址
gatewayspring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# 命名空间gateway的id
spring.cloud.nacos.config.namespace=d1e3e651-9bc0-4cfe-a65f-7ffde3547272
5、nacos里创建命名空间gateway,然后在命名空间里创建文件gulimall-gateway.yml
spring:
application:
name: gulimall-gateway
6、在项目里创建application.yml,配置网关信息
spring:
cloud:
gateway:
# 路由规则
routes:
- id: baidu_route
uri: http://www.baidu.com
# 断言,判断成功才去跳转到指定地方 :www.baidu.com
predicates:
# 当参数url==baidu就跳转到www.baidu.com
- Query=url,baidu
- id: qq_route
uri: http://www.qq.com
predicates:
- Query=url,qq
测试 http://localhost:88?url=baidu 跳到百度页面
测试 http://localhost:88?url=qq 跳到qq页面