Why optimize
When we need to connect with a new SMS service provider, what do we need to do?
1. Add a toolkit for a new third-party platform under sms
2. Add a new judgment in our case, judge the platfrom field and call the new toolkit. We also need to manually encapsulate the smsConfig class required by each channel
3. In the process of our query, we have to query the contents of the channel table every time, the contents of the channel signature table every time, and the contents of the channel template table every time. The performance is not high
We can package the query part into our toolkit and pass in the template code and signature code of jixinda
How to optimize
Space for time: can we read some information into memory after the project starts, so that we don't have to query the database every time
The lost space is memory, and time is the efficiency of code execution. Space for time refers to reading some information into memory first, and then each reading is not an IO operation on the disk, but an operation on memory
What information do we need to initialize
How to optimize
Space for time: can we read some information into memory after the project starts, so that we don't have to query the database every time
The lost space is memory, and time is the efficiency of code execution. Space for time refers to reading some information into memory first, and then each reading is not an IO operation on the disk, but an operation on memory
1. Information in channel table
Scheme 1: we read the information in config into our memory and create a List configs,
Then, when we call, we traverse the ids passed by the api service to determine which channel to use to construct the tool class corresponding to the third party
No, we also need to create our tool class when sending, and use this tool class to call the specific implementation logic. We should instantiate our tool class when building and save it in memory
Scheme 2: we first read the contents of config in mysql and instantiate the tool class of the channel into our memory according to the contents of congfig
How to implement: create objects and encapsulate attributes through reflection
Look at the Demo
Two classes, man person, are provided
@Data public class Person { private String name; private Integer age; } @Data public class Man extends Person{ private String sex; private String play; public String toString(){ return "name:"+super.getName()+",age:"+super.getAge()+",sex:"+this.sex+",play:"+this.play; } } public static void main(String[] args) { try { String classPath = "cn.itheima.reflex.pojo.Man"; Class classes = ReflexDemo1.class.getClassLoader().loadClass(classPath); Object o = classes.newInstance(); Method[] declaredMethods = classes.getDeclaredMethods(); //Traverse all methods for (Method indexMethod : declaredMethods) { if (indexMethod.getName().equals("getName")) { System.out.println("prove getName Method exists"); } if (indexMethod.getName().equals("getAge")) { System.out.println("prove getAge Method exists"); } if (indexMethod.getName().equals("getSex")) { System.out.println("prove getSex Method exists"); } if (indexMethod.getName().equals("getPlay")) { System.out.println("prove getPlay Method exists"); } } Method getName = classes.getMethod("getName", null); Method getAge = classes.getMethod("getAge", null); Method getSex = classes.getMethod("getSex", null); Method getPlay = classes.getMethod("getPlay", null); if (getName == null) { System.out.println("getName non-existent"); } if (getAge == null) { System.out.println("getAge non-existent"); } if (getSex == null) { System.out.println("getSex non-existent"); } if (getPlay == null) { System.out.println("getPlay non-existent"); } //Encapsulation properties Method setName = classes.getMethod("setName", String.class); setName.invoke(o, "Zhang San"); Method setAge = classes.getMethod("setAge", Integer.class); setAge.invoke(o, 20); Method setSex = classes.getMethod("setSex", String.class); setSex.invoke(o, "male"); Method setPlay = classes.getMethod("setPlay", String.class); setPlay.invoke(o, "Play basketball"); System.out.println(o); } catch (Exception e) { System.out.println("System exception"); } }
When we do not provide the get set method
How do we encapsulate parameters
The entity classes provided are as follows:
public class Man extends Person { private String sex; private String play; public String toString(){ return "name:"+super.name+",age:"+super.age+",sex:"+this.sex+",play:"+this.play; } } public class Person { public String name; public Integer age; } public static void main(String[] args) { try { String classPath = "cn.itheima.reflex.demo2.Man"; Class classes = ReflexDemo2.class.getClassLoader().loadClass(classPath); Object o = classes.newInstance(); //Gets the properties of the object's parent class //Gets the property object declared in the class based on reflection Field nameField = classes.getSuperclass().getDeclaredField("name"); Field ageField = classes.getSuperclass().getDeclaredField("age"); //Encapsulate attribute content nameField.set(o,"Zhang San"); ageField.set(o,20); Field sex = classes.getDeclaredField("sex"); Field play = classes.getDeclaredField("play"); //Sets the current property value that can be manipulated sex.setAccessible(true); play.setAccessible(true); sex.set(o,"male"); play.set(o,"Play games"); System.out.println(o); } catch (Exception e) { System.out.println("System exception"); } }
be careful:
sex.setAccessible(true);
play.setAccessible(true);
setAccessible means to allow access to private methods. If it is not set, we can't access some fields with access permission set
Then let's look at our business logic
We hope that after the service is started, the information in config will be read out, and the query will be sorted according to the level. According to the information in config, the util class will be automatically encapsulated into the system memory
How to set after service startup
- Declare spring Component @ Component
- Implement the CommandLineRunner interface and override the run method
In the run method, we need to read the contents of the config table, read it, and construct the tool class by reflection according to the name of the access platform
public void initConnect() { //TODO initializes the bean object of each channel according to the channel configuration //1. Query the database to get the channel list List<ConfigEntity> configs = configService.listForConnect(); log.info("Available channels queried:{}",configs); List beanList = new ArrayList(); //2. Traverse the channel list and create Bean objects for each channel through reflection (such as AliyunSmsService, MengWangSmsService, etc.) configs.forEach(config -> { try { //Encapsulates the SmsConfig configuration object required for the Bean object SmsConfig smsConfig = new SmsConfig(); smsConfig.setId(config.getId()); smsConfig.setDomain(config.getDomain().trim()); smsConfig.setName(config.getName().trim()); smsConfig.setPlatform(config.getPlatform().trim()); smsConfig.setAccessKeyId(config.getAccessKeyId().trim()); smsConfig.setAccessKeySecret(config.getAccessKeySecret().trim()); if (StringUtils.isNotBlank(config.getOther())) { LinkedHashMap linkedHashMap = JSON.parseObject(config.getOther(), LinkedHashMap.class); smsConfig.setOtherConfig(linkedHashMap); } //Dynamically splice the full class name of the bean instance to be created String className = "com.itheima.sms.sms." + config.getPlatform().trim() + "SmsService"; log.info("Prepare to create dynamically by reflection:{}",className); Class<?> aClass = Class.forName(className); //Gets the constructor object of the class Constructor<?> constructor = aClass.getConstructor(SmsConfig.class); //Create bean object Object beanService = constructor.newInstance(smsConfig); //The signatureService and templateService attributes in the bean object need to be assigned SignatureServiceImpl signatureService = SpringUtils.getBean(SignatureServiceImpl.class); TemplateServiceImpl templateService = SpringUtils.getBean(TemplateServiceImpl.class); //Gets the property object declared in the class based on reflection Field signatureServiceField = aClass.getSuperclass().getDeclaredField("signatureService"); Field templateServiceField = aClass.getSuperclass().getDeclaredField("templateService"); //Sets the current property value that can be manipulated signatureServiceField.setAccessible(true); templateServiceField.setAccessible(true); //Setting property values for bean objects signatureServiceField.set(beanService,signatureService); templateServiceField.set(beanService,templateService); beanList.add(beanService); }catch (Exception e){ e.printStackTrace(); } }); //3. Save the Bean object of each channel to connect_ In the list set if(!CONNECT_LIST.isEmpty()){ CONNECT_LIST.clear(); } CONNECT_LIST.addAll(beanList); log.info("Load initialized channels into the collection:{}",CONNECT_LIST); }
Load our tool class into our memory. When we call, we only need to find the tool class of the corresponding channel through level
When we call, how do we call it? The way we call it now is to new out the specific third party tool class and then call the third party's send method.
Here we introduce the strategy pattern
Learn about cats and dogs
Strategy mode - mainly master the Demo of Comparable and Comparator
What problems does the policy model mainly solve:
It solves the problem of multiple implementations of the same method, such as sorting. I want to sort by age, I want to sort by height, and I want to sort by weight
I can't create a comparator every time and rewrite the corresponding method every time, so there will be a lot of code for my comparison function,
I want to have the sorting code of age, height, weight, etc
This is method rewriting. We only have one comparison method, and the implementation of specific comparison is left to each class. Our subclasses only need to rewrite the corresponding methods
Then our problem arises again. Join me as a cat. I want to rank the first cat according to weight and the second cat according to height
At this time, we can't meet the requirements through method rewriting. When rewriting methods, we can't rewrite them again. We can only rewrite them once, so we can't meet the needs of multiple functions
The strategy model is to solve this problem. Of course, this problem can also be solved by anonymous inner classes, but anonymous inner classes are written dead and cannot be called for other classes
Therefore, the strategy mode is recommended
The policy pattern is to write the corresponding implementation into a specific class and implement a unified interface. When we use the corresponding method, we need to write the specific class (specific comparator class)
Tell us how
This ensures that the behavior of a class or its algorithm can be changed at run time
Advantages and disadvantages of strategic mode
Advantages: 1. The algorithm can be switched freely. 2. 2. Avoid using multiple conditional judgments. 3. Good scalability.
Disadvantages: 1. The number of policy classes will increase and the amount of code will increase. 2. All policy classes need to be exposed.
Usage scenario:
1. If there are many classes in a system, the only difference between them is their behavior,
Then, using the policy pattern, an object can dynamically choose one behavior among many behaviors.
2. A system needs to dynamically choose one of several algorithms.
3. If an object has a lot of behaviors, without appropriate patterns, these behaviors have to be implemented by multiple conditional selection statements.
Where is our policy pattern used for our system
First, all our third-party tool classes inherit from our abstract class AbstractSmsService
And there is a send method, which is the method that really calls our tool class
Whether we call abstractSmsService.send () directly when we execute the call, we just need to determine whether our abstractSmsService implementation class is called in a polymorphic way in advance
Let's look at the business logic of our server service. Refer to the flow chart
Explain with code
Is there a problem with our code now
What should we do if the manager service modifies the content of the channel during execution? Because we read our data into memory and instantiate the tool class according to the content in config
Is this information not synchronized
As a result, the data modified by our manager service cannot be synchronized to our server service in time
We need to do a method in the manager service to notify the server service to update memory messages
The modification method requires:
1) Get all useful redis services through server_ id_ Get all channel IDS in hash
2) Judge whether the time is greater than 5 minutes. If it is greater than 5 minutes, it means that the service has been disconnected from redis and needs to be deleted
3) If the service is normal, go to this topic_ HIGH_ The server key stores a message initializing the channel
The publish subscribe mode of redis is used here, and messages are sent in the form of broadcast
public void sendUpdateMessage() { // TODO sends a message to inform the SMS sending service to update the channel priority in memory Map map = redisTemplate.opsForHash().entries("SERVER_ID_HASH"); log.info("All SMS sending service instances:" + map); long currentTimeMillis = System.currentTimeMillis(); for (Object key : map.keySet()) { Object value = map.get(key); long parseLong = Long.parseLong(value.toString()); if(currentTimeMillis - parseLong > (1000 * 60 * 5)) { //Delete the available channels cached in redis. Because the channel priority changes, the available channels cached in redis need to be reloaded redisTemplate.delete("listForConnect"); }else{ //Indicates that the current instance status is normal ServerTopic serverTopic = ServerTopic.builder().option(ServerTopic.INIT_CONNECT).value(key.toString()).build(); // ServerTopic simpleEntityTopic = new ServerTopic(ServerTopic.INIT_CONNECT, key.toString()); //send message redisTemplate.convertAndSend("TOPIC_HIGH_SERVER",serverTopic.toString()); // redisTemplate.convertAndSend("TOPIC_HIGH_SERVER",simpleEntityTopic.toString()); return; } } } stay server The service side needs to receive manager Service messages First step statement spring assembly@Component realization MessageListener Interface rewrite onMessage Method will receive a message when there is a method call Take the message out to the object ServerTopic In the judgment object option What if it is init_connect We need to reinitialize the object We need to re execute Init method @Override public void onMessage(Message message, byte[] pattern) { //TODO message listening: call smsConnectLoader for channel initialization or channel update according to the message content //Deserialize the message body to get the json string String jsonMsg = redisTemplate.getDefaultSerializer().deserialize(message.getBody()).toString(); //Encapsulate the json string into a ServerTopic object ServerTopic serverTopic = JSON.parseObject(jsonMsg, ServerTopic.class); switch (serverTopic.getOption()){ case ServerTopic.INIT_CONNECT://Initialize channel smsConnectLoader.initConnect(); break; // case ServerTopic.USE_NEW_CONNECT: / / update channel // smsConnectLoader.changeNewConnect(); default: break; } }