----------------------------SpringCloudAlibaba----------------------------
SpringCloudAlibaba组件说明:
·服务注册与发现:
适配SpringCloud服务注册与发现标准,默认集成了Ribbon的支持
·服务限流降级:
默认支持Servlet、Feign、RestTemplate、Dubbon、和RocketMQ限流降级功能的接入,可以在运行
时通过控制台实时修改限流降级规则,还支持查看限流降级Metrics监控。
·分布式配置管理:
支持分布式系统中的外部化配置,配置更改时自动刷新。
·消息驱动能力:
基于SpringCloudStream为微服务应用构建消息驱动能力。
·分布式事务:
使用@GlobalTransactional注解,高效并且对业务零侵入地解决分布式事务问题。。
·阿里云对象存储:
阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、
任何地点存储和任意类型的数据。
·分布式任务调度:
提供秒级、精准、高可靠、高可用的定时(基于Cron表达式)任务调度服务。同时提供分布式
的任务执行模板,如网格任务。网格任务支持海量子任务均匀分配给所有Worker(schedulerx-client)上执行。
·阿里云短信服务:
覆盖全球的短信服务,友好,高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
----------------------------Nacos(赖抠死)服务注册与发现----------------------------
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
官方文档:https://nacos.io/zh-cn/
1.从github上下源码包
2.启动服务
Linux/Unix/Mac
启动命令(standalone代表着单机模式运行,非集群模式):
sh startup.sh -m standalone
如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:
bash startup.sh -m standalone
Windows
启动命令:
cmd startup.cmd
或者双击startup.cmd运行文件。
端口配置是server.port=8848
---------------------------nacos概述--------------------------
微服务架构中问题:
1.这么多服务,客户端如何访问?
2.这么多服务,服务之间如何通信?
3.这么多服务,如何治理?
4.服务挂了,怎么办?
SpringCloud是一套生态,为了解决微服务架构遇到的问题
SpringCloudNetflix
RestTemplate 远程调用
Eureka高可用组件:注册中心,发现服务
Ribbon+Eureka实现 服务调用 负载均衡
Feign实现服务调用
Hystrix 解决服务熔断、服务降级
Zuul网关、进入统一端口调用
Config配置中心
SpringCloudBus 消息总线
SpringCloudSleuth 链路追踪
ApacheDubbonZookeeper
Dubbon高性能、轻量级的开源Java RPC框架
服务注册注册与发现:Zookeeper
API网关,没有。使用第三方或自己实现
熔断机制没有,使用第三方Hystrix
SpringCloudAlibaba
Sentinel代替了SpiringCloud的Netflix的Hystrix
Ribbon+Nacos实现服务调用与负载均衡
Nacos代替了SpringCloudNetflix的Config配置中心以及Eureka注册和发现
Gateway代替了SpringCloudNetflix的Zuul网关
OpenFeign代替了Feign服务调用
SkyWalking代替了Sleuth链路追踪
-------------------环境搭建----------------------
1、创建一个poject,作为父工程
groupId:con.can.alibaba
artifactId:springcloud-alibaba
1.2、POM添加版本依赖锁定
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>con.can.alibaba</groupId>
<artifactId>springcloud-alibaba</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>shop-common</module>
<module>show-user</module>
</modules>
<!-- 父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<!-- 依赖版本的锁定 -->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<spring-alibaba.version>2.1.0.RELEASE</spring-alibaba.version>
</properties>
<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>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
版本对应
---------------Spring Cloud Version---------------
- Spring Cloud Greenwich
- Spring Cloud Finchley
- Spring Cloud Edgware
---------------Spring Cloud Alibaba Version--------------- - 2.1.0.RELEASE
- 2.0.0.RELEASE
- 1.5.0.RELEASE
---------------Spring Boot Version--------------- - 2.1.X.RELEASE
- 2.0.X.RELEASE
- 1.5.X.RELEASE
2、创建基础模块
2.1、创建Module
groupId:con.can.alibaba
artifactId:shop-common
2.2、POM引入依赖
<!-- 依赖 -->
<dependencies>
<!-- jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- JSON序列化 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- mybatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
</dependencies>
2.3、shop-common模块下创建三个实体类
包名:com.can.pojo
2.4、第一个实体类 User.java
package com.can.pojo;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* 用户
*/
@Entity(name="shop_user") //是实体类跟数据库表名的对应
@Data
public class User {
@Id//表示是主键
//TABLE:使用一个特定的数据库表格来保存主键。
//SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
//IDENTITY:主键由数据库自动生成(主要是自动增长型)
//AUTO:主键由程序控制。
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键生成策略(GenerationType.IDENTITY表示此主键是数据库自增)
private Integer uid; //主键
private String username; //用户名
private String password; //密码
private String telephone; //手机号
}
2.5、第二个实体类 Product.java
package com.can.pojo;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* 商品
*/
@Entity(name = "shop_product")
@Data
public class Product {
@Id
//TABLE:使用一个特定的数据库表格来保存主键。
//SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
//IDENTITY:主键由数据库自动生成(主要是自动增长型)
//AUTO:主键由程序控制。
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer pid; //主键
private String pname; //商品名称
private Double pprice; //商品价格
private Integer stock; //库存
}
2.6、第三个实体类 Order.java
package com.can.pojo;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* 订单
*/
@Entity(name = "shop_order")
@Data
public class Order {
@Id
//TABLE:使用一个特定的数据库表格来保存主键。
//SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
//IDENTITY:主键由数据库自动生成(主要是自动增长型)
//AUTO:主键由程序控制。
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long oid; //订单id
//用户
private Integer uid; //用户id
private String username; //用户名
//商品
private Integer pid; //商品Id
private String pname; //商品名称
private Double pprice; //商品价格
//数量
private Integer number; //购买数量
}
3、创建微服务
步骤:
1.创建模块,导入依赖
2.创建SpringBoot主类
3.加入配置类
4.创建必要的接口和实现类(controller service dao)
3.1、创建用户微服务
1.新建一个Moudel
2.artifactId:shop-user
3.java下创建包:com.can
4.新建启动类:UserApplication
package com.can;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
5.resources编写配置文件application.yml
server:
port: 8071
spring:
application:
name: server-user
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: root
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
hibernate:
ddl-auto: update
show-sql: true
数据库得先创建好
然后编写dao,service,controller
controller:
package com.can.controller;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
}
DAO:
package com.can.dao;
import com.can.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User,Integer> {
}
service:
package com.can.service;
public interface UserService {
}
serviceImpl:
package com.can.service.imp;
import com.can.service.UserService;
public class UserServiceImpl implements UserService {
}
3.2、创建商品微服务shop-product和订单微服务shop-order
跟用户微服务步骤一样,直接复制粘贴
用户微服务微服务shop-user端口号:8071
商品微服务shop-product端口号:8081
订单微服务shop-order端口号:8091
3.3、商品微服务调用方法测试
1.在controller添加查询商品信息方法
package com.can.controller;
import com.alibaba.fastjson.JSON;
import com.can.pojo.Product;
import com.can.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class ProductController {
@Autowired
private ProductService productService;
//查询商品信息
@RequestMapping("/product/{pid}")
public Product queryProductById(@PathVariable("pid") Integer pid){
log.info("进行了{}号商品信息得查询",pid);
Product product = productService.findByPid(pid);
log.info("商品信息查询成功,内容为{}", JSON.toJSONString(product));
return product;
}
}
Service接口添加方法
package com.can.service;
import com.can.pojo.Product;
public interface ProductService {
//根据Pid查询商品信息
Product findByPid(Integer pid);
}
ProductService实现类
package com.can.service.imp;
import com.can.dao.ProductDao;
import com.can.pojo.Product;
import com.can.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public Product findByPid(Integer pid) {
return productDao.findById(pid).get();
}
}
然后启动商品微服务
执行url http://localhost:8081/product/1
调用成功即可
4、编写Order微服务
1.Controller添加下单方法
package com.can.controller;
import com.alibaba.fastjson.JSON;
import com.can.pojo.Order;
import com.can.pojo.Product;
import com.can.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
@RequestMapping("/order/prod/{pid}")
public Order queryOrder(@PathVariable("pid") Integer pid){
log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
//调用商品微服务,查询商品信息
Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//下单(创建订单)
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户1");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.createOrder(order);
log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
return order;
}
}
Service:
package com.can.service;
import com.can.pojo.Order;
public interface OrderService {
void createOrder(Order order);
}
Service实现类:
package com.can.service.imp;
import com.can.dao.OrderDao;
import com.can.pojo.Order;
import com.can.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Override
public void createOrder(Order order) {
orderDao.save(order);
}
}
dao:
package com.can.dao;
import com.can.pojo.Order;
import com.can.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderDao extends JpaRepository<Order,Integer> {
}
5、但是这种方式调用服务者会有很大的问题
问题:
//1.一旦服务提供者的地址信息变化了,我们就不得不去修改服务调用者的java代码
//2.提供者做了集群,服务调用者一方无法实现负载均衡去调用
//3.一旦微服务变得越来越多,如何来管理这个服务清单就成了问题
这时候就需要一个服务治理者,服务提供者向服务治理者进行一个注册,然后服务消费者也去服务治理者获取服务信息
通过注册中心动态实现服务治理。
6、Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是SpringCloudAlibaba
组件之一、负责服务注册发现和服务配置,可以这样认为nacos=eureka+config。
6.1、Nacos简介
Nacos致力于帮助n您发现、配置和管理微服务。Nacos提供了一组简单易用的特性集,帮助n您快速实现动态
服务发现、服务配置、服务元数据及流量管理。
nacos的作用就是一个注册中心,用来管理注册上来的各个微服务。
6.2、向服务注册到注册中心nacos中
1.打开之前下载的nacos,默认端口是8848
2.浏览器请求 http://192.168.137.1:8848/nacos/index.html查看是否启动成功
3.在商品product微服务POM中添加nacos客户端相关依赖
<!-- Nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
4.在启动类添加注解@EnableDiscoveryClient //开启用于被注册中心发现
@EnableEurekaClient是只针对于eureka注册中心使用,如果是其他注册中心则使用@EnableDiscoveryClient
在高版本中不加这两个注解也可以被注册到注册中心中了
如果不向被注册中心注册到服务列表中可以通过两种方式
1.application配置文件中添加
spring:
cloud:
service-registry:
auto-registration:
enabled: false
2.在启动类@EnableDiscoveryClient(autoRegister = false)添加autoRegister = false即可
5.application.yml中添加nacaos配置
cloud: #nacos注册中心ip
nacos:
discovery:
server-addr: 127.0.0.1:8848
6.然后启动Product微服务
7.在http://192.168.137.1:8848/nacos/index.html的Nacos的Ui界面的服务管理-服务列表中查询就可以看见商品微服务注册到服务端了
8.将订单微服务也注册到nacaos注册中心,跟商品微服务是同样的步骤1.依赖2.启动类注解3.application.yml配置文件添加nacos配置
7、通过DiscoveryClient动态实现服务调用者调用提供者
1.打开Order订单微服务controller
package com.can.controller;
import com.alibaba.fastjson.JSON;
import com.can.pojo.Order;
import com.can.pojo.Product;
import com.can.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
//获取一些配置的信息,得到具体的微服务!
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/order/prod/{pid}")
public Order queryOrder(@PathVariable("pid") Integer pid){
log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
// Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
List<ServiceInstance> instances = discoveryClient.getInstances("server-product-8081");
ServiceInstance service;
Product product = null;
if (instances!=null){
service = instances.get(0);
product =
restTemplate.getForObject(
"http://"+service.getHost()+":"+service.getPort()+"/product/"+pid,Product.class);
}
log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//下单(创建订单)
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户1");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.createOrder(order);
log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
return order;
}
//解决问题前的实现方法
// public Order queryOrder(@PathVariable("pid") Integer pid){
// log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
// //调用商品微服务,查询商品信息
// //问题:
// //1.一旦服务提供者的地址信息变化了,我们就不得不去修改服务调用者的java代码
// //2.提供者做了集群,服务调用者一方无法实现负载均衡去调用
// //3.一旦微服务变得越来越多,如何来管理这个服务清单就成了问题
// Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
// log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//
// //下单(创建订单)
// Order order = new Order();
// order.setUid(1);
// order.setUsername("测试用户1");
// order.setPid(product.getPid());
// order.setPname(product.getPname());
// order.setPprice(product.getPprice());
// order.setNumber(1);
//
// orderService.createOrder(order);
// log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
// return order;
// }
}
8、上面问题解决了但是又有了新的问题,上面通过DiscoveryClient服务名获取到服务信息,但是获取的服务可能是多个集群的情况
这时候就要考虑到负载均衡了
1.所以我们要添加一个SpringBoot
2.通过idea打开Edit Configurations
3.复制一份ProductApplication
4.VM options修改为-Dserver.port=8082
5.保存,启动,nacosUi界面查看服务是否被注册
8.1、接下来就可以实现负载均衡,解决使用哪个服务
8.2、自定义负载均衡
package com.can.controller;
import com.alibaba.fastjson.JSON;
import com.can.pojo.Order;
import com.can.pojo.Product;
import com.can.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Random;
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
//获取一些配置的信息,得到具体的微服务!
@Autowired
private DiscoveryClient discoveryClient;
//下单--自定义负载均衡
@RequestMapping("/order/prod/{pid}")
public Order queryOrder(@PathVariable("pid") Integer pid){
log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
// Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
//通过访问名 获取到服务信息
List<ServiceInstance> instances = discoveryClient.getInstances("server-product-8081");
//随机选择
ServiceInstance service;
Product product = null;
if (instances!=null){
int index = new Random().nextInt(instances.size());
service = instances.get(index);
product =
restTemplate.getForObject(
"http://"+service.getHost()+":"+service.getPort()+"/product/"+pid,Product.class);
}
log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//下单(创建订单)
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户1");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.createOrder(order);
log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
return order;
}
//通过DiscoveryClient获取服务信息,解决动态调用服务的问题
// @RequestMapping("/order/prod/{pid}")
// public Order queryOrder(@PathVariable("pid") Integer pid){
// log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
//
Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
//
// //通过访问名 获取到服务信息
// List<ServiceInstance> instances = discoveryClient.getInstances("server-product-8081");
// ServiceInstance service;
// Product product = null;
// if (instances!=null){
// service = instances.get(0);
// product =
// restTemplate.getForObject(
// "http://"+service.getHost()+":"+service.getPort()+"/product/"+pid,Product.class);
// }
//
// log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//
// //下单(创建订单)
// Order order = new Order();
// order.setUid(1);
// order.setUsername("测试用户1");
// order.setPid(product.getPid());
// order.setPname(product.getPname());
// order.setPprice(product.getPprice());
// order.setNumber(1);
//
// orderService.createOrder(order);
// log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
// return order;
// }
//解决问题前的实现方法
// public Order queryOrder(@PathVariable("pid") Integer pid){
// log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
// //调用商品微服务,查询商品信息
// //问题:
// //1.一旦服务提供者的地址信息变化了,我们就不得不去修改服务调用者的java代码
// //2.提供者做了集群,服务调用者一方无法实现负载均衡去调用
// //3.一旦微服务变得越来越多,如何来管理这个服务清单就成了问题
// Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
// log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//
// //下单(创建订单)
// Order order = new Order();
// order.setUid(1);
// order.setUsername("测试用户1");
// order.setPid(product.getPid());
// order.setPname(product.getPname());
// order.setPprice(product.getPprice());
// order.setNumber(1);
//
// orderService.createOrder(order);
// log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
// return order;
// }
}
8.3、基于Ribbon实现负载均衡
Ribbon是SpringCloud的一个组件,它可以使用一个注解就能轻松的搞定负载均衡
1.第一步在RestTemplate的生成方法上添加LoadBalanced注解
package com.can;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
//autoRegister = false代表不被注册中心注册到服务列表
//@EnableDiscoveryClient(autoRegister = false)
@EnableDiscoveryClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
@LoadBalanced //基于Ribbon负载均衡的注解
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.修改调用方式,有了Ribbon可以直接使用服务名(spring.application.name)来调用方法了
Ribbon负载均衡,默认策略是 轮询
package com.can.controller;
import com.alibaba.fastjson.JSON;
import com.can.pojo.Order;
import com.can.pojo.Product;
import com.can.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
//获取一些配置的信息,得到具体的微服务!
@Autowired
private DiscoveryClient discoveryClient;
//下单--Ribbon负载均衡(默认是轮询)
@RequestMapping("/order/prod/{pid}")
public Order queryOrder(@PathVariable("pid") Integer pid){
log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
String url = "http://server-product-8081";
Product product =
restTemplate.getForObject(
url+"/product/"+pid,Product.class);
log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//下单(创建订单)
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户1");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.createOrder(order);
log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
return order;
}
// //下单--自定义负载均衡
// @RequestMapping("/order/prod/{pid}")
// public Order queryOrder(@PathVariable("pid") Integer pid){
// log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
//
Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
//
// //通过访问名 获取到服务信息
// List<ServiceInstance> instances = discoveryClient.getInstances("server-product-8081");
// //随机选择
//
// ServiceInstance service;
// Product product = null;
// if (instances!=null){
// int index = new Random().nextInt(instances.size());
// service = instances.get(index);
// product =
// restTemplate.getForObject(
// "http://"+service.getHost()+":"+service.getPort()+"/product/"+pid,Product.class);
// }
//
// log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//
// //下单(创建订单)
// Order order = new Order();
// order.setUid(1);
// order.setUsername("测试用户1");
// order.setPid(product.getPid());
// order.setPname(product.getPname());
// order.setPprice(product.getPprice());
// order.setNumber(1);
//
// orderService.createOrder(order);
// log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
// return order;
// }
//通过DiscoveryClient获取服务信息,解决动态调用服务的问题
// @RequestMapping("/order/prod/{pid}")
// public Order queryOrder(@PathVariable("pid") Integer pid){
// log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
//
Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
//
// //通过访问名 获取到服务信息
// List<ServiceInstance> instances = discoveryClient.getInstances("server-product-8081");
// ServiceInstance service;
// Product product = null;
// if (instances!=null){
// service = instances.get(0);
// product =
// restTemplate.getForObject(
// "http://"+service.getHost()+":"+service.getPort()+"/product/"+pid,Product.class);
// }
//
// log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//
// //下单(创建订单)
// Order order = new Order();
// order.setUid(1);
// order.setUsername("测试用户1");
// order.setPid(product.getPid());
// order.setPname(product.getPname());
// order.setPprice(product.getPprice());
// order.setNumber(1);
//
// orderService.createOrder(order);
// log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
// return order;
// }
//解决问题前的实现方法
// public Order queryOrder(@PathVariable("pid") Integer pid){
// log.info("{}号商品的下单请求,并调用商品微服务查询商品信息",pid);
// //调用商品微服务,查询商品信息
// //问题:
// //1.一旦服务提供者的地址信息变化了,我们就不得不去修改服务调用者的java代码
// //2.提供者做了集群,服务调用者一方无法实现负载均衡去调用
// //3.一旦微服务变得越来越多,如何来管理这个服务清单就成了问题
// Product product = restTemplate.getForObject("http://localhost:8081/product/"+pid,Product.class);
// log.info("查询到{}号商品信息,内容是{}",pid, JSON.toJSONString(product));
//
// //下单(创建订单)
// Order order = new Order();
// order.setUid(1);
// order.setUsername("测试用户1");
// order.setPid(product.getPid());
// order.setPname(product.getPname());
// order.setPprice(product.getPprice());
// order.setNumber(1);
//
// orderService.createOrder(order);
// log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order));
// return order;
// }
}
3.如果需要其他策略就在yml中配置
server-product-8081: #调用的提供者的名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #负载均衡的规则,表示加权规则,yml配置优先级第一,Java代码第二,默认的最后
8.4、基于Feign实现服务调用
1.Feign是SpringCloud提供的一个声明式的伪Http客户端,它使得调用远程服务就像调用本地服务一样简单,
只需要创建一个接口并添加一个注解即可。
2.Nacos很好得兼容了Feign、Feign默认集成了Ribbon,所以在Nacos下使用Feign默认就实现了负载均衡得效果.
3.Feign的使用
3.1.加入Feign的依赖
<!-- Feign依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.2.启动上添加注解 @EnableFeignClients//开启feign客户端
package com.can;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
//autoRegister = false代表不被注册中心注册到服务列表
//@EnableDiscoveryClient(autoRegister = false)
@EnableDiscoveryClient
@EnableFeignClients//开启feign客户端
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
@LoadBalanced //基于Ribbon负载均衡的注解
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3.3、新增一个Feign接口ProductService,用于实现Feign服务调用
package com.can.service;
import com.can.pojo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "server-product-8081")
//@FeignClient:
//value用于指定 Nacos下的哪个微服务
public interface ProductService {
@RequestMapping("/product/{pid}") //指定请求的URL
Product findByPid(@PathVariable("pid") Integer pid);
}
3.3.修改订单微服务的controller调用方法
package com.can.controller;
import com.alibaba.fastjson.JSON;
import com.can.pojo.Order;
import com.can.pojo.Product;