spring cloud alibaba
第一步我们需要创建一个父工程,引入对应的依赖,参考对应版本引入
<properties>
<!--规定好版本,-->
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.1.4.RELEASE</spring-boot.version>
<alibaba-spring.version>2.1.3.RELEASE</alibaba-spring.version>
<cloud-spring.version>Greenwich.SR1</cloud-spring.version>
</properties>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${cloud-spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${alibaba-spring.version}</version>
<type>pom</type> <!--pom,import是不能省略的-->
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
首先dependencyManagement里面的依赖,类似是子模块中父模块,因为标签只能引入一个父模块,所以需要这种写法,
nacos使用
- 首先下载对应的版本的nacos
- 创建一个子客户端模块,并引入nacos依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 使用nacos注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
- 编写配置文件
spring:
application:
name: consumer # nacos中注册的服务名
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos地址
server:
port: 80
- 创建一个服务端子模块,引入nacos依赖
- 编写配置文件
spring:
application:
name: payment-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
server:
port: 8001
这样两个都注册到了nacos中心里,客户端在访问服务端的时候不需要指明具体的url地址
客户端使用restTemplate 访问服务端的接口
以下是服务端接口
@RestController
public class payCon {
@Value("${server.port}")
String port;
@GetMapping("/pay")
public String pay() {
return "pay success" + port;
}
}
客户端之前访问肯定是
restTempalte.get(‘http://localhost:端口/pay’)这种一点不友好
现在客户端配置rabbion实现负载均衡,访问只需要服务名即可
以下是客户端代码
/**
* @create 2023-04-02 16:35:05
*//*
@author ljj
@date 2023-04-02 16:35
*/
@Configuration
public class Config {
@Bean
@LoadBalanced //如果不加负载均衡则不能调用服务名
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
public class ConController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/topay")
public String toPay() {
String forObject = restTemplate.getForObject("http://payment-service/pay", String.class);
return forObject;
}
}
这样一来就能够顺利调用
nacos 配置文件中心
基本上都会用的到,配置文件会有很多公共的地方,这些我们将它统一配置起来
使用方法也很简单
- 引入依赖
<!-- 服务配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
编写配置文件,这里我们用的是bootstrap.yml,这个配置文件的优先级最高
spring:
application:
name: nacos_config
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml #因为配置文件中心默认读取的是properties文件,如果nacos中的配置文件格式改了,那么这里需要进行配置,否则读取不到文件
shared-configs[0]: #自定义配置文件data-id,可以配置多个,自定义的配置文件
data-id: demo.yml
refresh: true
shared-configs[1]:
data-id: demo-mysql.yml
refresh: true
然后将会从nacos配置中心读取配置文件
使用openfeign调用服务
正常情况下,客户端访问服务端的时候会使用openfeign来访问比较方便。以下是使用方法
- 首先添加依赖,只需要客户端添加即可
<!-- openfeign服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
服务端的代码不需要更换
- 启动类需要添加注解 @EnableFeignClients(basePackages = “那个包下”)
- openfeign 接口,这里的返回类型,方法名、请求参数等要与服务端写的接口一致,正常直接复制拿过来即可
package com.openFeign.service;
import com.entity.People;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
/**
* @create 2023-04-03 09:30:11
*//*
@author ljj
@date 2023-04-03 9:30
*/
@Service
@FeignClient(value = "payment-service" )//指明服务端的服务名
public interface FeignService {
@GetMapping("/pay")
public String toPay();
// 地址传参 get?name=?
@GetMapping("/param")
public String senPrams(@RequestParam("name") String name);
//路径传参/param/xxx
@GetMapping("/param/{name}")
public String pathParams(@PathVariable("name") String name);
//传递实体参数
@PostMapping("/save")
public People save(@RequestBody People people);
}
使用方法就是和平时的使用mvc接口访问一样,在客户端进行调用就行
以下是服务端接口
/**
* @create 2023-04-02 16:33:52
*//*
@author ljj
@date 2023-04-02 16:33
*/
@RestController
public class payCon {
@Value("${server.port}")
String port;
@GetMapping("/pay")
public String pay() {
return "pay success" + port;
}
@GetMapping("/param")
public String getParams(@RequestParam("name") String name) {
return name;
}
@GetMapping("/param/{name}")
public String pathParams(@PathVariable("name") String name) {
return name + "---";
}
//接收实体参数
@PostMapping("/save")
public People getPeople(@RequestBody People people) {
int a = 10 / 0;
return people;
}
}
客户端中调用openfeign,其实将那个接口注入进来就行,别的就正常调用即可
/**
* @create 2023-04-03 09:29:57
*//*
@author ljj
@date 2023-04-03 9:29
*/
@RestController
public class FeignController {
@Autowired
FeignService feignService;
@GetMapping("/to_pay")
public String toPay() {
return feignService.toPay();
}
@GetMapping("/param")
public String senPrams(@RequestParam("name") String name) {
return feignService.senPrams(name);
}
@GetMapping("/param/{name}")
public String pathParams(@PathVariable("name") String name) {
return feignService.pathParams(name);
}
@GetMapping("/save")
public Object save() {
People people = new People();
people.setName("李俊杰");
people.setSex("男");
return feignService.save(people);
}
}
sentinel 熔断降级
通常使用服务端,防止服务出错而展示的不友好信息
- 添加相关依赖
<!-- sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 修改配置文件
spring:
application:
name: sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 #需要自己下载对应版本的sentinel客户端,并启动
- 接口使用
package com.payment.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.entity.People;
import org.apache.tomcat.jni.Time;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* @create 2023-04-02 16:33:52
*//*
@author ljj
@date 2023-04-02 16:33
*/
@RestController
public class payCon {
@Value("${server.port}")
String port;
@GetMapping("/pay")
@SentinelResource(value = "pays", blockHandler = "payHandler")//当接口出现熔断降级,则会触发payHandler方法,并响应
public String pay() {
return "pay success" + port;
}
@GetMapping("/get")
@SentinelResource(value = "get", blockHandler = "getHandler")
public String get() {
return "获取成功!";
}
public String getHandler(BlockException blockException) {
return "限流!";
}
public String payHandler(BlockException blockException) {
return "请稍后响应";
}
@GetMapping("/param")
public String getParams(@RequestParam("name") String name) {
return name;
}
@GetMapping("/param/{name}")
@SentinelResource(value = "param",blockHandler = "pathParamHandler")
public String pathParams(@PathVariable("name") String name) {
return name + "---";
}
public String pathParamHandler(String name,BlockException blockException){
return "热点数据限流";
}
//接收实体参数
@PostMapping("/save")
public People getPeople(@RequestBody People people) {
int a = 10 / 0;
return people;
}
//线程数降级
@GetMapping("/thr")
public String thre() throws InterruptedException {
Thread.sleep(3000);
return "ok";
}
//异常降级
@GetMapping("/err")
public String err() throws InterruptedException {
int a = 10 / 0;
return "ok";
}
}
SentinelResource注解修饰的是个性化的熔断降级方案,如果不添加改注解,则使用默认的方案,我们也可以重写默认的全局熔断降级方案,但是不影响个性化的
//没被SentinelResource修饰的接口出错后,则会使用该方案
package com.payment.exec;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
/**
* @create 2023-04-05 17:12:47
*//*
@author ljj
@date 2023-04-05 17:12
*/
@Component
public class SentinelException implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
System.out.println(e instanceof SystemBlockException);
PrintWriter writer = httpServletResponse.getWriter();
writer.write(new ObjectMapper().writeValueAsString("服务被sentinel限制!"));
writer.flush();
}
}
openfeign-sentinel整合
- 所有依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- openfeign服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
- 配置
spring:
application:
name: openfeign-sentinel
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
server:
port: 8003
feign:
sentinel:
enabled: true #启动
- service层
package com.cloud.service;
import com.cloud.service.impl.FenSenServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @create 2023-04-08 10:21:21
*/
@FeignClient(value = "sentinel-service", fallback = FenSenServiceImpl.class)//fallback熔断降级后的处理
public interface FenSenService {
@GetMapping("/pay/payment")
public String payment(@RequestParam("name") String name);
}
如果该接口对应的服务出现错误,则会返回这里的信息
package com.cloud.service.impl;
import com.cloud.service.FenSenService;
import org.springframework.stereotype.Component;
/**
* @create 2023-04-08 10:22:23
*//*
@author ljj
@date 2023-04-08 10:22
*/
@Component
public class FenSenServiceImpl implements FenSenService {
@Override
public String payment(String name) {
return "付款失败,服务异常!";
}
}
gateway 网关 sentinel可以合用
- 引入依赖
<!-- spring cloud gateway整合sentinel的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- sentinel的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 配置
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
server-addr: 127.0.0.1:8848
gateway:
#路由规则
routes:
- id: openfeign_sentinel_route #路由的唯一标识
uri: lb://openfeign-sentinel #需要转发的地址 lb: 使用nacos中的本地负载均衡
#断言规则,用于路由规则的匹配,输入url的时候需要前缀/gateway后面才能够正常访问
predicates:
- Path=/gateway/**
filters:
- StripPrefix=1 #转发之前去掉第一层路径
sentinel:
transport:
dashboard: 127.0.0.1:8080
测试访问
localhost:8088/gateway/feign/pay