首先规划一个项目,功能需求如下图:
服务端分为账号和支付两个服务,客户端需要调用账号服务的登录接口和支付服务的查询余额接口,同时为了简化客户端处理,登录成功后账号服务会调用查询余额接口一同返回余额信息。
在技术选型上,尽可能使用Alibaba的组件,主要选择如下:
- 使用Nacos作为配置中心
- 使用Nacos作为注册中心,实现服务注册和发现
- 使用Feign作为服务调用组件
- 使用Ribbon实现服务调用的负载均衡
- 使用Sentinel实现限流和熔断, 并结合Nacos实现动态配置
- 使用Spring Cloud Gateway作为API网关
架构设计如下图:
一、动态配置
创建一个SpringBoot应用payment-service
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
application.yml
spring: profiles: active: dev server: port: 8082 sleep: 0
创建一个pojo类:Balance.java
public class Balance { private int id; private int diamond; private int ticket; private String message; public Balance() { } public Balance(int id, int diamond, int ticket) { this(id, diamond, ticket, "OK"); } public Balance(int id, int diamond, int ticket, String message) { this.id = id; this.diamond = diamond; this.ticket = ticket; this.message = message; } //Setter/Getter略 }
创建Controller
@RestController public class PaymentController { @Value("${sleep:0}") private int sleep; final static Map<Integer, Balance> balanceMap = new HashMap() {{ put(1, new Balance(1, 10, 1000)); put(2, new Balance(2, 0, 10000)); put(3, new Balance(3, 100, 0)); } }; @RequestMapping("/pay/balance") public Balance getBalance(Integer id) { System.out.println("request: /pay/balance?id=" + id + ", sleep: " + sleep); if(sleep > 0) { try { Thread.sleep(sleep); } catch (InterruptedException e) { e.printStackTrace(); } } if(id != null && balanceMap.containsKey(id)) { return balanceMap.get(id); } return new Balance(0, 0, 0); } }
启动应用并测试
后台显示request: /pay/balance?id=1, sleep: 0
引入Nacos实现sleep参数动态配置
<properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR1</spring-cloud.version> <alibaba.version>0.9.0.RELEASE</alibaba.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>${alibaba.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
添加bootstrap.yml,其中 server-addr即Nacos监听IP和端口
spring: application: name: payment-service cloud: nacos: config: server-addr: 127.0.0.1:8848
在Controller 上添加 @RefreshScope注解
@RestController @RefreshScope public class PaymentController {
打开Nacos管理后台创建配置payment-service-dev.properties,修改sleep=5000
点击发布
重启payment-service,可以看到启动窗口中显示
b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='payment-service-dev.properties'}, NacosPropertySource {name='payment-service.properties'}]}
刷新http://localhost:8082/pay/balance?id=1可以看到响应延迟5秒,同时控制台显示
request: /pay/balance?id=1, sleep: 5000
证明Nacos上的配置已经生效并覆盖了本地配置。
二、注册中心
1 添加注册依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>${alibaba.version}</version> </dependency>
2 配置Nacos发现服务地址,修改后的bootstrap.yml如下
spring: application: name: payment-service cloud: nacos: config: server-addr: 127.0.0.1:8848 discovery: server-addr: 127.0.0.1:8848
3 在启动类上添加注解@EnableDiscoveryClient
@SpringBootApplication @EnableDiscoveryClient public class PaymentServiceApplication {
控制台中输出
nacos registry, payment-service 192.168.75.1:8082 register finished
进入Nacos管理后台,在服务列表中已经可以看到payment-service服务(服务名为spring.application.name)
至此,服务Provider的工作完成,下面来写Consumer。
Consumer为账号服务,提供注册、登录等,其中部分接口需要调用payment-service服务获得余额信息合并后返回给客户端。
新建名称为account-service的SpringBoot应用,依赖、配置文件与payment-service基本相同,仅修改以下两项:
spring.application.name=account-service server.port=8081
业务代码:
User.java
public class User { private int id; private String name; private Balance balance; public User(int id, String name) { this.id = id; this.name = name; } public User() { } }
AccountController.java
@RestController public class AccountController { final static Map<Integer, User> userMap = new HashMap() {{ put(1, new User(1, "张三")); put(2, new User(2, "李四")); put(3, new User(3, "王五")); } }; @RequestMapping("/acc/user") public User getUser(@RequestParam Integer id) { if(id != null && userMap.containsKey(id)) { User user = userMap.get(id); return user; } return new User(0, ""); } }
接下来添加调用payment-service服务的代码。添加相关依赖,如下:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId>
在主类上开启Feign功能
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class AccountServiceApplication {
添加BalanceService接口
@FeignClient(name = "payment-service", fallback = BalanceServiceFallback.class) public interface BalanceService { @RequestMapping(value = "/pay/balance", method = RequestMethod.GET) Balance getBalance(@RequestParam("id") Integer id); }
添加BalanceServiceFallback.java
@Component public class BalanceServiceFallback implements BalanceService { @Override public Balance getBalance(Integer id) { return new Balance(0, 0, 0, "降级"); } }
在account-service中添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>${alibaba.version}</version> </dependency>
在bootstrap.yml中启用
feign: sentinel: enabled: true
修改sleep=5000并测试
降级成功。
代码地址:https://pan.baidu.com/s/1sMd7fJpQWUYnN4M67PBGEg
提取码:0gf0