在父工程中加载springcloudalibaba依赖用来管理版本信息
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.5.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
在子工程中添加nacos的依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
更改Nacos的注册端口:
在yml中添加如下配置:
spring:
cloud:
nacos:
server-addr: localhost:8848 #nacos服务地址
就可以完成Nacos注册
集群是指一个服务中运行的多个端口集合在一起构成一个集群,例如将userservice的8080和8081端口设置在上海集群,再将8083和8084设置在杭州集群等。
集群的配置(修改yml文件):
spring:
cloud:
nacos:
discovery:
cluster-name: XX # 集群名称(自定义)
设置负载均衡规则(对于配置的写法与Eureka一样,但传入的包名不同):
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #定义针对于userservice的负载均衡规则
注意这种情况下负载均衡策略为优先在和自己同集群的端口中发送请求,若找不到再去其他端口找,但这样子会在控制台提出一个非同集群的警告
可以在Nacos的控制台中点击Edit来编辑权重信息,权重数值在0-1之间,对应的比例代表着其被随机访问的概率的比例,例如一个1一个0.1,被访问的概率之比就是10比1
将服务的权重调整为0则代表不会有服务进行访问,这种情况适用于服务器升级维护。
在Nacos服务器的命名空间内容中加载新的命名空间,右上角创建命名空间,填写名称和描述,Id自动生成。
在yml配置文件中进行命名空间的配置:
spring:
cloud:
nacos:
discovery:
namespace: d9e54854-e561-4b09-ad00-4624f5b6397d #创建的命名空间的Id
处在不同命名空间的服务不能相互访问
服务消费者会定时从Nacos服务器中拉取服务列表并缓存到Dynamic中。
服务提供者:
另外,若Nacos检测到服务信息有服务无法访问则会立刻将这个信息推送给服务消费者(快)
在yml文件中进行是否为临时实例的配置:
spring:
cloud:
nacos:
discovery:
ephemeral: false #配置为非临时实例,默认为临时实例
同:
异:
配置管理需要实现热更新、及时读取的需求,使用Nacos配置管理进行处理:
在Nacos服务器的配置中心、配置管理中添加:
配置id为:服务名称-dev.yaml:userservice-dev.yaml
勾选对应的格式(yaml):
编写配置文件(Nacos中只需要配置有可能会经常改变的代码,不经常改变的代码不需要部署到Nacos中):
添加依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
创建bootstrap.yml文件(bootstrap.yml文件的读取优先级要高于application.yml):
因为bootstrap的优先级较高,所以在bootstrap定义过的配置就不需要再application中配置了
spring:
application:
name: userservice
profiles:
active: dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
使用@Value和@RefreshScope方式实现热更新
在Controller类上添加@RefreshScope注解
@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController {
热更新原理:会对配置文件进行检测,如果检测发现配置文件有变化则会重新读取配置文件并执行相应的变化。
测试配置获取(在UserController中进行):
@Value("${pattern.dateformat}")
private String dateformat;
@GetMapping("now")
public String now() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
}
创建配置类(config/PatternProperties):
@ConfigurationProperties(prefix = "pattern") //获取配置,获取配置文件中的属性,所有以prefix为前缀,以变量名为后缀的配置都会被获取给对应的属性
@Component //注入为Java的Bean
@Data
public class PatternProperties {
private String dateformat; //获取到pattern.dateformat属性
}
在Controller中通过获取容器中的类进行配置的使用。
@Autowired
public PatternProperties patternProperties;
@GetMapping("now")
public String now() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));
}
有些配置信息在许多环境的搭建、生产和应用过程中都是相同的、我们没必要在所有的配置文件中都进行定义,所以可以定义一个一致的配置文件对这个信息进行定义:
微服务在启动时,Nacos会读取以下两个配置:userservice-dev.yml、userservice.yml
即:服务名-环境名.后缀
其中-dev在开发时会引入,-test在测试时会引入,而普通的userservice.yml会在任意的环境下都被读取进来,所以公共的配置文件可以写在【服务名.yml】文件中。
注意,这个文件是从Nacos服务器中配置的,且这里的Id一定要写.yaml尽量不要简写。
# 测试
pattern:
envSharedValue: 多环境共享属性值
故现有的两个配置文件:userservice.yaml、userservice-dev.yaml中,-dev的配置文件只在开发文件中生效,userservice.yaml在所有的环境中生效(注意在bootstrap中的配置active:dev配置了配置文件的环境名,这个名称必须与Nacos服务器中创建的配置文件的环境名一致)。
同时,文件的环境也可以在服务器右键 -> Edit Configuration -> Active profiles中对环境进行配置:(dev(开发)、test(测试))
注意:如果在多个配置文件中都有针对于同一个属性的配置,那么他们的优先级:服务名-profile.yaml > 服务名.yaml > 本地配置
Nacos在实际应用中都是有多个结点的,这个结点的集合称作Nacos集群,而具体的服务要选取其中之一进行请求处理(负载均衡),这里的负载均衡一般由Nginx实现,而众多的Nacos结点共同访问一整个Mysql集群,这样来实现Nacos的集群,以提高系统的并发响应能力。
由于只有一台电脑,所以集群的ip地址相同,但是具有不同的端口号
配置Nacos节点的IP地址:
打开文件:Nacos -> conf -> cluster.conf.example(将其重命名,去掉.example)并编辑(空文件夹、加入如下例配置,这个ip地址必须是你Nacos配置的Ip地址,这里的192.168.0.108是主机的地址,相当于127.0.0.1但是不要写127.0.0.1,将ip地址固定(网络配置中)并写固定的地址)
192.168.0.108:8845
192.168.0.108:8846
192.168.0.108:8847
编辑conf文件夹下的application.properties
修改端口为三个端口之一并且在后边的复制时将三个不同的Nacos的端口号对应修改
打开spring.datasource.platform属性,以标注数据库
打开db.num属性以标注数据库的数量
打开db.url、db.user.0、db.password.0以标注数据库的连接信息以及账号密码
将nacos重命名为三个结点名并进行复制,移动到需要的文件夹位置。
集群启动,在bin文件夹下使用cmd命令:startup.cmd
注意之前带有-m的命令为单点启动的命令
配置Nginx,进入Nginx的conf文件夹内并编辑Nigix.conf
在http标签内进行如下定义:
upstream nacos-cluster标签定义了nacos的ip地址入口
server标签定义了监听的端口号(以80开头的请求都会被监听)
location /nacos定义了将请求中/nacos的请求都将其拦截,注意尽量只有一个server标签
upstream nacos-cluster {
server 127.0.0.1:8845;
server 127.0.0.1:8846;
server 127.0.0.1:8847;
}
server {
listen 80;
server_name localhost;
location /nacos {
proxy_pass http://nacos-cluster;
}
}
在nginx-1.18.0下通过命令启动start niginx.exe
在userservcie的bootstrap.yaml中修改端口号配置,不再直接访问Nacos的地址,而是访问Nginx负载均衡过的Nacos地址,将端口地址修改为:localhost:80
我们之前采用RestTemplate进行服务远程调用时,代码可读性很差,另外,手动插入url在url路径复杂时会十分不方便,而Feign是一种可以解决上述问题的服务远程调用方式。
Feign是一种声明式的Http客户端,可以帮助我们优雅的实现请求的调用
这个依赖与Feign的其他配置信息都是要在发送服务调用请求的模块中进行的。例如这个例子中就是放在orderservice模块中进行的。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
添加自动装配Feign的注解:
在启动类上添加注解:
@EnableFeignClients
public class OrderApplication {
该注解可以完成开启Feign的AutoConfigutration的功能
添加Feign的接口
创建clients/UserClient接口
//FeignClient注解用来标注这是一个Feign发送请求的注解
//里面的参数代表Nacos注册中心中的服务名,代表要请求哪里的服务
//注意@FeignClient注解也有依赖注入的功能
@FeignClient("userservice")
public interface UserClient {
//这里方法的返回值应该是请求的返回类型
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
测试:在Service中添加测试进行调用:
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
Order order = orderMapper.findById(orderId);
//调用Feign请求并获取到需要的数据
User user = userClient.findById(orderId);
order.setUser(user);
return order;
}
在application.yml文件
` 中添加日志信息配置如下可以修改Feign的日志级别:NONE、BASIC、HEADERS、FULL
# Feign的日志级别
feign:
client:
config:
default:
logger-level: FULL
另外、也可以使用配置类的方式实现日志级别的装配,若使用配置类的方式实现日志修改,则需要在对应的Feign的Clients请求上标注注解(只在这个服务请求中生效)或者在启动类的Feign注解中添加参数(在这个模块的全局生效)
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel() {
return Logger.Level.BASIC;
}
}
局部生效注解:
//FeignClient注解用来标注这是一个Feign发送请求的注解
//里面的参数代表Nacos注册中心中的服务名,代表要请求哪里的服务
//configuration中标注了日志级别信息
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration.class)
public interface UserClient {
//这里方法的返回值应该是请求的返回类型
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
全模块生效注解:
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
//全局生效注解
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
Feign的性能优化主要在于两个方面:
Http请求的发送方式
在Feign中,发送Http请求的默认方式为URLConnection方式,而这种方式不支持连接池,如果能够使用连接池会有更好的性能表现(Apache HttpClient、OKHttp)
降低日志级别
Headers以及Full的日志级别会使客户端生产出更多日志(进行更多监听)故降低日志级别以提升系统整体性能。
将Feign默认Http请求发送方式更改为Apache HttpClient方式:
添加HttpClient依赖:
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-httpclientartifactId>
dependency>
在配置文件中打开HttpClient(默认为true,但需要引入依赖)
#打开Feign的HttpClient
feign:
httpclient:
enabled: true
max-connections: 200 #连接池最大连接数
max-connections-per-route: 50 #单个模块的最大连接数
同时,连接池中最大连接数以及单个模块的最大连接数都需要使用JMeter进行压测之后找到最合适的数值
Fegin有两种实践方式:
Feign的统一管理方式实现:
引入Feign依赖
创建一个Feign-api模块,并将Clients包、config包以及实体类(pojo文件夹下)导入到这个包中,已导入的模块可以删除。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
向需要Feign的地方引入自己创建的API
<dependency>
<groupId>cn.itcast.demogroupId>
<artifactId>feign-apiartifactId>
<version>1.0version>
dependency>
之后将需要的类进行导入后,还要将Feign的导入加入到OrderService中,其有两种实现方式:
将整个Feign的包进行指定,但这样会将所有的Feign组件全部导入,会导入不需要的类
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
指定FeignClient的字节码,这样就只会导入需要的Feign(建议方式)
@EnableFeignClients(clients = {UserClient.class})
实践效果:
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class, clients = {UserClient.class})
public class OrderApplication {