构建Cloud
父工程依赖
<?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>org.example</groupId>
<artifactId>cloud-demo2</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>common-api</module>
<module>user-service</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR10</spring-cloud.version>
<mysql.version>8.0.27</mysql.version>
<mybatis.version>2.1.1</mybatis.version>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<spring.cloud.alibaba>2.2.5.RELEASE</spring.cloud.alibaba>
</properties>
<dependencyManagement>
<dependencies>
<!-- springCloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springCloudAlibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>
构建公共模块
构建user-service(生产者)
依赖
<dependencies>
<!--nacos客户端依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Eureka客户端-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.example</groupId>
<artifactId>common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<finalName>userserver</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
启动类
@SpringBootApplication
@MapperScan("com.example.userservice.mapper")
//@MapperScan({"com.example.userservice.mapper", "com.example.anotherpackage.mapper"})
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
mapper
public interface UserMapper {
@Select("select * from tb_user where id = #{id}")
User findById(Long id);
}
service
public interface UserService {
User queryById(Long id);
}
impl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public User queryById(Long id) {
return userMapper.findById(id);
}
}
controller
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/serverport")
public String serverport(){
return serverPort;
}
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id) {
User user = userService.queryById(id);
user.setServerPort(serverPort);
return user;
}
}
application.yml
server:
port: 8081
spring:
application:
name: user-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://xxxxxx:3306/cloud-user?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=GMT%2B8&useCursorFetch=true
username: xxxxx
password: xxxxxxx
#默认就是8848
cloud:
nacos:
server-addr: localhost:8848
#redis:
#port: 6379
#database: 0
#password: 1111111111111
#ssl: false
###################################################
##redis 集群环境配置
#cluster:
# nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003
# commandTimeout: 5000
mybatis:
type-aliases-package: com.example.userservice.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.example.userservice.mapper: debug
#eureka:
# client:
# #表示是否将自己注册进EurekaServer默认为true
# register-with-eureka: true
# #是否从EurekaServer抓取已有的注册信息默认true 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
# fetchRegistry: true
# service-url:
# defaultZone: http://localhost:7001/eureka
构建order-service(消费者)
依赖
<dependencies>
<!--nacos客户端依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Eureka客户端-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.example</groupId>
<artifactId>common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<finalName>orderserver</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
启动类
@MapperScan("com.example.orderservice.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
@LoadBalanced//默认用的Ribbon的轮询策略
public RestTemplate restTemplate(){
return new RestTemplate();
}
// @Bean
// public IRule randomRule(){
// return new RandomRule();
// }
}
mapper
public interface OrderMapper {
@Select("select * from tb_order where id = #{id}")
Order findById(Long id);
}
service
public interface OrderService {
Order queryOrderById(Long orderId);
}
impl
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplate发起http请求,查询用户
// 2.1.url路径
String url = "http://user-service/user/" + order.getUserId();
// 2.2.发送http请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
}
控制器
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
// 根据id查询订单并返回
return orderService.queryOrderById(orderId);
}
}
application.yml
server:
port: 80
spring:
application:
name: order-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://xxxxxx:3306/cloud-order?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=GMT%2B8&useCursorFetch=true
username: xxxxx
password: xxxxxx
#默认就是8848
cloud:
nacos:
server-addr: localhost:8848
#redis:
#port: 6379
#database: 0
#password: 1111111111111
#ssl: false
###################################################
##redis 集群环境配置
#cluster:
# nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003
# commandTimeout: 5000
mybatis:
type-aliases-package: com.example.orderservice.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.example.userservice.mapper: debug
#将服务注册到Eureka
#eureka:
# client:
# #表示是否将自己注册进EurekaServer默认为true
# register-with-eureka: true
# #是否从EurekaServer抓取已有的注册信息默认true 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
# fetchRegistry: true
# service-url:
# defaultZone: http://localhost:7001/eureka
# 全局配置
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
# 特定服务配置
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
勾下 启动下 再去掉
Nacos安装
GitHub主页:GitHub - alibaba/nacos: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications. - alibaba/nacoshttps://github.com/alibaba/nacos
GitHub的Release下载页:Releases · alibaba/nacos · GitHuban easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications. - Releases · alibaba/nacoshttps://github.com/alibaba/nacos/releases
此版本用例1.4.1
Release 1.4.1 (Jan 15, 2021) · alibaba/nacos · GitHuban easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications. - Release 1.4.1 (Jan 15, 2021) · alibaba/nacoshttps://github.com/alibaba/nacos/releases/tag/1.4.1
单机启动
startup.cmd -m standalone
http://localhost:8848/nacos/index.html
访问 localhost:8848/nacos/index.html 账密 nacos
这样Nacos服务端就启动了
Nacos服务注册
且一样有Ribbon的负载均衡
Nacos服务多级存储模型
点击详情
配置服务集群
例如:我想在将userservice 8081 配置称SH 将8082和8083配置成HZ
那么现在我希望orderserver访问HZ的集群
这样的话 orderService会不会默认先访问HZ的集群
发现并没有 还是3个都轮询访问 原因在于Ribbon
重启orderservice
发现只会访问8082和8083 而且是随机的 此时我将8082和8083两个服务停掉 就会访问8081 看样子集群配置是个优先的选项 但是当集群无法访问 会自动切换到跨集群
Nacos权重设置
默认都是1 随机访问 可以在0到1之间设置
如果我将8083设置成0.1
因为现在消费者也是HZ集群
明显现在调用 大部分都是8082
如果设置成0 那么就不会被访问 有什么好处呢??比如服务器维护 nice
Nacos环境隔离
Nacos的服务存储和数据存储的最外层namespace
namespace-->group--->服务--->集群-->实例
下面新建个命名空间
如何将服务放到自定义的命名空间里 需要代码层面的配置
重启
那么这样会产生什么效果呢????
无法访问了 也就是说在不同的namespace下 无法访问
Nacos的临时实例和非临时实例
临时实例采用心跳向Nacos汇报 挂了挂了会推送给消费者意思不健康了并且会马上剔除
非临时实例是Nacos主动询问 挂了会推送给消费者意思不健康了 不会剔除(除非你在Nacos管理页面手动删除)而是等待修复完成
如何注册非临时实例(默认都是临时实例)
Nacos统一配置管理
这里涉及一个Boot启动 读取配置文件的优先级问题 因为原先Nacos的配置是保存在application.yml中的,那么现在需要Nacos来管理配置文件, 那前提是不是需要知道Nacos配置的服务器地址在哪里,不知道怎么从nacos上读配置文件
如下图 因此建议将Nacos的配置放在bootstrao.yml中
引入Naocis配置管理客户端的依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
userService和orderService都引入
配置时注意缩进
我这里就把application.yml全部注释了
理论讲 bootstarp里配置的application可以删除 当然可以不删但别不一样 不然覆盖了
我这里整个注释了 就完全按Nacos上配置的来
Nacos配置热更新
Nacos配置共享
Feign调用
首先在调用方导入Feign依赖
启动类加上@EnableFeignClients
原先是通过restTemplate
下面用Feign的方式 声明feign调用接口
修改原先的调用方法 编程体验不佳......
Feign本身就集成了负载均衡功能 依赖中带了Ribbon
自定义Feign配置
主要是配置Feign调用日志级别
日志配置方式
这样配置没成功
用配置类 这个类上不要加@Configuration
Feign性能优化
Feign使用最佳实践
这种方式Spring官方不推荐 会造成紧耦合 API成面形成了紧耦合
也存在问题 就是会把用不到的也引入进来
方式二的实现
依赖
这也有点麻烦 要把实体类转掉 可以把feign-api和common模块合并
@Autowired private UserClient userClient;
这个也肯定用不了了 那么就用@Bean 没试过 试试呗