SpringCloud基本入门知识
QQ交流群:797156985
如需破解jerbrant全家桶,请加群:272712006
破解网址: https://www.jianshu.com/p/133af2e4fe3f
请关注公众号:窗前居士
一、创建一个普通的maven工程(最普通的那种),然后清除里面的src目录。
二、右击工程名,创建SpringBoot工程的子module
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency> 添加配置
spring.application.name=eureka
server.port=8001
#表示当前项目不要注册
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false 启动类上添加Eureka服务注解
@EnableEurekaServer
右击工程名,添加新的SpringBoot模块provider
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> 添加配置
spring.application.name=provider
server.port=8003
eureka.client.service-url.defaultZone=http://localhost:8001/eureka首先在provider里添加一个接口
@RestController
public class HelloController
{
@GetMapping("/hello")
public String hello(){
return "hello";
}
}然后我们在创建一个消费者consumer,来消费provider里的接口
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>添加配置
spring.application.name=consumer
server.port=8004
eureka.client.service-url.defaultZone=http://localhost:8001/eureka调用provider代码
使用HttpURLConnection发起请求,请求地址固定~~
@RestController
public class UserController
{
HttpURLConnection conn=null;
@GetMapping("/hello1")
public String hello() throws MalformedURLException
{
try
{
URL url = new URL("http://localhost:8003/hello");
conn= (HttpURLConnection) url.openConnection();
if(conn.getResponseCode()==200){
final BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final String s = br.readLine();
br.close();
return s;
}
}
catch (IOException e)
{
e.printStackTrace();
}
return "err";
}
}最后浏览器结果
可以看出,这样我们就可以消费服务端提供的接口,但实际上这是写死,是不可能这么写的,否则一个地方要改其他很多地方都要改,所以地址要动态获取。
旧版本的Eureka需要在服务启动类加上@EnableEurekaClient注解,新版的不需要
代码改造
借助Eureka Client的DiscoveryClient,我们可以从Eureka Server中查询到服务的详细信息
@GetMapping("/hello2")
public String hello2() throws MalformedURLException
{
//他返回的是一个list集合,因为你有可能是集群化部署。
final List<ServiceInstance> provider = discoveryClient.getInstances("provider");
final ServiceInstance serviceInstance = provider.get(0);
final String host = serviceInstance.getHost();//host主机名
final int port = serviceInstance.getPort();//端口名
try
{
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("http://")
.append(host)
.append(":")
.append(port)
.append("/hello");
URL url = new URL(stringBuffer.toString());
conn = (HttpURLConnection) url.openConnection();
if (conn.getResponseCode() == 200)
{
final BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final String s = br.readLine();
br.close();
return s;
}
}
catch (IOException e)
{
e.printStackTrace();
}
return "err";
}DiscoveryClient查询到的服务列表是一个集合,因为服务在部署过程中,可能是集群化部署。
集群化部署
一、改造服务提供者
@RestController
public class HelloController
{
@Value("${server.port}")
Integer port;
@GetMapping("/hello")
public String hello()
{
return "hello"+port;
}
}二、打包服务提供者,为了开通不同端口
java -jar provider-0.0.1-SNAPSHOT.jar --server.port=1111
三、打开注册中心,看是否成功注册
这样的话,DiscoveryClient里的服务实例就不再是一个了,而是两个。
四、手动实现负载均衡
int count=0;
@GetMapping("/hello3")
public String hello3()
{
//他返回的是一个list集合,因为你有可能是集群化部署。
final List<ServiceInstance> provider = discoveryClient.getInstances("provider");
final ServiceInstance serviceInstance = provider.get((count++) % provider.size());
final String host = serviceInstance.getHost();//host主机名
final int port = serviceInstance.getPort();//端口名
try
{
final StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("http://")
.append(host)
.append(":")
.append(port)
.append("/hello");
URL url = new URL(stringBuffer.toString());
conn = (HttpURLConnection) url.openConnection();
if (conn.getResponseCode() == 200)
{
final BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final String s = br.readLine();
br.close();
return s;
}
}
catch (IOException e)
{
e.printStackTrace();
}
return "err";
}访问端口 http://localhost:8004/hello3 ,返回的结果,你会很清楚的看见两个端口来回切换。
一、把RestTemplate注册到spring容器中
@Bean
public RestTemplate restTemplateOne()
{
return new RestTemplate();
}
@Bean
@LoadBalanced //开启负载均衡,默认算法为轮询
public RestTemplate restTemplate()
{
return new RestTemplate();
}二、在客户端的控制器里调用服务端接口
@Autowired
@Qualifier("restTemplateOne") //作区分的,基础东西,不解释
RestTemplate restTemplateOne;
@GetMapping("/hello2")
public String hello2()
{
//他返回的是一个list集合,因为你有可能是集群化部署。
final List<ServiceInstance> provider = discoveryClient.getInstances("provider");
final ServiceInstance serviceInstance = provider.get(0);
final String host = serviceInstance.getHost();//host主机名
final int port = serviceInstance.getPort();//端口名
final StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("http://")
.append(host)
.append(":")
.append(port)
.append("/hello");
final String s = restTemplateOne.getForObject(stringBuffer.toString(), String.class);
return s;
}轮询调用
@GetMapping("/hello3")
public String hello3()
{
return restTemplate.getForObject("http://provider/hello", String.class);
}会发现,实现效果都一样,但是代码量明显小了很多。
这里我为什么要定义两个bean呢?这两个RestTemplate是不一样的,其中一个调用的是Http的服务,另一个调用的是注册中心的服务,混用的话,解析provider的时候会出错。
一、首先要在provider中定义一个接口
@GetMapping("/hello2")
public String hello2(String name)
{
return name;
}二、然后在consumer中调用
@GetMapping("/hello4")
public void hello4()
{
String s = restTemplate.getForObject("http://provider/hello2?name={1}",
String.class,"leo");
System.out.println(s);
ResponseEntity<String> s1 = restTemplate.getForEntity("http://provider/hello2?name={1}",String.class, "leo");
String body = s1.getBody();
System.out.println(body);
final HttpStatus statusCode = s1.getStatusCode();
System.out.println(statusCode);
}
//-------控制台打印结果---------
//leo
//leo
//200 OK这里有一个注意的地方,getForObject和getForEntity传参的时候,用数字做一种类似于占位符的一种
String s;
HashMap<String, Object> map = new HashMap<>();
map.put("name", "张三");
s = restTemplate.getForObject("http://provider/hello2?name={name}",
String.class, map);
//另一种传参方式一、首先添加一个commons模块,然后分别被provider和consumer两个模块所引用
二、provider提供两个接口
@PostMapping("/user1") //key/value形式传参
public User addUser1(User user){
return user;
}
@PostMapping("/user2") //json形式传参
public User addUser2(@RequestBody User user){
return user;
}三、consumer消费者
@GetMapping("/hello5")
public void hello5()
{
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("username","leo");
map.add("password","123");
map.add("id",1);
User user = restTemplate.postForObject("http://provider/user1", map, User.class);
System.out.println(user);
user.setId(2);
user.setUsername("zhbcm");
user.setPassword("123");
user = restTemplate.postForObject("http://provider/user2", user, User.class);
System.out.println(user);
}一次请求,返回两个post请求的结果
测试使用postForLocation
一、provider提供一个RegisterController
@Controller
public class RegisterController
{
@PostMapping("/register")
public String register(User user){
return "redirect:http://provider/loginPage?username="+user.getUsername();
}
@GetMapping("/loginPage")
@ResponseBody
public String loginPage(String username){
return "login"+username;
}1.因为这里是重定向,响应结果一定是302,否则postForLocation无效
2.重定向的路径要写成绝对路径,否则consumer中调用会出错
二、consumer调用
@GetMapping("/hello6")
public void hello6(){
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("username","leo");
map.add("password","123");
map.add("id",1);
URI user = restTemplate.postForLocation("http://provider/register", map);
String s = restTemplate.getForObject(user, String.class);
System.out.println(s);
}put接口传参其实和post很像也是支持kv形式传参和json形式传参
provider提供端
@PutMapping("/updateUser1")
public void updateUser1(User user)
{
System.out.println(user);
}
@PutMapping("/updateUser2")
public void updateUser2(@RequestBody User user)
{
System.out.println(user);
}consumer调用端
@GetMapping("/hello7")
public void hello7(){
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("username","leo");
map.add("password","123");
map.add("id",1);
restTemplate.put("http://provider/updateUser1", map);
//System.out.println(user);
User user = new User();
user.setId(2);
user.setUsername("zhbcm");
user.setPassword("123");
restTemplate.put("http://provider/updateUser2", user);
//System.out.println(user);
}provider提供端
@DeleteMapping("/deleteUser1")
public void deleteUser1(Integer id)
{
System.out.println(id);
}
@DeleteMapping("/deleteUser2/{id}")
public void deleteUser2(@PathVariable Integer id)
{
System.out.println(id);
}consumer消费者端
@GetMapping("/hello8")
public void hello8(){
restTemplate.delete("http://provider/deleteUser1?id={1}",1);
restTemplate.delete("http://provider/deleteUser2/{1}",2);
}一、去consul官网去下载Linux安装包,下载慢的可以加群找我,上面两个群号都可以。
二、你可能要安装zip命令,如果你没有的话。
命令: yum list | grep zip/unzip #获取安装列表
安装命令: yum install zip #提示输入时,请输入y;
安装命令:yum install unzip #提示输入时,请输入y;
三、解压完成之后,进入consul目录启动
./consul agent -dev -ui -node=consul-dev -client=192.168.5.218
最后放的是你自己虚拟机IP
四、打开浏览器输入 http://192.168.5.218:8500/ ,看到界面显示说明完成。要关闭防火墙。
一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>二、添加配置
spring.application.name=consul-provider
server.port=8007
spring.cloud.consul.host=192.168.5.218
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.service-name=consul-provider三、启动类上加上@EnableDiscoveryClient注解
四、提供者代码
@RestController
public class HelloController
{
@GetMapping("/hello")
public String hello(){
return "hello";
}
}最后启动服务
一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>二、添加配置
spring.application.name=consul-provider
server.port=8008
spring.cloud.consul.host=192.168.5.218
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.service-name=consul-provider三、启动类上加上@EnableDiscoveryClient注解
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulConsumerApplication
{
public static void main(String[] args)
{
SpringApplication.run(ConsulConsumerApplication.class, args);
}
@Bean
RestTemplate restTemplate()
{
return new RestTemplate();
}
}四、消费者调用
@RestController
public class HelloController
{
@Autowired
LoadBalancerClient client;
@Autowired
RestTemplate restTemplate;
@GetMapping("/hello")
public void hello()
{
ServiceInstance choose = client.choose("consul-provider");
System.out.println(choose.getUri());
System.out.println(choose.getServiceId());
String s = restTemplate.getForObject(choose.getUri() + "/hello", String.class);
System.out.println(s);
}
}最后在浏览器输入: http://localhost:8008/hello
控制台打印成功:
Hystrix叫做熔断器,在微服务系统中,一个项目可能会牵涉多个系统,任何一个模块出错可能会导致整个系统出问题。所以我们希望可以有一样东西,如果某一个模块出错,不再影响整个系统!
一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
二、添加配置
spring.application.name=hystrix
server.port=8009
eureka.client.service-url.defaultZone=http://localhost:8001/eureka
三、启动类配置
@SpringBootApplication
@EnableCircuitBreaker
//@SpringCloudApplication可以使用这个注解来代替、
public class HystrixApplication
{
public static void main(String[] args)
{
SpringApplication.run(HystrixApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate()
{
return new RestTemplate();
}
}关于@SpringCloudApplication解释,他和@SpringBootApplication差不多,是一种复合注解,其源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}可以看出这个注解是@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker三个注解的组合。
四、核心代码
HelloService.java
@Service
public class HelloService
{
@Autowired
RestTemplate restTemplate;
/**
* 我们将调用provider里的hello接口,但是这个调用可能会失败,所以我们要在这个方法上加上
* @HystrixCommand注解,配置里面fallbackMethod的属性,表示调用该方法失败时,会调用临时
* 的一个替代方法。
*/
@HystrixCommand(fallbackMethod = "error")//服务降级
public String hello()
{
return restTemplate.getForObject("http://provider/hello", String.class);
}
/**
* @description:
* 名字要和fallbackMethod返回值一致,方法返回值也要一致。总不能上面失败的方法返回是String类型
* 你下面定义的临时调用方法返回Integer类型,这显然是不行的。
* @since v1.0.0
* author Leo
* date 2020/3/26
*/
public String error()
{
return "error";
}
}HelloController.java
@RestController
public class HelloController
{
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}最后,测试的时候如果出现问题的话,会出现error。
一、创建Command.java继承HystrixCommand
public class Command extends HystrixCommand<String>
{
RestTemplate restTemplate;
public Command(Setter setter, RestTemplate restTemplate)
{
super(setter);
this.restTemplate = restTemplate;
}
@Override
protected String run() throws Exception
{
return restTemplate.getForObject("http://provider/hello",String.class);
}
}二、调用方法
@Autowired
RestTemplate restTemplate;
@GetMapping("/hello2")
public void hello2(){
Command command = new Command(HystrixCommand.Setter.
withGroupKey(HystrixCommandGroupKey.Factory.asKey("leo")), restTemplate);
// String execute = command.execute();//直接执行
// System.out.println(execute);
try
{
Future<String> queue = command.queue();
String s = queue.get();
System.out.println(s);//先入队后执行
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}这里一个实例只能执行一次,所以如果通过execute方法执行,就不要使用queue。
首先定义如下方法
@HystrixCommand(fallbackMethod = "error")
public Future<String> hello2(){
return new AsyncResult<String>(){
@Override
public String invoke()
{
return restTemplate.getForObject("http://provider/hello",String.class);
}
};
}调用方法
@GetMapping("/hello3")
public void hello3(){
Future<String> future = helloService.hello2();
try
{
String s = future.get();
System.out.println(s);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}一、首先修改provider的hello2接口
@GetMapping("/hello2")
public String hello2(String name)
{
System.out.println(new Date()+"--->"+name);
return name;
}二、在断路器中添加代码
HelloService.java
@HystrixCommand(fallbackMethod = "error2")
@CacheResult //这个注解表示该方法的请求结果会被缓存起来,缓存key就是参数,value就是方法的返回值
public String hello3(String name){
return restTemplate.getForObject("http://provider/hello2?name={1}",String.class,name);
}
public String error2(String name)
{
return "error"+name;
}然而,这样配置完成之后,缓存并不会立即生效,一般来说缓存都有一个生命周期,所以这里也是,需要进行初始化HystrixRequestContext,才可以生效。close之后缓存失效。
@GetMapping("/hello4")
public void hello4(){
HystrixRequestContext ctx = HystrixRequestContext.initializeContext();
String s = helloService.hello3("leo");
s = helloService.hello3("leo");
ctx.close();
}发起请求: http://localhost:8009/hello4
返回结果:Thu Mar 26 17:18:10 CST 2020--->leo
他只返回了一次,说明第二次返回的结果是读取第一次的缓存。
非注解形式,配置复杂!
provider接口定义
@RestController
public class UserController
{
@GetMapping("/user/{ids}")
public List<User> getUserByIds(@PathVariable String ids){
System.out.println(ids);
String[] split = ids.split(",");
ArrayList<User> list = new ArrayList<>();
for (String s : split)
{
User user = new User();
user.setId(Integer.parseInt(s));
list.add(user);
}
return list;
}
}在hystrix定义UserService
@Service
public class UserService
{
@Autowired
RestTemplate restTemplate;
public List<User> getUserByIds(List<Integer> ids){
User[] users = restTemplate.getForObject("http://provider/user/{1}", User[].class, StringUtils.join(ids, ','));
return Arrays.asList(users);
}
}定义UserBatchCommand
public class UserBatchCommand extends HystrixCommand<List<User>>
{
private List<Integer> ids;
private UserService userService;
@Override
protected List<User> run() throws Exception
{
return userService.getUserByIds(ids);
}
public UserBatchCommand(List<Integer> ids, UserService userService)
{
super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("batchCmd"))
.andCommandKey(HystrixCommandKey.Factory.asKey("batchKey")));
this.ids = ids;
this.userService = userService;
}
}定义请求合并的方法
public class UserCollapseCommand extends HystrixCollapser<List<User>, User, Integer>
{
private UserService userService;
private Integer id;
public UserCollapseCommand(UserService userService, Integer id)
{
super(HystrixCollapser.Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("UserCollapseCommand"))
.andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter()
.withTimerDelayInMilliseconds(200)));
this.userService = userService;
this.id = id;
}
//返回请求参数
@Override
public Integer getRequestArgument()
{
return id;
}
//请求合并方法
@Override
protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Integer>> collection)
{
ArrayList<Integer> ids = new ArrayList<>(collection.size());
for (CollapsedRequest<User, Integer> request : collection)
{
ids.add(request.getArgument());
}
return new UserBatchCommand(ids,userService);
}
//请求结果分发
@Override
protected void mapResponseToRequests(List<User> users, Collection<CollapsedRequest<User, Integer>> collection)
{
int count=0;
for (CollapsedRequest<User, Integer> request : collection)
{
request.setResponse(users.get(count++));
}
}
}最后调试
@GetMapping("/hello5")
public void hello5() throws ExecutionException, InterruptedException
{
HystrixRequestContext ctx = HystrixRequestContext.initializeContext();
UserCollapseCommand cmd1 = new UserCollapseCommand(userService, 99);
UserCollapseCommand cmd2 = new UserCollapseCommand(userService, 98);
UserCollapseCommand cmd3 = new UserCollapseCommand(userService, 97);
UserCollapseCommand cmd4 = new UserCollapseCommand(userService, 96);
Future<User> queue1 = cmd1.queue();
Future<User> queue2 = cmd2.queue();
Future<User> queue3 = cmd3.queue();
Future<User> queue4 = cmd4.queue();
User user1 = queue1.get();
User user2 = queue2.get();
User user3 = queue3.get();
User user4 = queue4.get();
System.out.println(user1);
System.out.println(user2);
System.out.println(user3);
System.out.println(user4);
ctx.close();
}从控制台打印我们可以看到
96,97,98,99
四次请求被合在一起打印到了控制台上~如果想看出效果,可以使用延迟加载来实现。
注解形式配置
UserService.java
@Service
public class UserService
{
@Autowired
RestTemplate restTemplate;
@HystrixCollapser(batchMethod = "getUserByIds", collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "200")})
public Future<User> getUsersById(Integer id)
{
return null;
}
@HystrixCommand
public List<User> getUserByIds(List<Integer> ids)
{
User[] users = restTemplate.getForObject("http://provider/user/{1}", User[].class, StringUtils.join(ids, ','));
return Arrays.asList(users);
}
}测试方法
@GetMapping("/hello6")
public void hello6() throws ExecutionException, InterruptedException
{
HystrixRequestContext ctx = HystrixRequestContext.initializeContext();
Future<User> q1 = userService.getUsersById(99);
Future<User> q2 = userService.getUsersById(98);
Future<User> q3 = userService.getUsersById(97);
User user1 = q1.get();
User user2 = q2.get();
User user3 = q3.get();
System.out.println(user1);
System.out.println(user2);
System.out.println(user3);
Thread.sleep(2000);
Future<User> q4 = userService.getUsersById(97);
User user4 = q4.get();
System.out.println(user4);
ctx.close();
}控制台打印结果:
User{id=99, username='null', password='null'} User{id=98, username='null', password='null'} User{id=97, username='null', password='null'} 2020-03-26 20:26:49.127 INFO 9264 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: provider.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647 User{id=97, username='null', password='null'}
一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
二、配置文件,使项目注册到Eureka上
spring.application.name=openfeign
server.port=7000
eureka.client.service-url.defaultZone=http://localhost:8001/eureka三、启动类上添加注解,开启对feign的支持
@EnableFeignClients
四、定义service接口
@FeignClient("provider")//绑定服务,服务提供者的spring.application.name=provider
public interface HelloService
{
@GetMapping("/hello")
String hello();//定义一个方法名
}定义controller
@RestController
public class HelloController
{
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}五、启动服务
启动Eureka、Provider再启动Feign
然后控制台输入: http://localhost:7000/hello
和普通参数传递的区别
1.参数要绑定参数名
2.如果通过header来传递参数,要记得中文转码
一、测试接口
@FeignClient("provider")//绑定服务
public interface HelloService
{
@GetMapping("/hello")
String hello();//定义一个方法名
@GetMapping("/hello2")
String hello2(@RequestParam("name") String name);
@PostMapping("/user2")
User addUser(@RequestBody User user);
@DeleteMapping("/deleteUser2/{id}")
void deleteUserById(@PathVariable("id") Integer id);
@GetMapping("/user3")
void getUserByName(@RequestHeader("name") String name);
}二、调用接口
@GetMapping("/hello2")
public String hello2() throws UnsupportedEncodingException
{
String s = helloService.hello2("leo");
System.out.println(s);
User user = new User();
user.setId(1);
user.setUsername("leo");
user.setPassword("123");
User user1 = helloService.addUser(user);
System.out.println(user1);
helloService.deleteUserById(1);
helloService.getUserByName(URLEncoder.encode("leo","UTF-8"));
return helloService.hello();
}Feign调用相比之前的RestTemplate,在代码上明显更加简洁、方便。Feign主要是通过Service接口绑定对应的远程服务,在接口上使用Mapping映射,这是最大的特点!至此,Service与服务提供者绑定后,控制器再进行调用!
将provider和OpenFeign中公共的部分提取出来,一起使用!
一、首先创建一个普通的maven工程,这个工程需要web依赖和common模块的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>leo.study</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>二、创建公共接口
public interface IUserService
{
@GetMapping("/hello")
String hello();//定义一个方法名
@GetMapping("/hello2")
String hello2(@RequestParam("name") String name);
@PostMapping("/user2")
User addUser2(@RequestBody User user);
@DeleteMapping("/deleteUser2/{id}")
void deleteUser2(@PathVariable("id") Integer id);
@GetMapping("/user3")
void getUserByName(@RequestHeader("name") String name) throws UnsupportedEncodingException;
}三、把抽好的模块放到provider和OpenFeign里
<dependency>
<artifactId>feign-api</artifactId>
<groupId>leo.study</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>四、provider实现接口
@RestController
public class HelloController implements IUserService
{
@Value("${server.port}")
Integer port;
@Override
// @GetMapping("/hello") 因为实现了接口,映射关系随着接口的实现被带过来了
public String hello()
{
return "hello" + port;
}
@Override
// @GetMapping("/hello2")
public String hello2(String name)
{
System.out.println(new Date()+"--->"+name);
return name;
}
@PostMapping("/user1")
public User addUser1(User user)
{
return user;
}
// @PostMapping("/user2")
@Override
public User addUser2(@RequestBody User user)
{
return user;
}
@PutMapping("/updateUser1")
public void updateUser1(User user)
{
System.out.println(user);
}
@PutMapping("/updateUser2")
public void updateUser2(@RequestBody User user)
{
System.out.println(user);
}
@DeleteMapping("/deleteUser1")
public void deleteUser1(Integer id)
{
System.out.println(id);
}
// @DeleteMapping("/deleteUser2/{id}")
@Override
public void deleteUser2(@PathVariable Integer id)
{
System.out.println(id);
}
// @GetMapping("/user3")
@Override
public void getUserByName(@RequestHeader String name) throws UnsupportedEncodingException
{
System.out.println(URLDecoder.decode(name, "UTF-8"));
}
}
注意:因为实现了接口,映射关系随着接口的实现被带过来了,所以在重新继承之后,映射关系可以省略。
五、OpenFeign继承公共模块IUserService
只需要继承就可以!
@FeignClient("provider")//绑定服务
public interface HelloService extends IUserService
{
/**
* author Leo
* date 2020/3/27
@GetMapping("/hello")
String hello();//定义一个方法名
@GetMapping("/hello2")
String hello2(@RequestParam("name") String name);
@PostMapping("/user2")
User addUser(@RequestBody User user);
@DeleteMapping("/deleteUser2/{id}")
void deleteUserById(@PathVariable("id") Integer id);
@GetMapping("/user3")
void getUserByName(@RequestHeader("name") String name);
*/
}关于继承特性:代码简洁不容易出错
在OpenFeign中我们可以通过配置日志,来查看整个请求的调用,日志级别一共分为四种:
- NONE:不开启日志,默认就是这个
- BASIC:记录请求方法,URL,响应状态吗,执行时间
- HEADERS:在BASIC基础上增加响应头
- FULL:在HEADERS基础上,增加body以及请求元数据
以上四种级别可以通过Bean来配置:
@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication
{
public static void main(String[] args)
{
SpringApplication.run(OpenfeignApplication.class, args);
}
@Bean
Logger.Level logLevel()
{
return Logger.Level.FULL;
}
}配置文件:
logging.level.com.leo.openfeign.HelloService=debug#开启请求数据压缩
feign.compression.request.enabled=true
#开启响应数据压缩
feign.compression.response.enabled=true
#压缩的数据类型
feign.compression.request.mime-types=text/html,application/json
#压缩的数据下限,2048表示当数据超过2048时才会进行压缩
feign.compression.request.min-request-size=2048Hystrix里的容错、服务降级的功能,在OpenFeign里一样可以使用。
一、定义一个HelloServiceFallBack实现HelloService,要加@Component注解
@Component //添加该注解
@RequestMapping("/leo") //防止请求地址重复
public class HelloServiceFallBack implements HelloService
{
@Override
public String hello()
{
return "error1";
}
@Override
public String hello2(String name)
{
return "error2";
}
@Override
public User addUser2(User user)
{
return null;
}
@Override
public void deleteUser2(Integer id)
{
}
@Override
public void getUserByName(String name) throws UnsupportedEncodingException
{
}
}HelloService里的@FeignClient注解里添加一个fallback属性,值:HelloServiceFallBack.class
但是这样会存在一个问题:
HelloServiceFallBack实现的是HelloService,而HelloService继承IUserService,而IUserService已经存在了一些映射方法
这就相当于把相同的接口定义了两次,所以需要做一个区分。
二、开启Hystrix:
feign.hystrix.enabled=true三、启动:
关闭provider,重启OpenFeign
页面返回error1,说明成功!
或者可以自定义FallBackFactory来实现降级:
一、定义一个FallBackFactory
@Component
public class FallBackFactory implements FallbackFactory<HelloService>
{
@Override
public HelloService create(Throwable throwable)
{
return new HelloService()
{
@Override
public String hello()
{
return "error--->1";
}
@Override
public String hello2(String name)
{
return "error--->2";
}
@Override
public User addUser2(User user)
{
return null;
}
@Override
public void deleteUser2(Integer id)
{
}
@Override
public void getUserByName(String name) throws UnsupportedEncodingException
{
}
};
}
}二、HelloService接口
@FeignClient(value = "provider",fallbackFactory = FallBackFactory.class)//绑定服务HelloService三、重启OpenFeign
页面返回 error--->1
它是GreenWich版推荐的一个容错方案,相比Hystrix,Resilience4j专为Java8以及函数式编程设计的。
Resilience4j提供功能:
- 断路器
- 限流
- 基于信号量的隔离
- 缓存
- 限时
- 请求重试
一、添加依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<version>0.13.2</version>
</dependency>二、添加测试代码
正常的例子
public class ResilienceTest
{
@Test
public void test1()
{
//注意,创建普通的maven工程,他的模块自带的jdk版本是5,你需要改成8
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
//故障率阈值百分比,一旦超过,断路器就会打开
.failureRateThreshold(50)
//断路器保持打开的时间,在达到设置时间之后,断路器会进入到一个半打开状态!
.waitDurationInOpenState(Duration.ofMillis(1000))
//当断路器处于半打开状态时,环形缓冲区的大小
.ringBufferSizeInHalfOpenState(2)
.ringBufferSizeInClosedState(2)
.build();
CircuitBreakerRegistry of = CircuitBreakerRegistry.of(config);
CircuitBreaker cb = of.circuitBreaker("leo", config);
CheckedFunction0<String> supplier = CircuitBreaker.decorateCheckedSupplier(cb, () -> "hello ResilienceTest");
Try<String> map = Try.of(supplier)
.map(v -> v + " hello");
System.out.println(map.isSuccess());
System.out.println(map.get());
}
}出异常的断路器
@Test
public void test2()
{
//注意,创建普通的maven工程,他的模块自带的jdk版本是5,你需要改成8
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
//故障率阈值百分比,一旦超过,断路器就会打开
.failureRateThreshold(50)
//断路器保持打开的时间,在达到设置时间之后,断路器会进入到一个半打开状态!
.waitDurationInOpenState(Duration.ofMillis(1000))
//当断路器处于半打开状态时,环形缓冲区的大小
// .ringBufferSizeInHalfOpenState(2)
.ringBufferSizeInClosedState(2)
.build();
CircuitBreakerRegistry of = CircuitBreakerRegistry.of(config);
CircuitBreaker cb = of.circuitBreaker("leo");
System.out.println(cb.getState());
cb.onError(0,new RuntimeException());
System.out.println(cb.getState());
cb.onError(0,new RuntimeException());
System.out.println(cb.getState());
CheckedFunction0<String> supplier = CircuitBreaker.decorateCheckedSupplier(cb, () -> "hello ResilienceTest");
Try<String> map = Try.of(supplier)
.map(v -> v + " hello");
System.out.println(map.isSuccess());
System.out.println(map.get());
}ringBufferSizeInClosedState(2)表示有两条数据时才会去统计故障率,所以手动测试时,至少调用两次onError,断路器才会打开!
防止某一时间请求流量过大,致使provider无法处理那么多请求,致使宕机。
添加依赖:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
<version>0.13.2</version>
</dependency>限流测试代码:
@Test
public void test3()
{
RateLimiterConfig config = RateLimiterConfig.custom()
//阈值刷新时间
.limitRefreshPeriod(Duration.ofMillis(1000))
//阈值刷新频次
.limitForPeriod(2)
//冷却时间
.timeoutDuration(Duration.ofMillis(1000))
.build();
RateLimiter limiter = RateLimiter.of("leo", config);
CheckedRunnable checkedRunnable = RateLimiter.decorateCheckedRunnable(limiter, () ->
{
System.out.println(new Date());
});
Try.run(checkedRunnable)
.andThenTry(checkedRunnable)
.andThenTry(checkedRunnable)
.andThenTry(checkedRunnable)
.onFailure(t->{
System.out.println(t.getMessage());
});
}上面的例子,我们设置了每秒处理两次请求,而我们创建了4个任务实例,执行以后正常来说,打印出来的时间应该是两个不同的时间,间隔一秒
Fri Mar 27 16:30:08 CST 2020
Fri Mar 27 16:30:08 CST 2020
Fri Mar 27 16:30:09 CST 2020
Fri Mar 27 16:30:09 CST 2020一、添加依赖
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
<version>0.13.2</version>
</dependency>二、测试代码
@Test
public void test04(){
RetryConfig config = RetryConfig.custom()
//重试次数
.maxAttempts(5)
//间隔时间
.waitDuration(Duration.ofMillis(500))
//抛出哪个异常就进行重试
.retryExceptions(RuntimeException.class)
.build();
Retry of = Retry.of("leo", config);
Retry.decorateRunnable(of, new Runnable()
{
int count=0;
@Override
public void run()
{
if(count++ <3){
throw new RuntimeException();
}
}
}).run();
}一、创建一个springboot工程,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.3.1</version>
</dependency>排除目前不需要的依赖:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.2.0</version>
<exclusions>
<!-- 没有配置的 需先排除 不然会报错 -->
<exclusion>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
</exclusion>
<exclusion>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
</exclusion>
<exclusion>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bulkhead</artifactId>
</exclusion>
<exclusion>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-timelimiter</artifactId>
</exclusion>
</exclusions>
</dependency>二、配置文件
resilience4j:
retry:
retry-aspect-order: 399 #表示Retry的优先级
backends:
retryA:
maxRetryAttempts: 5 #重试次数
waitDuration: 500 #重复等待时间
exponentialBackoffMultiplier: 1.1 #间隔乘数
retryExceptions:
- java.lang.RuntimeException
spring:
application:
name: resilience4j
server:
port: 7001
eureka:
client:
service-url:
defaultZone:
http://localhost:8001/eureka三、配置RestTemplate
@SpringBootApplication
public class ResilienceSpringApplication
{
public static void main(String[] args)
{
SpringApplication.run(ResilienceSpringApplication.class, args);
}
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
}四、业务代码
HelloService
@Service
@Retry(name = "retryA")
public class HelloService
{
@Autowired
RestTemplate restTemplate;
public String hello(){
return restTemplate.getForObject("http://localhost:8003/hello",String.class);
}
}HelloController
@RestController
public class HelloController
{
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}一、首先要把依赖里排除circuitbreaker的删掉
二、在配置文件中配置断路器
circuitbreaker:
instances:
cbA:
ringBufferSizeInClosedState: 5
ringBufferSizeInHalfOpenState: 3
waitInterval: 5000
recordExpections:
- org.springframework.web.client.HttpServerErrorException
circuit-breaker-aspect-order: 398三、HelloService添加注解
@Service
//@Retry(name = "retryA") //表示要使用重试策略
@CircuitBreaker(name = "cbA",fallbackMethod = "error")
public class HelloService
{
@Autowired
RestTemplate restTemplate;
public String hello(){
return restTemplate.getForObject("http://localhost:8003/hello",String.class);
}
public String error(Throwable t){
return "error";
}
}这里要说明一下,定义的这个error方法里必须要放Throwable参数,即使你不想打印错误信息,也要放!
浏览器输入: http://localhost:7001/hello
一、首先要对provider里添加依赖
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.2.0</version>
<exclusions>
<!-- 没有配置的 需先排除 不然会报错 -->
<exclusion>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
</exclusion>
<!-- <exclusion>-->
<!-- <groupId>io.github.resilience4j</groupId>-->
<!-- <artifactId>resilience4j-ratelimiter</artifactId>-->
<!-- </exclusion>-->
<exclusion>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bulkhead</artifactId>
</exclusion>
<exclusion>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-timelimiter</artifactId>
</exclusion>
</exclusions>
</dependency>
二、编写配置文件
spring.application.name=provider
server.port=8003
eureka.client.service-url.defaultZone=http://localhost:8001/eureka
#设置一秒钟处理一个请求
resilience4j.ratelimiter.limiters.rLA.limit-for-period=1
#刷新时间
resilience4j.ratelimiter.limiters.rLA.limit-refresh-period=1s
#冷却时间
resilience4j.ratelimiter.limiters.rLA.timeout-duration=1s
三、通过@RateLimiter注解标记接口实现限流
@Override
@RateLimiter(name = "rLA")
// @GetMapping("/hello") 因为实现了接口,映射关系随着接口的实现被带过来了
public String hello()
{
// int i=1/0;
String s="hello"+port;
System.out.println(new Date());
return s;
}四、在客户端模拟多次请求,查看限流效果
public String hello()
{
for (int i = 0; i < 5; i++)
{
restTemplate.getForObject("http://localhost:8003/hello", String.class);
}
return "success";
}浏览器输入: http://localhost:7001/hello
控制台打印:
微服务由于数量众多,出故障的概率很高,所以单纯依靠人来运维。在早期的springcloud,服务监控主要使用Hystrix DashBoard,集群数据库监控使用Turbine
在G版,官方推荐使用的是MicroMeter
一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>二、配置文件
management.endpoints.web.exposure.include=*三、启动服务,查看运行数据
由于每个微服务地址都有可能发生变化,无法直接对外公布这些地址,基于安全、高内聚低耦合等设计,我们都要对内外部系统做一个切割,一个专门用来处理请求的组件。
- 权限问题统一处理
- 数据裁剪和聚合
- 简化客户端调用
- 可以针对不同的客户提供不同的网关支持
- 权限控制,可以做认证和授权
- 监控
- 动态路由
- 负载均衡
- 静态资源处理
Zuul中的功能基本上是基于过滤器来实现的,他的过滤器有几种不同的类型:
- PRE
- ROUTING
- POST
- ERROR
一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>二、添加配置
spring.application.name=zuul
server.port=8012
eureka.client.service-url.defaultZone=http://localhost:8001/eureka
三、启动类添加注解@EnableZuulProxy
启动Eureka、Provider、Zuul,然后浏览器输入: http://localhost:8012/provider/hello
路由规则自己配置:
zuul.routes.cloud-a.path=/cloud-a/**
zuul.routes.cloud-a.service-id=provider简化的话就是:
zuul.routes.provider=/cloud-a/**@Component
public class PerMissFilter extends ZuulFilter
{
//过滤器类型
@Override
public String filterType()
{
return "pre";
}
//过滤器优先级
@Override
public int filterOrder()
{
return 0;
}
//是否过滤
@Override
public boolean shouldFilter()
{
return true;
}
//核心过滤逻辑
@Override
public Object run() throws ZuulException
{
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();//获取当前请求
String name = request.getParameter("name");
String password = request.getParameter("password");
if(!"leo".equals(name)||!"123".equals(password)){
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.addZuulResponseHeader("content-type","text/html;charset=utf-8");
ctx.setResponseBody("非法访问!");
}
return null;
}
}简单定义过滤器,继承ZuulFilter,重写里面方法
特点:
- 限流
- 路径重写
- 动态路由
- 集成SpringCloud DiscoveryClient
- 集成断路器
和Zuul对比:
- 对比之下,gateway可以和其他组件更好融合。
- Zuul1不支持长连接,例如websocket
- 支持限流
- gateway基于netty开发,实现了异步和非阻塞,占用资源小,性能强于Zuul
SpringCloud Gateway支持编程式配置和yml配置
编程式配置
一、创建一个springboot项目,添加gateway依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>二、配置RouteLocator这个Bean,实现请求转发
@SpringBootApplication
public class GatewayApplication
{
public static void main(String[] args)
{
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
RouteLocator routeLocator(RouteLocatorBuilder builder)
{
return builder.routes()
.route("leo_route", r -> r.path("/get").uri("http://httpbin.org"))
.build();
}
}三、重启项目,访问 http://localhost:8080/get ,同样会转发到http://httpbin.org/get
yml配置
spring:
cloud:
gateway:
routes:
- id: leo_route
uri: http://httpbin.org
predicates:
- Path=/getproperties配置
spring.cloud.gateway.routes[0].id=leo_route
spring.cloud.gateway.routes[0].uri=http://httpbin.org
spring.cloud.gateway.routes[0].predicates[0]=Path=/get重启服务!
一、增加yml配置
spring:
cloud:
gateway:
routes:
- id: leo_route
uri: http://httpbin.org
predicates:
- Path=/get
discovery:
locator:
enabled: true #开启自动代理
application:
name: gateway
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka
logging:
level:
org.springframework.cloud.gateway: debug
把gateway注册到注册中心,然后去调用注册中心的其他服务
http://localhost:8080/PROVIDER/hello ,这个PROVIDER要和服务中心上的名字一致,包括大小写。
通过时间匹配
spring:
cloud:
gateway:
routes:
- id: leo_route
uri: http://httpbin.org
predicates:
- After=2021-01-01T01:01:01+08:00[Asia/Shanghai]表示请求时间在2021-01-01T01:01:01+08:00[Asia/Shanghai]之后才会被路由,否则不会被路由。
- Before 表示在某个时间之前,进行转发
- Between 表示在两个时间点之间,用英文逗号隔开
通过请求方式匹配
*- Method=*GET 表示只处理GET请求,其他请求不做处理!
通过请求路径匹配
通过传入参数匹配
*- Query=*name
表示请求里要有name属性才会转发,否则不做处理!
通过传入参数和传入值匹配
*- Query=name,leo.
表示传入参数为name,值为leo***,他的传入参数值必须符合这种格式,才会转发
多种方式可以组合使用
SpringCloud GateWay中的过滤器分为两大类
- GlobalFilter
- GateWayFilter
AddRequestParameter过滤器使用
spring:
cloud:
gateway:
routes:
- id: leo_route
# uri: http://httpbin.org
uri: lb://provider # lb LoadBalance,在多个实例场景下,自动实现负载均衡
filters:
- AddRequestParameter=name,leo
predicates:
- Method=/get浏览器输入: http://localhost:8080/hello2
返回输入的value
这里不需要手动去写provider,原因是配置文件里url已经写了
这个过滤器就是在请求转发路由的时候,自动额外添加参数
它是一个分布式系统配置管理解决方案,它包含了Client和Server。配置文件放在server端,通过接口的形式提供给client。
SpringCloud Config主要有哪些功能:
- 集中管理各个环境、微服务的配置文件
- 提供服务端和客户端的支持
- 配置文件修改后,快速生效
- 配置文件通过Git或者SVN进行管理,天然支持版本回退。
- 支持高并发,支持多种开发语言
一、本地创建目录,然后创建三个properties配置文件(最好和我的操作保持一致)
二、在GitHub上创建一个远程仓库,把client1文件上传,这里的话具体步骤就不说了
一、首先创建一个ConfigServer工程,创建时添加ConfigServer依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>二、配置文件
server.port=7002
spring.application.name=config-server
#配置仓库地址
spring.cloud.config.server.git.uri=https://github.com/jia707409741/configResp.git
#仓库中,配置文件目录
spring.cloud.config.server.git.search-paths=client1
三、启动类添加注解@EnableConfigServer
四、浏览器输入: http://localhost:7002/client1/dev/master
访问规则:
/{application}/{profile}/[{label}]
/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.yml
/{label}/{application}-{profile}.properties
一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>二、添加bootstrap.properties
#下面三行配置分别对应config-server中的{application}/{profile}以及{label}占位符
spring.application.name=client1
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.uri=http://localhost:7002
server.port=7003三、启动,浏览器输入: http://localhost:7003/hello
常见加密方案:
可逆加密
可以根据加密后的明文可以推断出明文加密方式:
1.对称加密
加密与解密的秘钥不一样,加密叫做公钥,解密叫做私钥
2.非对称加密
不可逆加密
理论上,无法通过加密后的密文推算出明文。
一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>二、配置文件
spring.rabbitmq.host=39.101.197.96
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
三、编写接收器
@EnableBinding(Sink.class)
public class MsgReceiver
{
@StreamListener(Sink.INPUT)
public void receive(Object payload)
{
System.out.println("receive: " + payload);
}
}四、启动项目
启动项目,并且进入rabbitmq后台管理页面里发送一条消息;
然后进去控制台看是否存在消息
一、自定义接口
public interface MyChannel
{
String INPUT="leo-INPUT";
String OUTPUT="leo-OUTPUT";
@Output(OUTPUT)
MessageChannel output();
@Input(INPUT)
SubscribableChannel input();
}二、自定义控制器
@EnableBinding(MyChannel.class)
public class CustomReceiver
{
@StreamListener(MyChannel.INPUT)
public void receive(Object payload){
System.out.println("receive "+payload);
}
}三、定义控制器
@RestController
public class HelloController
{
@Autowired
MyChannel myChannel;
@GetMapping("/hello")
public void hello(){
myChannel.output().send(MessageBuilder.withPayload("hello,controller").build());
}
}简单一个配置
spring.cloud.stream.bindings.leo-INPUT.group=g1
spring.cloud.stream.bindings.leo-OUTPUT.group=g1
简单配置
#开启消息分区(输入通道上)
spring.cloud.stream.bindings.leo-INPUT.consumer.partitioned=true
#消费者实例个数
spring.cloud.stream.instance-count=2
#当前实例的下标
spring.cloud.stream.instance-index=0
#这个表示这个消息将被下标为1的消费者所消费
spring.cloud.stream.bindings.leo-OUTPUT.producer.partition-key-expression=1
#消费者的节点数量(生产者上配置)
spring.cloud.stream.bindings.leo-OUTPUT.producer.partition-count=1一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>二、写控制器
package com.example.sleuth.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController
{
public static final Log log = LogFactory.getLog(HelloController.class);
@GetMapping("/hello")
public String hello()
{
log.info("hello");
return "hello";
}
}三、请求结果打印
2020-05-02 15:58:43.542 INFO [,4f7b6b12b746eecc,4f7b6b12b746eecc,false] 11240 --- [nio-8080-exec-1] c.e.sleuth.controller.HelloController : hello
一、配置文件里添加
spring.application.name=sleuth二、controller改造
package com.example.sleuth.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class HelloController
{
public static final Log log = LogFactory.getLog(HelloController.class);
@Autowired
RestTemplate restTemplate;
@GetMapping("/hello")
public String hello()
{
log.info("hello");
return "hello";
}
@GetMapping("/hello2")
public String hello2() throws InterruptedException
{
log.info("hello2");
Thread.sleep(1000);
return restTemplate.getForObject("http://localhost:8080/hello3",String.class);
}
@GetMapping("/hello3")
public String hello3() throws InterruptedException
{
log.info("hello3");
Thread.sleep(1000);
return "hello3";
}
}三、运行hello2看控制台打印
一个trace由多个span组成
一、启动类上添加注解@EnableAsync
package com.example.sleuth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableAsync
public class SleuthApplication
{
public static void main(String[] args)
{
SpringApplication.run(SleuthApplication.class, args);
}
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
}二、添加service
package com.example.sleuth.service;
import com.example.sleuth.controller.HelloController;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class HelloService
{
public static final Log log = LogFactory.getLog(HelloService.class);
@Async
public String backgroundFun()
{
log.info("backgroundFun");
return "backgroundFun";
}
}三、controller
@GetMapping("/hello4")
public String hello4(){
log.info("hello4");
return helloService.backgroundFun();
}四、控制台打印
需要有docker环境,如果没有请看我博客: https://blog.csdn.net/Curtisjia/article/details/104186314
一、安装ElasticSearch,通过docker安装
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.1.0
二、安装rabbitmq
docker run -d --hostname my-rabbit --name leo-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:3-management
三、安装zipkin
docker run -d -p 9411:9411 --name zipkin -e ES_HOSTS=192.168.0.118 -e STORAGE_TYPE=elasticsearch -e ES_HTTP_LOGGING=BASIC -e RABBIT_URI=amqp://guest:guest@192.168.0.118:5672 openzipkin/zipkin
进入zipkin:
一、添加依赖
二、配置文件
spring.application.name=zipkin01
#开启链路追踪
spring.sleuth.web.client.enabled=true
#配置采样比例
spring.sleuth.sampler.probability=1
#zipkin地址
spring.zipkin.base-url=http://192.168.0.118:9411
#开启zipkin
spring.zipkin.enabled=true
#追踪消息发送类型
spring.zipkin.sender.type=rabbit
spring.rabbitmq.host=192.168.0.118
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
三、controller
package com.example.zipkin01;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController
{
public static final Log log= LogFactory.getLog(HelloController.class);
@GetMapping("/hello")
public String hello(String name){
log.info("zipkin01 hello!");
return "hello"+name;
}
}

















