java服务调用+java注册中心(Eureka)
目录
管理方式
添加包
方便管理
替换 properties
然后添加下面代码
<!-- 统一管理jar包版本 -->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>3.4.0</mybatis.spring.boot.version>
</properties>
<!-- 1、只是声明依赖,并不实际引入,子项目按需声明使用的依赖 -->
<!-- 2、子项目可以继承父项目的 version 和 scope -->
<!-- 3、子项目若指定了 version 和 scope,以子项目为准 -->
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.spring.boot.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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.2.2.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
新建子类
添加jar包
这里 不写版本号使用父类版本号
添加启动程序
添加端口号
新建控制层
注意层级关系
创建完后在新建一个module
名为 provider8001
端口8001
添加启动程序
添加控制层
启动后,可以在Service 里面看见这两个的情况
这里我们访问
http://localhost:81/test?name=%22%E5%BC%A0%E4%B8%89%22
这里就会报错
这里我们就要添加一个config
ContextConfig
这里可以看到我们就不报错了
再次通过
http://localhost:81/test?name=%22%E5%BC%A0%E4%B8%89%22
这个网址访问
就可以访问了
java 注册中心
新建项目
eureka7001
配置项目
项目创建成功后,需要在启动类上加上一个注解(@EnableEurekaServer),标记它是一EurekaServer:
@EnableEurekaServer // 表达当前的项目 是注册中心 服务器
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
// 使用 cloud 依赖
@EnableEurekaServer // 表达当前的项目 是注册中心 服务器
public class Eureka7001 {
public static void main(String[] args) {
SpringApplication.run(Eureka7001.class, args);
}
}
在application.propeties加入配置:
server:
port: 7001
# eureka 默认配置 是向注册中心服务器 注册
# 7001 作为 服务端 自己不用向 自己注册
eureka:
client:
fetch-registry: false
register-with-eureka: false
# 把服务器端口 暴露出去 给其他的子项目使用
service-url:
defaultZone: http://localhost:7001/
配置完成后就可以启动项目了。
浏览器输入localhost:7001,当看见这个页面,就证明已经配置成功了
负载均衡(怎么实现)
8001 配置文件添加代码
添加完 application name 后会在注册中心显示
spring:
datasource:
password: root123
username: root
url: jdbc:mysql://localhost:3306/ebookSys?useSSL=false
driver-class-name: com.mysql.jdbc.Driver
application:
name: provider-service
server:
port: 8001
# 同时启用多个项目 如果端口号 一样 就会出现异常
eureka:
client:
fetch-registry: true
register-with-eureka: true
# 把服务器端口 暴露出去 给其他的子项目使用
service-url:
defaultZone: http://localhost:7001/eureka #单机版
7001 服务端 配置文件需要修改
server:
port: 7001
# eureka 默认配置 是向注册中心服务器 注册
# 7001 作为 服务端 自己不用向 自己注册
eureka:
client:
fetch-registry: false
register-with-eureka: false
# 把服务器端口 暴露出去 给其他的子项目使用
service-url:
defaultZone: http://localhost:7001/eureka
7001 服务端 jar 包
<?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">
<parent>
<artifactId>cloud1009</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka7001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入依赖 cloud注册中心 服务端依赖 server服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
8001 在Controller 里面添加 代码
package com.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// 用户服务 用来操作 用户数据表
@RestController
public class UserController {
@RequestMapping("/login")
public String login(String username, String password) {
System.out.println(username + password);
return "8001 login接口" + username + password;
}
// 获取配置信息里的端口号
@Value("${server.port}")
String port;
@RequestMapping("/myLBtest")
public String myLBtest(){
return port + " myLBtest";
}
}
8001 所用的 jar包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入依赖 cloud注册中心 服务端依赖 server服务端 client客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
</dependencies>
新建 provider8002 provider8003
上面弄完后 复制com 和 配置文件 application.yml 到 provider8002 provider8003 里面去 并修改端口号 为 8002 8003
修改启动类的文件名
然后启动
输入不同的端口下面就会跟着变
扩展 随机版
consumer80 配置文件
游览器默认端口 80
server:
port: 81
# 同时启用多个项目 如果端口号 一样 就会出现异常
eureka:
client:
fetch-registry: true
register-with-eureka: true
# 把服务器端口 暴露出去 给其他的子项目使用
service-url:
defaultZone: http://localhost:7001/eureka # 单机版
spring:
application:
name: consumer-service
使用 jar 包
<?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">
<parent>
<artifactId>cloud1009</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumer80</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<!-- 引入依赖 cloud注册中心 服务端依赖 server服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
controller 层代码
package com.controller;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
import java.util.Random;
@RestController
// RestController = com.controller + responsebody
public class TestController {
// 把spring 容器里面的 对象 自动 装配到当前类中
// restTemplate 首先 要存在spring 容器里面
@Resource
RestTemplate restTemplate;
// 负载均衡 由cloud提供的组件 来 按照特定规则 调用 服务 8001 8002 8003 (随机 轮换)
@RequestMapping("/test")
public String test(String name) {
System.out.println(name);
String url = "http://localhost:8001/login?username=张三&password=123";
// restTemplate.getForObject(url,String.class); 这个是调用8001 服务器 login 接口
String forObject = restTemplate.getForObject(url, String.class);
return "80服务器 test接口" + name + " " + forObject;
}
@Resource
DiscoveryClient discoveryClient;
@RequestMapping("/mylb")
public String mylb() {
// 1 先获取 provider 服务集合
List<ServiceInstance> instances = discoveryClient.getInstances("PROVIDER-SERVICE");
for (ServiceInstance s : instances) {
System.out.println(s.getUri());
System.out.println(s.getHost() + "" + s.getPort());
}
String url = serverUrl(instances);
// restTemplate.getForObject(url,String.class); 这个是调用8001 服务器 login 接口
String forObject = restTemplate.getForObject(url + "/myLBtest", String.class);
return forObject;
}
int count = 1;
// 参数 服务器 集合
// 返回值 其中的一个服务器地址
public String serverUrl(List<ServiceInstance> instances) {
// 所有服务器个数
int size = instances.size();
// System.out.println(size);
System.out.println("第" + count + "次访问服务");
// 顺序版
// int num = 0;
// if (count % size == 1) {
// num = 0;
// } else if (count % size == 2) {
// num = 1;
// } else {
// num = 2;
// }
count++;
// 随机版
Random random = new Random();
int num = random.nextInt(size);
System.out.println(num);
return instances.get(num).getUri().toString();
}
}
然后输入网址,点刷新 每次的结果不一样
负载均衡
创建 module consumer-feign80
导入jar包
所需jar包
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入依赖 cloud注册中心 服务端依赖 server服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
创建启动类
@EnableFeignClients // 开启feign 功能
创建控制层
package com.controller;
import com.service.FeiService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class UserController80 {
@Resource
FeiService feiService;
@RequestMapping("/test")
public String test(){
// 服务调用 feign 不在使用 resttempl 模板
System.out.println("test.....feign");
// 创建 service 层通过配置 feign 来实现调用服务
String login = feiService.login();
return login;
}
}
创建 service 层
package com.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "PROVIDER-SERVICE")
@Component
public interface FeiService {
// 8001 服务 方法头部保持一致
@RequestMapping("/login")
public String login();
}
注意启动是不要开启
consumer80
三种参数的传递
第一种 基本类型参数
首先在80添加测试代码
service添加
注意service 层代码要和8001-8003 的方法保持一致
@RequestMapping("/test1")
public String test1();
在8001 8002 8003 添加测试代码
8002 8003 修改 下面的输出
@RequestMapping("/test1")
public String test1(String name){
System.out.println("8001.." + name);
return "test1 8001" + name;
}
可以看到这里参数并没有传过来
在service 层 和 8001-8003 服务的Controller层 添加 RequestParam 注解
consumer-feign 80 的Controller 层 不需要添加 注解
第二种 地址栏传参
Controller 层添加
这里注意要在上面添加 注解
// 地址栏传参 restful 风格 访问
@RequestMapping("/test2/{id}")
public String test2(@PathVariable("id") Integer id){
System.out.println("test1..."+id);
String test = feiService.test2(id);
return test;
}
FeiService 层
@RequestMapping("/test2/{id}")
public String test2(@PathVariable("id") Integer id);
8001 - 8003 添加
其他的修改 下面输出就行
@RequestMapping("/test2/{id}")
public String test2(@PathVariable("id") Integer id){
System.out.println("8001.." + id);
return "test1 8001 " + id;
}
第三种 对象传参
Controller 层添加
注意这里不需要添加注解
// 3 对象传参
@RequestMapping("/test3")
public String test3(UserInfo userInfo){
System.out.println("test3..."+userInfo);
String test = feiService.test3(userInfo);
return test;
}
FeiService 层
@RequestMapping("/test3")
public String test3(@RequestBody UserInfo userInfo);
8001 - 8003 添加
其他的修改 下面输出就行
@RequestMapping("/test3")
public String test2(@RequestBody UserInfo userInfo){
System.out.println("8001.." + userInfo);
return "test3 8001 " + userInfo;
}
网关
新建module
需要用到的包
<dependencies>
<!-- eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- gateway 网关的包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
新建启动类
Gateway9527
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class Gateway9527 {
public static void main(String[] args) {
SpringApplication.run(Gateway9527.class, args);
}
}
添加配置文件
spring:
application:
name: gateway-service
# 服务名称
server:
port: 9527
# 同时启用多个项目 如果端口号 一样 就会出现异常
eureka:
client:
fetch-registry: true
register-with-eureka: true
# 把服务器端口 暴露出去 给其他的子项目使用
service-url:
defaultZone: http://localhost:7001/eureka #单机版
设置配置信息
GateConfig
package com.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GateConfig {
@Bean
// spring 容器 注入 一个 路由检查对象
// 网关 路由
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
// 1 RouteLocatorBuilder 通过这个build 生成一个 build对象 用api来控制页面访问
// mybatis factory build session mapper 对象
RouteLocatorBuilder.Builder routes = builder.routes();
// routes.route() 方法里面来实际 操作地址的 转换请求访问
// 当页面输入path guonei 地址的时候 网关 就会自动配置这个路径的映射 uri
// 要想实现还需要配置访问规则
routes.route("gate_route1",
r -> r.path("/guonei")
.uri("https://news.baidu.com/"))
.build();
// mvc config 配置 pattern mapping
return routes.build();
// https://news.baidu.com/guonei
// http://localhost:9527/guonei 类似隐藏真实的 服务器地址
}
}
网址拦截
spring:
application:
name: gateway-service
cloud:
gateway:
discovery:
locator:
enabled: true # 打开路由开关
routes: # 路由规则
- id: provider_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8002 #匹配后提供服务的路由地址
uri: lb://PROVIDER-SERVICE
# 匹配的 具体的服务
predicates: # 匹配规则 断言机制 path 只允许 path设置的 访问请求
# 这里 1 改成 * 号 就是不管是test1 test2 依次类推 ,都不能访问
- Path=/test1/**
# uri path 也就说只允许访间8001服务的test1方法
# 1 直接访问 8001 http://localhost:8001/test1?name=123 成功
# 2 http://localhost:9527/test1?name=123 通过网关 成功
# 3 http://localhost:8001/test2/123 test2 成功
# 4 http://localhost:9527/test2/123 失败
# 5,6 80服务器访问 test1 test2 http://localhost:8001/test2/222 都成功
server:
port: 9527
# 同时启用多个项目 如果端口号 一样 就会出现异常
eureka:
client:
fetch-registry: true
register-with-eureka: true
# 把服务器端口 暴露出去 给其他的子项目使用
service-url:
defaultZone: http://localhost:7001/eureka #单机版
这里我们通过9527访问 test1 是可以访问的
访问test2 test3访问不了
时间拦截
这个意思是10:07分前不可以访问,10:07分后可以访问
服务降级
避免前端访问错误
当服务不可用的时候避免前端返回错误最好是服务器返回友好提示服务不可用请稍后再试
需要jar 包
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
我们在 consumer-feign80 里开启 服务降级功能
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients // 开启 feign 功能
@EnableHystrix // 开启 服务降级功能
public class ConsumerFeign80 {
public static void main(String[] args) {
SpringApplication.run(ConsumerFeign80.class,args);
}
}
配置文件中添加
feign:
hystrix:
enabled: true
新建类
HysFeiServiceImpl
package com.service;
import com.model.UserInfo;
import org.springframework.stereotype.Component;
@Component
public class HysFeiServiceImpl implements FeiService{
@Override
public String login() {
return "login 接口 服务不可用,请稍后重试";
}
@Override
public String test1(String name) {
return "test1 接口 服务不可用,请稍后重试 " + name;
}
@Override
public String test2(Integer id) {
return "test2 接口 服务不可用,请稍后重试 " + id;
}
@Override
public String test3(UserInfo userInfo) {
return "test3 接口 服务不可用,请稍后重试 " + userInfo;
}
}
修改FeiService 接口
package com.service;
import com.model.UserInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
//@FeignClient(value = "PROVIDER-SERVICE")
//@FeignClient(value = "GATEWAY-SERVICE")
// feign 和 hystrix 配置
// 降解类 要实现 当前接口 重写 当前类的方法
@FeignClient(value = "GATEWAY-SERVICE",fallback = HysFeiServiceImpl.class)
@Component
public interface FeiService {
// 8001 服务 方法头部保持一致
@RequestMapping("/login")
public String login();
@RequestMapping("/test1")
public String test1(@RequestParam("name") String name);
@RequestMapping("/test2/{id}")
public String test2(@PathVariable("id") Integer id);
@RequestMapping("/test3")
public String test3(@RequestBody UserInfo userInfo);
}
我们访问test1
这里是可以访问的
可以看到test2 test3就不可以访问了